From d431cc4f14b1ad4be5126f3c5ee4faf6e5338acc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ricardo=20Gro=C3=9F?= Date: Wed, 28 Oct 2026 19:08:07 +0100 Subject: [PATCH] chore: retire the prober for ValueRanges' oracle kit The prober cost more than it paid: a release's `prober seeds --all` built the probe harness once per seed, 170 seeds at about four minutes, and one change to the fault responses staled 23 seeds of five probes. The replay, the footprints, the ledger, the covering arrays and the gate carried the same weight. Removed: tools/Prober and its tests, the corpus under tools/probe/ with its 38 probes and 170 seeds, the probes and probe-status jobs of dotnet.yml, the replay and gate steps of release.yml, .claude/probe-axes.md, the probe-triage skill, the probe conventions tests, and DocsQuery's check of the facts the corpus cited. The verified facts and their fixtures stay. Added, after CodoMetis.ValueRanges (test/oracles/README.md): - claims.json: the prober's 169 obligations as 150 claims, one per distinct sentence, each quoted verbatim, 112 held by a named test class; - surface-waivers.json: every public member of the run-time libraries is claimed or waived; the documents never read for claims are waived as unread (#98); - oracles.json and seeds/: five oracles that already existed (JsonParityTests, NullDelegateTests, AsynchronousContextTests, NotWireTypeTests, KeyGenerationVariantTests), each with a seed; all five caught by `oracles seeds`, about ten seconds each; - candidates.json and axes.md; - test/CodoMetis.TypeKit.Oracles.Tests, which holds the catalogue in CI, and tools/Oracles, the local `seeds` and `status`. Decision 112 supersedes decisions 96, 99 to 104 and 108, decision 107 but for its fixture rule, and decision 97's gate. AGENTS.md, CONTRIBUTING.md and design/plan.md describe the kit. Stacked on #96 for the decision's number. Closes #97 Co-Authored-By: Claude Opus 5.5 --- .claude/probe-axes.md | 755 - .claude/settings.json | 2 +- .claude/skills/probe-triage/SKILL.md | 261 - .claude/skills/verify-the-guard/SKILL.md | 4 +- .github/pull_request_template.md | 2 +- .github/workflows/dotnet.yml | 137 - .github/workflows/release.yml | 40 +- AGENTS.md | 93 +- CONTRIBUTING.md | 18 +- CodoMetis.TypeKit.slnx | 4 +- ...ber-gives-way-to-valueranges-oracle-kit.md | 55 + design/adr/README.md | 21 +- design/finder-benchmark-2026-10-16.md | 2 +- design/plan.md | 1 + design/test-strategy.md | 2 +- .../GeneratedHarnessTests.cs | 113 - .../ProbeCiteTests.cs | 135 - .../ProbeMessageTextTests.cs | 148 - .../ProbeNoteKeyTests.cs | 94 - .../ProbeRowsTests.cs | 103 - .../ReleaseWiringTests.cs | 149 +- .../SessionStartHookTests.cs | 2 +- .../FactCitationTests.cs | 216 - .../RepositoryTests.cs | 19 - .../SpanParsingTests.cs | 2 +- .../ApiSurface.cs | 87 + .../CodoMetis.TypeKit.Oracles.Tests.csproj | 22 + .../OracleCandidateTests.cs | 41 + .../OracleCatalogue.cs | 187 + .../OracleCatalogueTests.cs | 118 + .../OracleSeedTests.cs | 94 + .../OracleSurfaceCoverageTests.cs | 59 + .../SeedVerdictTests.cs | 65 + .../ClaimsFileTests.cs | 159 - .../CodoMetis.TypeKit.Prober.Tests.csproj | 29 - .../CoveringTests.cs | 80 - .../DeclaredApplicabilityTests.cs | 439 - .../DotnetTests.cs | 73 - .../FixTests.cs | 90 - .../CodoMetis.TypeKit.Prober.Tests/Fixture.cs | 167 - .../FreshnessTests.cs | 27 - .../GapsTests.cs | 160 - .../HarnessErrorTests.cs | 141 - .../LedgerTests.cs | 350 - .../NewProbeTests.cs | 193 - .../ObligationsTests.cs | 228 - .../PerCheckTests.cs | 492 - .../RegisterTests.cs | 171 - .../ReplayTemplateTests.cs | 98 - .../ReplayTests.cs | 368 - .../RowsPerProbeTests.cs | 299 - .../SeedsTests.cs | 408 - .../SourceLintTests.cs | 231 - .../ToolchainTests.cs | 142 - .../TryProbeTests.cs | 265 - .../CodoMetis.TypeKit.Samples.csproj | 23 - test/oracles/Candidates.cs | 112 + test/oracles/README.md | 38 + test/oracles/SeedVerdict.cs | 41 + test/oracles/axes.md | 44 + test/oracles/candidates.json | 1 + test/oracles/claims.json | 2029 ++ test/oracles/oracles.json | 32 + .../seeds/json-key-written-by-to-string.patch | 4 + ...patch-traverse-takes-a-null-selector.patch | 17 + test/oracles/seeds/result-reads-as-json.patch | 16 + ...sync-resumes-on-the-caller-s-context.patch | 17 + .../seeds/ushort-key-not-generated.patch | 8 +- test/oracles/surface-waivers.json | 28 + tools/AgentTools.slnf | 4 +- tools/BuildVerdict/BuildVerdict.csproj | 2 +- tools/Directory.Build.props | 2 +- tools/DocsQuery/Citations.cs | 260 - tools/DocsQuery/Facts.cs | 24 +- tools/DocsQuery/Queries.cs | 10 +- tools/DocsQuery/README.md | 5 +- tools/Oracles/Oracles.csproj | 22 + tools/Oracles/Program.cs | 215 + tools/Prober/Axes.cs | 386 - tools/Prober/Commands.cs | 747 - tools/Prober/Context.cs | 80 - tools/Prober/Covering.cs | 93 - tools/Prober/Files.cs | 142 - tools/Prober/Ledger.cs | 431 - tools/Prober/NewProbe.cs | 166 - tools/Prober/ProbeAdapter.cs | 116 - tools/Prober/ProbeRun.cs | 107 - tools/Prober/Prober.csproj | 37 - tools/Prober/Program.cs | 135 - tools/Prober/README.md | 181 - tools/Prober/ReleaseGate.cs | 100 - tools/Prober/Replay.cs | 440 - tools/Prober/Round.cs | 123 - tools/Prober/RoundLock.cs | 133 - tools/Prober/SchemaCheck.cs | 88 - tools/Prober/Schemas/claims.schema.json | 189 - .../Prober/Schemas/dismissed-line.schema.json | 27 - tools/Prober/Schemas/evidence.schema.json | 170 - tools/Prober/Schemas/finding-line.schema.json | 44 - .../Prober/Schemas/footprint-line.schema.json | 60 - tools/Prober/Schemas/ledger-line.schema.json | 44 - tools/Prober/Schemas/lock.schema.json | 53 - tools/Prober/Schemas/registry.schema.json | 143 - tools/Prober/Schemas/round.schema.json | 40 - tools/Prober/Schemas/rows.schema.json | 52 - tools/Prober/Schemas/run-line.schema.json | 43 - tools/Prober/Schemas/seeds-run.schema.json | 131 - tools/Prober/Schemas/universe.schema.json | 120 - tools/Prober/Schemas/waivers.schema.json | 36 - tools/Prober/Seeds.cs | 396 - tools/Prober/SourceLint.cs | 277 - tools/Prober/TryProbe.cs | 151 - tools/probe/corpus/claims.json | 16178 ---------------- tools/probe/corpus/dismissed.jsonl | 1 - tools/probe/corpus/harness/BaseProbe.cs | 11 - tools/probe/corpus/harness/BuildConsumer.cs | 125 - tools/probe/corpus/harness/BuildDecls.cs | 91 - tools/probe/corpus/harness/BuildRefusals.cs | 280 - tools/probe/corpus/harness/BuildRoslyn.cs | 167 - tools/probe/corpus/harness/BuildSource.cs | 58 - tools/probe/corpus/harness/C09Shapes.g.cs | 1031 - tools/probe/corpus/harness/C11Shapes.g.cs | 2596 --- tools/probe/corpus/harness/Cmp.cs | 128 - tools/probe/corpus/harness/ComposedProbes.cs | 228 - tools/probe/corpus/harness/Containers.g.cs | 25 - tools/probe/corpus/harness/DecimalSpelling.cs | 230 - tools/probe/corpus/harness/EfExtra.cs | 48 - tools/probe/corpus/harness/EfExtra2.cs | 82 - tools/probe/corpus/harness/EfHarness.cs | 191 - tools/probe/corpus/harness/EfShapes.g.cs | 962 - tools/probe/corpus/harness/EfShapes3.cs | 26 - tools/probe/corpus/harness/EfShapes3.g.cs | 155 - tools/probe/corpus/harness/EfShapes4.g.cs | 443 - tools/probe/corpus/harness/Equality.cs | 211 - tools/probe/corpus/harness/FaultClaim.cs | 412 - .../corpus/harness/FaultClaimControllers.g.cs | 206 - tools/probe/corpus/harness/FaultClaimSets.cs | 70 - .../probe/corpus/harness/FaultControllers.cs | 49 - tools/probe/corpus/harness/Faults.cs | 106 - tools/probe/corpus/harness/HandOrdered.cs | 28 - tools/probe/corpus/harness/HttpBaseline.cs | 800 - tools/probe/corpus/harness/HttpHost.cs | 438 - tools/probe/corpus/harness/JsonFamily.cs | 89 - .../corpus/harness/JsonFamilyShapes.g.cs | 493 - tools/probe/corpus/harness/JsonRow.cs | 183 - tools/probe/corpus/harness/KeyShapes.g.cs | 128 - .../probe/corpus/harness/L01JsonBytesProbe.cs | 21 - tools/probe/corpus/harness/L02NoQuoteProbe.cs | 10 - tools/probe/corpus/harness/L03OneRuleProbe.cs | 10 - .../probe/corpus/harness/L04RoundTripProbe.cs | 10 - tools/probe/corpus/harness/L05Shapes.g.cs | 575 - tools/probe/corpus/harness/OffTheWire.cs | 221 - tools/probe/corpus/harness/Positions.cs | 328 - tools/probe/corpus/harness/PreserveGuard.cs | 252 - tools/probe/corpus/harness/Registry.cs | 27 - tools/probe/corpus/harness/ReplayProbe.cs | 14 - tools/probe/corpus/harness/Row.cs | 592 - .../probe/corpus/harness/RuleControllers.g.cs | 32 - tools/probe/corpus/harness/SchemaClaim.cs | 720 - tools/probe/corpus/harness/Shapes.g.cs | 9559 --------- tools/probe/corpus/harness/ShrinkProbe.cs | 33 - tools/probe/corpus/harness/Sides.cs | 317 - tools/probe/corpus/harness/TextRow.cs | 664 - tools/probe/corpus/harness/TextShrink.cs | 14 - .../corpus/probes/C01DecimalSpellingProbe.cs | 12 - .../corpus/probes/C02OptionalTextProbe.cs | 75 - .../corpus/probes/C04WithoutFaultsProbe.cs | 68 - .../probe/corpus/probes/C06FaultKindsProbe.cs | 53 - .../corpus/probes/C07BlanketMappingProbe.cs | 84 - .../corpus/probes/C08SameNameWarningProbe.cs | 10 - .../probes/C09BaseRecordEqualityProbe.cs | 119 - .../corpus/probes/C10PreserveGuardProbe.cs | 12 - .../corpus/probes/C11ParseShapesBuildProbe.cs | 197 - .../probes/C12DescribedConverterProbe.cs | 10 - .../corpus/probes/C13PatchRequiredProbe.cs | 10 - .../corpus/probes/C14DeclaredProblemProbe.cs | 42 - .../probes/C15MvcFaultStatusDocProbe.cs | 51 - .../corpus/probes/C16OptionsAgreementProbe.cs | 10 - tools/probe/corpus/probes/C17Cmtk0014Probe.cs | 261 - tools/probe/corpus/probes/C18Cmtk1023Probe.cs | 245 - .../corpus/probes/C19Cmtk0010KindsProbe.cs | 504 - .../probe/corpus/probes/C20ShouldHoldProbe.cs | 182 - .../probes/C21OpenapiSchemaParityProbe.cs | 41 - .../probes/C22GeneratorRefusalsProbe.cs | 103 - .../corpus/probes/E01ColumnParityProbe.cs | 93 - .../corpus/probes/E02ValueTranslatesProbe.cs | 142 - .../corpus/probes/E03ColumnRoundTripProbe.cs | 82 - .../probe/corpus/probes/E04IntegerKeyProbe.cs | 75 - .../corpus/probes/E05OwnCompareToProbe.cs | 127 - .../probes/E06OwnConverterNoValueProbe.cs | 76 - .../corpus/probes/E07ClosedSetNameProbe.cs | 129 - .../probes/E08OptionResultNotColumnsProbe.cs | 76 - .../probe/corpus/probes/E09OrNoneInDbProbe.cs | 179 - .../probes/E10ReadBackSkipsValidationProbe.cs | 120 - .../corpus/probes/E11UseTypeKitOrderProbe.cs | 118 - .../corpus/probes/E12ConventionByTypeProbe.cs | 71 - .../probes/E13PrimitiveCollectionProbe.cs | 89 - .../corpus/probes/E14InheritanceKeyProbe.cs | 95 - .../corpus/probes/E15CompositeKeyProbe.cs | 95 - .../probes/E16OwnConfigurationWinsProbe.cs | 169 - tools/probe/corpus/probes/L05EqualityProbe.cs | 12 - .../probe/corpus/probes/L06OffTheWireProbe.cs | 12 - tools/probe/corpus/registry.json | 1340 -- tools/probe/corpus/rows/BaseProbe.json.gz | Bin 26510 -> 0 bytes .../rows/C01DecimalSpellingProbe.json.gz | Bin 4249 -> 0 bytes .../corpus/rows/C02OptionalTextProbe.json.gz | Bin 532 -> 0 bytes .../corpus/rows/C03ComposedProbe.json.gz | Bin 40196 -> 0 bytes .../corpus/rows/C04WithoutFaultsProbe.json.gz | Bin 19640 -> 0 bytes tools/probe/corpus/rows/C05RuleProbe.json.gz | Bin 40208 -> 0 bytes .../corpus/rows/C06FaultKindsProbe.json.gz | Bin 20115 -> 0 bytes .../rows/C07BlanketMappingProbe.json.gz | Bin 26510 -> 0 bytes .../rows/C08SameNameWarningProbe.json.gz | Bin 19919 -> 0 bytes .../rows/C09BaseRecordEqualityProbe.json.gz | Bin 1381 -> 0 bytes .../corpus/rows/C10PreserveGuardProbe.json.gz | Bin 10628 -> 0 bytes .../rows/C11ParseShapesBuildProbe.json.gz | Bin 3317 -> 0 bytes .../rows/C12DescribedConverterProbe.json.gz | Bin 28571 -> 0 bytes .../corpus/rows/C13PatchRequiredProbe.json.gz | Bin 20506 -> 0 bytes .../rows/C14DeclaredProblemProbe.json.gz | Bin 26293 -> 0 bytes .../rows/C15MvcFaultStatusDocProbe.json.gz | Bin 25960 -> 0 bytes .../rows/C16OptionsAgreementProbe.json.gz | Bin 19385 -> 0 bytes .../corpus/rows/C17Cmtk0014Probe.json.gz | Bin 3915 -> 0 bytes .../corpus/rows/C18Cmtk1023Probe.json.gz | Bin 3348 -> 0 bytes .../corpus/rows/C19Cmtk0010KindsProbe.json.gz | Bin 4370 -> 0 bytes .../corpus/rows/C20ShouldHoldProbe.json.gz | Bin 1869 -> 0 bytes .../rows/C21OpenapiSchemaParityProbe.json.gz | Bin 26676 -> 0 bytes .../rows/C22GeneratorRefusalsProbe.json.gz | Bin 1563 -> 0 bytes .../corpus/rows/E01ColumnParityProbe.json.gz | Bin 1439 -> 0 bytes .../rows/E02ValueTranslatesProbe.json.gz | Bin 1675 -> 0 bytes .../rows/E03ColumnRoundTripProbe.json.gz | Bin 1224 -> 0 bytes .../corpus/rows/E04IntegerKeyProbe.json.gz | Bin 324 -> 0 bytes .../corpus/rows/E05OwnCompareToProbe.json.gz | Bin 493 -> 0 bytes .../rows/E06OwnConverterNoValueProbe.json.gz | Bin 237 -> 0 bytes .../corpus/rows/E07ClosedSetNameProbe.json.gz | Bin 193 -> 0 bytes .../E08OptionResultNotColumnsProbe.json.gz | Bin 267 -> 0 bytes .../corpus/rows/E09OrNoneInDbProbe.json.gz | Bin 317 -> 0 bytes .../E10ReadBackSkipsValidationProbe.json.gz | Bin 309 -> 0 bytes .../rows/E11UseTypeKitOrderProbe.json.gz | Bin 193 -> 0 bytes .../rows/E12ConventionByTypeProbe.json.gz | Bin 235 -> 0 bytes .../rows/E13PrimitiveCollectionProbe.json.gz | Bin 181 -> 0 bytes .../rows/E14InheritanceKeyProbe.json.gz | Bin 156 -> 0 bytes .../corpus/rows/E15CompositeKeyProbe.json.gz | Bin 158 -> 0 bytes .../rows/E16OwnConfigurationWinsProbe.json.gz | Bin 275 -> 0 bytes .../corpus/rows/L01JsonBytesProbe.json.gz | Bin 18612 -> 0 bytes .../probe/corpus/rows/L02NoQuoteProbe.json.gz | Bin 14093 -> 0 bytes .../probe/corpus/rows/L03OneRuleProbe.json.gz | Bin 21960 -> 0 bytes .../corpus/rows/L04RoundTripProbe.json.gz | Bin 11466 -> 0 bytes .../corpus/rows/L05EqualityProbe.json.gz | Bin 2964 -> 0 bytes .../corpus/rows/L06OffTheWireProbe.json.gz | Bin 12248 -> 0 bytes ...ecimal-spelling-covers-every-decimal.patch | 22 - ...al-spelling-keeps-the-scale-it-reads.patch | 22 - ...-decimal-spelling-number-is-a-number.patch | 13 - ...-spelling-reads-a-number-or-a-string.patch | 13 - .../C01-decimal-spelling-smallest-scale.patch | 13 - ...-decimal-spelling-string-is-a-string.patch | 13 - ...onal-text-hidden-is-an-error-in-both.patch | 13 - .../C02-optional-text-null-is-blank.patch | 12 - ...onal-text-optional-blank-is-no-value.patch | 13 - ...onal-text-required-blank-is-an-error.patch | 12 - .../corpus/seeds/C03-composed-faults.patch | 13 - .../seeds/C03-detail-and-rule-intact.patch | 13 - .../corpus/seeds/C03-each-error-own-set.patch | 35 - .../seeds/C03-shared-status-types.patch | 14 - .../seeds/C03-unmapped-set-stops-start.patch | 13 - .../C04-left-out-everywhere-stops-start.patch | 13 - .../seeds/C04-narrows-wider-status.patch | 13 - .../seeds/C04-reporting-left-out-throws.patch | 13 - .../corpus/seeds/C04-without-faults.patch | 13 - .../probe/corpus/seeds/C05-no-namespace.patch | 13 - .../corpus/seeds/C05-rule-qualified.patch | 13 - .../corpus/seeds/C05-stays-the-errors.patch | 13 - .../seeds/C05-unnamed-value-throws.patch | 13 - tools/probe/corpus/seeds/C05-with-rule.patch | 13 - .../seeds/C06-description-as-detail.patch | 13 - .../probe/corpus/seeds/C06-fault-kinds.patch | 13 - .../seeds/C06-member-must-have-kind.patch | 13 - .../seeds/C06-no-description-no-detail.patch | 13 - .../corpus/seeds/C07-blanket-mapping.patch | 15 - tools/probe/corpus/seeds/C07-by-kind.patch | 13 - .../seeds/C07-enum-needs-own-mapping.patch | 13 - .../seeds/C07-fault-responses-blanket.patch | 19 - .../corpus/seeds/C07-mapping-refused.patch | 27 - .../seeds/C07-member-without-kind.patch | 13 - .../probe/corpus/seeds/C07-own-mapping.patch | 13 - .../seeds/C08-leave-one-out-fixes.patch | 13 - .../seeds/C08-qualified-chain-warns.patch | 13 - .../seeds/C08-qualified-names-fix.patch | 13 - .../corpus/seeds/C08-same-name-warning.patch | 13 - .../corpus/seeds/C08-warning-names-both.patch | 13 - ...-equality-equal-by-the-wrapped-value.patch | 13 - ...equality-in-a-collection-of-the-base.patch | 13 - ...serve-guard-ignore-cycles-unaffected.patch | 13 - ...erve-guard-own-converters-unaffected.patch | 13 - ...ed-where-references-could-be-written.patch | 13 - ...ve-guard-value-converters-unaffected.patch | 22 - ...es-build-abstract-string-constructor.patch | 13 - ...1-parse-shapes-build-implicit-result.patch | 13 - ...build-parsing-where-it-can-be-parsed.patch | 12 - .../C11-parse-shapes-build-ref-text.patch | 13 - .../seeds/C12-described-converter-own.patch | 13 - .../seeds/C12-described-converter.patch | 13 - .../seeds/C12-options-descriptions.patch | 13 - .../seeds/C12-parameter-keeps-aspnet.patch | 13 - .../corpus/seeds/C13-absent-is-default.patch | 20 - .../seeds/C13-doc-optional-where-absent.patch | 18 - .../corpus/seeds/C13-patch-required.patch | 13 - .../seeds/C14-application-json-declared.patch | 13 - .../seeds/C14-ascending-with-declared.patch | 13 - .../seeds/C14-declared-describes-fault.patch | 13 - .../corpus/seeds/C14-declared-problem.patch | 14 - .../seeds/C15-mvc-fault-status-doc.patch | 13 - .../corpus/seeds/C15-status-per-scope.patch | 13 - .../corpus/seeds/C15-without-faults-doc.patch | 13 - .../seeds/C16-error-names-descriptions.patch | 13 - .../seeds/C16-minimal-host-builds.patch | 13 - .../corpus/seeds/C16-options-agreement.patch | 13 - .../corpus/seeds/C16-patch-held-refused.patch | 12 - .../seeds/C16-same-converters-build.patch | 13 - ...C17-cmtk0014-a-cast-naming-no-member.patch | 13 - .../C17-cmtk0014-a-member-is-silent.patch | 13 - ...tk0014-constructor-accessibility-too.patch | 12 - .../seeds/C17-cmtk0014-default-and-zero.patch | 13 - .../C17-cmtk0014-reported-where-written.patch | 13 - ...C18-cmtk1023-a-warning-and-generated.patch | 11 - ...erated-equality-never-calls-the-base.patch | 13 - .../C18-cmtk1023-names-each-base-member.patch | 13 - ...ds-a-pair-once-at-the-later-argument.patch | 13 - ...inds-flags-enum-and-read-only-struct.patch | 13 - ...mtk0010-kinds-interface-and-unsealed.patch | 13 - ...010-kinds-methods-the-compiler-calls.patch | 12 - ...10-kinds-value-object-and-closed-set.patch | 13 - ...houldhold-a-failure-says-the-outcome.patch | 12 - ...ld-fails-unless-it-holds-that-reason.patch | 13 - ...houldhold-hands-back-detail-and-rule.patch | 13 - ...sting-shouldhold-uninitialized-fails.patch | 13 - ...ng-shouldhold-whichever-of-its-types.patch | 22 - ...api-schema-parity-container-elements.patch | 13 - ...openapi-schema-parity-dictionary-key.patch | 17 - ...schema-parity-every-document-version.patch | 12 - ...able-admits-null-as-the-wrapped-does.patch | 28 - ...a-parity-the-underlying-types-schema.patch | 12 - .../C22-generator-refusals-abstract.patch | 16 - ...closed-set-named-as-generated-member.patch | 12 - ...generator-refusals-equality-operator.patch | 15 - ...refusals-every-error-links-the-table.patch | 13 - ...2-generator-refusals-generic-wrapped.patch | 17 - ...r-refusals-named-as-generated-member.patch | 16 - ...ator-refusals-no-type-half-generated.patch | 13 - .../C22-generator-refusals-over-object.patch | 17 - ...-generator-refusals-over-system-enum.patch | 17 - ...generator-refusals-over-value-object.patch | 13 - ...rator-refusals-unsealed-record-class.patch | 17 - .../seeds/E01-column-parity-column-type.patch | 13 - .../seeds/E01-column-parity-facet-kept.patch | 23 - .../seeds/E01-column-parity-nullable.patch | 17 - ...translates-to-column-column-not-cast.patch | 15 - ...value-translates-to-column-get-value.patch | 13 - ...e-translates-to-column-value-or-null.patch | 13 - ...E02-value-translates-to-column-value.patch | 13 - .../seeds/E03-column-round-trip-keys.patch | 12 - ...olumn-round-trip-nullable-properties.patch | 17 - ...lumn-round-trip-stored-as-underlying.patch | 13 - ...ger-key-generated-byte-not-generated.patch | 13 - ...integer-key-generated-char-key-yours.patch | 13 - ...teger-key-generated-generated-on-add.patch | 13 - ...integer-key-generated-guid-key-yours.patch | 13 - ...key-generated-own-configuration-wins.patch | 18 - ...05-own-compareto-rejected-comparison.patch | 22 - ...-compareto-rejected-equality-allowed.patch | 13 - .../E05-own-compareto-rejected-min-max.patch | 12 - .../E05-own-compareto-rejected-ordering.patch | 15 - ...-own-compareto-rejected-query-filter.patch | 13 - ...mpareto-rejected-value-order-allowed.patch | 12 - ...verter-no-value-value-not-translated.patch | 13 - ...ter-no-value-value-object-comparison.patch | 28 - ...ored-as-name-in-collections-and-json.patch | 15 - ...set-stored-as-name-non-member-throws.patch | 17 - ...ed-set-stored-as-name-stored-as-name.patch | 13 - ...t-stored-as-name-unknown-name-throws.patch | 11 - ...ion-result-not-columns-error-says-so.patch | 13 - ...ption-result-not-columns-model-fails.patch | 20 - ...or-none-in-database-element-at-skips.patch | 13 - ...none-in-database-first-reads-one-row.patch | 13 - ...none-in-database-single-asks-for-two.patch | 13 - ...kips-validation-reads-without-create.patch | 20 - ...ck-skips-validation-revalidate-finds.patch | 13 - ...etypekit-order-order-does-not-matter.patch | 17 - ...-type-underlying-type-does-not-reach.patch | 35 - ...by-vo-type-value-object-type-reaches.patch | 22 - .../E13-primitive-collections-contains.patch | 22 - ...-primitive-collections-element-value.patch | 13 - ...-key-generation-tpc-assign-on-sqlite.patch | 14 - ...eritance-key-generation-tph-tpt-root.patch | 12 - .../E15-composite-key-as-for-primitives.patch | 13 - ...6-own-configuration-wins-column-type.patch | 17 - ...16-own-configuration-wins-conversion.patch | 17 - .../E16-own-configuration-wins-never.patch | 13 - .../E16-own-configuration-wins-on-add.patch | 18 - .../E16-own-configuration-wins-optional.patch | 17 - ...es-read-as-the-underlying-type-reads.patch | 12 - ...json-bytes-the-underlying-types-json.patch | 13 - .../L02-no-quote-json-never-repeats.patch | 22 - .../L02-no-quote-no-inner-that-quotes.patch | 13 - .../L02-no-quote-parsing-never-repeats.patch | 32 - .../seeds/L03-one-rule-json-runs-create.patch | 13 - .../L03-one-rule-parsing-runs-create.patch | 15 - ...L03-one-rule-refusals-name-the-fault.patch | 33 - .../L03-one-rule-tryfrom-runs-create.patch | 13 - ...-trip-invariant-whatever-the-culture.patch | 13 - ...rip-json-refusal-is-a-json-exception.patch | 13 - .../L04-round-trip-text-reads-back.patch | 13 - ...p-the-type-converter-writes-the-same.patch | 13 - ...ty-equality-is-the-underlying-values.patch | 13 - ...ordering-follows-the-underlying-type.patch | 13 - .../L05-equality-through-every-base.patch | 13 - .../seeds/L06-off-the-wire-never-read.patch | 21 - .../L06-off-the-wire-never-written.patch | 18 - tools/probe/enumerate.cs | 56 - tools/probe/generators/Directory.Build.props | 8 - .../probe/generators/Directory.Build.targets | 3 - tools/probe/generators/README.md | 85 - tools/probe/generators/containers.cs | 78 - tools/probe/generators/decls.cs | 261 - tools/probe/generators/ef.cs | 266 - tools/probe/generators/faults.cs | 139 - tools/probe/generators/json-family.cs | 220 - tools/probe/generators/rules.cs | 69 - tools/probe/generators/shapes.cs | 276 - 427 files changed, 3422 insertions(+), 61490 deletions(-) delete mode 100644 .claude/probe-axes.md delete mode 100644 .claude/skills/probe-triage/SKILL.md create mode 100644 design/adr/0112-the-prober-gives-way-to-valueranges-oracle-kit.md delete mode 100644 test/CodoMetis.TypeKit.Conventions.Tests/GeneratedHarnessTests.cs delete mode 100644 test/CodoMetis.TypeKit.Conventions.Tests/ProbeCiteTests.cs delete mode 100644 test/CodoMetis.TypeKit.Conventions.Tests/ProbeMessageTextTests.cs delete mode 100644 test/CodoMetis.TypeKit.Conventions.Tests/ProbeNoteKeyTests.cs delete mode 100644 test/CodoMetis.TypeKit.Conventions.Tests/ProbeRowsTests.cs delete mode 100644 test/CodoMetis.TypeKit.DocsQuery.Tests/FactCitationTests.cs create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/ApiSurface.cs create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/CodoMetis.TypeKit.Oracles.Tests.csproj create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/OracleCandidateTests.cs create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogue.cs create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogueTests.cs create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/OracleSeedTests.cs create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/OracleSurfaceCoverageTests.cs create mode 100644 test/CodoMetis.TypeKit.Oracles.Tests/SeedVerdictTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/ClaimsFileTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/CodoMetis.TypeKit.Prober.Tests.csproj delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/CoveringTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/DeclaredApplicabilityTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/DotnetTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/FixTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/Fixture.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/FreshnessTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/GapsTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/HarnessErrorTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/LedgerTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/NewProbeTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/ObligationsTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/PerCheckTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/RegisterTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/ReplayTemplateTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/ReplayTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/RowsPerProbeTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/SeedsTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/SourceLintTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/ToolchainTests.cs delete mode 100644 test/CodoMetis.TypeKit.Prober.Tests/TryProbeTests.cs create mode 100644 test/oracles/Candidates.cs create mode 100644 test/oracles/README.md create mode 100644 test/oracles/SeedVerdict.cs create mode 100644 test/oracles/axes.md create mode 100644 test/oracles/candidates.json create mode 100644 test/oracles/claims.json create mode 100644 test/oracles/oracles.json rename tools/probe/corpus/seeds/L01-json-bytes-as-a-dictionary-key-too.patch => test/oracles/seeds/json-key-written-by-to-string.patch (77%) create mode 100644 test/oracles/seeds/patch-traverse-takes-a-null-selector.patch create mode 100644 test/oracles/seeds/result-reads-as-json.patch create mode 100644 test/oracles/seeds/traverse-async-resumes-on-the-caller-s-context.patch rename tools/probe/corpus/seeds/E04-integer-key-generated-not-on-efs-list.patch => test/oracles/seeds/ushort-key-not-generated.patch (74%) create mode 100644 test/oracles/surface-waivers.json delete mode 100644 tools/DocsQuery/Citations.cs create mode 100644 tools/Oracles/Oracles.csproj create mode 100644 tools/Oracles/Program.cs delete mode 100644 tools/Prober/Axes.cs delete mode 100644 tools/Prober/Commands.cs delete mode 100644 tools/Prober/Context.cs delete mode 100644 tools/Prober/Covering.cs delete mode 100644 tools/Prober/Files.cs delete mode 100644 tools/Prober/Ledger.cs delete mode 100644 tools/Prober/NewProbe.cs delete mode 100644 tools/Prober/ProbeAdapter.cs delete mode 100644 tools/Prober/ProbeRun.cs delete mode 100644 tools/Prober/Prober.csproj delete mode 100644 tools/Prober/Program.cs delete mode 100644 tools/Prober/README.md delete mode 100644 tools/Prober/ReleaseGate.cs delete mode 100644 tools/Prober/Replay.cs delete mode 100644 tools/Prober/Round.cs delete mode 100644 tools/Prober/RoundLock.cs delete mode 100644 tools/Prober/SchemaCheck.cs delete mode 100644 tools/Prober/Schemas/claims.schema.json delete mode 100644 tools/Prober/Schemas/dismissed-line.schema.json delete mode 100644 tools/Prober/Schemas/evidence.schema.json delete mode 100644 tools/Prober/Schemas/finding-line.schema.json delete mode 100644 tools/Prober/Schemas/footprint-line.schema.json delete mode 100644 tools/Prober/Schemas/ledger-line.schema.json delete mode 100644 tools/Prober/Schemas/lock.schema.json delete mode 100644 tools/Prober/Schemas/registry.schema.json delete mode 100644 tools/Prober/Schemas/round.schema.json delete mode 100644 tools/Prober/Schemas/rows.schema.json delete mode 100644 tools/Prober/Schemas/run-line.schema.json delete mode 100644 tools/Prober/Schemas/seeds-run.schema.json delete mode 100644 tools/Prober/Schemas/universe.schema.json delete mode 100644 tools/Prober/Schemas/waivers.schema.json delete mode 100644 tools/Prober/Seeds.cs delete mode 100644 tools/Prober/SourceLint.cs delete mode 100644 tools/Prober/TryProbe.cs delete mode 100644 tools/probe/corpus/claims.json delete mode 100644 tools/probe/corpus/dismissed.jsonl delete mode 100644 tools/probe/corpus/harness/BaseProbe.cs delete mode 100644 tools/probe/corpus/harness/BuildConsumer.cs delete mode 100644 tools/probe/corpus/harness/BuildDecls.cs delete mode 100644 tools/probe/corpus/harness/BuildRefusals.cs delete mode 100644 tools/probe/corpus/harness/BuildRoslyn.cs delete mode 100644 tools/probe/corpus/harness/BuildSource.cs delete mode 100644 tools/probe/corpus/harness/C09Shapes.g.cs delete mode 100644 tools/probe/corpus/harness/C11Shapes.g.cs delete mode 100644 tools/probe/corpus/harness/Cmp.cs delete mode 100644 tools/probe/corpus/harness/ComposedProbes.cs delete mode 100644 tools/probe/corpus/harness/Containers.g.cs delete mode 100644 tools/probe/corpus/harness/DecimalSpelling.cs delete mode 100644 tools/probe/corpus/harness/EfExtra.cs delete mode 100644 tools/probe/corpus/harness/EfExtra2.cs delete mode 100644 tools/probe/corpus/harness/EfHarness.cs delete mode 100644 tools/probe/corpus/harness/EfShapes.g.cs delete mode 100644 tools/probe/corpus/harness/EfShapes3.cs delete mode 100644 tools/probe/corpus/harness/EfShapes3.g.cs delete mode 100644 tools/probe/corpus/harness/EfShapes4.g.cs delete mode 100644 tools/probe/corpus/harness/Equality.cs delete mode 100644 tools/probe/corpus/harness/FaultClaim.cs delete mode 100644 tools/probe/corpus/harness/FaultClaimControllers.g.cs delete mode 100644 tools/probe/corpus/harness/FaultClaimSets.cs delete mode 100644 tools/probe/corpus/harness/FaultControllers.cs delete mode 100644 tools/probe/corpus/harness/Faults.cs delete mode 100644 tools/probe/corpus/harness/HandOrdered.cs delete mode 100644 tools/probe/corpus/harness/HttpBaseline.cs delete mode 100644 tools/probe/corpus/harness/HttpHost.cs delete mode 100644 tools/probe/corpus/harness/JsonFamily.cs delete mode 100644 tools/probe/corpus/harness/JsonFamilyShapes.g.cs delete mode 100644 tools/probe/corpus/harness/JsonRow.cs delete mode 100644 tools/probe/corpus/harness/KeyShapes.g.cs delete mode 100644 tools/probe/corpus/harness/L01JsonBytesProbe.cs delete mode 100644 tools/probe/corpus/harness/L02NoQuoteProbe.cs delete mode 100644 tools/probe/corpus/harness/L03OneRuleProbe.cs delete mode 100644 tools/probe/corpus/harness/L04RoundTripProbe.cs delete mode 100644 tools/probe/corpus/harness/L05Shapes.g.cs delete mode 100644 tools/probe/corpus/harness/OffTheWire.cs delete mode 100644 tools/probe/corpus/harness/Positions.cs delete mode 100644 tools/probe/corpus/harness/PreserveGuard.cs delete mode 100644 tools/probe/corpus/harness/Registry.cs delete mode 100644 tools/probe/corpus/harness/ReplayProbe.cs delete mode 100644 tools/probe/corpus/harness/Row.cs delete mode 100644 tools/probe/corpus/harness/RuleControllers.g.cs delete mode 100644 tools/probe/corpus/harness/SchemaClaim.cs delete mode 100644 tools/probe/corpus/harness/Shapes.g.cs delete mode 100644 tools/probe/corpus/harness/ShrinkProbe.cs delete mode 100644 tools/probe/corpus/harness/Sides.cs delete mode 100644 tools/probe/corpus/harness/TextRow.cs delete mode 100644 tools/probe/corpus/harness/TextShrink.cs delete mode 100644 tools/probe/corpus/probes/C01DecimalSpellingProbe.cs delete mode 100644 tools/probe/corpus/probes/C02OptionalTextProbe.cs delete mode 100644 tools/probe/corpus/probes/C04WithoutFaultsProbe.cs delete mode 100644 tools/probe/corpus/probes/C06FaultKindsProbe.cs delete mode 100644 tools/probe/corpus/probes/C07BlanketMappingProbe.cs delete mode 100644 tools/probe/corpus/probes/C08SameNameWarningProbe.cs delete mode 100644 tools/probe/corpus/probes/C09BaseRecordEqualityProbe.cs delete mode 100644 tools/probe/corpus/probes/C10PreserveGuardProbe.cs delete mode 100644 tools/probe/corpus/probes/C11ParseShapesBuildProbe.cs delete mode 100644 tools/probe/corpus/probes/C12DescribedConverterProbe.cs delete mode 100644 tools/probe/corpus/probes/C13PatchRequiredProbe.cs delete mode 100644 tools/probe/corpus/probes/C14DeclaredProblemProbe.cs delete mode 100644 tools/probe/corpus/probes/C15MvcFaultStatusDocProbe.cs delete mode 100644 tools/probe/corpus/probes/C16OptionsAgreementProbe.cs delete mode 100644 tools/probe/corpus/probes/C17Cmtk0014Probe.cs delete mode 100644 tools/probe/corpus/probes/C18Cmtk1023Probe.cs delete mode 100644 tools/probe/corpus/probes/C19Cmtk0010KindsProbe.cs delete mode 100644 tools/probe/corpus/probes/C20ShouldHoldProbe.cs delete mode 100644 tools/probe/corpus/probes/C21OpenapiSchemaParityProbe.cs delete mode 100644 tools/probe/corpus/probes/C22GeneratorRefusalsProbe.cs delete mode 100644 tools/probe/corpus/probes/E01ColumnParityProbe.cs delete mode 100644 tools/probe/corpus/probes/E02ValueTranslatesProbe.cs delete mode 100644 tools/probe/corpus/probes/E03ColumnRoundTripProbe.cs delete mode 100644 tools/probe/corpus/probes/E04IntegerKeyProbe.cs delete mode 100644 tools/probe/corpus/probes/E05OwnCompareToProbe.cs delete mode 100644 tools/probe/corpus/probes/E06OwnConverterNoValueProbe.cs delete mode 100644 tools/probe/corpus/probes/E07ClosedSetNameProbe.cs delete mode 100644 tools/probe/corpus/probes/E08OptionResultNotColumnsProbe.cs delete mode 100644 tools/probe/corpus/probes/E09OrNoneInDbProbe.cs delete mode 100644 tools/probe/corpus/probes/E10ReadBackSkipsValidationProbe.cs delete mode 100644 tools/probe/corpus/probes/E11UseTypeKitOrderProbe.cs delete mode 100644 tools/probe/corpus/probes/E12ConventionByTypeProbe.cs delete mode 100644 tools/probe/corpus/probes/E13PrimitiveCollectionProbe.cs delete mode 100644 tools/probe/corpus/probes/E14InheritanceKeyProbe.cs delete mode 100644 tools/probe/corpus/probes/E15CompositeKeyProbe.cs delete mode 100644 tools/probe/corpus/probes/E16OwnConfigurationWinsProbe.cs delete mode 100644 tools/probe/corpus/probes/L05EqualityProbe.cs delete mode 100644 tools/probe/corpus/probes/L06OffTheWireProbe.cs delete mode 100644 tools/probe/corpus/registry.json delete mode 100644 tools/probe/corpus/rows/BaseProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C01DecimalSpellingProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C02OptionalTextProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C03ComposedProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C04WithoutFaultsProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C05RuleProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C06FaultKindsProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C07BlanketMappingProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C08SameNameWarningProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C09BaseRecordEqualityProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C10PreserveGuardProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C11ParseShapesBuildProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C12DescribedConverterProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C13PatchRequiredProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C14DeclaredProblemProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C15MvcFaultStatusDocProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C16OptionsAgreementProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C17Cmtk0014Probe.json.gz delete mode 100644 tools/probe/corpus/rows/C18Cmtk1023Probe.json.gz delete mode 100644 tools/probe/corpus/rows/C19Cmtk0010KindsProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C20ShouldHoldProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C21OpenapiSchemaParityProbe.json.gz delete mode 100644 tools/probe/corpus/rows/C22GeneratorRefusalsProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E01ColumnParityProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E02ValueTranslatesProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E03ColumnRoundTripProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E04IntegerKeyProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E05OwnCompareToProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E06OwnConverterNoValueProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E07ClosedSetNameProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E08OptionResultNotColumnsProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E09OrNoneInDbProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E10ReadBackSkipsValidationProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E11UseTypeKitOrderProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E12ConventionByTypeProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E13PrimitiveCollectionProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E14InheritanceKeyProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E15CompositeKeyProbe.json.gz delete mode 100644 tools/probe/corpus/rows/E16OwnConfigurationWinsProbe.json.gz delete mode 100644 tools/probe/corpus/rows/L01JsonBytesProbe.json.gz delete mode 100644 tools/probe/corpus/rows/L02NoQuoteProbe.json.gz delete mode 100644 tools/probe/corpus/rows/L03OneRuleProbe.json.gz delete mode 100644 tools/probe/corpus/rows/L04RoundTripProbe.json.gz delete mode 100644 tools/probe/corpus/rows/L05EqualityProbe.json.gz delete mode 100644 tools/probe/corpus/rows/L06OffTheWireProbe.json.gz delete mode 100644 tools/probe/corpus/seeds/C01-decimal-spelling-covers-every-decimal.patch delete mode 100644 tools/probe/corpus/seeds/C01-decimal-spelling-keeps-the-scale-it-reads.patch delete mode 100644 tools/probe/corpus/seeds/C01-decimal-spelling-number-is-a-number.patch delete mode 100644 tools/probe/corpus/seeds/C01-decimal-spelling-reads-a-number-or-a-string.patch delete mode 100644 tools/probe/corpus/seeds/C01-decimal-spelling-smallest-scale.patch delete mode 100644 tools/probe/corpus/seeds/C01-decimal-spelling-string-is-a-string.patch delete mode 100644 tools/probe/corpus/seeds/C02-optional-text-hidden-is-an-error-in-both.patch delete mode 100644 tools/probe/corpus/seeds/C02-optional-text-null-is-blank.patch delete mode 100644 tools/probe/corpus/seeds/C02-optional-text-optional-blank-is-no-value.patch delete mode 100644 tools/probe/corpus/seeds/C02-optional-text-required-blank-is-an-error.patch delete mode 100644 tools/probe/corpus/seeds/C03-composed-faults.patch delete mode 100644 tools/probe/corpus/seeds/C03-detail-and-rule-intact.patch delete mode 100644 tools/probe/corpus/seeds/C03-each-error-own-set.patch delete mode 100644 tools/probe/corpus/seeds/C03-shared-status-types.patch delete mode 100644 tools/probe/corpus/seeds/C03-unmapped-set-stops-start.patch delete mode 100644 tools/probe/corpus/seeds/C04-left-out-everywhere-stops-start.patch delete mode 100644 tools/probe/corpus/seeds/C04-narrows-wider-status.patch delete mode 100644 tools/probe/corpus/seeds/C04-reporting-left-out-throws.patch delete mode 100644 tools/probe/corpus/seeds/C04-without-faults.patch delete mode 100644 tools/probe/corpus/seeds/C05-no-namespace.patch delete mode 100644 tools/probe/corpus/seeds/C05-rule-qualified.patch delete mode 100644 tools/probe/corpus/seeds/C05-stays-the-errors.patch delete mode 100644 tools/probe/corpus/seeds/C05-unnamed-value-throws.patch delete mode 100644 tools/probe/corpus/seeds/C05-with-rule.patch delete mode 100644 tools/probe/corpus/seeds/C06-description-as-detail.patch delete mode 100644 tools/probe/corpus/seeds/C06-fault-kinds.patch delete mode 100644 tools/probe/corpus/seeds/C06-member-must-have-kind.patch delete mode 100644 tools/probe/corpus/seeds/C06-no-description-no-detail.patch delete mode 100644 tools/probe/corpus/seeds/C07-blanket-mapping.patch delete mode 100644 tools/probe/corpus/seeds/C07-by-kind.patch delete mode 100644 tools/probe/corpus/seeds/C07-enum-needs-own-mapping.patch delete mode 100644 tools/probe/corpus/seeds/C07-fault-responses-blanket.patch delete mode 100644 tools/probe/corpus/seeds/C07-mapping-refused.patch delete mode 100644 tools/probe/corpus/seeds/C07-member-without-kind.patch delete mode 100644 tools/probe/corpus/seeds/C07-own-mapping.patch delete mode 100644 tools/probe/corpus/seeds/C08-leave-one-out-fixes.patch delete mode 100644 tools/probe/corpus/seeds/C08-qualified-chain-warns.patch delete mode 100644 tools/probe/corpus/seeds/C08-qualified-names-fix.patch delete mode 100644 tools/probe/corpus/seeds/C08-same-name-warning.patch delete mode 100644 tools/probe/corpus/seeds/C08-warning-names-both.patch delete mode 100644 tools/probe/corpus/seeds/C09-base-record-equality-equal-by-the-wrapped-value.patch delete mode 100644 tools/probe/corpus/seeds/C09-base-record-equality-in-a-collection-of-the-base.patch delete mode 100644 tools/probe/corpus/seeds/C10-preserve-guard-ignore-cycles-unaffected.patch delete mode 100644 tools/probe/corpus/seeds/C10-preserve-guard-own-converters-unaffected.patch delete mode 100644 tools/probe/corpus/seeds/C10-preserve-guard-refused-where-references-could-be-written.patch delete mode 100644 tools/probe/corpus/seeds/C10-preserve-guard-value-converters-unaffected.patch delete mode 100644 tools/probe/corpus/seeds/C11-parse-shapes-build-abstract-string-constructor.patch delete mode 100644 tools/probe/corpus/seeds/C11-parse-shapes-build-implicit-result.patch delete mode 100644 tools/probe/corpus/seeds/C11-parse-shapes-build-parsing-where-it-can-be-parsed.patch delete mode 100644 tools/probe/corpus/seeds/C11-parse-shapes-build-ref-text.patch delete mode 100644 tools/probe/corpus/seeds/C12-described-converter-own.patch delete mode 100644 tools/probe/corpus/seeds/C12-described-converter.patch delete mode 100644 tools/probe/corpus/seeds/C12-options-descriptions.patch delete mode 100644 tools/probe/corpus/seeds/C12-parameter-keeps-aspnet.patch delete mode 100644 tools/probe/corpus/seeds/C13-absent-is-default.patch delete mode 100644 tools/probe/corpus/seeds/C13-doc-optional-where-absent.patch delete mode 100644 tools/probe/corpus/seeds/C13-patch-required.patch delete mode 100644 tools/probe/corpus/seeds/C14-application-json-declared.patch delete mode 100644 tools/probe/corpus/seeds/C14-ascending-with-declared.patch delete mode 100644 tools/probe/corpus/seeds/C14-declared-describes-fault.patch delete mode 100644 tools/probe/corpus/seeds/C14-declared-problem.patch delete mode 100644 tools/probe/corpus/seeds/C15-mvc-fault-status-doc.patch delete mode 100644 tools/probe/corpus/seeds/C15-status-per-scope.patch delete mode 100644 tools/probe/corpus/seeds/C15-without-faults-doc.patch delete mode 100644 tools/probe/corpus/seeds/C16-error-names-descriptions.patch delete mode 100644 tools/probe/corpus/seeds/C16-minimal-host-builds.patch delete mode 100644 tools/probe/corpus/seeds/C16-options-agreement.patch delete mode 100644 tools/probe/corpus/seeds/C16-patch-held-refused.patch delete mode 100644 tools/probe/corpus/seeds/C16-same-converters-build.patch delete mode 100644 tools/probe/corpus/seeds/C17-cmtk0014-a-cast-naming-no-member.patch delete mode 100644 tools/probe/corpus/seeds/C17-cmtk0014-a-member-is-silent.patch delete mode 100644 tools/probe/corpus/seeds/C17-cmtk0014-constructor-accessibility-too.patch delete mode 100644 tools/probe/corpus/seeds/C17-cmtk0014-default-and-zero.patch delete mode 100644 tools/probe/corpus/seeds/C17-cmtk0014-reported-where-written.patch delete mode 100644 tools/probe/corpus/seeds/C18-cmtk1023-a-warning-and-generated.patch delete mode 100644 tools/probe/corpus/seeds/C18-cmtk1023-generated-equality-never-calls-the-base.patch delete mode 100644 tools/probe/corpus/seeds/C18-cmtk1023-names-each-base-member.patch delete mode 100644 tools/probe/corpus/seeds/C19-cmtk0010-kinds-a-pair-once-at-the-later-argument.patch delete mode 100644 tools/probe/corpus/seeds/C19-cmtk0010-kinds-flags-enum-and-read-only-struct.patch delete mode 100644 tools/probe/corpus/seeds/C19-cmtk0010-kinds-interface-and-unsealed.patch delete mode 100644 tools/probe/corpus/seeds/C19-cmtk0010-kinds-methods-the-compiler-calls.patch delete mode 100644 tools/probe/corpus/seeds/C19-cmtk0010-kinds-value-object-and-closed-set.patch delete mode 100644 tools/probe/corpus/seeds/C20-testing-shouldhold-a-failure-says-the-outcome.patch delete mode 100644 tools/probe/corpus/seeds/C20-testing-shouldhold-fails-unless-it-holds-that-reason.patch delete mode 100644 tools/probe/corpus/seeds/C20-testing-shouldhold-hands-back-detail-and-rule.patch delete mode 100644 tools/probe/corpus/seeds/C20-testing-shouldhold-uninitialized-fails.patch delete mode 100644 tools/probe/corpus/seeds/C20-testing-shouldhold-whichever-of-its-types.patch delete mode 100644 tools/probe/corpus/seeds/C21-openapi-schema-parity-container-elements.patch delete mode 100644 tools/probe/corpus/seeds/C21-openapi-schema-parity-dictionary-key.patch delete mode 100644 tools/probe/corpus/seeds/C21-openapi-schema-parity-every-document-version.patch delete mode 100644 tools/probe/corpus/seeds/C21-openapi-schema-parity-nullable-admits-null-as-the-wrapped-does.patch delete mode 100644 tools/probe/corpus/seeds/C21-openapi-schema-parity-the-underlying-types-schema.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-abstract.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-closed-set-named-as-generated-member.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-equality-operator.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-every-error-links-the-table.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-generic-wrapped.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-named-as-generated-member.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-no-type-half-generated.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-over-object.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-over-system-enum.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-over-value-object.patch delete mode 100644 tools/probe/corpus/seeds/C22-generator-refusals-unsealed-record-class.patch delete mode 100644 tools/probe/corpus/seeds/E01-column-parity-column-type.patch delete mode 100644 tools/probe/corpus/seeds/E01-column-parity-facet-kept.patch delete mode 100644 tools/probe/corpus/seeds/E01-column-parity-nullable.patch delete mode 100644 tools/probe/corpus/seeds/E02-value-translates-to-column-column-not-cast.patch delete mode 100644 tools/probe/corpus/seeds/E02-value-translates-to-column-get-value.patch delete mode 100644 tools/probe/corpus/seeds/E02-value-translates-to-column-value-or-null.patch delete mode 100644 tools/probe/corpus/seeds/E02-value-translates-to-column-value.patch delete mode 100644 tools/probe/corpus/seeds/E03-column-round-trip-keys.patch delete mode 100644 tools/probe/corpus/seeds/E03-column-round-trip-nullable-properties.patch delete mode 100644 tools/probe/corpus/seeds/E03-column-round-trip-stored-as-underlying.patch delete mode 100644 tools/probe/corpus/seeds/E04-integer-key-generated-byte-not-generated.patch delete mode 100644 tools/probe/corpus/seeds/E04-integer-key-generated-char-key-yours.patch delete mode 100644 tools/probe/corpus/seeds/E04-integer-key-generated-generated-on-add.patch delete mode 100644 tools/probe/corpus/seeds/E04-integer-key-generated-guid-key-yours.patch delete mode 100644 tools/probe/corpus/seeds/E04-integer-key-generated-own-configuration-wins.patch delete mode 100644 tools/probe/corpus/seeds/E05-own-compareto-rejected-comparison.patch delete mode 100644 tools/probe/corpus/seeds/E05-own-compareto-rejected-equality-allowed.patch delete mode 100644 tools/probe/corpus/seeds/E05-own-compareto-rejected-min-max.patch delete mode 100644 tools/probe/corpus/seeds/E05-own-compareto-rejected-ordering.patch delete mode 100644 tools/probe/corpus/seeds/E05-own-compareto-rejected-query-filter.patch delete mode 100644 tools/probe/corpus/seeds/E05-own-compareto-rejected-value-order-allowed.patch delete mode 100644 tools/probe/corpus/seeds/E06-own-converter-no-value-value-not-translated.patch delete mode 100644 tools/probe/corpus/seeds/E06-own-converter-no-value-value-object-comparison.patch delete mode 100644 tools/probe/corpus/seeds/E07-closed-set-stored-as-name-in-collections-and-json.patch delete mode 100644 tools/probe/corpus/seeds/E07-closed-set-stored-as-name-non-member-throws.patch delete mode 100644 tools/probe/corpus/seeds/E07-closed-set-stored-as-name-stored-as-name.patch delete mode 100644 tools/probe/corpus/seeds/E07-closed-set-stored-as-name-unknown-name-throws.patch delete mode 100644 tools/probe/corpus/seeds/E08-option-result-not-columns-error-says-so.patch delete mode 100644 tools/probe/corpus/seeds/E08-option-result-not-columns-model-fails.patch delete mode 100644 tools/probe/corpus/seeds/E09-or-none-in-database-element-at-skips.patch delete mode 100644 tools/probe/corpus/seeds/E09-or-none-in-database-first-reads-one-row.patch delete mode 100644 tools/probe/corpus/seeds/E09-or-none-in-database-single-asks-for-two.patch delete mode 100644 tools/probe/corpus/seeds/E10-read-back-skips-validation-reads-without-create.patch delete mode 100644 tools/probe/corpus/seeds/E10-read-back-skips-validation-revalidate-finds.patch delete mode 100644 tools/probe/corpus/seeds/E11-usetypekit-order-order-does-not-matter.patch delete mode 100644 tools/probe/corpus/seeds/E12-convention-by-vo-type-underlying-type-does-not-reach.patch delete mode 100644 tools/probe/corpus/seeds/E12-convention-by-vo-type-value-object-type-reaches.patch delete mode 100644 tools/probe/corpus/seeds/E13-primitive-collections-contains.patch delete mode 100644 tools/probe/corpus/seeds/E13-primitive-collections-element-value.patch delete mode 100644 tools/probe/corpus/seeds/E14-inheritance-key-generation-tpc-assign-on-sqlite.patch delete mode 100644 tools/probe/corpus/seeds/E14-inheritance-key-generation-tph-tpt-root.patch delete mode 100644 tools/probe/corpus/seeds/E15-composite-key-as-for-primitives.patch delete mode 100644 tools/probe/corpus/seeds/E16-own-configuration-wins-column-type.patch delete mode 100644 tools/probe/corpus/seeds/E16-own-configuration-wins-conversion.patch delete mode 100644 tools/probe/corpus/seeds/E16-own-configuration-wins-never.patch delete mode 100644 tools/probe/corpus/seeds/E16-own-configuration-wins-on-add.patch delete mode 100644 tools/probe/corpus/seeds/E16-own-configuration-wins-optional.patch delete mode 100644 tools/probe/corpus/seeds/L01-json-bytes-read-as-the-underlying-type-reads.patch delete mode 100644 tools/probe/corpus/seeds/L01-json-bytes-the-underlying-types-json.patch delete mode 100644 tools/probe/corpus/seeds/L02-no-quote-json-never-repeats.patch delete mode 100644 tools/probe/corpus/seeds/L02-no-quote-no-inner-that-quotes.patch delete mode 100644 tools/probe/corpus/seeds/L02-no-quote-parsing-never-repeats.patch delete mode 100644 tools/probe/corpus/seeds/L03-one-rule-json-runs-create.patch delete mode 100644 tools/probe/corpus/seeds/L03-one-rule-parsing-runs-create.patch delete mode 100644 tools/probe/corpus/seeds/L03-one-rule-refusals-name-the-fault.patch delete mode 100644 tools/probe/corpus/seeds/L03-one-rule-tryfrom-runs-create.patch delete mode 100644 tools/probe/corpus/seeds/L04-round-trip-invariant-whatever-the-culture.patch delete mode 100644 tools/probe/corpus/seeds/L04-round-trip-json-refusal-is-a-json-exception.patch delete mode 100644 tools/probe/corpus/seeds/L04-round-trip-text-reads-back.patch delete mode 100644 tools/probe/corpus/seeds/L04-round-trip-the-type-converter-writes-the-same.patch delete mode 100644 tools/probe/corpus/seeds/L05-equality-equality-is-the-underlying-values.patch delete mode 100644 tools/probe/corpus/seeds/L05-equality-ordering-follows-the-underlying-type.patch delete mode 100644 tools/probe/corpus/seeds/L05-equality-through-every-base.patch delete mode 100644 tools/probe/corpus/seeds/L06-off-the-wire-never-read.patch delete mode 100644 tools/probe/corpus/seeds/L06-off-the-wire-never-written.patch delete mode 100644 tools/probe/enumerate.cs delete mode 100644 tools/probe/generators/Directory.Build.props delete mode 100644 tools/probe/generators/Directory.Build.targets delete mode 100644 tools/probe/generators/README.md delete mode 100644 tools/probe/generators/containers.cs delete mode 100644 tools/probe/generators/decls.cs delete mode 100644 tools/probe/generators/ef.cs delete mode 100644 tools/probe/generators/faults.cs delete mode 100644 tools/probe/generators/json-family.cs delete mode 100644 tools/probe/generators/rules.cs delete mode 100644 tools/probe/generators/shapes.cs diff --git a/.claude/probe-axes.md b/.claude/probe-axes.md deleted file mode 100644 index b19a06b0..00000000 --- a/.claude/probe-axes.md +++ /dev/null @@ -1,755 +0,0 @@ -# Probe axes: CodoMetis.TypeKit - -The `prober` agent reads this file at the start of every round. It says how to write and run a probe -here, where the claims come from, which external APIs to enumerate, which type shapes to generate, -what to compare against, and what is intended. Everything specific to this repository lives here; -the agent itself is generic. - -## Harness - -Write probes as xunit theories in **`test/CodoMetis.TypeKit.Samples`**, in a folder `Probes/` of the -scratch copy, one file per claim, class name ending in `Probe`, rows fed through `[MemberData]` from -the covering array (`prober rows`), read from a JSON file beside the probe. That project is the -closest thing to a consumer: it references every package, weaves value objects through the real -fabric, and loads the analyzer as an analyzer. A probe file may declare its own value objects -(`readonly partial record struct ProbeId : IValue;`); the declarations of the type-shape -catalogue, `tools/probe/corpus/harness/*.g.cs`, are written by the file-based apps of -`tools/probe/generators/` (`dotnet run tools/probe/generators/.cs -- ...`, its README), never by hand, -and `GeneratedHarnessTests` in the conventions tests holds each file to its app's output. - -- Start a probe with `prober new --claim ` and iterate with `prober try --probe

[--rows i,j,...]` - (tools/Prober/README.md, "Writing a probe"): `new` stages the skeleton in this harness's form, with the - claim's obligations quoted and a throwing check each, the probe's own rows and the registry entry; `try` - builds the corpus and the staging in the replay's worktree and runs that one probe, printing each row's - outcome and recording nothing. Never copy the corpus into a worktree or write a build or sync script. -- Build and run only through the verdict tool, which pins English output, builds before it tests, - names the exit codes and prints failing tests by name with their assertion: - `dotnet tools/BuildVerdict/bin/Release/net10.0/build-verdict.dll build test/CodoMetis.TypeKit.Samples` - (cold, about 1 to 2 minutes), then `dotnet tools/BuildVerdict/bin/Release/net10.0/build-verdict.dll test - --project test/CodoMetis.TypeKit.Samples --filter-class "*Probe"`. Build the tool first, `dotnet build - tools/BuildVerdict -c Release`: a fresh worktree has no dll and a stale one refuses (exit 3). - The full log is at `$(git rev-parse --absolute-git-dir)/build-verdict-last.log`. -- Warnings are errors. Add `#pragma warning disable xUnit1051` at the top of a probe that awaits - without a cancellation token; an obsolete setting (`IgnoreNullValues`) needs its `SYSLIB` id - suppressed. A probe that deliberately uses something the analyzer refuses suppresses that one CMTK - id around the line and says why. -- HTTP probes: `WebApplication.CreateBuilder(new WebApplicationOptions { EnvironmentName = - Environments.Production })` (Development adds the developer exception page, and a refusal then - arrives as a 500), `builder.WebHost.UseTestServer()`, the JSON options through - `builder.Services.ConfigureHttpJsonOptions(...)`, then `app.StartAsync()` and `app.GetTestClient()`. - The OpenAPI document is at `/openapi/v1.json` after `builder.Services.AddOpenApi(o => { - o.OpenApiVersion = ...; o.AddTypeKit(); })` and `app.MapOpenApi()`. A controller must be `internal` - and added through an `IApplicationFeatureProvider`; a public one is discovered by - every other host in the project. A host with controllers must put its own converters and resolvers - on MVC's `JsonOptions` too, or the document is refused on purpose (`DescribedOptionsAgreement`). - `test/CodoMetis.TypeKit.AspNetCore.Tests/ProbeHost.cs` is a working example to copy from. -- How a row ends: a check returns null or the failure text, and runs through `Rec.Run`, which fails the - test for a violation or a harness error and skips it, with its reasons, for any other outcome but a - pass; a check that throws ends as a harness error, never a violation, and no seed counts it caught. A check - says how it compared with `r.Note(NoteKeys.Name, detail)`. The key, a member of `NoteKeys` in `harness/Row.cs`, - alone decides the note's outcome: every key is a pass key but `framework-refuses`, the three-sided - differential's expected refusal, and a check that did not apply, refused or could not tell says so through - its scope with a citation (below), never a note key. Keys are constants the compiler checks: a new one goes - into `NoteKeys` with its outcome on one line, and a string, a misspelled member or a key built from text does - not compile (decision 103). A probe outside the namespace `Probes.Shapes` writes `using NoteKeys = Probes.Shapes.NoteKeys;`. - Never match a failure's text to excuse it. A difference the framework makes on its own is shown by a - baseline in the same row (the document as ASP.NET writes it without `AddTypeKit()`, - `Hx.DocRefusedByAspNetAlone`, a refusal compared by exception type and stage as `Sides.Alike` compares one), - not by a substring. A document both refuse alike is the framework's refusal for a check that compared nothing - else, and leaves a check that compared an answer on the wire first to be judged by that answer. A row that would fail for an open finding calls - `r.Skip("F7", why)`, the finding as a string literal: `register` and `publish` refuse a skip whose finding - is built from text or is not open in the ledger, naming the file and line. The gate refuses while any row - skips, and the replay fails a skip whose finding the ledger has fixed or dismissed. -- Checks: a comparison is `r.Check("obligation", Baseline.Twin, c => ...)` (or the `async` form), one per - obligation of the claim (`claim` for a claim without them), against one baseline of `Baseline` (`Twin`, - `Framework`, `RoundTrip`, `Model`, `Refusal`, `Cost`, `Fact("KB-031")`, `Docs("file#anchor")`). The check reads - through `c["axis"]` (or the row, inside the body: both read for the check), notes how it compared with - `c.Note(NoteKeys.Compared, ...)`, and says why it did not compare with `c.Inapplicable(Cite...)`, - `c.Refused(Cite...)` or `c.Inconclusive(reason)`, each returning null. A citation is `Cite.NotPromised("id")` (an - entry of the claim's `not-promised`), `Cite.Docs("file#anchor")`, `Cite.Fact("KB-031")` or `Cite.Record("0094")`, - written with a string literal; `register`, `publish` and the conventions tests refuse anything else and a - citation that names nothing. A failure returned is the check's violation, an exception its harness error (the - row's other checks still run), and a pass with no baseline a harness error. A row passes when no check failed - and one passed, so a check that did not apply voids no other; the ledger credits each passing check with the - axes it read and the row's, never a pair put together from two checks. A seed naming an obligation is caught - only by a check naming it. -- Two-sided checks: the twin is `Entry.Plain`, the wrapped type at the value object's nullability (a class value object over a - struct faces the struct made nullable; the closed set faces a string), written by `shapes.cs` beside the value object. Build both - sides with `Sides.Twins(position, containing, entry, plain, vo)`, which puts the row's position and containing type around each and - throws, a harness error, where the two do not hold the same shape. Under `json.TypeInfoResolver=source-generated-context` the options - resolve through `ProbeContext` alone, which lists every shape, position and containing type and each twin, with no reflection behind - it; `json.TypeInfoResolverChain=context-then-default` puts the reflection fallback after it. A shape the context does not list is a - harness error, never a fallback (tools/probe/generators/README.md, "The twin and the context"). -- EF probes: SQLite in memory (`Data Source=:memory:` with the connection held open). PostgreSQL needs - a server and is out of reach. -- Analyzer probes (what compiles and what CMTK reports): in `test/CodoMetis.TypeKit.Analyzers.Tests`, - a `CSharpCompilation` over `RealTypeKit.References` (`AnalyzerTest.cs`) with the analyzers attached, - asserting the diagnostics. The type-shape catalogue is the row there, as source text. -- Native AOT is measured only by `./test/consumer-smoke-test.sh` (minutes, needs clang). Probe it only - when the caller asks for it. - -## Seeds: every probe shows it can fail - -A probe that passes proves something only if it can fail. Every registered probe names one seeded -product defect ([decision 100](../design/adr/0100-every-probe-is-shown-to-fail-against-a-seeded-defect.md)): -a patch against `src/` that breaks what its claim promises, in `tools/probe/corpus/seeds/.patch`, -and a sentence saying what it breaks, in its registry entry: - -```json -"seed": { "patch": "seeds/L01-json-bytes.patch", "defect": "A dictionary key is written with ToString(), not the wrapped type's key converter and DictionaryKeyPolicy." } -``` - -- Write the seed with the probe. Make the defect in `src/` of the scratch copy, take `git diff -- ` - as the patch, undo it by inverse edit (`git apply -R `), and register with `prober register ... - --seed seeds/.patch --defect ""`. An environment toggle (`"env": {...}`, set for the - build and every run) is a seed too, where the product reads one; none does today. -- Seed the defect the claim is about, the one a consumer would meet: a facet dropped, a refusal that - quotes its input, a rule skipped, a key no longer generated. A seed whose build fails proves nothing, - and the command counts it as a failure. -- `prober seeds [--probe P]` applies each seed in a scratch worktree of its own (`replay --where` names - it), builds, replays the probe there and requires a row that ends as a violation the unseeded probe - does not have; a missed seed fails it. It records no evidence and takes no lock. The worktree is per - checkout, so runs in two checkouts (a linked worktree each) never meet, but two runs at once in one - checkout share it. One build per seed, so it runs locally, never in CI: after writing a probe or - changing its harness, and before a release. -- Without a build, the prober tests hold the rest on every run (`SeedsTests`): every registered probe - has a seed, and its patch touches only `src/` and still applies. A product change that moves the - patched lines fails there; make the seed again against the new code. -- A seed per obligation ([decision 104](../design/adr/0104-a-claim-states-its-obligations-and-each-has-a-seed.md)). - A seed that breaks some assertion of a probe shows the probe can fail, not that the claim is checked, so - a probe whose claim lists `obligations` carries `seeds`, one per obligation it checks, each a seed as - above with an `obligation` naming it; `seed` may name one too. A seed that names none counts only for a - claim without `obligations`. Register each with `prober register --probe P --claim C --filter "*P" - --obligation --seed seeds/-.patch --defect ""`, the command `prober new` prints - for each obligation: it replaces a seed of the entry that names the same obligation and refuses an id the - claim does not list. Never edit the staged registry by hand. `prober seeds` runs each seed of a probe on its own and reports it as - `P/`, in its output and in `seeds-last.json`; `prober status` shows per claim its obligations - and the ones no seed names; and `SeedsTests` refuses, without a build, a seed naming an obligation its - claim does not list and a listed obligation of a claim with probes that no seed of them names. - -## Claim sources - -In this order: the root `README.md` ("What TypeKit guarantees" first), `docs/*.md`, the package -READMEs (`src/*/README.md`), then `design/adr/*.md` for intent. Statements in `docs/` and the READMEs -are promises to consumers; a design record explains, and decides what is intended. - -## Axes - -The axes are data, read by `prober universe` (tools/Prober), which builds each claim's -axes from its surfaces. The prober assigns surfaces to a claim; it does not choose axes. - -- **Generated axes** come from `tools/probe/enumerate.cs`, which enumerates by reflection every - public settable property of `JsonSerializerOptions` (`json.*`), `OpenApiOptions` (`openapi.*`) and - `MvcOptions` (`mvc.*`) at the versions this repository pins. `prober replay` runs it - outside the repository (so neither `global.json` nor central package management applies to it), with - its package versions taken from `Directory.Packages.props`, whenever a dependency input changes, and - `--all` always does. A setting whose type is not a bool or an enum takes its values from `hand` below. -- Every generated axis is either reached by a surface's pattern or listed under `ignored` with the - reason. The script reports any other as **unassigned**: a setting a new framework version added, - which needs a decision here. -- `shape.*` is the type-shape catalogue: the forms a consumer's own type takes. Generate the - declarations a round needs with an app of `tools/probe/generators/` (extend one, or add one and its row - in `GeneratedHarnessTests`), once per round, as one pairwise set shared by every claim. -- A claim may drop a surface axis only by quoting the document sentence that limits it (`narrow`). -- A claim may drop pairs no row can hold, or that the documents do not promise, only with the reason - (`exclude`: a list of `{"reason": "...", "pairs": [["a=1", "b=2"], ...]}`): one sentence, citing the docs - sentence, the record or the KB fact where one applies. Nothing checks a reason mechanically, so - `prober universe` prints each one beside the claim's narrowings for a reviewer, and the prober refuses a - claims.json with any field its schema (`tools/Prober/Schemas/claims.schema.json`) does not have - ([decision 102](../design/adr/0102-an-exclusion-carries-its-reason.md)). -- An obligation may say where it does not apply, by the claim's own axis values and a citation - (`inapplicable`: a list of `{"axes": {"shape.kind": ["closed-set"]}, "cite": "not-promised:closed-set-has-no-scale"}`, - the cite `not-promised:id`, `docs:file#anchor`, `fact:KB-NNN` or `record:NNNN`). `Row.Check` reads the same - declarations and records such a check inapplicable before its body runs; a pair no obligation wants is never - put in a generated row, and the gate leaves it out as it leaves out an exclusion. A condition only the shape - instance decides at run time stays in the check's body as `c.Inapplicable(cite)`. -- `refused` drops a value the framework itself refuses on its own, so no row wastes its pairs on it. A reason that - rests on a verified fact names it (`fact:KB-NNN`), and that fact has a test: `docs-query check` fails - `fact-not-executable-cited` for a refused entry, a not-promised entry, an applicability condition or a harness - citation of a fact without one ([decision 107](../design/adr/0107-a-cited-fact-is-executable-and-seeds-run-by-footprint.md)). - `shape.refused-kind` holds the declarations the generators refuse; only the generation surface takes - it, where the refusal is the claim. Each value is a declaration and the id the Generators README's - "Build errors" table gives it, declared as C22's probe does (`harness/BuildRefusals.cs`): an unsealed - record class; a value object over another value object; over `List`; an abstract record or class with - a marker (CMTK1022 since `1ef30be`: the label keeps the id it had when C09's and C11's rows were - generated, which carry it); over `object`; over `System.Enum`; a value object named as a member the - generators add (`From`, `Parse`, `MinValue`, ...); a closed set named as one of its generated members - (`Name`, `All`, `Match`, ...); a value object with a hand-written `operator ==` and `!=` between two of - the type. The last five are the refusals of the fixes `53188ed` and `e4b751c`, which no probe reached - until C22 (design/finder-benchmark-2026-10-16.md). -- A claim lists what it promises in `obligations`, one entry per promise a probe can check on its own: an - `id`, the `sentence` quoted verbatim from the claim's source and checked as a narrowing is, and the `axes` - values it concerns where it is not the whole claim. A claim without the field has one obligation, the - claim itself, under the id `claim`. Each obligation of a claim with probes has a seed that names it - ([decision 104](../design/adr/0104-a-claim-states-its-obligations-and-each-has-a-seed.md)). -- What a claim does not promise goes in `not-promised`: an `id`, the `source` (`file#heading`) of the - document or record that makes the difference intended, its `sentence`, quoted verbatim and checked against - that source, and the `axes` values it concerns. A note of outcome expected-refusal, a narrowing or an - applicability condition cites the entry by its id. `prober universe` refuses an obligation or an entry - whose sentence its document does not hold, and an id listed twice, naming the claim and the id. -- `out-of-reach` names a surface no probe reaches, with the reason: one that needs a server, a toolchain - or minutes a replay does not have. `prober gaps` prints it beside the settings no surface reaches, the - settings without values, the refused shapes and values, and every not-promised entry and narrowing, each - with its recorded reason. It is a report for the release checklist and a feature-research round; it - decides nothing and runs no model. -- `http.host` has two values without `[ApiController]`, where MVC infers no `[FromBody]` and answers no - automatic 400. `controller-without-ApiController` is for **runtime claims only**: ASP.NET's API explorer - leaves such a controller out of the document (docs/aspnet-core.md, "Bind value objects in MVC"), so a - document check has nothing there to read. A document (OpenAPI) claim lists `http.host` in its own - `axes` in `claims.json` without it. `controller-without-ApiController-visible` hosts the same - controllers made visible by an `IApplicationModelConvention` that sets `ApiExplorer.IsVisible = true` - (`VisibleToApiExplorer` in the harness), so document checks reach them; every http claim takes it. -- `process.*` holds what other code in the same process may have done first (2026-10-15: a wrapped - type's `TypeConverter` looked up before the value object's parsing broke that parsing). - -```json probe-axes -{ - "hand": { - "json.PropertyNamingPolicy": [ - "CamelCase", - "SnakeCaseLower", - "SnakeCaseUpper", - "KebabCaseLower", - "KebabCaseUpper", - "own-upper-case" - ], - "json.DictionaryKeyPolicy": [ - "CamelCase", - "SnakeCaseLower", - "KebabCaseUpper", - "own-upper-case" - ], - "json.ReferenceHandler": [ - "Preserve", - "IgnoreCycles" - ], - "json.Converters": [ - "own-for-wrapped", - "own-for-value-object", - "JsonStringEnumConverter", - "StoredJsonConverterFactory" - ], - "json.TypeInfoResolver": [ - "source-generated-context", - "default-with-renaming-modifier" - ], - "json.TypeInfoResolverChain": [ - "context-then-default" - ], - "json.Encoder": [ - "UnsafeRelaxedJsonEscaping" - ], - "json.MaxDepth": [ - "3" - ], - "json.DefaultBufferSize": [ - "1" - ], - "json.IndentCharacter": [ - "tab" - ], - "json.IndentSize": [ - "1" - ], - "json.NewLine": [ - "lf" - ], - "openapi.CreateSchemaReferenceId": [ - "own-prefixing", - "null-inlines-all" - ], - "openapi.ShouldInclude": [ - "default-only" - ], - "openapi.transformers": [ - "own-schema-transformer", - "own-operation-transformer", - "own-document-transformer" - ], - "mvc.InputFormatters": [ - "xml-added" - ], - "mvc.OutputFormatters": [ - "xml-added" - ], - "mvc.ModelBinderProviders": [ - "own-provider-first" - ], - "mvc.Conventions": [ - "own-route-prefix" - ], - "mvc.MaxModelBindingCollectionSize": [ - "2" - ], - "mvc.MaxModelValidationErrors": [ - "1" - ], - "mvc.MaxValidationDepth": [ - "1" - ] - }, - "ignored": { - "json.WriteIndented": "layout only; JSON parity compares under the same options", - "json.IndentCharacter": "layout only", - "json.IndentSize": "layout only", - "json.NewLine": "layout only", - "json.DefaultBufferSize": "buffering only; DefaultBufferSize 1 is kept in hand for a stress round on request", - "openapi.ShouldInclude": "selects endpoints, never a schema's shape", - "mvc.EnableActionInvokers": "invocation pipeline, not binding or formatting", - "mvc.EnableEndpointRouting": "TypeKit supports endpoint routing only", - "mvc.RequireHttpsPermanent": "transport", - "mvc.SslPort": "transport", - "mvc.CacheProfiles": "caching headers", - "mvc.SuppressAsyncSuffixInActionNames": "action naming", - "mvc.SuppressInputFormatterBuffering": "buffering", - "mvc.SuppressOutputFormatterBuffering": "buffering", - "mvc.MaxIAsyncEnumerableBufferLimit": "streaming", - "mvc.MaxModelBindingRecursionDepth": "recursion limit of binding, reached only by self-referencing models", - "mvc.ModelMetadataDetailsProviders": "covered by own-provider-first on binders", - "mvc.ModelValidatorProviders": "validation providers; TypeKit validates in Create", - "mvc.ValueProviderFactories": "value sources; the binding axis covers them" - }, - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class", - "closed-set" - ], - "shape.site": [ - "top-level", - "nested-in-class", - "nested-in-struct", - "nested-two-levels", - "in-generic-type" - ], - "shape.inheritance": [ - "none", - "base-depth-1", - "base-depth-2", - "base-depth-3", - "base-explicit-IEquatable", - "explicit-interface-member" - ], - "shape.wrapped": [ - "string", - "Guid", - "int", - "long", - "byte", - "ulong", - "decimal", - "double", - "bool", - "char", - "DateTime", - "DateTimeOffset", - "DateOnly", - "TimeOnly", - "TimeSpan", - "enum", - "flags-enum", - "Uri", - "NodaTime.Instant", - "own-record-class" - ], - "shape.position": [ - "root", - "property", - "nullable-property", - "field", - "list-element", - "array-element", - "immutable-array-element", - "read-only-list-element", - "dictionary-key", - "dictionary-value", - "record-ctor-parameter", - "required-init", - "in-patch", - "polymorphic-derived-property" - ], - "shape.containing": [ - "class", - "record-class", - "struct", - "record-struct", - "generic-type" - ], - "input.json": [ - "valid", - "refused-by-create", - "wrong-token", - "null", - "empty-string", - "at-type-limit", - "quoted-number", - "escape-sequence", - "duplicate-property", - "metadata-property" - ], - "http.host": [ - "minimal", - "mvc", - "both", - "route-group", - "controller-without-ApiController", - "controller-without-ApiController-visible" - ], - "http.binding": [ - "route", - "query", - "header", - "body", - "form", - "as-parameters" - ], - "http.response": [ - "result-to-http-result", - "composed-fault", - "two-faults-same-name", - "group-with-fault-status", - "endpoint-without-faults", - "declared-produces-problem" - ], - "ef.query": [ - "where-equals", - "where-value-compare", - "order-by", - "contains", - "first-or-none", - "select", - "group-by", - "execute-update", - "compiled-query", - "as-no-tracking" - ], - "ef.mapping": [ - "property", - "key", - "integer-key", - "nullable", - "owned", - "complex", - "closed-set" - ], - "text.entry": [ - "Parse", - "TryParse", - "TryFrom", - "type-converter", - "IParsable", - "ISpanParsable", - "ToString-format", - "TryFormat-short-span" - ], - "text.culture": [ - "invariant", - "de-DE", - "tr-TR", - "ar-SA", - "globalization-invariant" - ], - "or.shape": [ - "Option", - "Result-T-E", - "Result-E", - "Task-of-Result", - "ValueTask-of-Result" - ], - "or.operation": [ - "Match", - "Bind-Map-chain", - "query-syntax", - "Sequence-Transpose", - "Result.Catch", - "FirstOrNone", - "default-instance", - "after-await-elsewhere" - ], - "shape.refused-kind": [ - "unsealed-record-class (CMTK1006)", - "over-value-object (CMTK1005)", - "generic-wrapped (CMTK1005)", - "abstract (CMTK1005)", - "over-object (CMTK1005)", - "over-System.Enum (CMTK1005)", - "named-as-generated-member (CMTK1005)", - "closed-set-named-as-generated-member (CMTK1021)", - "equality-operator (CMTK1011)" - ], - "process.converter-lookup": [ - "none", - "wrapped-type-converter-looked-up-first" - ], - "http.nullability": [ - "as-declared", - "nullable" - ] - }, - "surfaces": { - "json": { - "axes": [ - "json.*", - "shape.kind", - "shape.wrapped", - "shape.position", - "shape.containing", - "input.json" - ], - "watched": [ - "src/CodoMetis.TypeKit.Generators", - "src/CodoMetis.TypeKit/ValueObjects", - "src/CodoMetis.TypeKit/DecimalJsonConverters.cs", - "src/CodoMetis.TypeKit/Patch.cs" - ] - }, - "http": { - "axes": [ - "json.*", - "openapi.*", - "mvc.*", - "http.*", - "process.*", - "shape.kind", - "shape.wrapped", - "shape.position", - "shape.containing", - "input.json" - ], - "watched": [ - "src/CodoMetis.TypeKit.AspNetCore", - "src/CodoMetis.TypeKit/Fault.cs", - "src/CodoMetis.TypeKit/FaultOfSets.cs", - "src/CodoMetis.TypeKit.Generators" - ] - }, - "ef": { - "axes": [ - "ef.*", - "shape.kind", - "shape.wrapped", - "shape.site" - ], - "watched": [ - "src/CodoMetis.TypeKit.EntityFrameworkCore", - "src/CodoMetis.TypeKit.Generators" - ] - }, - "text": { - "axes": [ - "text.*", - "process.*", - "shape.kind", - "shape.wrapped" - ], - "watched": [ - "src/CodoMetis.TypeKit/StringTextExtensions.cs", - "src/CodoMetis.TypeKit/TextFault.cs", - "src/CodoMetis.TypeKit.Generators" - ] - }, - "generation": { - "axes": [ - "shape.*" - ], - "watched": [ - "src/CodoMetis.TypeKit.Generators", - "src/CodoMetis.TypeKit/ValueObjects" - ] - }, - "option-result": { - "axes": [ - "or.*", - "shape.wrapped" - ], - "watched": [ - "src/CodoMetis.TypeKit/Option.cs", - "src/CodoMetis.TypeKit/OptionExtensions.cs", - "src/CodoMetis.TypeKit/Result.cs", - "src/CodoMetis.TypeKit/ResultT.cs", - "src/CodoMetis.TypeKit/ResultExtensions.cs", - "src/CodoMetis.TypeKit/SequenceOptionExtensions.cs", - "src/CodoMetis.TypeKit/ValueTypeOptionExtensions.cs" - ] - }, - "analyzers": { - "axes": [ - "shape.*" - ], - "watched": [ - "src/CodoMetis.TypeKit.Analyzers", - "src/CodoMetis.TypeKit/TypeArgumentConstraints.cs" - ] - }, - "logging": { - "axes": [ - "shape.kind", - "shape.wrapped", - "input.json" - ], - "watched": [ - "src/CodoMetis.TypeKit.Serilog", - "src/CodoMetis.TypeKit/Refusals.cs" - ] - } - }, - "out-of-reach": { - "postgresql": "needs a server; the EF probes run SQLite in memory, and the EF translation claims are checked by translation alone", - "native-aot": "measured only by the consumer smoke test (tools/ConsumerSmokeTest), minutes and a platform linker; no probe publishes with Native AOT" - }, - "refused": { - "shape.site": { - "in-generic-type": "CMTK1005: the generators refuse a value object nested in a generic type at build (Generators README, Build errors)" - }, - "json.ReadCommentHandling": { - "Allow": "System.Text.Json refuses it on the options object" - }, - "json.DefaultIgnoreCondition": { - "Always": "System.Text.Json refuses it on the options object" - } - }, - "replay": { - "project": "test/CodoMetis.TypeKit.Samples", - "probeDir": "test/CodoMetis.TypeKit.Samples/Probes", - "prepare": [ - [ - "dotnet", - "build", - "tools/BuildVerdict", - "-c", - "Release", - "-nologo", - "-v:q" - ] - ], - "build": [ - "dotnet", - "tools/BuildVerdict/bin/Release/net10.0/build-verdict.dll", - "build", - "{project}" - ], - "test": [ - "dotnet", - "tools/BuildVerdict/bin/Release/net10.0/build-verdict.dll", - "test", - "--no-build", - "--project", - "{project}", - "--filter-class", - "{filter}", - "--", - "--results-directory", - "{results}" - ], - "coverage": [ - "--coverage", - "--coverage-output-format", - "cobertura", - "--coverage-output", - "{cobertura}" - ], - "families": { - "http": [ - "http" - ], - "ef": [ - "ef" - ], - "json": [ - "json" - ], - "text": [ - "text", - "logging" - ], - "build": [ - "generation", - "analyzers", - "option-result" - ] - }, - "include": [ - "src" - ], - "compileTimeFootprint": [ - "src/CodoMetis.TypeKit.Generators", - "Directory.Packages.props", - "Directory.Build.props", - "global.json", - "test/CodoMetis.TypeKit.Samples/CodoMetis.TypeKit.Samples.csproj" - ], - "coverageTargets": "\n \n \n \n\n", - "toolchain": { - "dotnet": [ - "dotnet", - "--version" - ] - }, - "enumerate": { - "script": "tools/probe/enumerate.cs", - "inputs": [ - "Directory.Packages.props", - "global.json" - ] - } - } -} -``` - - -## Oracles this repository promises - -- **JSON parity (differential).** A shape with a value object and the same shape with the wrapped - type, serialized under the same options, give the same bytes, and each reads the other's JSON. - `test/CodoMetis.TypeKit.Generators.Tests/JsonParityTests.cs` shows the pattern. -- **Schema parity (differential).** The OpenAPI schema of a value-object property equals the one - ASP.NET Core publishes for the wrapped type in the same position, in every document version. -- **Wire and document agree (differential).** The statuses and problem types a fault response sends - are the ones the document lists for that operation, in both hosting models. -- **Column parity (differential).** The EF model gives a value-object property the wrapped type's - column type, and a query over `.Value` translates to the same SQL as the query over the wrapped - property. -- **One set of rules (differential).** `TryFrom`, `Parse`, `TryParse`, the JSON converter, the type - converter and `FromKnownGood` accept exactly what `Create` accepts, and every refusal names the - fault `Create` returned. -- **No refusal quotes the input (exception).** Put a sentinel such as `SECRET-7f3a` in refused input; - it must not appear in any exception message, inner exception, `ToString()`, ProblemDetails body or - log line. The one exception is a dictionary key in `JsonException.Path`. -- **Off the wire (exception).** `Option` and `Result` throw `NotSupportedException` from - System.Text.Json in both directions, in every position, unless the application registered its - own converter. -- **Equality (metamorphic).** Equal value objects have equal hash codes, `CompareTo` is zero exactly - when `Equals` is true, and both agree with the wrapped type's, through every base in the - inheritance chain. -- **Round trip (metamorphic).** `Parse(x.ToString())`, a JSON write then read, and a column write - then read give back an equal value object. -- **Loud, not silent (exception).** A declaration that cannot be generated fails the build with a - CMTK id; it never compiles into a type with nothing in it. - -## Intended: a refusal or difference here is a pass - -- A `DateTime` is written as UTC; number handling set by an attribute on the containing type reaches - no converter (README, "What TypeKit guarantees"). -- A struct value object refuses a JSON `null`. -- A rejected dictionary key appears in `JsonException.Path`, and so in MVC's 400. -- Under a source-generated context, a value object over an enum needs the enum listed in the context. -- A validated value object has no `From`; an abstract value object fails with CMTK1022, and a generic - wrapped type with CMTK1005. -- A missing `Option` property, and an empty collection or dictionary of `Option` or `Result`, pass - through System.Text.Json without an exception, since no converter runs (a wording question for the - README, raised 2026-10-15, not a finding). -- The EF Core package and `StoredJsonConverterFactory` skip validation by design. -- A host whose MVC and minimal-API JSON options describe a type differently has its document refused - (`DescribedOptionsAgreement`). -- System.Text.Json refusing a combination of its own options with an exception of its own (for - example `IgnoreNullValues` together with a `DefaultIgnoreCondition`). -- Anything a record in `design/adr/` decides. Search the records before calling a refusal a finding. -- Findings already reported by `code-analyst` are in - `.claude/agent-memory-local/code-analyst/review-2-1-0-findings.md`. Do not report them again. - -## Budget - -Twelve claims a round unless the caller says otherwise. One build of the Samples project serves them -all; group the round's probes there. diff --git a/.claude/settings.json b/.claude/settings.json index 5429aeeb..4c835160 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -11,7 +11,7 @@ }, { "type": "command", - "command": "dotnet build tools/AgentTools.slnf -c Release -nologo -v q -tl:off -clp:NoSummary >/dev/null 2>&1 || PATH=\"/usr/local/share/dotnet:$PATH\" dotnet build tools/AgentTools.slnf -c Release -nologo -v q -tl:off -clp:NoSummary || echo \"Session start: the tools in $PWD (build-verdict, prober, docs-query, consumer-smoke-test) did not build with the dotnet on PATH, nor with the installer's in /usr/local/share/dotnet; a dotnet without the SDK global.json pins is the usual cause. Run dotnet build tools/AgentTools.slnf -c Release to see why, and before using them.\"", + "command": "dotnet build tools/AgentTools.slnf -c Release -nologo -v q -tl:off -clp:NoSummary >/dev/null 2>&1 || PATH=\"/usr/local/share/dotnet:$PATH\" dotnet build tools/AgentTools.slnf -c Release -nologo -v q -tl:off -clp:NoSummary || echo \"Session start: the tools in $PWD (build-verdict, docs-query, consumer-smoke-test, pr-hygiene) did not build with the dotnet on PATH, nor with the installer's in /usr/local/share/dotnet; a dotnet without the SDK global.json pins is the usual cause. Run dotnet build tools/AgentTools.slnf -c Release to see why, and before using them.\"", "timeout": 300 } ] diff --git a/.claude/skills/probe-triage/SKILL.md b/.claude/skills/probe-triage/SKILL.md deleted file mode 100644 index 1373245c..00000000 --- a/.claude/skills/probe-triage/SKILL.md +++ /dev/null @@ -1,261 +0,0 @@ ---- -name: probe-triage -description: Decide what one failing probe row means, by four questions in a fixed order — a harness error, a pinned framework fact, an intended difference the claim does not promise, or a finding for the code-analyst — stopping at the first that applies and recording the step where the corpus keeps it. Use for every failing row at the end of a probing round, for a `prober status` finding (`R--`, `F`), for a row a replay or `prober try` printed as failed, and whenever someone asks whether a probe failure is real. ---- - -# Probe triage - -A failing row is a question, not a finding. Most of what the prober has cost went into answering it by -judgement, mid-round: the gate round of 2026-10-16 wrote 18 probes, found two product defects and about -ten harness traps, and left five failing rows of C07 untriaged. This skill answers the question by -lookup, in a fixed order, so two people (or a person and an agent) reach the same answer for the same -row, and the answer is written where the next replay and the next reader find it. - -**The order is the rule.** Ask the four questions in turn and stop at the first that applies. Do not skip -ahead because a row "looks like" a product bug: a harness that compares unlike things produces the most -convincing product bugs there are. Every outcome names its step, `triage 1` to `triage 4`. - -The writer of a probe never decides what a refusal means. The probe checks; this skill decides. - -## Before the steps: get the row, and make it fail again - -You start from one of these: - -- **A finding id** from `prober status`: `R--` (the replay saw a probe that passed with the - same identity fail) or `F`. Its last line in the branch's `findings.jsonl` names the probe and the - commit. The state directory, `$STATE` below, is `PROBE_STATE_DIR` when set, else the main checkout's - `.claude/agent-memory-local/prober/`; the branch's directory is `branches//`. -- **A row a run printed:** `prober try` and `prober replay` print the failed probe and how each row ended. - A replay that says "fails after a change to its corpus, axes, environment or this tool; triage the probe, - not the product" opened no finding: start at step 1 and expect to stop there. -- **A row's evidence line:** the line `Rec.Run` writes, or the probe's evidence after a replay: - - ```bash - jq '.rows["358"] | {category, reasons, applied}' "$STATE/branches/release-2.2.0/evidence/BaseProbe.json" - ``` - -From it, write down: the claim, the probe, the row's index, its category (`violation`, `harness-error`, -`inconclusive`), its reasons, and the settings that applied. Then run the row at HEAD, three times: - -```bash -dotnet tools/Prober/bin/Release/net10.0/prober.dll try --probe BaseProbe --rows 358 -dotnet tools/Prober/bin/Release/net10.0/prober.dll try --probe BaseProbe --rows 358 --no-build # twice more -``` - -A row that passes at HEAD was fixed meanwhile: say so with the commit, and stop. A row that does not -fail every time is flaky, and flaky is not a finding: record it as `flaky` (a round's ledger line, or -your report) and stop. Only a -row that fails every time goes on to step 1. - -## 1. Is it a harness error? - -**It applies when** the row's reasons carry `harness-error`, or the failure comes from the harness rather -than from the product: - -- the category is `harness-error`: a check threw (`UNEXPECTED : ...`, which decision 103 makes - a harness error and never a violation), a check `prober new` staged and nobody wrote, or the equivalence - precondition (`inputs not equivalent: wrapped [...] vs value object [...]`), which says the two sides - were not the same shape and so were never comparable; -- a 404 on a bind route, a context type the source-generated `ProbeContext` does not list, a host that did - not start for a reason the row's settings do not explain; -- a `violation` whose whole difference lies in something the harness built or named rather than something - TypeKit wrote: a component or route name made from the twin's own type name, a twin at another - nullability, kind or position than the value object (concept H2), an input the harness placed - differently on each side. Read the two sides' answers in the reasons, or in the full log of the `try` - run, and find the first byte that differs. If it is the harness's, this step applies. - -**Then** fix the harness so the row compares like with like, and run the row again. No finding is filed. -A row that now fails with a different difference starts again at step 1. - -**Record:** - -- the harness fix, as a `test:` commit whose body names the row and says `triage 1`: what the harness - did and what it does now (94554b3 is the model). In a round, the fix goes to the staging's `harness/` - and the round's report says it; the session that publishes commits it; -- with a finding id, a line appended to the branch's `findings.jsonl`: - - ```json - {"schemaVersion":1,"id":"R-BaseProbe-1a2b3c4","status":"withdrawn","reason":"triage 1 harness-error: the /schema/ endpoint had no twin at the value object's nullability; fixed in 94554b3"} - ``` - -## 2. Does a fact at the pinned version explain it? - -**It applies when** a fact in `design/knowledge/` says the framework, without TypeKit, does what the row -saw: the same refusal at the same operation, or the same document, at the version the repository pins. -A fact about the framework explains a row only where the framework alone makes the difference. A fact -that describes how the framework treats the wrapped type, while the value object differs from it, does -not explain the row; it is the evidence of step 4. - -**Check with** the framework, the setting and the operation the row names: - -```bash -dotnet tools/DocsQuery/bin/Release/net10.0/docs-query.dll find controller API explorer document visible --kind fact -dotnet tools/DocsQuery/bin/Release/net10.0/docs-query.dll entry KB-002 -dotnet tools/DocsQuery/bin/Release/net10.0/docs-query.dll check # no fact-behind or fact-ahead for it -``` - -A miss of `find` proves nothing: try the setting's own name and the exception type too. Read the fact's -frontmatter. It is **executable** when its `test:` line names a test under `test/CodoMetis.TypeKit.Facts` -(`test: test/CodoMetis.TypeKit.Facts/KB002.cs`). Run that test: - -```bash -dotnet tools/BuildVerdict/bin/Release/net10.0/build-verdict.dll test --project test/CodoMetis.TypeKit.Facts --filter-class "*KB002" -``` - -**Then:** - -- **An executable fact that passes and explains the row** makes it a framework refusal. The check is - changed to cite the fact, no finding is filed. -- **A fact whose pin is stale**, or whose test fails, settles nothing until it is verified again - (decision 93). Verify it first. -- **A fact without a test** (`test: none, ...` or `prose`) that would explain the row is a research task: - measure the behaviour in `test/CodoMetis.TypeKit.Facts` as `KB.cs`, give the fact its `test:` - line, then cite it. A fact without a test may be cited in a reason and nothing more: it never prunes a - row and never excuses one. -- **No fact, but the row's own baseline shows the framework alone doing it** (the document - `Hx.DocRefusedByAspNetAlone` refuses for ASP.NET alone too, or the wrapped type alone refuses under the - same options): that is a new framework behaviour. It becomes a new fact with its test under - `design/knowledge/` (its README, "Adding one"), never a new note key. Then cite it. -- Otherwise go to step 3. - -**Record:** - -- the citation, where the corpus holds one: a `not-promised` entry of the claim whose `source` is the - fact's file and whose `sentence` is quoted from it (step 3's form), an `exclude` group whose `reason` - names `KB-NNN`, or a value in the axes file's `refused` object whose text names `KB-NNN`, or the check's - own `Cite.Fact("KB-NNN")`; -- with a finding id: - - ```bash - dotnet tools/Prober/bin/Release/net10.0/prober.dll dismiss --id R-C03ComposedProbe-1a2b3c4 --reason "triage 2 fact KB-002: a controller without [ApiController] is missing from the document for ASP.NET alone, so the row's document part has nothing to check" - ``` - - `dismiss` stages the line; `prober publish` puts it in `tools/probe/corpus/dismissed.jsonl`; -- in a round, a fact to measure goes in the report under "Facts to measure", with the row and the - configuration. The agent writes no test; the session that takes the report does. - -## 3. Does the claim say it does not promise this? - -**It applies when** a `not-promised` entry of the row's claim covers the row's settings, or a sentence of -the docs or of a decision record says the difference is intended. - -**Check with** the claim's entries and narrowings, and the docs: - -```bash -dotnet tools/Prober/bin/Release/net10.0/prober.dll gaps | grep 'C07-fault-responses-blanket' -jq '.[] | select(.id == "C07-fault-responses-blanket") | {narrow, "not-promised"}' tools/probe/corpus/claims.json -dotnet tools/DocsQuery/bin/Release/net10.0/docs-query.dll find nullable value object schema null --top 5 -``` - -An entry covers the row when each of its `axes` values is among the row's applied settings. A docs -sentence counts only when it says this difference, in this place, is intended. Your sense that it "must be -on purpose" does not count, and neither does the axes file's "Intended" list alone: an item there that no -document or record states is a gap of this step, not an answer. - -**Then:** - -- **An entry covers it:** the difference is intended. The row's check cites the entry by its id, no - finding is filed. -- **A document or record says so, but the claim has no entry:** that is a docs or claims gap, filed as - `docs:` work, no finding. Add the entry; if the consumer docs do not say it plainly, add the sentence - first (the `plain-docs` skill), then quote it. -- **Two documents disagree,** or a record calls intended what a consumer page promises: not this step's - to settle. It goes to the code-analyst as a question (step 4's hand-off, "a contradiction"). -- Otherwise go to step 4. - -**Record:** - -- the entry, in `claims.json` (in a round, the staging's `claims.json`), in the form of - `tools/Prober/Schemas/claims.schema.json`. `universe` refuses a sentence its `source` does not hold: - - ```json - "not-promised": [ - { - "id": "invariant-globalization-refuses-as-text", - "source": "docs/text.md#globalization-invariant-mode", - "sentence": "So `AsText()` throws `PlatformNotSupportedException` there, on its first call, whatever the string.", - "axes": { "text.culture": ["globalization-invariant"], "entry": ["AsText", "AsOptionalText"] } - } - ] - ``` - - committed as `docs:` with the sentence it quotes, beside any docs change; -- with a finding id: - - ```bash - dotnet tools/Prober/bin/Release/net10.0/prober.dll dismiss --id R-C02OptionalTextProbe-1a2b3c4 --reason "triage 3 not-promised C02-optional-text/invariant-globalization-refuses-as-text" - ``` - - and for a gap, `--reason "triage 3 docs-gap: ; entry to add"`. - -## 4. A finding: hand it to the code-analyst - -Only a row that none of steps 1 to 3 explains gets here. It is still not a finding until the -`code-analyst` agent, which reads the product path and seeds the defect in a scratch copy, confirms it. - -**Prepare the hand-off:** - -- **The row**: claim, probe, index, commit, category, the reasons, the settings that applied. -- **The shrunk row**, from the harness's `Shrinker`. There is no prober command for it yet. Run it in the - replay's worktree, which the `try` above has just built with the corpus overlaid: - - ```bash - WT=$(dotnet tools/Prober/bin/Release/net10.0/prober.dll replay --where | awk '/^replay:/ {print $2}') - cd "$WT" && PROBE_SHRINK="C07-fault-responses-blanket:358:baseline" \ - dotnet "$OLDPWD/tools/BuildVerdict/bin/Release/net10.0/build-verdict.dll" test --no-build \ - --project test/CodoMetis.TypeKit.Samples --filter-class "Probes.ShrinkProbe" - cat /tmp/shrink.txt - ``` - - `PROBE_SHRINK` is `::`: the rows file the probe reads (`Rows.Data("...")` in the - probe), the rows, and the check, `baseline` for the HTTP calibration or a name registered in - `ShrinkChecks.All` (C03, C05, L02, L03, L04). A check with no name there is handed over unshrunk, and - the hand-off says so. -- **The obligation** the failing check serves: its id and sentence from the claim's `obligations` (the - probe's header quotes them). When the check serves none of the claim's obligations, say so: "none - listed; the check holds : ". That is itself something for the analyst to - report (below). -- **Each side's answer**: the wrapped twin's with TypeKit, the value object's with TypeKit, and the - framework alone where the row ran it, each as the reasons or the log give it. -- **Why steps 1 to 3 did not apply**, one line each, with the facts and entries you looked at. - -**Hand it over** with the Agent tool, `subagent_type: code-analyst`, and a prompt that starts -`Target: triage hand-off` and carries the five items above. The analyst reads the path the row exercised, -reproduces it outside the probe, seeds the defect, and answers Confirmed, Likely or Question. Where you -cannot start an agent (the `prober` agent started as a subagent), put the hand-off in your report under -"For code-analyst", marked "awaiting confirmation", file nothing, and let the caller hand it over. - -**Then:** - -- **Confirmed:** file `F`, the next number not used in the branch's `findings.jsonl` or the corpus's - `dismissed.jsonl`. The fix is a `fix:` commit with the regression test the analyst named (the - `verify-the-guard` skill: revert it, then the variants), ending with `Defect-Class:` and - `Found-By: PR`, and its body names the analyst's confirmation. 6866822 is the model. -- **Likely or Question:** no `F`. The row stays failing, and the question goes to the maintainer in - the round's report under "For code-analyst", with the analyst's answer. -- **A missing obligation** the analyst reports (a check that holds a promise no claim lists): it is filed - as claims work, the obligation added to the claim whose source states it, or a new claim, with its seed - (decision 104). - -**Record**, only after the analyst confirms, a line appended to the branch's `findings.jsonl`, with the -shrunk probe kept in the memory's `open/`: - -```json -{"schemaVersion":1,"id":"F3","claim":"C07-fault-responses-blanket","status":"open","commit":"00d3b8f6...","probe":"open/F3NullableBodySchemaProbe.cs","reason":"triage 4 finding: an MVC [FromBody] VO? over ulong writes oneOf[null, {type:[integer,string]}] where ulong? writes [null,integer,string]; confirmed by code-analyst 2026-10-16"} -``` - -An `R-` finding that became `F` gets a `withdrawn` line whose reason is `triage 4 finding: filed as F`, -so one defect is open once. - -## What each step leaves behind - -| Step | Finding | Change | Commit | -|---|---|---|---| -| 1 harness error | `withdrawn`, `triage 1 harness-error: ...` | the harness | `test:` | -| 2 fact | `prober dismiss`, `triage 2 fact KB-NNN: ...` | the check cites the fact; a new fact gets its test first | `test:` for the citation, `docs:` or `test:` for the fact | -| 3 not promised | `prober dismiss`, `triage 3 not-promised /` or `triage 3 docs-gap: ...` | the `not-promised` entry, the docs sentence | `docs:` | -| 4 finding | `F` open, `triage 4 finding: ...; confirmed by code-analyst` | the fix and its test | `fix:` with `Found-By: PR` | - -A row without a finding id leaves the change and the commit alone. Every reason starts with -`triage `, so `grep '"triage [123]' findings.jsonl dismissed.jsonl` counts what the rounds cost -and what they found. diff --git a/.claude/skills/verify-the-guard/SKILL.md b/.claude/skills/verify-the-guard/SKILL.md index 1db2c705..b83c563d 100644 --- a/.claude/skills/verify-the-guard/SKILL.md +++ b/.claude/skills/verify-the-guard/SKILL.md @@ -51,8 +51,8 @@ before it is reported done: | `AIS`, what a type argument is | `TypeArgumentKinds`, beside it, 79 kinds and 37 pairs: classes to delegates, arrays, `dynamic`, interfaces, the abstract bases, a type built from a parameter, a parameter under each constraint and attribute, and pairs | An oracle per constraint over what each kind is, from the rule's docs, run as `TypeArgumentConstraintKindTests` runs CMTK0010 | | `AIS`, any other input | The kinds the rule registers (`Register*Action`), plus method groups, crefs, extension blocks (method, property, static member, operator, the lowered call), generic attributes, `nameof`, null-conditional access, lambdas, local functions and generated code (`GeneratedCodeTests`) | No catalogue yet: build it in `Variants/` on `TypeArgumentSites`' pattern, the subject from the rule and the shapes from the catalogue | | `EFT`, a key or a query of the EF satellite | `KeyShapes`, `QueryShapes` and `WrappedKinds` in `test/CodoMetis.TypeKit.EntityFrameworkCore.Tests/Variants/`: 13 key shapes (alone, each mapping's root, abstract or not, the dependents whose key is a foreign key, composite, configured) by 13 wrapped types on three providers; 22 wrapped types, own orderings included, by accessor, site (column, nullable, collection element, captured list, navigation, JSON, query filter), converter (TypeKit's, named, three foreign forms) and operation | A differential against the same entity over the wrapped type, with the docs' departures as the oracle, run as `KeyGenerationVariantTests` and `QueryTranslationVariantTests` do; a new case is a new value of an axis | - | `GTS` | The `shape.*` axes of `.claude/probe-axes.md` and the Samples' value objects | One declaration per value of each axis the fixed code reads (kind, site, inheritance at least), or the rows of the claim that covers it: `prober rows --claim C --full` | - | `FCI` | The settings of the API the fix touches: the `json.*`, `openapi.*`, `mvc.*` axes | The claim's pairwise rows, `prober rows --claim C`, as a theory or as the claim's probe | + | `GTS` | The declaration shapes of `test/oracles/axes.md` and the Samples' value objects | One declaration per value of each axis the fixed code reads (kind, site, inheritance at least) | + | `FCI` | The settings of the API the fix touches: the JSON, OpenAPI and host axes of `test/oracles/axes.md` | A theory over every value of the axes the fix reads, or a probe class over their product (`test/oracles/axes.md`) | Run every shape both ways where the catalogue has two: the breaking input reported (or refused) once, the meeting one never. A false report is a sibling as much as a silence is. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index 1b0c17ac..bd2c952d 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -3,7 +3,7 @@ Closes # + + + + + + + + + + + + + + diff --git a/test/CodoMetis.TypeKit.Oracles.Tests/OracleCandidateTests.cs b/test/CodoMetis.TypeKit.Oracles.Tests/OracleCandidateTests.cs new file mode 100644 index 00000000..035555af --- /dev/null +++ b/test/CodoMetis.TypeKit.Oracles.Tests/OracleCandidateTests.cs @@ -0,0 +1,41 @@ +using System.Text.RegularExpressions; + +namespace CodoMetis.TypeKit.Oracles.Tests; + +///

+/// The candidate registry (test/oracles/candidates.json) names only registered oracles and tracks every entry +/// to an issue or to a labelled failure (design/adr/0112). A misspelt oracle name would match nothing, and the row +/// would silently keep failing as unknown. +/// +public sealed class OracleCandidateTests +{ + [Fact] + public void Every_candidate_names_a_registered_oracle_and_a_row() + { + var oracles = OracleCatalogue.Oracles.Select(oracle => oracle.Class).ToHashSet(StringComparer.Ordinal); + var problems = new List(); + + foreach (var candidate in Candidates.All) + { + if (!oracles.Contains(candidate.Oracle)) problems.Add($"'{candidate.Row}' names oracle '{candidate.Oracle}', which oracles.json does not register"); + if (candidate.Row.Trim().Length < 3) problems.Add($"a candidate of {candidate.Oracle} has no row text"); + if (candidate.Family.Trim().Length == 0) problems.Add($"'{candidate.Row}' has no family label"); + if (candidate.Method is { } method && !Regex.IsMatch(File.ReadAllText(SourceOf(candidate.Oracle)), $@"\b{Regex.Escape(method)}\s*\(")) + problems.Add($"'{candidate.Row}' is scoped to method '{method}', which {candidate.Oracle} does not declare"); + if (candidate.Issue.Length > 0 && !candidate.Issue.StartsWith('#') && !candidate.Issue.StartsWith("http", StringComparison.Ordinal)) + problems.Add($"'{candidate.Row}' has an issue that is neither '#' nor a URL: '{candidate.Issue}'"); + } + + problems.ShouldBeEmpty(); + } + + [Fact] + public void Candidates_are_unique_per_oracle() => + Candidates.All.GroupBy(candidate => (candidate.Oracle, candidate.Row)).Where(group => group.Count() > 1) + .Select(group => $"{group.Key.Oracle}: '{group.Key.Row}'") + .ShouldBeEmpty("Duplicate candidates."); + + private static string SourceOf(string oracle) => + Directory.EnumerateFiles(Path.Combine(OracleCatalogue.Root, "test"), oracle + ".cs", SearchOption.AllDirectories) + .First(path => !path.Contains($"{Path.DirectorySeparatorChar}obj{Path.DirectorySeparatorChar}", StringComparison.Ordinal)); +} diff --git a/test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogue.cs b/test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogue.cs new file mode 100644 index 00000000..9e499a07 --- /dev/null +++ b/test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogue.cs @@ -0,0 +1,187 @@ +using System.Text.Json; +using System.Text.Json.Serialization; +using System.Text.RegularExpressions; + +namespace CodoMetis.TypeKit.Oracles.Tests; + +/// +/// The files under test/oracles/, loaded once: the claims the documents make, the oracle classes that hold +/// them, the seeds that prove the oracles can fail, the waivers for public members no claim covers, and the known +/// disagreements (design/adr/0112). +/// +/// +/// Everything here is data a maintainer edits by hand, and the Oracle*Tests hold it consistent with the +/// repository: a claim's quote must still be in its document, a surface must still exist, an oracle must still be a +/// test class with a seed. The tests are what make the catalogue a worklist rather than a wish list. +/// +internal static partial class OracleCatalogue +{ + // Declared first: static initializers run in textual order, and the loaders below use it. + private static readonly JsonSerializerOptions Json = new() + { + PropertyNameCaseInsensitive = true, + ReadCommentHandling = JsonCommentHandling.Skip, + AllowTrailingCommas = true, + }; + + internal static string Root { get; } = FindRoot(); + + internal static string Directory { get; } = Path.Combine(Root, "test", "oracles"); + + internal static IReadOnlyList Claims { get; } = Load>("claims.json"); + + internal static IReadOnlyList Oracles { get; } = Load>("oracles.json"); + + /// Surface patterns no claim names, each with the reason it needs no oracle. + internal static IReadOnlyDictionary SurfaceWaivers { get; } = Load>("surface-waivers.json"); + + internal static IReadOnlyList Seeds { get; } = + [.. new DirectoryInfo(Path.Combine(Directory, "seeds")).EnumerateFiles("*.patch").OrderBy(file => file.Name, StringComparer.Ordinal).Select(Seed.Read)]; + + /// The documents that make promises to consumers; every one must carry at least one claim. + internal static readonly string[] PromiseDocuments = + [ + "README.md", + "docs/json.md", + "docs/text.md", + "docs/merge-patches.md", + "docs/ef-core.md", + "docs/type-constraints.md", + "src/CodoMetis.TypeKit.AspNetCore/README.md", + "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md", + "src/CodoMetis.TypeKit.Generators/README.md", + "src/CodoMetis.TypeKit.Analyzers/README.md", + "src/CodoMetis.TypeKit.Testing/README.md", + ]; + + /// Every test class under test/, a class whose file holds a [Fact] or a [Theory], with its project. + internal static IReadOnlyDictionary TestClasses { get; } = DiscoverTestClasses(); + + /// GitHub's heading slug: lower case, punctuation dropped, spaces to hyphens. + internal static string Slug(string heading) => + Regex.Replace(Regex.Replace(heading.Trim().ToLowerInvariant(), @"[^\p{L}\p{N}\s-]", ""), @"\s", "-"); + + /// Headings of a Markdown file, as slugs, in order. + internal static IReadOnlyList HeadingSlugs(string relativePath) + { + var inCode = false; + var slugs = new List(); + foreach (var line in File.ReadLines(Path.Combine(Root, relativePath))) + { + if (line.StartsWith("```", StringComparison.Ordinal)) inCode = !inCode; + if (inCode) continue; + var match = Regex.Match(line, @"^#{1,6}\s+(.*?)\s*#*\s*$"); + if (match.Success) slugs.Add(Slug(match.Groups[1].Value)); + } + + return slugs; + } + + /// A file's text with every run of whitespace collapsed to one space, for quote matching across line wraps. + internal static string Flattened(string relativePath) => Regex.Replace(File.ReadAllText(Path.Combine(Root, relativePath)), @"\s+", " "); + + private static T Load(string file) + { + var path = Path.Combine(Directory, file); + using var stream = File.OpenRead(path); + return JsonSerializer.Deserialize(stream, Json) ?? throw new InvalidOperationException($"'{path}' is empty."); + } + + private static Dictionary DiscoverTestClasses() + { + var classes = new Dictionary(StringComparer.Ordinal); + var testRoot = Path.Combine(Root, "test"); + var separator = Path.DirectorySeparatorChar; + + foreach (var file in System.IO.Directory.EnumerateFiles(testRoot, "*.cs", SearchOption.AllDirectories)) + { + if (file.Contains($"{separator}obj{separator}", StringComparison.Ordinal) || file.Contains($"{separator}bin{separator}", StringComparison.Ordinal)) continue; + + var text = File.ReadAllText(file); + if (!TestAttribute().IsMatch(text)) continue; + + var project = Path.GetRelativePath(testRoot, file).Split(separator)[0]; + foreach (Match match in ClassDeclaration().Matches(text)) + classes.TryAdd(match.Groups["name"].Value, project); + } + + return classes; + } + + private static string FindRoot() + { + const string marker = "CodoMetis.TypeKit.slnx"; + for (var directory = new DirectoryInfo(AppContext.BaseDirectory); directory is not null; directory = directory.Parent) + { + if (File.Exists(Path.Combine(directory.FullName, marker))) return directory.FullName; + } + + throw new InvalidOperationException($"Could not find '{marker}' above '{AppContext.BaseDirectory}'."); + } + + [GeneratedRegex(@"\[(Fact|Theory)\b")] + private static partial Regex TestAttribute(); + + [GeneratedRegex(@"(?m)^(?:public|internal)\s+(?:sealed\s+|static\s+|partial\s+|abstract\s+)*class\s+(?\w+)")] + private static partial Regex ClassDeclaration(); + + /// A sentence a document states about the library's behaviour, and what holds it. + internal sealed class Claim + { + public string Id { get; init; } = ""; + + /// file#heading-slug: where the sentence is. + public string Source { get; init; } = ""; + + /// The sentence, verbatim, so the catalogue test can find it in the file. + public string Quote { get; init; } = ""; + + /// The restatement as "under conditions, observable result". + [JsonPropertyName("claim")] + public string Statement { get; init; } = ""; + + /// claim, differential, metamorphic, exception or silent-default. + public string Oracle { get; init; } = ""; + + /// The public members a test of the claim calls, as patterns. + public IReadOnlyList Surfaces { get; init; } = []; + + /// The test classes that assert the claim; empty is the worklist. + public IReadOnlyList HeldBy { get; init; } = []; + + /// The source file, before the #. + internal string File => Source.Split('#')[0]; + + /// The heading slug after the #, or null for a claim about the whole file. + internal string? Heading => Source.Contains('#') ? Source[(Source.IndexOf('#') + 1)..] : null; + } + + /// A test class that checks the library against an independent answer over many cases. + internal sealed record Oracle(string Class, string Project, string Kind, string Against); + + /// A patch under seeds/: its header lines, the oracles it names and the files it changes. + internal sealed record Seed(FileInfo File, string Defect, IReadOnlyList Oracles, string Body) + { + internal static Seed Read(FileInfo file) + { + var text = System.IO.File.ReadAllText(file.FullName); + var diffAt = text.IndexOf("diff --git", StringComparison.Ordinal); + var header = diffAt < 0 ? text : text[..diffAt]; + return new Seed( + file, + HeaderValue(header, "Seed"), + HeaderValue(header, "Oracles").Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries), + diffAt < 0 ? "" : text[diffAt..]); + } + + /// The a/ paths the diff changes. + internal IReadOnlyList ChangedFiles => + [.. Regex.Matches(Body, @"^diff --git a/(?\S+) b/", RegexOptions.Multiline).Select(match => match.Groups["path"].Value)]; + + private static string HeaderValue(string header, string key) + { + var match = Regex.Match(header, $@"^{key}:\s*(?.+?)\s*$", RegexOptions.Multiline); + return match.Success ? match.Groups["value"].Value : ""; + } + } +} diff --git a/test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogueTests.cs b/test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogueTests.cs new file mode 100644 index 00000000..3fe3a0b8 --- /dev/null +++ b/test/CodoMetis.TypeKit.Oracles.Tests/OracleCatalogueTests.cs @@ -0,0 +1,118 @@ +using System.Text.RegularExpressions; + +namespace CodoMetis.TypeKit.Oracles.Tests; + +/// +/// The claim catalogue (test/oracles/claims.json) stays true to the documents it quotes and to the tests it +/// names (design/adr/0112). +/// +/// +/// A claim is a sentence a consumer-facing document states about the library, in a form a test can decide, with the +/// oracle kind, the public members a test of it calls and the test classes that hold it. The catalogue is only worth +/// keeping if it cannot drift: a reworded sentence stales its claim, a renamed test class is found, a surface that no +/// longer exists is noticed. A claim with an empty heldBy is allowed: that is the worklist. +/// +public sealed class OracleCatalogueTests +{ + /// 150 at the first catalogue (2026-10-28). A floor, not a target: discovery would otherwise pass on an empty array. + private const int KnownClaims = 150; + + private static readonly string[] Kinds = ["claim", "differential", "metamorphic", "exception", "silent-default"]; + + [Fact] + public void The_catalogue_is_not_empty_and_its_ids_are_unique_and_sorted() + { + var ids = OracleCatalogue.Claims.Select(claim => claim.Id).ToList(); + + ids.Count.ShouldBeGreaterThanOrEqualTo(KnownClaims); + ids.Distinct(StringComparer.Ordinal).Count().ShouldBe(ids.Count, "Duplicate claim ids."); + ids.ShouldBe(ids.Order(StringComparer.Ordinal).ToList(), "Claims are sorted by id."); + ids.Where(id => !Regex.IsMatch(id, @"^[A-Z]\d{2}-[a-z0-9-]+$")) + .ShouldBeEmpty("Every id is a letter, two digits, a hyphen and a kebab-case slug."); + } + + [Fact] + public void Every_claim_names_an_oracle_kind() => + OracleCatalogue.Claims.Where(claim => !Kinds.Contains(claim.Oracle)).Select(claim => $"{claim.Id}: '{claim.Oracle}'") + .ShouldBeEmpty("The oracle kind is one of " + string.Join("|", Kinds) + "."); + + [Fact] + public void Every_claim_quotes_its_source_verbatim() + { + var problems = new List(); + var flattened = new Dictionary(StringComparer.Ordinal); + var headings = new Dictionary>(StringComparer.Ordinal); + + foreach (var claim in OracleCatalogue.Claims) + { + if (!File.Exists(Path.Combine(OracleCatalogue.Root, claim.File))) + { + problems.Add($"{claim.Id}: source file '{claim.File}' does not exist"); + continue; + } + + if (claim.Heading is { } heading) + { + if (!headings.TryGetValue(claim.File, out var slugs)) headings[claim.File] = slugs = OracleCatalogue.HeadingSlugs(claim.File); + if (!slugs.Contains(heading)) problems.Add($"{claim.Id}: '{claim.File}' has no heading '#{heading}'"); + } + + if (!flattened.TryGetValue(claim.File, out var text)) flattened[claim.File] = text = OracleCatalogue.Flattened(claim.File); + + var quote = Regex.Replace(claim.Quote, @"\s+", " ").Trim(); + if (quote.Length < 20) + problems.Add($"{claim.Id}: the quote is too short to pin a sentence: '{quote}'"); + else if (!text.Contains(quote, StringComparison.Ordinal)) + problems.Add($"{claim.Id}: '{claim.File}' no longer contains the quoted sentence; reword the claim or retire it: '{quote}'"); + } + + problems.ShouldBeEmpty("Claims out of step with their documents."); + } + + [Fact] + public void Every_claim_names_test_classes_that_exist() + { + OracleCatalogue.TestClasses.Count.ShouldBeGreaterThanOrEqualTo(200, "Too few test classes were discovered under test/."); + + OracleCatalogue.Claims.SelectMany(claim => claim.HeldBy.Select(held => (claim.Id, held))) + .Where(pair => !OracleCatalogue.TestClasses.ContainsKey(pair.held)) + .Select(pair => $"{pair.Id} -> {pair.held}") + .ShouldBeEmpty("heldBy names a test class that does not exist."); + } + + [Fact] + public void Every_claim_names_surfaces_that_exist() => + OracleCatalogue.Claims.SelectMany(claim => claim.Surfaces.Select(surface => (claim.Id, surface))) + .Where(pair => ApiSurface.Matching(pair.surface).Count == 0) + .Select(pair => $"{pair.Id} -> '{pair.surface}'") + .ShouldBeEmpty("A claim names a public member that does not exist (keys are TypeName.Member, nested types with '+', '*' is a wildcard)."); + + [Fact] + public void Every_claim_names_at_least_one_surface() => + OracleCatalogue.Claims.Where(claim => claim.Surfaces.Count == 0).Select(claim => claim.Id) + .ShouldBeEmpty("A claim with no surface cannot be tested."); + + [Fact] + public void Every_promise_document_carries_a_claim() + { + var claimed = OracleCatalogue.Claims.Select(claim => claim.File).ToHashSet(StringComparer.Ordinal); + + OracleCatalogue.PromiseDocuments.Where(document => !claimed.Contains(document)) + .ShouldBeEmpty("A consumer-facing document has no claim in the catalogue."); + } + + /// + /// heldBy may name any test class, but a class named like an oracle, a sweep over generated cases or a probe over + /// the product of consumer settings (test/oracles/axes.md), is registered, so that it has a seed. + /// + [Fact] + public void A_class_named_like_an_oracle_that_holds_a_claim_is_registered() + { + var oracles = OracleCatalogue.Oracles.Select(oracle => oracle.Class).ToHashSet(StringComparer.Ordinal); + + OracleCatalogue.Claims.SelectMany(claim => claim.HeldBy).Distinct() + .Where(held => held.EndsWith("OracleTests", StringComparison.Ordinal) || held.EndsWith("ParityTests", StringComparison.Ordinal) || held.EndsWith("ProbeTests", StringComparison.Ordinal)) + .Where(held => !oracles.Contains(held)) + .ShouldBeEmpty("A class named like an oracle holds a claim but is not in oracles.json, so no seed is required of it."); + } +} diff --git a/test/CodoMetis.TypeKit.Oracles.Tests/OracleSeedTests.cs b/test/CodoMetis.TypeKit.Oracles.Tests/OracleSeedTests.cs new file mode 100644 index 00000000..40070919 --- /dev/null +++ b/test/CodoMetis.TypeKit.Oracles.Tests/OracleSeedTests.cs @@ -0,0 +1,94 @@ +using System.Diagnostics; + +namespace CodoMetis.TypeKit.Oracles.Tests; + +/// +/// Every registered oracle ships a seed, and every seed still plants its defect (design/adr/0112). +/// +/// +/// +/// A test that passes with and without a defect is not a guard. The rule has always been to prove a guard by +/// reverting its fix (.claude/skills/verify-the-guard); a seed is that revert kept as a file. +/// test/oracles/seeds/<defect>.patch changes src/ only, names the oracle classes it fails in an +/// Oracles: header line, and was shown to fail them by oracles seeds. +/// +/// +/// These tests build nothing: an oracle without a seed, a seed that names no oracle, a seed that touches a test, and a +/// seed a refactor has stranded all fail here. Whether a seed is still caught is the local tool's job, a copy, a build +/// of one project and a run of one class per seed, before a release. +/// +/// +public sealed class OracleSeedTests +{ + private const int KnownOracles = 5; + + [Fact] + public void Every_oracle_is_a_test_class_in_its_project() + { + OracleCatalogue.Oracles.Count.ShouldBeGreaterThanOrEqualTo(KnownOracles); + + OracleCatalogue.Oracles + .Where(oracle => !OracleCatalogue.TestClasses.TryGetValue(oracle.Class, out var project) || project != oracle.Project) + .Select(oracle => $"{oracle.Class} (registered in {oracle.Project}, found in {(OracleCatalogue.TestClasses.TryGetValue(oracle.Class, out var found) ? found : "no project")})") + .ShouldBeEmpty("oracles.json names a class that is not a test class in that project."); + } + + [Fact] + public void Every_oracle_has_a_seed() + { + var seeded = OracleCatalogue.Seeds.SelectMany(seed => seed.Oracles).ToHashSet(StringComparer.Ordinal); + + OracleCatalogue.Oracles.Select(oracle => oracle.Class).Where(oracle => !seeded.Contains(oracle)) + .ShouldBeEmpty("An oracle has no seed: add test/oracles/seeds/.patch with an 'Oracles:' line naming it, and show it caught with `oracles seeds`."); + } + + [Fact] + public void Every_seed_names_a_defect_and_registered_oracles() + { + OracleCatalogue.Seeds.ShouldNotBeEmpty("No seeds under test/oracles/seeds/."); + + var oracles = OracleCatalogue.Oracles.Select(oracle => oracle.Class).ToHashSet(StringComparer.Ordinal); + var problems = new List(); + foreach (var seed in OracleCatalogue.Seeds) + { + if (seed.Defect.Length == 0) problems.Add($"{seed.File.Name}: no 'Seed:' header line describing the defect"); + if (seed.Oracles.Count == 0) problems.Add($"{seed.File.Name}: no 'Oracles:' header line naming the classes it fails"); + problems.AddRange(seed.Oracles.Where(oracle => !oracles.Contains(oracle)).Select(oracle => $"{seed.File.Name}: names '{oracle}', which oracles.json does not register")); + if (seed.Body.Length == 0) problems.Add($"{seed.File.Name}: holds no diff"); + } + + problems.ShouldBeEmpty(); + } + + [Fact] + public void Every_seed_touches_only_source() => + OracleCatalogue.Seeds.SelectMany(seed => seed.ChangedFiles.Select(file => (seed.File.Name, file))) + .Where(pair => !pair.file.StartsWith("src/", StringComparison.Ordinal)) + .Select(pair => $"{pair.Name} changes '{pair.file}'") + .ShouldBeEmpty("A seed plants a defect in the library, never in a test."); + + [Fact] + public void Every_seed_applies_to_the_current_tree() + { + var problems = new List(); + foreach (var seed in OracleCatalogue.Seeds) + { + var (exit, output) = Git("apply", "--check", seed.File.FullName); + if (exit != 0) problems.Add($"{seed.File.Name} no longer applies; refresh it against src/: {output.Trim()}"); + } + + problems.ShouldBeEmpty(); + } + + private static (int Exit, string Output) Git(params string[] arguments) + { + var info = new ProcessStartInfo("git") { WorkingDirectory = OracleCatalogue.Root, RedirectStandardOutput = true, RedirectStandardError = true }; + foreach (var argument in arguments) info.ArgumentList.Add(argument); + + using var git = Process.Start(info) ?? throw new InvalidOperationException("git could not be started; the seed checks need it on PATH."); + var error = git.StandardError.ReadToEndAsync(); + var output = git.StandardOutput.ReadToEnd(); + git.WaitForExit(); + return (git.ExitCode, output + error.Result); + } +} diff --git a/test/CodoMetis.TypeKit.Oracles.Tests/OracleSurfaceCoverageTests.cs b/test/CodoMetis.TypeKit.Oracles.Tests/OracleSurfaceCoverageTests.cs new file mode 100644 index 00000000..cc46ca96 --- /dev/null +++ b/test/CodoMetis.TypeKit.Oracles.Tests/OracleSurfaceCoverageTests.cs @@ -0,0 +1,59 @@ +namespace CodoMetis.TypeKit.Oracles.Tests; + +/// +/// Every public member of the shipping libraries is named by a claim's surfaces or waived with a reason, so the API +/// cannot grow without deciding which oracle holds it (design/adr/0112). +/// +/// +/// enumerates the members by reflection, test/oracles/claims.json says which a claim +/// covers, and test/oracles/surface-waivers.json says which need no oracle and why. A waiver that no longer +/// matches an unclaimed member is stale and fails too, so the list shrinks as the claims grow. +/// +public sealed class OracleSurfaceCoverageTests +{ + /// A floor against the enumeration going quiet. + private const int KnownMembers = 300; + + [Fact] + public void The_surface_is_enumerated() + { + ApiSurface.Members.Count.ShouldBeGreaterThanOrEqualTo(KnownMembers); + ApiSurface.Members.ShouldContain("Result.CatchAsync"); + ApiSurface.Members.ShouldContain("Option.Some"); + ApiSurface.Members.ShouldContain("FaultResponse.WithType"); + } + + [Fact] + public void Every_public_member_is_claimed_or_waived() + { + var claimed = Claimed(); + var waived = OracleCatalogue.SurfaceWaivers.Keys.SelectMany(ApiSurface.Matching).ToHashSet(StringComparer.Ordinal); + + ApiSurface.Members.Where(member => !claimed.Contains(member) && !waived.Contains(member)) + .ShouldBeEmpty("These public members are named by no claim and no waiver. Either a document promises something about " + + "them, and the claim goes into test/oracles/claims.json, or they need no oracle, and test/oracles/surface-waivers.json says why."); + } + + [Fact] + public void No_waiver_is_stale() + { + var claimed = Claimed(); + var stale = new List(); + + foreach (var (glob, reason) in OracleCatalogue.SurfaceWaivers) + { + var members = ApiSurface.Matching(glob); + if (members.Count == 0) + stale.Add($"'{glob}' matches no public member"); + else if (members.All(claimed.Contains)) + stale.Add($"'{glob}' matches only members a claim already covers ({members.Count}): remove the waiver"); + if (reason.Trim().Length < 10) + stale.Add($"'{glob}' needs a reason"); + } + + stale.ShouldBeEmpty("Waivers to clean up."); + } + + private static HashSet Claimed() => + OracleCatalogue.Claims.SelectMany(claim => claim.Surfaces).Distinct().SelectMany(ApiSurface.Matching).ToHashSet(StringComparer.Ordinal); +} diff --git a/test/CodoMetis.TypeKit.Oracles.Tests/SeedVerdictTests.cs b/test/CodoMetis.TypeKit.Oracles.Tests/SeedVerdictTests.cs new file mode 100644 index 00000000..70830918 --- /dev/null +++ b/test/CodoMetis.TypeKit.Oracles.Tests/SeedVerdictTests.cs @@ -0,0 +1,65 @@ +namespace CodoMetis.TypeKit.Oracles.Tests; + +/// +/// The rule oracles seeds judges a failed test by (), fed the messages +/// writes: a seed is caught when one row of a failed test is not a known candidate, +/// whatever else shares its message. +/// +public sealed class SeedVerdictTests +{ + private static readonly Candidates.Candidate Unfiled = new("AnyOracle", "row 1:", "a known disagreement", Issue: ""); + + [Fact] + public void A_message_with_a_known_row_and_another_row_catches() => + SeedVerdict.Catches(MessageOf([Unfiled], ["row 1: expected 1, got 2", "row 2: expected 1, got 3"])).ShouldBeTrue(); + + [Fact] + public void A_message_whose_every_row_is_known_does_not_catch() => + SeedVerdict.Catches(MessageOf([Unfiled], ["row 1: expected 1, got 2"])).ShouldBeFalse(); + + /// The other rows come first, so a message cut short still shows one; known rows are what is cut. + [Fact] + public void A_message_cut_short_still_shows_the_row_that_catches() + { + var message = MessageOf([Unfiled], [.. Enumerable.Range(0, 30).Select(i => $"row 1: known variant {i}"), "row 2: expected 1, got 3"], shown: 5); + + message.ShouldContain("… and 26 more"); + SeedVerdict.Catches(message).ShouldBeTrue(); + } + + [Fact] + public void A_message_cut_short_whose_every_row_is_known_does_not_catch() + { + var message = MessageOf([Unfiled], [.. Enumerable.Range(0, 30).Select(i => $"row 1: known variant {i}")], shown: 5); + + message.ShouldContain("… and 25 more"); + SeedVerdict.Catches(message).ShouldBeFalse(); + } + + /// A known row whose text spans lines is still one known row: its continuation has no marker and read as a caught row. + [Fact] + public void A_known_row_spanning_lines_does_not_catch() + { + var unfiled = new Candidates.Candidate("AnyOracle", "text {1,", "a known disagreement", Issue: ""); + var message = MessageOf([unfiled], ["text {1,\n2}: expected {1,2}, refused"]); + + SeedVerdict.Catches(message).ShouldBeFalse(message); + message.ShouldContain("{1,⏎2}"); + } + + /// A seed that makes a known row agree changes what the sweep answers, which is a catch. + [Fact] + public void A_candidate_fixed_catches() => SeedVerdict.Catches(MessageOf([Unfiled], [])).ShouldBeTrue(); + + [Theory] + [InlineData("Shouldly.ShouldAssertException: value should be 1 but was 2")] + [InlineData("Assert.Fail: the text '[known candidate: x]' appears inside a line, not as a row")] + public void Any_other_assertion_catches(string message) => SeedVerdict.Catches(message).ShouldBeTrue(); + + [Fact] + public void A_sweep_with_no_failure_and_no_candidate_passes() => + Should.NotThrow(() => Candidates.Judge([], [], "a sweep")); + + private static string MessageOf(IReadOnlyList known, IReadOnlyList failures, int shown = 25) => + Should.Throw(() => Candidates.Judge(known, failures, "a sweep", shown)).Message; +} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/ClaimsFileTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/ClaimsFileTests.cs deleted file mode 100644 index c7fa558b..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/ClaimsFileTests.cs +++ /dev/null @@ -1,159 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// claims.json is read against its schema, and each exclusion carries the reason a reviewer reads (decision 102): -/// a field nobody reads is refused, not kept, and a publish rewrites the file only where it changed. -/// -public sealed class ClaimsFileTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private void Claims(JsonObject claim) - { - var c = new JsonObject { ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s") }; - foreach (var (key, value) in claim) c[key] = value?.DeepClone(); - fx.Write("tools/probe/corpus/claims.json", new JsonArray(c).ToJsonString()); - } - - private static JsonArray Pairs(params (string A, string B)[] pairs) => new([.. pairs.Select(p => (JsonNode)new JsonArray(p.A, p.B))]); - - [Theory] - [InlineData(null, "claim C1: exclude[0].reason is required")] - [InlineData("", "claim C1: exclude[0].reason does not match")] - [InlineData(" ", "claim C1: exclude[0].reason does not match")] - public void An_exclusion_without_a_reason_is_refused_naming_the_claim(string? reason, string expected) - { - var exclusion = new JsonObject { ["pairs"] = Pairs(("x=1", "y=2")) }; - if (reason is not null) exclusion["reason"] = reason; - Claims(new JsonObject { ["exclude"] = new JsonArray(exclusion) }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain(expected); - } - - [Fact] - public void A_bare_pair_is_refused_as_an_exclusion_without_its_reason() - { - Claims(new JsonObject { ["exclude"] = Pairs(("x=1", "y=2")) }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain("claim C1: exclude[0] is not an object"); - } - - [Theory] - [InlineData("excludeReasons")] - [InlineData("excludeWhy")] - public void A_reason_field_of_its_own_is_refused(string field) - { - Claims(new JsonObject - { - ["exclude"] = new JsonArray(new JsonObject { ["reason"] = "No row holds both.", ["pairs"] = Pairs(("x=1", "y=2")) }), - [field] = new JsonArray(new JsonObject { ["match"] = "x=1 x y=2", ["reason"] = "No row holds both." }), - }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain($"claim C1: {field} is not a field the schema has"); - } - - [Fact] - public void A_field_of_its_own_in_an_exclusion_is_refused() - { - Claims(new JsonObject - { - ["exclude"] = new JsonArray(new JsonObject { ["reason"] = "No row holds both.", ["why"] = "twice", ["pairs"] = Pairs(("x=1", "y=2")) }), - }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain("claim C1: exclude[0].why is not a field the schema has"); - } - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void The_universe_shows_each_exclusion_with_its_reason(string newLine) - { - Claims(new JsonObject - { - ["exclude"] = new JsonArray(new JsonObject { ["reason"] = "No row holds x=1 with y=2.", ["pairs"] = Pairs(("y=2", "x=1")) }), - }); - var (exit, output, error) = fx.LedgerEnding([], newLine, "universe", "--base", "HEAD"); - exit.ShouldBe(0, error); - output.ReplaceLineEndings("\n").ShouldContain("C1: 2 axes, 3 pairs\n exclude 1 pair(s), first x=1 y=2: No row holds x=1 with y=2.\n"); - } - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void The_universe_shows_each_narrowing_with_its_quote(string newLine) - { - Claims(new JsonObject { ["narrow"] = new JsonObject { ["y"] = "The claim." } }); - var (exit, output, error) = fx.LedgerEnding([], newLine, "universe", "--base", "HEAD"); - exit.ShouldBe(0, error); - output.ReplaceLineEndings("\n").ShouldContain("C1: 1 axes, 0 pairs\n narrow y: \"The claim.\"\n"); - } - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void A_publish_that_changes_nothing_keeps_the_file_as_it_is(string staged) - { - // The form publish writes, by hand: a reason with a backtick, a plus and angle brackets, all literal, and \n line - // ends, whatever this source file was checked out with. The staged copy may come with either line end. - var tracked = """ - [ - { - "id": "C1", - "source": "README.md#a", - "surfaces": [ - "s" - ], - "exclude": [ - { - "reason": "A `+` row and an Option row cannot meet.", - "pairs": [ - [ - "x=1", - "y=2" - ] - ] - } - ] - } - ] - - """.ReplaceLineEndings("\n"); - fx.Write("tools/probe/corpus/claims.json", tracked); - fx.Commit("claims"); - fx.Write(".claude/agent-memory-local/prober/branches/main/staging/claims.json", tracked.ReplaceLineEndings(staged)); - var (exit, _, error) = fx.Ledger("publish"); - exit.ShouldBe(0, error); - fx.Sh("git", "diff", "--exit-code", "--", "tools/probe/corpus/claims.json"); - File.ReadAllText(Path.Combine(fx.Root, "tools/probe/corpus/claims.json")).ShouldBe(tracked, "byte for byte: the escapes and the line ends are the ones publish writes"); - } - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void The_corpus_claims_match_their_schema_in_the_form_publish_writes(string checkedOut) - { - var root = AppContext.BaseDirectory; - while (!File.Exists(Path.Combine(root, "CodoMetis.TypeKit.slnx"))) root = Path.GetDirectoryName(root) ?? throw new InvalidOperationException("no CodoMetis.TypeKit.slnx above the test output"); - var path = Path.Combine(root, "tools/probe/corpus/claims.json"); - var claims = Ledger.ReadClaims(path, "tools/probe/corpus/claims.json"); - claims.ShouldNotBeEmpty(); - // Git may check the file out with either line end (core.autocrlf on Windows); what it stores, and diffs, has \n. - var text = File.ReadAllText(path).ReplaceLineEndings(checkedOut); - Files.Format(JsonNode.Parse(text)!).ShouldBe(text.ReplaceLineEndings("\n"), "claims.json is in the form `prober publish` writes, so a publish diffs only what it changes"); - } - - [Fact] - public void A_schema_keyword_the_check_does_not_read_is_an_error_not_a_pass() - { - var schema = new JsonObject { ["type"] = "string", ["maxLength"] = 3 }; - Should.Throw(() => SchemaCheck.Problems("abcd", schema)).Message.ShouldContain("maxLength"); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/CodoMetis.TypeKit.Prober.Tests.csproj b/test/CodoMetis.TypeKit.Prober.Tests/CodoMetis.TypeKit.Prober.Tests.csproj deleted file mode 100644 index 2126c5f2..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/CodoMetis.TypeKit.Prober.Tests.csproj +++ /dev/null @@ -1,29 +0,0 @@ - - - - - - - - - - - - - - - - - <_Parameter1>ToolOutput - <_Parameter2>$([System.IO.Path]::GetFullPath('$(MSBuildThisFileDirectory)../../tools/Prober/bin/$(Configuration)/$(TargetFramework)/')) - - - - diff --git a/test/CodoMetis.TypeKit.Prober.Tests/CoveringTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/CoveringTests.cs deleted file mode 100644 index 0a824ae1..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/CoveringTests.cs +++ /dev/null @@ -1,80 +0,0 @@ -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// The covering array never puts an excluded pair or one no obligation wants in a row, not even to fill an axis whose every -/// value would bring one (#54 and #59 saw `rows` place them): such an axis is left out of that row, and an axis with no wanted -/// pair at all is left out of every row. -/// -public sealed class CoveringTests -{ - private static Inapplicability Declared(string cite, params (string Axis, string[] Values)[] axes) => - new(axes.ToDictionary(a => a.Axis, a => a.Values.ToList(), StringComparer.Ordinal), cite); - - private static Claim Over(string[] axes, IEnumerable? excluded = null, params Inapplicability[] declared) => new() - { - Id = "C1", Source = "README.md#a", Surfaces = ["s"], Watched = [], Excluded = [.. excluded ?? []], - Obligations = declared.Length == 0 ? [] : [new("keys", "The claim.", new SortedDictionary>(StringComparer.Ordinal)) { Inapplicable = declared }], - Axes = new(axes.ToDictionary(a => a, _ => new List { "1", "2" }), StringComparer.Ordinal), - }; - - private static bool Holds(IReadOnlyDictionary row, Pair pair) => - new[] { pair.A, pair.B }.All(item => row.TryGetValue(item[..item.IndexOf('=', StringComparison.Ordinal)], out var v) && v == item[(item.IndexOf('=', StringComparison.Ordinal) + 1)..]); - - private static IEnumerable Shown(IEnumerable pairs) => pairs.Select(p => p.ToString()).Order(StringComparer.Ordinal); - - [Fact] - public void An_axis_whose_every_value_brings_an_excluded_pair_is_left_out_of_the_row() - { - // x=1 goes with no y (as shape.containing goes with no shape.position=root): a row holding x=1 holds no y. - Pair[] excluded = [new("x=1", "y=1"), new("x=1", "y=2")]; - var claim = Over(["x", "y", "z"], excluded); - for (var seed = 1; seed <= 20; seed++) - { - var rows = Covering.Rows(claim, claim.Pairs(), 2000, seed); - rows.ShouldNotContain(r => excluded.Any(p => Holds(r.Row, p)), $"seed {seed}"); - rows.Where(r => r.Row["x"] == "1").ShouldAllBe(r => !r.Row.ContainsKey("y"), $"seed {seed}"); - Shown(rows.SelectMany(r => r.Pairs)).ShouldBe(Shown(claim.Pairs()), $"seed {seed}: every pair once, x=1 with z among them"); - } - } - - [Fact] - public void An_axis_whose_every_value_brings_a_pair_no_obligation_wants_is_left_out_of_the_row() - { - var claim = Over(["x", "y", "z"], null, Declared("record:0001", ("x", ["1"]), ("y", ["1", "2"]))); - var dead = claim.DeclaredInapplicable().Keys.ToList(); - dead.Count.ShouldBe(2); - for (var seed = 1; seed <= 20; seed++) - { - var rows = Covering.Rows(claim, claim.PairsWanted(), 2000, seed); - rows.ShouldNotContain(r => dead.Any(p => Holds(r.Row, p)), $"seed {seed}"); - Shown(rows.SelectMany(r => r.Pairs)).ShouldBe(Shown(claim.PairsWanted()), $"seed {seed}"); - } - } - - [Fact] - public void An_axis_with_no_wanted_pair_is_in_no_row() - { - // every pair of z is excluded (as shape.refused-kind is where the refusal is not the claim): rows over x and y alone. - var claim = Over(["x", "y", "z"], [.. from x in new[] { "x=1", "x=2", "y=1", "y=2" } from z in new[] { "z=1", "z=2" } select new Pair(x, z)]); - for (var seed = 1; seed <= 5; seed++) - { - var rows = Covering.Rows(claim, claim.Pairs(), 2000, seed); - rows.ShouldAllBe(r => !r.Row.ContainsKey("z"), $"seed {seed}"); - Shown(rows.SelectMany(r => r.Pairs)).ShouldBe(["x=1 y=1", "x=1 y=2", "x=2 y=1", "x=2 y=2"], $"seed {seed}"); - } - } - - [Fact] - public void A_seeded_row_leaves_out_an_axis_no_value_of_which_can_join_it() - { - // c=1 goes with no d: the row seeded from a=1 c=1 is a, b and c. - var claim = Over(["a", "b", "c", "d"], [new("c=1", "d=1"), new("c=1", "d=2")]); - Covering.Seeded(claim, new Pair("a=1", "c=1"), claim.DeclaredInapplicable()) - .ShouldBe(new SortedDictionary(StringComparer.Ordinal) { ["a"] = "1", ["b"] = "1", ["c"] = "1" }); - Covering.Seeded(claim, new Pair("a=1", "b=2"), claim.DeclaredInapplicable()) - .ShouldBe(new SortedDictionary(StringComparer.Ordinal) { ["a"] = "1", ["b"] = "2", ["c"] = "1" }, - "c=1 is c's first value, and d then has none"); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/DeclaredApplicabilityTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/DeclaredApplicabilityTests.cs deleted file mode 100644 index aae5e585..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/DeclaredApplicabilityTests.cs +++ /dev/null @@ -1,439 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; -using Probes.Shapes; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// An obligation declares where it does not apply, as axis values with a citation: Axes.Build holds the -/// declaration to the claim's axes, values and not-promised list; a pair is wanted by an obligation unless one of -/// its declarations matches it, and a pair no obligation wants is declared inapplicable, which `universe`, `gaps` -/// and `status` show with its citations, the gate leaves out as it leaves out an exclusion, and the covering -/// generator never puts in a row. Fixture: C1 over x and y, each 1 and 2, so four pairs. -/// -public sealed class DeclaredApplicabilityTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private static JsonObject Declared(string cite, params (string Axis, string[] Values)[] axes) => new() - { - ["axes"] = new JsonObject(axes.Select(a => KeyValuePair.Create(a.Axis, (JsonNode?)new JsonArray([.. a.Values.Select(v => (JsonNode)v)])))), - ["cite"] = cite, - }; - - private static JsonObject Obligation(string id, params JsonObject[] inapplicable) - { - var o = new JsonObject { ["id"] = id, ["sentence"] = "The claim." }; - if (inapplicable.Length > 0) o["inapplicable"] = new JsonArray([.. inapplicable]); - return o; - } - - /// C1 with the obligations given and one not-promised entry, no-one. - private void Claim(params JsonObject[] obligations) => - fx.Write("tools/probe/corpus/claims.json", new JsonArray(new JsonObject - { - ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["obligations"] = new JsonArray([.. obligations]), - ["not-promised"] = new JsonArray(new JsonObject { ["id"] = "no-one", ["source"] = "README.md#a", ["sentence"] = "The claim." }), - }).ToJsonString()); - - /// - /// keys does not apply where x=1 and y=1 (one declaration over both axes), nulls nowhere x=1: the pair x=1 y=1 is - /// wanted by neither, x=1 y=2 by keys alone. - /// - private void OnePairWantedByNone() => Claim( - Obligation("keys", Declared("docs:README.md#a", ("x", ["1"]), ("y", ["1"]))), - Obligation("nulls", Declared("not-promised:no-one", ("x", ["1"])))); - - private string Refused() - { - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - return error; - } - - [Fact] - public void A_declaration_naming_an_axis_the_claim_does_not_have_is_refused() - { - Claim(Obligation("keys", Declared("not-promised:no-one", ("z", ["1"])))); - Refused().ShouldContain("claim C1: obligation keys: inapplicable[0] names axis z, which the claim does not have"); - } - - [Fact] - public void A_declaration_naming_a_value_the_claim_does_not_list_is_refused() - { - Claim(Obligation("keys", Declared("not-promised:no-one", ("x", ["1"]))), Obligation("nulls", Declared("not-promised:no-one", ("x", ["3"])))); - Refused().ShouldContain("claim C1: obligation nulls: inapplicable[0] names x=3, a value the claim does not list for x"); - } - - [Theory] - [InlineData("fact:KB-31", "is not a knowledge entry's id (fact:KB-031)")] - [InlineData("record:94", "is not a decision record's number (record:0094)")] - [InlineData("docs:../README.md#a", "is not a docs source (docs:docs/json.md#decimals-in-one-spelling)")] - [InlineData("not-promised:No-one", "is not a not-promised id (not-promised:closed-set-has-no-text)")] - public void A_malformed_cite_is_refused(string cite, string why) - { - Claim(Obligation("keys", Declared(cite, ("x", ["1"])))); - Refused().ShouldContain($"claim C1: obligation keys: inapplicable[0]: cite {cite} {why}"); - } - - [Fact] - public void A_cite_of_a_not_promised_id_the_claim_lacks_is_refused() - { - Claim(Obligation("keys", Declared("not-promised:no-such-entry", ("x", ["1"])))); - Refused().ShouldContain("claim C1: obligation keys: inapplicable[0] cites not-promised:no-such-entry, which the claim's not-promised list does not hold"); - } - - [Fact] - public void A_field_a_declaration_does_not_have_is_refused_by_the_schema() - { - var declaration = Declared("not-promised:no-one", ("x", ["1"])); - declaration["reason"] = "x=1 has no key"; - Claim(Obligation("keys", declaration)); - Refused().ShouldContain("claim C1: obligations[0].inapplicable[0].reason is not a field the schema has"); - } - - [Fact] - public void A_pair_wanted_by_no_obligation_is_listed_by_universe_with_its_citations_and_the_claim_keeps_its_pairs() - { - OnePairWantedByNone(); - var (exit, output, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(0, error); - output.ReplaceLineEndings("\n").ShouldContain( - "C1: 2 axes, 4 pairs\n inapplicable 1 pair(s), wanted by no obligation, first x=1 y=1: docs:README.md#a, not-promised:no-one\n"); - } - - [Fact] - public void Gaps_lists_the_pairs_no_obligation_wants_under_their_own_heading() - { - OnePairWantedByNone(); - var (exit, output, error) = fx.Ledger("gaps", "--base", "HEAD"); - exit.ShouldBe(0, error); - output.ReplaceLineEndings("\n").ShouldContain( - "pairs no obligation wants (declared inapplicable): 1\n C1: inapplicable 1 pair(s), wanted by no obligation, first x=1 y=1: docs:README.md#a, not-promised:no-one\n"); - } - - private static Dictionary R(string x, string y) => new(StringComparer.Ordinal) { ["x"] = x, ["y"] = y }; - - /// P passes rows over the three pairs some obligation wants, never x=1 y=1. - private void RecordTheWantedPairs() - { - fx.Rows([("1", "2"), ("2", "1"), ("2", "2")]); - fx.Commit("claims and rows"); - fx.Ledger("universe", "--base", "HEAD").Exit.ShouldBe(0); - fx.Footprint(); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", fx.Evidence([R("1", "2"), R("2", "1"), R("2", "2")])).Exit.ShouldBe(0); - } - - [Fact] - public void Status_reports_the_inapplicable_pairs_and_is_done_once_every_wanted_pair_is_current() - { - OnePairWantedByNone(); - RecordTheWantedPairs(); - var (exit, output, error) = fx.Ledger("status", "--json"); - exit.ShouldBe(0, error); - var status = JsonNode.Parse(output)!; - var c1 = status["claims"]!["C1"]!; - c1["pairs"]!.GetValue().ShouldBe(4, "the claim's pairs stay its pairs"); - c1["current"]!.GetValue().ShouldBe(3); - c1["inapplicable_pairs"]!.GetValue().ShouldBe(1); - status["inapplicable_pairs"]!.GetValue().ShouldBe(1); - status["done"]!.GetValue().ShouldBeTrue("the one pair not current is wanted by no obligation"); - fx.Ledger("status").Out.ShouldContain(" C1: 3/4, 1 inapplicable"); - } - - [Fact] - public void Status_is_not_done_while_a_wanted_pair_is_not_current() - { - Claim(Obligation("keys"), Obligation("nulls")); - RecordTheWantedPairs(); - var status = JsonNode.Parse(fx.Ledger("status", "--json").Out)!; - status["claims"]!["C1"]!["inapplicable_pairs"]!.GetValue().ShouldBe(0); - status["done"]!.GetValue().ShouldBeFalse("x=1 y=1 is wanted and not current"); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void The_gate_leaves_out_a_pair_wanted_by_no_obligation_as_it_leaves_out_an_exclusion(bool declared) - { - fx.Sh("git", "tag", "v1"); - if (declared) OnePairWantedByNone(); else Claim(Obligation("keys"), Obligation("nulls")); - RecordTheWantedPairs(); - fx.Write("README.md", "# a\n\nThe claim.\n\nMore words.\n"); - fx.Commit("touch the claim"); - - var (exit, output, _) = fx.Ledger("gate", "--since", "v1", "--json"); - var gate = JsonNode.Parse(output)!; - gate["touched"]!.AsArray().Select(c => c!.GetValue()).ShouldBe(["C1"]); - if (declared) - { - exit.ShouldBe(0, output); - gate["uncovered_pairs"]!.GetValue().ShouldBe(0); - gate["inapplicable_pairs"]!.GetValue().ShouldBe(1); - } - else - { - exit.ShouldBe(1); - gate["reasons"]!.AsArray().Select(r => r!.GetValue()).ShouldContain("C1: 1 of 4 pairs uncovered (touched since v1)"); - } - } - - [Fact] - public void Rows_for_a_claim_ask_for_no_pair_no_obligation_wants() - { - OnePairWantedByNone(); - fx.Commit("claims"); - fx.Ledger("universe", "--base", "HEAD").Exit.ShouldBe(0); - var rows = JsonNode.Parse(fx.Ledger("rows", "--claim", "C1", "--full").Out)!; - rows["rows"]!.AsArray().Select(r => $"{r!["row"]!["x"]} {r["row"]!["y"]}").ShouldNotContain("1 1"); - rows["uncovered_after"]!.GetValue().ShouldBe(0, "a pair wanted by no obligation is not left uncovered"); - } - - [Fact] - public void A_declaration_changes_the_identity_of_its_claim_s_probes() - { - Claim(Obligation("keys"), Obligation("nulls")); - fx.Commit("claims"); - var before = fx.Ledger("identity", "--probe", "P").Out; - OnePairWantedByNone(); - fx.Commit("declared"); - fx.Ledger("identity", "--probe", "P").Out.ShouldNotBe(before, "Row.Check applies the declarations, so a result under others is not this one"); - } - - [Fact] - public void The_overlay_stages_the_claims_beside_the_rows_with_the_staging_s_over_the_corpus_s() - { - Claim(Obligation("keys"), Obligation("nulls")); - fx.Commit("claims"); - var ledger = new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), "main"); - var staged = new JsonArray(new JsonObject - { - ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["obligations"] = new JsonArray(Obligation("keys", Declared("docs:README.md#a", ("x", ["1"])))), - }); - fx.Write(Path.GetRelativePath(fx.Root, Path.Combine(ledger.StagingDir, "claims.json")), staged.ToJsonString()); - var worktree = fx.Root + "-overlay"; - try - { - var probeDir = global::Prober.Replay.Overlay(ledger, new JsonObject { ["probeDir"] = "probes" }, worktree); - var claims = JsonNode.Parse(File.ReadAllText(Path.Combine(probeDir, "claims.json")))!.AsArray(); - JsonNode.DeepEquals(claims, staged).ShouldBeTrue(claims.ToJsonString()); - global::Prober.Replay.Overlay(ledger, new JsonObject { ["probeDir"] = "probes" }, worktree); - File.Exists(Path.Combine(probeDir, "claims.json")).ShouldBeTrue("a second overlay keeps it"); - } - finally - { - try { Directory.Delete(worktree, recursive: true); } catch (IOException) { } - } - } -} - -/// The wanted pairs of a claim, computed on the claim itself, and the covering generator over them. -public sealed class WantedPairsTests -{ - private static Inapplicability Declared(string cite, params (string Axis, string[] Values)[] axes) => - new(axes.ToDictionary(a => a.Axis, a => a.Values.ToList(), StringComparer.Ordinal), cite); - - private static global::Prober.Obligation Obligation(string id, params Inapplicability[] inapplicable) => - new(id, "The claim.", new SortedDictionary>(StringComparer.Ordinal)) { Inapplicable = inapplicable }; - - private static Claim Over(int axes, params global::Prober.Obligation[] obligations) => new() - { - Id = "C1", Source = "README.md#a", Surfaces = ["s"], Watched = [], Excluded = [], Obligations = obligations, - Axes = new(new[] { "x", "y", "z" }.Take(axes).ToDictionary(a => a, _ => new List { "1", "2" }), StringComparer.Ordinal), - }; - - private static IEnumerable Shown(IEnumerable pairs) => pairs.Select(p => p.ToString()).Order(StringComparer.Ordinal); - - [Fact] - public void An_obligation_wants_the_pairs_none_of_its_declarations_matches_on_either_side() - { - // keys: not where x=1 (the first of a pair, ordinally); nulls: not where y=1 (the second); counts: nowhere declared. - var claim = Over(2, Obligation("keys", Declared("record:0001", ("x", ["1"]))), Obligation("nulls", Declared("record:0002", ("y", ["1"]))), Obligation("counts")); - Shown(claim.PairsWantedBy("keys")).ShouldBe(["x=2 y=1", "x=2 y=2"]); - Shown(claim.PairsWantedBy("nulls")).ShouldBe(["x=1 y=2", "x=2 y=2"]); - Shown(claim.PairsWantedBy("counts")).ShouldBe(["x=1 y=1", "x=1 y=2", "x=2 y=1", "x=2 y=2"]); - claim.Pairs().Count.ShouldBe(4, "the claim's pairs stay its pairs"); - claim.DeclaredInapplicable().ShouldBeEmpty("counts wants every pair"); - } - - [Fact] - public void A_pair_no_obligation_wants_is_declared_inapplicable_with_the_citations_that_match_it() - { - // keys wants x=2 y=1 alone, nulls x=1 y=2 alone. - var claim = Over(2, - Obligation("keys", Declared("record:0001", ("x", ["1"])), Declared("record:0003", ("y", ["2"]))), - Obligation("nulls", Declared("record:0002", ("y", ["1"])), Declared("record:0004", ("x", ["2"])))); - Shown(claim.PairsWanted()).ShouldBe(["x=1 y=2", "x=2 y=1"]); - var dead = claim.DeclaredInapplicable(); - Shown(dead.Keys).ShouldBe(["x=1 y=1", "x=2 y=2"]); - dead[new Pair("x=1", "y=1")].ShouldBe(["record:0001", "record:0002"]); - dead[new Pair("x=2", "y=2")].ShouldBe(["record:0003", "record:0004"]); - } - - [Fact] - public void A_declaration_over_two_axes_matches_only_the_pair_holding_both() - { - var claim = Over(3, Obligation("keys", Declared("record:0001", ("x", ["1"]), ("y", ["1"])))); - Shown(claim.DeclaredInapplicable().Keys).ShouldBe(["x=1 y=1"], "x=1 with z, or y=1 with z, is in rows where the other axis is 2"); - } - - [Fact] - public void The_covering_rows_never_hold_a_pair_no_obligation_wants_and_count_each_wanted_pair_once() - { - // Both obligations want the same eleven pairs: none of x=1 with y=1. - var claim = Over(3, Obligation("keys", Declared("record:0001", ("x", ["1"]), ("y", ["1"]))), Obligation("nulls", Declared("record:0002", ("x", ["1"]), ("y", ["1"])))); - for (var seed = 1; seed <= 20; seed++) - { - var rows = Covering.Rows(claim, claim.Pairs(), 2000, seed); - rows.ShouldNotContain(r => r.Row["x"] == "1" && r.Row["y"] == "1", $"seed {seed}"); - var gained = rows.SelectMany(r => r.Pairs).ToList(); - gained.Count.ShouldBe(11, $"seed {seed}: each wanted pair once"); - Shown(gained).ShouldBe(Shown(claim.PairsWanted()), $"seed {seed}"); - } - } - - [Fact] - public void A_seeded_row_fills_each_other_axis_with_a_value_that_puts_no_unwanted_pair_in_it() - { - var claim = Over(3, Obligation("keys", Declared("record:0001", ("x", ["1"]), ("z", ["1"])))); - var row = Covering.Seeded(claim, new Pair("y=1", "z=1"), claim.DeclaredInapplicable()); - row.ShouldBe(new SortedDictionary(StringComparer.Ordinal) { ["x"] = "2", ["y"] = "1", ["z"] = "1" }); - } - - [Theory] - [InlineData("not-promised:closed-set-has-no-text")] - [InlineData("not-promised:Closed")] - [InlineData("docs:docs/json.md#decimals-in-one-spelling")] - [InlineData("docs:README.md")] - [InlineData("docs:README.md#What TypeKit guarantees")] - [InlineData("docs:/etc/passwd.md")] - [InlineData("docs:../README.md#a")] - [InlineData("fact:KB-031")] - [InlineData("fact:KB-31")] - [InlineData("record:0094")] - [InlineData("record:94")] - [InlineData("README.md#a")] - [InlineData("note:x")] - public void A_cite_the_universe_accepts_is_one_the_harness_can_cite(string cite) - { - var harness = Record.Exception(() => Cite.Parse(cite)) is null; - (Axes.CiteProblem(cite) is null).ShouldBe(harness, harness ? "the harness cites it" : "the harness refuses it"); - } -} - -/// -/// The harness applies the claim's declarations before a check's body runs: where one matches the row's values the -/// check is inapplicable with the declaration's citation, for that check only, and the body does not run. Without a -/// staged claims file, as for a probe these tests run in-process, no claim declares anything. -/// -[Collection(nameof(ProbeOut))] -public sealed class DeclaredCheckTests : IDisposable -{ - private readonly string dir = Path.Combine(Path.GetTempPath(), "prober-tests", Guid.NewGuid().ToString("N")); - private readonly string? before = Rows.ClaimsFile; - - public DeclaredCheckTests() - { - Directory.CreateDirectory(dir); - File.WriteAllText(Path.Combine(dir, "claims.json"), new JsonArray(new JsonObject - { - ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["obligations"] = new JsonArray( - new JsonObject - { - ["id"] = "keys", ["sentence"] = "The claim.", - ["inapplicable"] = new JsonArray( - new JsonObject { ["axes"] = new JsonObject { ["x"] = new JsonArray("1"), ["y"] = new JsonArray("2") }, ["cite"] = "not-promised:no-key-here" }, - new JsonObject { ["axes"] = new JsonObject { ["y"] = new JsonArray("3") }, ["cite"] = "fact:KB-031" }), - }, - new JsonObject { ["id"] = "nulls", ["sentence"] = "The claim." }), - }).ToJsonString()); - Rows.ClaimsFile = Path.Combine(dir, "claims.json"); - } - - public void Dispose() - { - Rows.ClaimsFile = before; - try { Directory.Delete(dir, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - /// Runs one row of C1 through Rec.Run with a check of keys and one of nulls, synchronous or not; which bodies ran, and the line written. - private (List Ran, Exception? Ended, JsonObject Line) Run(string x, string y, bool async = false, string? claim = "C1") - { - var ran = new List(); - var outDir = Path.Combine(dir, "out"); - var previous = Environment.GetEnvironmentVariable("PROBE_OUT"); - Environment.SetEnvironmentVariable("PROBE_OUT", outDir); - Exception? ended = null; - try - { - Rec.Run("P", new Row(0, new Dictionary { ["x"] = x, ["y"] = y }, claim), async r => - { - foreach (var obligation in new[] { "keys", "nulls" }) - _ = async - ? await r.Check(obligation, Baseline.Twin, c => { ran.Add(obligation); return Task.FromResult(c["x"] == "0" ? "differs" : null); }) - : r.Check(obligation, Baseline.Twin, c => { ran.Add(obligation); return c["x"] == "0" ? "differs" : null; }); - return null; - }).GetAwaiter().GetResult(); - } - catch (Exception e) { ended = e; } - finally { Environment.SetEnvironmentVariable("PROBE_OUT", previous); } - var line = File.ReadAllLines(Path.Combine(outDir, "P.jsonl")).Select(l => JsonNode.Parse(l)!.AsObject()).Last(); - File.Delete(Path.Combine(outDir, "P.jsonl")); - return (ran, ended, line); - } - - [Theory] - [InlineData(false)] - [InlineData(true)] - public void A_check_its_claim_declares_inapplicable_for_the_row_s_values_does_not_run_and_records_the_cite(bool async) - { - var (ran, ended, line) = Run("1", "2", async); - ran.ShouldBe(["nulls"], "keys does not apply where x=1 and y=2"); - ended.ShouldBeNull("nulls compared and passed; keys did not apply, which voids nothing"); - var keys = HarnessRun.Check(line, "keys"); - Files.Str(keys["category"]).ShouldBe("inapplicable"); - Files.Strings(keys["reasons"]).ShouldBe(["inapplicable:not-promised:no-key-here(declared x=1 y=2)"]); - Files.StringMap(keys["applied"]).ShouldBe(new Dictionary { ["x"] = "1", ["y"] = "2" }, "the axes the declaration matched"); - Files.Str(HarnessRun.Check(line, "nulls")["category"]).ShouldBe("pass", "the declaration is keys', for that check only"); - } - - [Fact] - public void Each_declaration_of_the_obligation_is_applied_and_one_over_two_axes_needs_both() - { - Run("1", "1").Ran.ShouldBe(["keys", "nulls"], "x=1 with y=1 is not x=1 with y=2"); - Run("2", "2").Ran.ShouldBe(["keys", "nulls"], "x=2 with y=2 is not x=1 with y=2"); - var (ran, _, line) = Run("2", "3"); - ran.ShouldBe(["nulls"]); - Files.Strings(HarnessRun.Check(line, "keys")["reasons"]).ShouldBe(["inapplicable:fact:KB-031(declared y=3)"]); - } - - [Fact] - public void A_row_in_hand_without_a_claim_or_without_a_staged_claims_file_sees_no_declarations() - { - Run("1", "2", claim: null).Ran.ShouldBe(["keys", "nulls"]); - Rows.ClaimsFile = null; - Run("1", "2").Ran.ShouldBe(["keys", "nulls"]); - Rows.ClaimsFile = Path.Combine(dir, "none.json"); - Run("1", "2").Ran.ShouldBe(["keys", "nulls"]); - } - - [Fact] - public void A_copy_of_a_row_keeps_its_claim() - { - var row = new Row(0, new Dictionary { ["x"] = "1", ["y"] = "2" }, "C1"); - row.Fresh().Claim.ShouldBe("C1"); - row.With("y", "1").Claim.ShouldBe("C1"); - } - - [Fact] - public void A_row_of_a_rows_file_holds_the_file_s_claim() - { - var path = Path.Combine(dir, "P.json"); - File.WriteAllText(path, """{"claim": "C1", "rows": [{"row": {"x": "1", "y": "2"}}]}"""); - Rows.Read(path).ShouldHaveSingleItem().Claim.ShouldBe("C1"); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/DotnetTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/DotnetTests.cs deleted file mode 100644 index 40c07acf..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/DotnetTests.cs +++ /dev/null @@ -1,73 +0,0 @@ -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// Which dotnet a tool starts (tools/Shared/Dotnet.cs, design/adr/0105): the one on PATH when it satisfies -/// global.json, the SDK installer's when only that one does, and otherwise the one on PATH, whose own error then -/// reaches whoever reads the output. -/// -public sealed class DotnetTests : IDisposable -{ - private readonly string installer = Directory.CreateTempSubdirectory("dotnet-installer-").FullName; - public void Dispose() => Directory.Delete(installer, recursive: true); - - private string Executable => Path.Combine(installer, OperatingSystem.IsWindows() ? "dotnet.exe" : "dotnet"); - - private void Install() => File.WriteAllText(Executable, ""); - - [Fact] - public void The_one_on_path_when_it_satisfies_the_pin() - { - Install(); - var asked = new List(); - Dotnet.Choose(installer, d => { asked.Add(d); return true; }).ShouldBe(("dotnet", null)); - asked.ShouldBe(["dotnet"], "the installer's is not even asked"); - } - - [Fact] - public void The_installers_when_only_it_satisfies_the_pin() - { - Install(); - Dotnet.Choose(installer, d => d != "dotnet").ShouldBe((Executable, installer)); - } - - [Fact] - public void The_one_on_path_when_neither_does() - { - Install(); - Dotnet.Choose(installer, _ => false).ShouldBe(("dotnet", null)); - } - - [Fact] - public void The_one_on_path_when_the_installer_put_none() - { - var asked = new List(); - Dotnet.Choose(installer, d => { asked.Add(d); return d != "dotnet"; }).ShouldBe(("dotnet", null)); - asked.ShouldBe(["dotnet"]); - } - - [Fact] - public void A_dotnet_satisfies_a_directory_whose_pin_it_has_and_not_one_whose_pin_it_lacks() - { - Dotnet.Satisfies("dotnet", installer).ShouldBeTrue("no global.json: any SDK"); - File.WriteAllText(Path.Combine(installer, "global.json"), """{"sdk": {"version": "0.0.1", "rollForward": "disable"}}"""); - Dotnet.Satisfies("dotnet", installer).ShouldBeFalse("an SDK no dotnet has"); - } - - [Fact] - public void A_program_that_does_not_start_satisfies_nothing() => - Dotnet.Satisfies(Path.Combine(installer, "no-such-dotnet"), installer).ShouldBeFalse(); - - [Fact] - public void A_child_is_pointed_at_the_chosen_directory() - { - var environment = new Dictionary { ["PATH"] = "/usr/bin" }; - Dotnet.Apply(installer, environment); - environment["PATH"].ShouldBe(installer + Path.PathSeparator + "/usr/bin"); - environment["DOTNET_ROOT"].ShouldBe(installer); - - var untouched = new Dictionary { ["PATH"] = "/usr/bin" }; - Dotnet.Apply(null, untouched); - untouched.Keys.ShouldBe(["PATH"]); - untouched["PATH"].ShouldBe("/usr/bin"); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/FixTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/FixTests.cs deleted file mode 100644 index 38cf460c..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/FixTests.cs +++ /dev/null @@ -1,90 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// prober fix F --commit SHA writes the fixed line of findings.jsonl as finding-line.schema.json wants it, where a -/// fixed finding was a line written by hand (F3, #45): the open line's claim and probe, the commit's full id. -/// -public sealed class FixTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private string Findings => Path.Combine(fx.BranchDir, "findings.jsonl"); - - private void Open(string id) => - File.AppendAllText(Findings, new JsonObject - { - ["schemaVersion"] = 1, ["id"] = id, ["claim"] = "C1", ["status"] = "open", ["probe"] = "P", ["reason"] = "row 3 differs", - }.ToJsonString() + "\n"); - - private JsonObject Last() => JsonNode.Parse(File.ReadAllLines(Findings)[^1])!.AsObject(); - - private Ledger Ledger() => new(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null); - - [Fact] - public void It_writes_the_fixed_line_with_the_open_lines_claim_and_probe_and_the_full_commit() - { - Open("F9"); - var head = fx.Sh("git", "rev-parse", "HEAD").Trim(); - var (exit, output, error) = fx.Ledger("fix", "F9", "--commit", head[..7], "--reason", "fixed by the converter's null"); - exit.ShouldBe(0, error); - output.ShouldBe($"F9 fixed at {head[..7]}{Environment.NewLine}"); - var line = Last(); - line.Select(kv => kv.Key).ShouldBe(["schemaVersion", "id", "claim", "status", "commit", "probe", "reason"]); - line["schemaVersion"]!.GetValue().ShouldBe(1); - line["id"]!.GetValue().ShouldBe("F9"); - line["claim"]!.GetValue().ShouldBe("C1"); - line["status"]!.GetValue().ShouldBe("fixed"); - line["commit"]!.GetValue().ShouldBe(head); - line["probe"]!.GetValue().ShouldBe("P"); - line["reason"]!.GetValue().ShouldBe("fixed by the converter's null"); - Ledger().OpenFindings().ShouldBeEmpty(); - Ledger().Findings()["F9"]["status"]!.GetValue().ShouldBe("fixed"); - } - - [Fact] - public void A_commit_outside_heads_history_is_recorded_and_counts_once_it_is_in_it() - { - Open("F9"); - fx.Sh("git", "checkout", "-q", "-b", "fix"); - fx.Write("src/a.cs", "class A { }\n"); - fx.Sh("git", "-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qam", "the fix"); // the state stays untracked - var fix = fx.Sh("git", "rev-parse", "HEAD").Trim(); - fx.Sh("git", "checkout", "-q", "main"); - var (exit, output, error) = fx.Ledger("fix", "F9", "--commit", fix); - exit.ShouldBe(0, error); - output.ShouldContain($"F9 fixed at {fix[..7]}; it counts once that commit is in HEAD's history"); - Ledger().OpenFindings().ShouldBe(["F9"]); - fx.Sh("git", "merge", "-q", "--ff-only", "fix"); - Ledger().OpenFindings().ShouldBeEmpty(); - } - - [Theory] - [InlineData("F8", "HEAD", 1, "F8 is no finding of this ledger")] - [InlineData("F9", "0123456789abcdef0123456789abcdef01234567", 1, "--commit 0123456789abcdef0123456789abcdef01234567 is no commit of this repository")] - [InlineData("F9", "", 2, "--commit is required")] - public void It_refuses_a_finding_the_ledger_does_not_hold_and_a_commit_the_repository_lacks(string id, string commit, int code, string message) - { - Open("F9"); - var before = File.ReadAllText(Findings); - var (exit, _, error) = commit.Length > 0 ? fx.Ledger("fix", id, "--commit", commit) : fx.Ledger("fix", id); - exit.ShouldBe(code); - error.ShouldContain(message); - File.ReadAllText(Findings).ShouldBe(before); - } - - [Fact] - public void It_refuses_a_finding_already_fixed_and_a_call_without_one_id() - { - Open("F9"); - fx.Ledger("fix", "F9", "--commit", "HEAD").Exit.ShouldBe(0); - var (exit, _, error) = fx.Ledger("fix", "F9", "--commit", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain("F9 is fixed, not open"); - fx.Ledger("fix", "--commit", "HEAD").Exit.ShouldBe(2); - fx.Ledger("fix", "F9", "F10", "--commit", "HEAD").Exit.ShouldBe(2); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/Fixture.cs b/test/CodoMetis.TypeKit.Prober.Tests/Fixture.cs deleted file mode 100644 index b388e557..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/Fixture.cs +++ /dev/null @@ -1,167 +0,0 @@ -using System.Diagnostics; -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// A repository with one claim C1 over axes x and y (values 1 and 2) and one probe P covering it. -internal sealed class Fixture : IDisposable -{ - public Fixture() - { - Root = Path.Combine(Path.GetTempPath(), "prober-tests", Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(Root); - Memory = Path.Combine(Root, ".claude", "agent-memory-local", "prober"); - BranchDir = Path.Combine(Memory, "branches", "main"); - Sh("git", "init", "-q", "-b", "main"); - Write("README.md", "# a\n\nThe claim.\n"); - Write("src/a.cs", "class A {}\n"); - Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - }.ToJsonString() + "\n```\n"); - Write("tools/probe/corpus/claims.json", """[{"id": "C1", "source": "README.md#a", "surfaces": ["s"]}]"""); - Write("tools/probe/corpus/registry.json", """{"probes": {"P": {"claim": "C1", "filter": "*P"}}}"""); - Write("tools/probe/corpus/harness/P.cs", "// probe P\n"); - Rows([("1", "1"), ("2", "2")]); - Commit("init"); - Directory.CreateDirectory(BranchDir); - File.WriteAllText(Path.Combine(Memory, "axes-generated.json"), """{"axes": {}, "needsValues": []}"""); - Ledger("universe", "--base", "HEAD").Exit.ShouldBe(0); - } - - public string Root { get; } - public string Memory { get; } - public string BranchDir { get; } - - public void Write(string relative, string text) - { - var path = Path.Combine(Root, relative); - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - File.WriteAllText(path, text); - } - - /// P's rows: each probe has a rows file of its own, its claim inside. - public void Rows(IEnumerable<(string X, string Y)> rows) => - Write("tools/probe/corpus/rows/P.json", new JsonObject - { - ["claim"] = "C1", - ["rows"] = new JsonArray([.. rows.Select(r => (JsonNode)new JsonObject { ["row"] = new JsonObject { ["x"] = r.X, ["y"] = r.Y } })]), - }.ToJsonString()); - - public void Commit(string message) - { - Sh("git", "add", "-A"); - Sh("git", "-c", "user.name=t", "-c", "user.email=t@t", "commit", "-qm", message); - } - - public string Sh(string file, params string[] args) - { - var start = new ProcessStartInfo(file) { WorkingDirectory = Root, RedirectStandardOutput = true, RedirectStandardError = true }; - foreach (var a in args) start.ArgumentList.Add(a); - using var p = Process.Start(start)!; - var output = p.StandardOutput.ReadToEnd(); var error = p.StandardError.ReadToEnd(); - p.WaitForExit(); - p.ExitCode.ShouldBe(0, $"{file} {string.Join(' ', args)}: {output}{error}"); - return output; - } - - /// Runs a prober command in-process, in this repository, with the environment given. - public (int Exit, string Out, string Err) Ledger(params string[] args) => LedgerWith(new Dictionary(), args); - - public (int Exit, string Out, string Err) LedgerWith(Dictionary env, params string[] args) => LedgerEnding(env, Environment.NewLine, args); - - /// As , with the line end the output writers use, so a test sees Windows' output on any OS. - public (int Exit, string Out, string Err) LedgerEnding(Dictionary env, string newLine, params string[] args) - { - var output = new StringWriter { NewLine = newLine }; var error = new StringWriter { NewLine = newLine }; - var exit = Program.Run(new Context(Root, env, output, error), args); - return (exit, output.ToString(), error.ToString()); - } - - /// Runs a prober command in-process in another checkout of this repository (a linked worktree). - public (int Exit, string Out, string Err) LedgerIn(string checkout, params string[] args) - { - var output = new StringWriter(); var error = new StringWriter(); - var exit = Program.Run(new Context(checkout, new Dictionary(), output, error), args); - return (exit, output.ToString(), error.ToString()); - } - - /// A linked worktree of this repository (git worktree add) on a new branch, beside it; removed with the fixture. - public string Linked(string branch) - { - var path = Root + "-" + branch; - Sh("git", "worktree", "add", "-q", "-b", branch, path); - linked.Add(path); - return path; - } - - private readonly List linked = []; - - /// The lock file of a branch's state, wherever the tool puts it. - public string LockFile(string branch = "main") => - RoundLock.LockPath(new Ledger(new Context(Root, new Dictionary(), TextWriter.Null, TextWriter.Null), branch)); - - /// Runs the built dll as its own process: for the tests that race several takers. - public Process Spawn(params string[] args) - { - var start = new ProcessStartInfo("dotnet") { WorkingDirectory = Root, RedirectStandardOutput = true, RedirectStandardError = true }; - start.ArgumentList.Add(typeof(Program).Assembly.Location); - foreach (var a in args) start.ArgumentList.Add(a); - return Process.Start(start)!; - } - - public void Footprint() - { - var blob = Sh("git", "rev-parse", "HEAD:src/a.cs").Trim(); - File.AppendAllText(Path.Combine(BranchDir, "footprints.jsonl"), - new JsonObject { ["probe"] = "P", ["footprint"] = new JsonObject { ["src/a.cs"] = blob }, ["compileTime"] = new JsonObject(), ["toolchain"] = new JsonObject() }.ToJsonString() + "\n"); - } - - /// - /// What a replay of P records: the rows it ran, how each ended and the settings that applied; outcomes, when - /// given, sets each row's category and the finding it skips for. - /// - public string Evidence(IReadOnlyList> applied, string category = "pass", int[]? executed = null, - IReadOnlyList<(string Category, string? Finding)>? outcomes = null) - { - var idx = Enumerable.Range(0, applied.Count).ToArray(); - var path = Path.Combine(Root, "evidence.json"); - File.WriteAllText(path, new JsonObject - { - ["variants"] = new JsonArray(new JsonObject - { - ["env"] = new JsonObject(), ["expected"] = new JsonArray([.. idx.Select(i => (JsonNode)i)]), - ["executed"] = new JsonArray([.. (executed ?? idx).Select(i => (JsonNode)i)]), - }), - ["rows"] = new JsonObject(idx.Select(i => KeyValuePair.Create(i.ToString(System.Globalization.CultureInfo.InvariantCulture), (JsonNode?)new JsonObject - { - ["category"] = outcomes?[i].Category ?? category, ["applied"] = new JsonObject(applied[i].Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)kv.Value))), - ["findings"] = outcomes?[i].Finding is { } finding ? new JsonArray(finding) : new JsonArray(), - }))), - }.ToJsonString()); - return path; - } - - public int Covered() - { - var (exit, output, error) = Ledger("status", "--json"); - exit.ShouldBe(0, error); - return (int)JsonNode.Parse(output)!["claims"]!["C1"]!["current"]!.GetValue(); - } - - public void AgeTheLock() - { - var path = LockFile(); - var held = JsonNode.Parse(File.ReadAllText(path))!.AsObject(); - held["heartbeat"] = held["heartbeat"]!.GetValue() - 21 * 60; - File.WriteAllText(path, held.ToJsonString()); - } - - public void Dispose() - { - foreach (var dir in linked.Append(Root)) - try { Directory.Delete(dir, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/FreshnessTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/FreshnessTests.cs deleted file mode 100644 index c71a4a7a..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/FreshnessTests.cs +++ /dev/null @@ -1,27 +0,0 @@ -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// The dll refuses to answer from other sources than the ones it was built from, its schemas among them, -/// as every tool does (tools/Directory.Build.targets, tools/Shared/SourceFingerprint.cs). -/// -public sealed class FreshnessTests -{ - [Fact] - public void The_stamp_covers_the_schemas() => - SourceFingerprint.Embedded.ShouldContain(line => line.StartsWith("tools/Prober/Schemas/", StringComparison.Ordinal) && line.Contains(".json|", StringComparison.Ordinal)); - - [Theory] - [InlineData("tools/Prober/Program.cs")] - [InlineData("tools/Prober/Schemas/round.schema.json")] - public void A_stale_dll_refuses_and_names_the_rebuild(string source) - { - using var sandbox = new ToolSandbox("Prober", "prober"); - File.AppendAllText(Path.Combine(sandbox.Repo, source), "\n"); - - var (code, _, error) = sandbox.Run(arguments: ["status"]); - - code.ShouldBe(3); - error.ShouldContain($"({source} differs)"); - error.ShouldContain("Run `dotnet build tools/Prober -c "); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/GapsTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/GapsTests.cs deleted file mode 100644 index d9db71e3..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/GapsTests.cs +++ /dev/null @@ -1,160 +0,0 @@ -using System.Text.Json.Nodes; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// `prober gaps` prints what the corpus leaves out, each under its heading with its recorded reason, and says -/// none under a heading with nothing: a report from the tracked corpus and the generated axes that decides -/// nothing (decision 104). -/// -public sealed class GapsTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - /// The output after its first line, which names the branch and the commit. - private string Gaps(string newLine) - { - var (exit, output, error) = fx.LedgerEnding([], newLine, "gaps", "--base", "HEAD"); - exit.ShouldBe(0, "a report decides nothing: " + error); - var text = output.ReplaceLineEndings("\n"); - text.ShouldStartWith("prober gaps: main at "); - return text[(text.IndexOf('\n', StringComparison.Ordinal) + 1)..]; - } - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void Each_gap_is_listed_under_its_heading_with_its_reason(string newLine) - { - fx.Write("design/adr/0001-limits.md", "# 1. Limits\n\nAn ordering of keys is not promised.\n\nNo culture but the invariant one is promised.\n"); - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject - { - ["x"] = new JsonArray("1", "2", "3"), ["y"] = new JsonArray("1", "2"), - ["shape.refused-kind"] = new JsonArray("in-generic-type"), - }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["refused"] = new JsonObject { ["x"] = new JsonObject { ["3"] = "the framework refuses 3 on its own (KB-001)" } }, - ["out-of-reach"] = new JsonObject { ["postgresql"] = "needs a server" }, - }.ToJsonString() + "\n```\n"); - File.WriteAllText(Path.Combine(fx.Memory, "axes-generated.json"), new JsonObject - { - ["axes"] = new JsonObject { ["json.z"] = new JsonArray("true", "false") }, - ["needsValues"] = new JsonArray("json.w"), - ["versions"] = new JsonObject { ["System.Text.Json"] = "10.0.0" }, - }.ToJsonString()); - fx.Write("tools/probe/corpus/claims.json", new JsonArray(new JsonObject - { - ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["narrow"] = new JsonObject { ["y"] = "The claim." }, - ["not-promised"] = new JsonArray( - new JsonObject - { - ["id"] = "order", ["source"] = "design/adr/0001-limits.md#1-limits", ["sentence"] = "An ordering of keys is not promised.", - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2") }, - }, - new JsonObject { ["id"] = "culture", ["source"] = "design/adr/0001-limits.md#1-limits", ["sentence"] = "No culture but the invariant one is promised." }), - }).ToJsonString()); - - Gaps(newLine).ShouldBe(""" - settings no surface reaches and nothing ignores (enumerated at System.Text.Json 10.0.0): 1 - json.z (no surface reaches it and it is not ignored) - settings without values: 1 - json.w (no hand values and not ignored) - declarations the generators refuse (shape.refused-kind): 1 - in-generic-type - values the frameworks refuse on their own: 1 - x=3: the framework refuses 3 on its own (KB-001) - not promised, by what it concerns: 3 - the whole claim: C1/culture "No culture but the invariant one is promised." (design/adr/0001-limits.md#1-limits) - x=1|2: C1/order "An ordering of keys is not promised." (design/adr/0001-limits.md#1-limits) - y: C1 narrows it, "The claim." (README.md#a) - pairs no obligation wants (declared inapplicable): none - pairs no passing check read both axes of (the last replay's evidence): none - out of reach: 1 - postgresql: needs a server - - """.ReplaceLineEndings("\n")); - } - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void A_corpus_without_gaps_says_none_under_each_heading(string newLine) => - Gaps(newLine).ShouldBe(""" - settings no surface reaches and nothing ignores: none - settings without values: none - declarations the generators refuse (shape.refused-kind): none - values the frameworks refuse on their own: none - not promised, by what it concerns: none - pairs no obligation wants (declared inapplicable): none - pairs no passing check read both axes of (the last replay's evidence): none - out of reach: none - - """.ReplaceLineEndings("\n")); - - /// - /// C1 over x, y and z: shape wants every pair and its passing checks read x and y; parse does not apply where x=1 and - /// z=1 and its passing checks read x and z. x=1 z=1 is wanted by shape alone, which never read z, and no passing check - /// read y with z: those five pairs are listed. A check that did not pass reads nothing for the listing. - /// - [Fact] - public void The_pairs_no_obligation_wanting_them_read_both_axes_of_are_listed() - { - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2"), ["z"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y", "z"), ["watched"] = new JsonArray("src") } }, - }.ToJsonString() + "\n```\n"); - fx.Write("tools/probe/corpus/claims.json", new JsonArray(new JsonObject - { - ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["obligations"] = new JsonArray( - new JsonObject { ["id"] = "shape", ["sentence"] = "The claim." }, - new JsonObject - { - ["id"] = "parse", ["sentence"] = "The claim.", - ["inapplicable"] = new JsonArray(new JsonObject { ["axes"] = new JsonObject { ["x"] = new JsonArray("1"), ["z"] = new JsonArray("1") }, ["cite"] = "docs:README.md#a" }), - }), - }).ToJsonString()); - fx.Commit("three axes"); - fx.Ledger("universe", "--base", "HEAD").Exit.ShouldBe(0); - Gaps("\n").ShouldContain("pairs no passing check read both axes of (the last replay's evidence): none\n", Case.Sensitive, "no evidence yet: nothing to say"); - - static JsonObject Check(string obligation, string category, params (string Axis, string Value)[] read) => new() - { - ["obligation"] = obligation, ["category"] = category, ["baseline"] = "twin", - ["applied"] = new JsonObject(read.Select(r => KeyValuePair.Create(r.Axis, (JsonNode?)r.Value))), - }; - var evidence = Path.Combine(fx.Root, "evidence.json"); - File.WriteAllText(evidence, new JsonObject - { - ["variants"] = new JsonArray(new JsonObject { ["env"] = new JsonObject(), ["expected"] = new JsonArray(0), ["executed"] = new JsonArray(0) }), - ["rows"] = new JsonObject - { - ["0"] = new JsonObject - { - ["category"] = "pass", ["applied"] = new JsonObject(), ["findings"] = new JsonArray(), - ["checks"] = new JsonArray( - Check("shape", "pass", ("x", "1"), ("y", "1")), - Check("parse", "pass", ("x", "2"), ("z", "1")), - Check("parse", "inapplicable", ("y", "2"), ("z", "2"))), - }, - }, - }.ToJsonString()); - fx.Footprint(); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", evidence).Exit.ShouldBe(0); - - Gaps("\n").ShouldContain(""" - pairs no passing check read both axes of (the last replay's evidence): 5 - C1: x=1 z=1 - C1: y=1 z=1 - C1: y=1 z=2 - C1: y=2 z=1 - C1: y=2 z=2 - - """.ReplaceLineEndings("\n")); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/HarnessErrorTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/HarnessErrorTests.cs deleted file mode 100644 index be0ec024..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/HarnessErrorTests.cs +++ /dev/null @@ -1,141 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; -using Probes.Shapes; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// The harness writes rows to the process's PROBE_OUT, so its tests run one at a time. -[CollectionDefinition(nameof(ProbeOut), DisableParallelization = true)] -public sealed class ProbeOut; - -/// -/// A harness error is never a catch (decision 103): the probe corpus's own Rec.Run (harness/Row.cs, -/// compiled in) ends a row whose check throws as a harness error, not as a violation, and `seeds`, reading -/// the lines it writes, counts no such row caught, nor a violation whose reasons carry a harness error. -/// -[Collection(nameof(ProbeOut))] -public sealed class HarnessErrorTests : IDisposable -{ - private readonly Fixture fx = new(); - private readonly string cache; - - public HarnessErrorTests() - { - cache = fx.Root + "-cache"; - fx.Sh("git", "config", "core.autocrlf", "false"); - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["replay"] = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src") }, - }.ToJsonString() + "\n```\n"); - fx.Write("src/a.cs", "class A { /* DEFECT */ }\n"); - fx.Write("tools/probe/corpus/seeds/C1.patch", fx.Sh("git", "diff", "--", "src/a.cs")); - fx.Write("src/a.cs", "class A {}\n"); - fx.Write("tools/probe/corpus/registry.json", new JsonObject - { - ["probes"] = new JsonObject - { - ["P"] = new JsonObject { ["claim"] = "C1", ["filter"] = "*P", ["seed"] = new JsonObject { ["defect"] = "A holds the defect.", ["patch"] = "seeds/C1.patch" } }, - }, - }.ToJsonString()); - fx.Commit("seed"); - } - - public void Dispose() - { - fx.Dispose(); - try { Directory.Delete(cache, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - /// Runs one row through the harness's Rec.Run, writing to ; the exception it ends the test with, if any. - private static Exception? Harness(string outDir, int idx, Func> check) - { - var previous = Environment.GetEnvironmentVariable("PROBE_OUT"); - Environment.SetEnvironmentVariable("PROBE_OUT", outDir); - try - { - Rec.Run("C1", new Row(idx, new Dictionary { ["x"] = "1", ["y"] = "1" }), check).GetAwaiter().GetResult(); - return null; - } - catch (Exception ended) { return ended; } - finally { Environment.SetEnvironmentVariable("PROBE_OUT", previous); } - } - - private static Task Throws(Row _) => throw new InvalidOperationException("the check broke"); - - [Fact] - public void A_check_that_throws_ends_as_a_harness_error_and_fails_the_test_naming_the_exception() - { - var outDir = Path.Combine(fx.Root, "rows-out"); - var ended = Harness(outDir, 1, Throws); - ended.ShouldNotBeNull().Message.ShouldContain("UNEXPECTED InvalidOperationException: the check broke"); - - var line = JsonNode.Parse(File.ReadAllLines(Path.Combine(outDir, "C1.jsonl")).ShouldHaveSingleItem())!; - Files.Str(line["category"]).ShouldBe("harness-error", "an exception is the harness breaking, never the claim's check firing"); - Files.Strings(line["reasons"]).ShouldBe(["harness-error:UNEXPECTED InvalidOperationException: the check broke"]); - Files.Str(line["status"]).ShouldBe("fail"); - } - - [Fact] - public void A_seed_that_only_makes_a_check_throw_is_missed_not_caught() - { - var (exit, output) = Seeds(new HarnessAdapter(Throws)); - exit.ShouldBe(1, output); - output.ShouldContain("P (C1): missed, no row violates that did not already (2 rows: harness-error 1, pass 1; 1 ended in a harness error, which is no catch;"); - output.ShouldNotContain("caught,"); - } - - [Fact] - public void A_violation_whose_reasons_carry_a_harness_error_is_no_catch() - { - var (exit, output) = Seeds(new HarnessAdapter(r => - { - r.Outcome("harness-error", "the check could not tell"); - return Task.FromResult("and failed anyway"); - })); - exit.ShouldBe(1, output); - output.ShouldContain("P (C1): missed, no row violates that did not already (2 rows: pass 1, violation 1; 1 ended in a harness error, which is no catch;"); - } - - [Fact] - public void A_violation_through_the_harness_is_a_catch() - { - var (exit, output) = Seeds(new HarnessAdapter(_ => Task.FromResult("the claim does not hold"))); - exit.ShouldBe(0, output); - output.ShouldContain("P (C1): caught, 1 of 2 rows violate; e.g. #1: violation:the claim does not hold"); - } - - private (int Exit, string Out) Seeds(IProbeAdapter adapter) - { - var output = new StringWriter(); - var context = new Context(fx.Root, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, output, output); - int exit; - try { exit = global::Prober.Seeds.Run(context, new Args([], new HashSet(StringComparer.Ordinal)), adapter); } - catch (Refusal refusal) { output.WriteLine(refusal.Message); exit = refusal.ExitCode; } - return (exit, output.ToString()); - } - - /// - /// The probe P over C1's two rows, each run through the real harness: row 0 passes, and row 1 runs - /// while src/a.cs holds DEFECT. - /// - private sealed class HarnessAdapter(Func> seeded) : IProbeAdapter - { - public Ran Build(string worktree, IReadOnlyDictionary environment) => new(0, "Build: OK", ""); - - public Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment) - { - var defect = File.ReadAllText(Path.Combine(worktree, "src", "a.cs")).Contains("DEFECT", StringComparison.Ordinal); - var outDir = environment["PROBE_OUT"]!; - var failed = false; - for (var idx = 0; idx < 2; idx++) - failed |= Harness(outDir, idx, idx == 1 && defect ? seeded : _ => Task.FromResult(null)) is not null; - return new Ran(failed ? 1 : 0, "", ""); - } - - public (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory) => (null, "none"); - - public IReadOnlyDictionary Environment(string cacheDirectory) => new Dictionary(); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/LedgerTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/LedgerTests.cs deleted file mode 100644 index 62ec4ad4..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/LedgerTests.cs +++ /dev/null @@ -1,350 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// Coverage is earned only by replayed evidence, bound to the identity it ran under (the review's priority 1). -public sealed class AccountingTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private static Dictionary R(string x, string y) => new(StringComparer.Ordinal) { ["x"] = x, ["y"] = y }; - private static readonly IReadOnlyList> TwoRows = [R("1", "1"), R("2", "2")]; - - private void RecordPass(IReadOnlyList> applied, string category = "pass", int[]? executed = null) - { - fx.Footprint(); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", fx.Evidence(applied, category, executed)).Exit.ShouldBe(0); - } - - [Fact] - public void A_pass_credits_the_pairs_of_the_rows_it_ran() - { - RecordPass(TwoRows); - fx.Covered().ShouldBe(2); - } - - [Fact] - public void The_status_line_counts_what_the_json_counts() - { - RecordPass(TwoRows); - fx.Ledger("status").Out.ShouldStartWith("main at "); - fx.Ledger("status").Out.ShouldContain(": 2 of 4 pairs current (50.0%), 0 open finding(s)"); - } - - [Fact] - public void An_old_pass_does_not_credit_a_row_added_since() - { - RecordPass(TwoRows); - fx.Rows([("1", "1"), ("2", "2"), ("1", "2")]); - fx.Commit("a new row"); - fx.Covered().ShouldBe(0, "the pass was for another corpus: nothing of it may count now"); - } - - [Fact] - public void A_changed_harness_voids_the_pass() - { - RecordPass(TwoRows); - fx.Write("tools/probe/corpus/harness/P.cs", "// probe P, changed\n"); - fx.Commit("harness"); - fx.Covered().ShouldBe(0); - } - - [Fact] - public void A_changed_axes_file_voids_the_pass() - { - RecordPass(TwoRows); - var path = Path.Combine(fx.Root, ".claude/probe-axes.md"); - fx.Write(".claude/probe-axes.md", File.ReadAllText(path).Replace("\"watched\":[\"src\"]", "\"watched\":[\"src\",\"README.md\"]", StringComparison.Ordinal)); - fx.Commit("axes"); - fx.Covered().ShouldBe(0); - } - - [Fact] - public void Only_the_settings_that_applied_earn_credit() - { - RecordPass([R("1", "1"), R("1", "2")]); // row 2 asked for x=2 and ran as x=1 - fx.Covered().ShouldBe(2, "x1y1 and the applied x1y2, never the requested x2y2"); - var rows = JsonNode.Parse(fx.Ledger("rows", "--claim", "C1").Out)!["rows"]!.AsArray(); - rows.SelectMany(r => r!["pairs"]!.AsArray()).Select(p => $"{p![0]} {p[1]}").ShouldContain("x=2 y=2"); - } - - [Fact] - public void An_axis_the_harness_never_read_earns_nothing() - { - RecordPass([new(StringComparer.Ordinal) { ["x"] = "1" }, new(StringComparer.Ordinal) { ["x"] = "2" }]); - fx.Covered().ShouldBe(0); - } - - [Fact] - public void A_refusal_is_no_evidence_of_success() - { - RecordPass(TwoRows, category: "expected-refusal"); - fx.Covered().ShouldBe(0); - } - - [Fact] - public void A_row_that_did_not_run_is_never_credited() - { - RecordPass(TwoRows, executed: [0]); - fx.Covered().ShouldBe(1); - } -} - -/// -/// What earned nothing is counted where the coverage is read, and a row that steps around a finding binds: the -/// gate refuses while one does, and the ledger names a skip whose finding is fixed or dismissed. -/// -public sealed class OutcomeTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private static Dictionary R(string x, string y) => new(StringComparer.Ordinal) { ["x"] = x, ["y"] = y }; - - private string Record(params (string Category, string? Finding)[] outcomes) - { - fx.Footprint(); - var evidence = fx.Evidence([R("1", "1"), R("2", "2")], outcomes: outcomes); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", evidence).Exit.ShouldBe(0); - return evidence; - } - - private List StaleSkips(string evidence) => - new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null) - .StaleSkips(JsonNode.Parse(File.ReadAllText(evidence)) as JsonObject); - - private void Finding(string status, string commit) => - File.AppendAllText(Path.Combine(fx.BranchDir, "findings.jsonl"), - new JsonObject { ["id"] = "F9", ["claim"] = "C1", ["status"] = status, ["commit"] = commit }.ToJsonString() + "\n"); - - [Fact] - public void Status_counts_how_the_rows_of_a_claim_ended() - { - Record(("pass", null), ("expected-refusal", null)); - var outcomes = JsonNode.Parse(fx.Ledger("status", "--json").Out)!["claims"]!["C1"]!["outcomes"]!; - outcomes["pass"]!.GetValue().ShouldBe(1); - outcomes["expected-refusal"]!.GetValue().ShouldBe(1); - fx.Ledger("status").Out.ShouldContain("C1: 1/4 (rows: expected-refusal 1, pass 1; checks: expected-refusal 1, pass 1)"); - } - - [Fact] - public void A_skip_for_a_finding_is_shown_and_refused_by_the_gate_without_any_ledger_of_findings() - { - fx.Sh("git", "tag", "v1"); - Record(("pass", null), ("known-finding", "F9")); - fx.Ledger("status").Out.ShouldContain("1 row(s) skip for finding F9"); - var (exit, output, _) = fx.Ledger("gate", "--since", "v1", "--json"); - exit.ShouldNotBe(0); - JsonNode.Parse(output)!["reasons"]!.AsArray().Select(r => r!.GetValue()).ShouldContain("P: 1 row(s) skip for finding F9; fix it and remove the skip, or dismiss it"); - } - - [Fact] - public void A_skip_for_an_open_finding_stands() - { - var evidence = Record(("known-finding", "F9"), ("pass", null)); - Finding("open", ""); - StaleSkips(evidence).ShouldBeEmpty(); - } - - [Fact] - public void A_skip_for_a_fixed_finding_is_stale() - { - var evidence = Record(("known-finding", "F9"), ("pass", null)); - var head = fx.Sh("git", "rev-parse", "HEAD").Trim(); - Finding("open", ""); - Finding("fixed", head); - StaleSkips(evidence).ShouldBe([$"1 row(s) skip for F9, which is fixed at {head[..7]}: remove the skip"]); - } - - [Fact] - public void A_skip_for_a_dismissed_finding_is_stale() - { - var evidence = Record(("known-finding", "F9"), ("pass", null)); - Finding("open", ""); - fx.Write("tools/probe/corpus/dismissed.jsonl", new JsonObject { ["id"] = "F9", ["reason"] = "intended", ["date"] = "2026-10-15" }.ToJsonString() + "\n"); - StaleSkips(evidence).ShouldBe(["1 row(s) skip for F9, which is dismissed: remove the skip"]); - } -} - -/// One round at a time, on every OS: an exclusive create, liveness by heartbeat, a mutex for takeovers. -public sealed class LockTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - [Fact] - public void A_second_holder_is_refused_while_the_first_is_alive() - { - fx.Ledger("lock", "--holder", "a", "--mode", "explore").Exit.ShouldBe(0); - fx.Ledger("lock", "--holder", "b", "--mode", "replay").Exit.ShouldNotBe(0); - } - - [Fact] - public void A_holder_whose_heartbeat_stopped_is_taken_over() - { - fx.Ledger("lock", "--holder", "a", "--mode", "explore").Exit.ShouldBe(0); - fx.AgeTheLock(); - fx.Ledger("lock", "--holder", "b", "--mode", "replay").Exit.ShouldBe(0); - } - - [Fact] - public void A_heartbeat_keeps_the_holder_alive() - { - fx.Ledger("lock", "--holder", "a", "--mode", "explore").Exit.ShouldBe(0); - fx.AgeTheLock(); - fx.Ledger("heartbeat", "--holder", "a").Exit.ShouldBe(0); - fx.Ledger("lock", "--holder", "b", "--mode", "replay").Exit.ShouldNotBe(0); - } - - [Fact] - public void Two_takers_at_once_get_one_lock() => RaceOf(8).ShouldBe(1); - - [Fact] - public void Racing_takers_of_a_dead_holder_get_one_lock() - { - fx.Ledger("lock", "--holder", "dead", "--mode", "explore").Exit.ShouldBe(0); - fx.AgeTheLock(); - RaceOf(8).ShouldBe(1); - } - - [Fact] - public void Racing_threads_over_a_dead_holder_get_one_lock() - { - // Threads released together by a barrier collide in the critical section far more tightly than - // processes can; ten rounds, each must end with exactly one holder. - for (var round = 0; round < 10; round++) - { - fx.Ledger("lock", "--holder", $"dead{round}", "--mode", "explore").Exit.ShouldBe(0); - fx.AgeTheLock(); - const int takers = 16; - using var barrier = new Barrier(takers); - var exits = new int[takers]; - var threads = Enumerable.Range(0, takers).Select(i => new Thread(() => - { - barrier.SignalAndWait(); - exits[i] = fx.Ledger("lock", "--holder", $"t{round}-{i}", "--mode", "replay").Exit; - })).ToList(); - threads.ForEach(t => t.Start()); - threads.ForEach(t => t.Join()); - exits.Count(e => e == 0).ShouldBe(1, $"round {round}"); - File.Delete(fx.LockFile()); - } - } - - /// - /// What crashed the test host on windows-latest (run 38732628888): a taker creating round.lock.mutex while - /// its last holder's delete was still pending got UnauthorizedAccessException, which Windows throws there - /// instead of IOException, and the mutex loop let it through. Here the OS refuses the create every time, - /// the same exception on every platform: a directory in the mutex's place on Windows, a state directory - /// without write permission elsewhere. A taker waits, then refuses; it never throws. - /// - [Fact] - public void A_mutex_the_OS_refuses_to_create_is_waited_for_and_refused_not_thrown() - { - var directory = Path.GetDirectoryName(fx.LockFile())!; - Directory.CreateDirectory(directory); - var mutex = fx.LockFile() + ".mutex"; - if (OperatingSystem.IsWindows()) Directory.CreateDirectory(mutex); - else File.SetUnixFileMode(directory, UnixFileMode.UserRead | UnixFileMode.UserExecute); - try - { - var (exit, _, error) = fx.Ledger("lock", "--holder", "a", "--mode", "explore"); - - exit.ShouldNotBe(0); - error.ShouldContain("mutex"); - } - finally - { - if (OperatingSystem.IsWindows()) Directory.Delete(mutex); - else File.SetUnixFileMode(directory, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); - } - } - - [Fact] - public void A_lock_of_one_branch_leaves_another_branchs_lock_free() - { - var other = fx.Linked("other"); - fx.Ledger("lock", "--holder", "a", "--mode", "explore").Exit.ShouldBe(0); - - var (exit, output, error) = fx.LedgerIn(other, "lock", "--holder", "b", "--mode", "explore"); - - exit.ShouldBe(0, error + output); - File.Exists(fx.LockFile("other")).ShouldBeTrue(); - File.Exists(fx.LockFile("main")).ShouldBeTrue("a's lock is still held"); - } - - [Fact] - public void A_second_lock_of_one_branch_is_refused_from_another_checkout() - { - var other = fx.Linked("other"); - fx.Ledger("lock", "--holder", "a", "--mode", "explore").Exit.ShouldBe(0); - - fx.LedgerIn(other, "lock", "--branch", "main", "--holder", "b", "--mode", "replay").Exit.ShouldNotBe(0); - } - - private int RaceOf(int takers) - { - var processes = Enumerable.Range(0, takers).Select(i => fx.Spawn("lock", "--holder", $"h{i}", "--mode", "replay")).ToList(); - foreach (var p in processes) { p.StandardOutput.ReadToEnd(); p.StandardError.ReadToEnd(); p.WaitForExit(); } - return processes.Count(p => p.ExitCode == 0); - } -} - -/// The release gate needs no model: every obligation is a probe or a reviewed waiver. -public sealed class GateTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private void TouchTheClaim() - { - fx.Write("README.md", "# a\n\nThe claim, reworded.\n"); - fx.Commit("docs"); - } - - [Fact] - public void An_unprobed_touched_claim_fails_with_what_is_missing() - { - fx.Sh("git", "tag", "v1"); - TouchTheClaim(); - var (exit, output, _) = fx.Ledger("gate", "--since", "v1", "--json"); - exit.ShouldNotBe(0); - JsonNode.Parse(output)!["reasons"]!.AsArray().Select(r => r!.GetValue()).ShouldContain(r => r.StartsWith("C1:", StringComparison.Ordinal)); - } - - [Fact] - public void A_reviewed_waiver_meets_the_obligation_without_a_probe() - { - fx.Sh("git", "tag", "v1"); - fx.Write("tools/probe/corpus/registry.json", """{"probes": {}}"""); - fx.Write("tools/probe/corpus/waivers.json", """[{"claim": "C1", "reason": "covered by the suite's C1Tests", "reviewed_by": "maintainer", "date": "2026-10-15"}]"""); - TouchTheClaim(); - var (exit, _, error) = fx.Ledger("gate", "--since", "v1"); - exit.ShouldBe(0, error); - } - - [Fact] - public void An_unsigned_waiver_meets_nothing() - { - fx.Sh("git", "tag", "v1"); - fx.Write("tools/probe/corpus/registry.json", """{"probes": {}}"""); - fx.Write("tools/probe/corpus/waivers.json", """[{"claim": "C1", "reason": "later"}]"""); - TouchTheClaim(); - fx.Ledger("gate", "--since", "v1").Exit.ShouldNotBe(0); - } - - [Fact] - public void State_goes_where_PROBE_STATE_DIR_says() - { - var state = Path.Combine(Path.GetTempPath(), "prober-state-" + Guid.NewGuid().ToString("N")); - try - { - fx.LedgerWith(new() { ["PROBE_STATE_DIR"] = state }, "lock", "--holder", "ci", "--mode", "replay").Exit.ShouldBe(0); - Directory.EnumerateFiles(state, "round.lock", SearchOption.AllDirectories).ShouldNotBeEmpty(); - File.Exists(fx.LockFile()).ShouldBeFalse(); - } - finally { if (Directory.Exists(state)) Directory.Delete(state, recursive: true); } - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/NewProbeTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/NewProbeTests.cs deleted file mode 100644 index 2d9071e6..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/NewProbeTests.cs +++ /dev/null @@ -1,193 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// `new` stages what a probe writer used to do by hand: the skeleton in the harness's form with the claim's -/// obligations quoted and one throwing check each, the claim's rows, and the registry entry with no seed yet, -/// so the seeds every obligation owes are named. C1 lists two obligations; C7-two-words lists none. -/// -public sealed class NewProbeTests : IDisposable -{ - private readonly Fixture fx = new(); - - public NewProbeTests() - { - fx.Write("README.md", "# a\n\nThe claim.\n\nA key is written as the wrapped type writes it.\n\nA null is refused.\n"); - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["replay"] = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src"), ["families"] = new JsonObject { ["f"] = new JsonArray("s") } }, - }.ToJsonString() + "\n```\n"); - fx.Write("tools/probe/corpus/claims.json", new JsonArray( - new JsonObject - { - ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["obligations"] = new JsonArray( - new JsonObject { ["id"] = "keys", ["sentence"] = "A key is written as the wrapped type writes it." }, - new JsonObject { ["id"] = "nulls", ["sentence"] = "A null is refused." }), - }, - new JsonObject { ["id"] = "C7-two-words", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s") }).ToJsonString()); - fx.Commit("claims with obligations"); - fx.Ledger("universe", "--base", "HEAD").Exit.ShouldBe(0); - } - - public void Dispose() => fx.Dispose(); - - private string Staged(string relative) => Path.Combine(fx.BranchDir, "staging", relative); - - [Fact] - public void It_stages_the_skeleton_the_rows_and_the_registry_entry() - { - var (exit, output, error) = fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe"); - exit.ShouldBe(0, output + error); - - var source = File.ReadAllText(Staged("probes/C1KeysProbe.cs")); - source.ShouldContain("// Claim C1 (README.md#a), surfaces s, family f."); - source.ShouldContain("// keys: \"A key is written as the wrapped type writes it.\""); - source.ShouldContain("// nulls: \"A null is refused.\""); - source.ShouldContain("// TODO keys: \"A key is written as the wrapped type writes it.\""); - source.ShouldContain("// TODO nulls: \"A null is refused.\""); - - // The probe's own rows, C1's other probe P's untouched: P has covered nothing, so a covering array over all four pairs. - var staged = JsonNode.Parse(File.ReadAllText(Staged("rows/C1KeysProbe.json")))!; - staged["claim"]!.GetValue().ShouldBe("C1"); - staged["rows"]!.AsArray().Select(r => $"{r!["row"]!["x"]}{r["row"]!["y"]}").Order().ShouldBe(["11", "12", "21", "22"]); - File.Exists(Staged("rows/P.json")).ShouldBeFalse("a new probe leaves its sibling's rows as they are"); - - var entry = JsonNode.Parse(File.ReadAllText(Staged("registry.json")))!["probes"]!["C1KeysProbe"]!.AsObject(); - entry["claim"]!.GetValue().ShouldBe("C1"); - entry["filter"]!.GetValue().ShouldBe("*C1KeysProbe"); - entry["seeds"]!.AsArray().ShouldBeEmpty(); - entry.ContainsKey("seed").ShouldBeFalse(); - - output.ShouldContain("still owed, per obligation:"); - output.ShouldContain(" keys: the check CheckKeys"); - output.ShouldContain(" nulls: the check CheckNulls"); - } - - [Fact] - public void The_seeds_each_obligation_owes_are_named() - { - fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe").Exit.ShouldBe(0); - var problems = global::Prober.Seeds.Problems(new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null)); - problems.ShouldContain("C1KeysProbe: no seed, its registry entry names none"); - problems.ShouldContain("C1: obligation keys has no seed in any of its probes"); - problems.ShouldContain("C1: obligation nulls has no seed in any of its probes"); - - var (exit, output, _) = fx.Ledger("seeds", "--dry-run"); - exit.ShouldBe(0); - output.ShouldContain(" C1KeysProbe/keys: (none)"); - output.ShouldContain(" C1KeysProbe/nulls: (none)"); - } - - [Fact] - public void A_claim_not_in_the_claims_is_refused_and_nothing_staged() - { - var (exit, _, error) = fx.Ledger("new", "--claim", "C9"); - exit.ShouldBe(1); - error.ShouldContain("no claim C9 in claims.json"); - Directory.Exists(Path.Combine(fx.BranchDir, "staging")).ShouldBeFalse(); - } - - [Fact] - public void A_name_a_probe_has_is_refused_and_nothing_changes() - { - fx.Write("tools/probe/corpus/registry.json", """{"probes": {"P": {"claim": "C1", "filter": "*P"}, "C1OldProbe": {"claim": "C1", "filter": "*C1OldProbe"}}}"""); - fx.Commit("a second probe of C1"); - var (exit, _, error) = fx.Ledger("new", "--claim", "C1", "--probe", "C1OldProbe"); - exit.ShouldBe(1); - error.ShouldContain("a probe C1OldProbe exists already (in the registry)"); - fx.Ledger("new", "--claim", "C1", "--probe", "P").Exit.ShouldBe(2, "a name that does not end in Probe"); - Directory.Exists(Path.Combine(fx.BranchDir, "staging")).ShouldBeFalse(); - - fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe").Exit.ShouldBe(0); - var registry = File.ReadAllText(Staged("registry.json")); - var source = File.ReadAllText(Staged("probes/C1KeysProbe.cs")); - fx.Ledger("new", "--claim", "C7-two-words", "--probe", "C1KeysProbe").Exit.ShouldBe(1, "a second `new` of one name"); - File.ReadAllText(Staged("registry.json")).ShouldBe(registry); - File.ReadAllText(Staged("probes/C1KeysProbe.cs")).ShouldBe(source); - } - - [Theory] - [InlineData("C20-testing-shouldhold", "C20TestingShouldholdProbe")] - [InlineData("E02-value-translates-to-column", "E02ValueTranslatesToColumnProbe")] - [InlineData("L01-json-bytes", "L01JsonBytesProbe")] - public void The_name_defaults_to_the_claim_s_prefix_and_words(string claim, string probe) => NewProbe.DefaultName(claim).ShouldBe(probe); - - [Fact] - public void Without_a_name_the_default_is_staged() - { - fx.Ledger("new", "--claim", "C7-two-words").Exit.ShouldBe(0); - // The registry's key, not the file: a file system that ignores case finds the file under another spelling. - JsonNode.Parse(File.ReadAllText(Staged("registry.json")))!["probes"]!.AsObject().Select(kv => kv.Key).ShouldBe(["C7TwoWordsProbe"]); - File.ReadAllText(Staged("probes/C7TwoWordsProbe.cs")).ShouldContain("// claim: the claim as a whole"); - } - - [Fact] - public void A_family_that_is_not_the_claim_s_is_refused() - { - var (exit, _, error) = fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe", "--family", "g"); - exit.ShouldBe(2); - error.ShouldContain("claim C1 is of family f (its first surface, s), not g"); - fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe", "--family", "f").Exit.ShouldBe(0); - } - - /// - /// The lines that make a probe a probe of this harness, with its own names taken out: the namespace, the - /// class, the rows as member data, the theory and the row's run through Rec.Run with a check of the row. - /// - private static List Form(string source) => - [.. source.Split('\n').Select(line => line.Trim()).Select(line => line switch - { - _ when line.StartsWith("namespace ", StringComparison.Ordinal) => line, - _ when Regex.IsMatch(line, @"^public sealed class \w+Probe$") => "public sealed class {probe}", - _ when line.StartsWith("public static TheoryData Data()", StringComparison.Ordinal) => - Regex.Replace(line, @"Rows\.Data\(""[^""]+""\)", "Rows.Data({probe})"), - _ when line.StartsWith("[Theory", StringComparison.Ordinal) => line, - _ when line.StartsWith("public Task ", StringComparison.Ordinal) => Regex.Replace(line, @"Rec\.Run\(probe, idx, [^;]+\);$", "Rec.Run(probe, idx, {check});"), - _ => null, - }).OfType()]; - - private static readonly string Corpus = Path.Combine(RepositoryRoot(), "tools", "probe", "corpus", "probes"); - - [Theory] - [InlineData("C08SameNameWarningProbe")] - [InlineData("C12DescribedConverterProbe")] - [InlineData("C20ShouldHoldProbe")] - public void The_skeleton_has_the_form_of_the_corpus_s_probes(string probe) - { - fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe").Exit.ShouldBe(0); - var skeleton = File.ReadAllText(Staged("probes/C1KeysProbe.cs")); - var corpus = File.ReadAllText(Path.Combine(Corpus, probe + ".cs")); - Form(skeleton).ShouldBe(Form(corpus)); - Form(skeleton).Count.ShouldBe(5, "namespace, class, rows, theory and run"); - var usings = corpus.Split('\n').Where(l => l.StartsWith("using ", StringComparison.Ordinal)).Select(l => l.Trim()).ToList(); - skeleton.Split('\n').Where(l => l.StartsWith("using ", StringComparison.Ordinal)).ShouldAllBe(l => usings.Contains(l.Trim())); - } - - [Fact] - public void Every_obligation_s_check_throws_until_it_is_written() - { - fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe").Exit.ShouldBe(0); - var skeleton = File.ReadAllText(Staged("probes/C1KeysProbe.cs")); - foreach (var method in new[] { "CheckKeys", "CheckNulls" }) - { - skeleton.ShouldContain($"static Task {method}(Row r) => throw new NotImplementedException("); - Regex.IsMatch(skeleton, $@"static async Task Check\(Row r\) =>[^;]*await {method}\(r\)").ShouldBeTrue($"Check runs {method}"); - } - Regex.Matches(skeleton, @"=> throw new NotImplementedException\(").Count.ShouldBe(2); - skeleton.ShouldNotContain("return null"); - skeleton.ShouldNotContain("FromResult"); - } - - private static string RepositoryRoot() - { - var root = AppContext.BaseDirectory; - while (!File.Exists(Path.Combine(root, "CodoMetis.TypeKit.slnx"))) root = Path.GetDirectoryName(root) ?? throw new InvalidOperationException("no CodoMetis.TypeKit.slnx above the test output"); - return root; - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/ObligationsTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/ObligationsTests.cs deleted file mode 100644 index 94369bb4..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/ObligationsTests.cs +++ /dev/null @@ -1,228 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// A claim lists what it promises, each obligation a sentence quoted from its source, and what it does not -/// promise, each with the sentence that makes it intended; every obligation of a claim with probes has a seed -/// that names it (decision 104). The probe here is a fake adapter: row N of C1 violates while SEED_ROW is N. -/// -public sealed class ObligationsTests : IDisposable -{ - private readonly Fixture fx = new(); - private readonly string cache; - private readonly FakeAdapter adapter = new(); - - public ObligationsTests() - { - cache = fx.Root + "-cache"; - fx.Sh("git", "config", "core.autocrlf", "false"); - fx.Write("README.md", "# a\n\nThe claim.\n\nA key is written as the wrapped type writes it.\n\nA null is refused.\n"); - fx.Write("design/adr/0001-limits.md", "# 1. Limits\n\nAn ordering of keys is not promised.\n"); - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["replay"] = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src") }, - }.ToJsonString() + "\n```\n"); - fx.Commit("sources and replay configuration"); - } - - public void Dispose() - { - fx.Dispose(); - try { Directory.Delete(cache, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - private static JsonObject Obligation(string id, string sentence) => new() { ["id"] = id, ["sentence"] = sentence }; - - private static JsonArray TwoObligations() => new( - Obligation("keys", "A key is written as the wrapped type writes it."), - Obligation("nulls", "A null is refused.")); - - private void Claims(JsonObject claim) - { - var c = new JsonObject { ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s") }; - foreach (var (key, value) in claim) c[key] = value?.DeepClone(); - fx.Write("tools/probe/corpus/claims.json", new JsonArray(c).ToJsonString()); - } - - /// Registers P for C1 with the seeds given, each an environment toggle that makes the check of its obligation violate in row row. - private void Register(JsonObject? seed = null, params (string Obligation, string Row)[] seeds) - { - var entry = new JsonObject { ["claim"] = "C1", ["filter"] = "*P" }; - if (seed is not null) entry["seed"] = seed; - if (seeds.Length > 0) - entry["seeds"] = new JsonArray([.. seeds.Select(s => (JsonNode)new JsonObject - { - ["obligation"] = s.Obligation, ["env"] = new JsonObject { ["SEED_ROW"] = s.Row, ["SEED_CHECK"] = s.Obligation }, ["defect"] = $"{s.Obligation} is broken.", - })]); - fx.Write("tools/probe/corpus/registry.json", new JsonObject { ["probes"] = new JsonObject { ["P"] = entry } }.ToJsonString()); - fx.Commit("register"); - } - - private static JsonObject EnvSeed() => new() { ["env"] = new JsonObject { ["SEED_ROW"] = "1" }, ["defect"] = "Row 1 is broken." }; - - private List Problems() => Seeds.Problems(new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null)); - - private JsonObject Status() - { - var (exit, output, error) = fx.Ledger("status", "--json"); - exit.ShouldBe(0, error); - return JsonNode.Parse(output)!["claims"]!["C1"]!.AsObject(); - } - - /// Each obligation of the claim in `status --json`, as "id: the probes whose seeds break it". - private static List ObligationsOf(JsonObject claim) => - [.. claim["obligations"]!.AsArray().Select(o => $"{o!["id"]!.GetValue()}: {string.Join(", ", o["seeded_by"]!.AsArray().Select(p => p!.GetValue()))}")]; - - [Fact] - public void An_obligation_quoting_a_sentence_its_source_does_not_hold_is_refused() - { - Claims(new JsonObject { ["obligations"] = new JsonArray(Obligation("keys", "A key is written by ToString().")) }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain("claim C1: obligation keys quotes a sentence not found in README.md"); - } - - [Fact] - public void A_not_promised_entry_is_checked_against_its_own_source() - { - // The sentence is in the claim's README, not in the record the entry names, so it is refused. - Claims(new JsonObject - { - ["not-promised"] = new JsonArray(new JsonObject { ["id"] = "order", ["source"] = "design/adr/0001-limits.md#1-limits", ["sentence"] = "A null is refused." }), - }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain("claim C1: not-promised order quotes a sentence not found in design/adr/0001-limits.md"); - } - - [Fact] - public void A_not_promised_entry_its_source_holds_is_kept() - { - Claims(new JsonObject - { - ["not-promised"] = new JsonArray(new JsonObject { ["id"] = "order", ["source"] = "design/adr/0001-limits.md#1-limits", ["sentence"] = "An ordering of keys is not promised." }), - }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(0, error); - } - - [Fact] - public void A_duplicate_obligation_id_is_refused() - { - Claims(new JsonObject - { - ["obligations"] = new JsonArray(Obligation("keys", "A key is written as the wrapped type writes it."), Obligation("keys", "A null is refused.")), - }); - var (exit, _, error) = fx.Ledger("universe", "--base", "HEAD"); - exit.ShouldBe(1); - error.ShouldContain("claim C1: obligation keys is listed twice"); - } - - [Fact] - public void A_claim_without_obligations_has_one_the_claim_seeded_by_a_seed_naming_none() - { - Register(EnvSeed()); - ObligationsOf(Status()).ShouldBe(["claim: P"]); - Problems().ShouldBeEmpty(); - } - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void Status_shows_the_obligations_no_seed_breaks(string newLine) - { - Claims(new JsonObject { ["obligations"] = TwoObligations() }); - Register(null, ("keys", "1")); - ObligationsOf(Status()).ShouldBe(["keys: P", "nulls: "]); - var (exit, output, error) = fx.LedgerEnding([], newLine, "status"); - exit.ShouldBe(0, error); - output.ReplaceLineEndings("\n").ShouldContain("\n C1: 0/4 obligations 2 (1 without a seed: nulls), 2 uncredited\n"); - - Register(null, ("keys", "1"), ("nulls", "0")); - fx.LedgerEnding([], newLine, "status").Out.ReplaceLineEndings("\n").ShouldContain("\n C1: 0/4 obligations 2, each seeded, 2 uncredited\n"); - } - - [Fact] - public void The_check_without_a_build_names_an_obligation_no_seed_breaks() - { - Claims(new JsonObject { ["obligations"] = TwoObligations() }); - Register(EnvSeed(), ("keys", "1")); - Problems().ShouldBe(["C1: obligation nulls has no seed in any of its probes"], "a seed naming no obligation proves the probe, not an obligation the claim lists"); - Register(null, ("keys", "1"), ("nulls", "0")); - Problems().ShouldBeEmpty(); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void The_check_without_a_build_names_a_seed_naming_an_obligation_the_claim_does_not_list(bool listed) - { - if (listed) Claims(new JsonObject { ["obligations"] = new JsonArray(Obligation("keys", "A key is written as the wrapped type writes it.")) }); - Register(null, ("keys", "1"), ("kyes", "0")); - Problems().ShouldBe(listed - ? ["P/kyes: names obligation kyes, which claim C1 does not list"] - : ["P/keys: names obligation keys, which claim C1 does not list", "P/kyes: names obligation kyes, which claim C1 does not list"]); - } - - [Fact] - public void Seeds_runs_each_seed_under_its_obligation() - { - Claims(new JsonObject { ["obligations"] = TwoObligations() }); - Register(null, ("keys", "1"), ("nulls", "7")); - var output = new StringWriter(); var error = new StringWriter(); - var context = new Context(fx.Root, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, output, error); - var exit = Seeds.Run(context, new Args([], new HashSet(StringComparer.Ordinal) { "dry-run" }), adapter); - - exit.ShouldBe(1, "the nulls seed makes no row violate, so it is missed and fails the command"); - var text = output.ToString().ReplaceLineEndings("\n"); - text.ShouldContain(": 2 seeds of 1 probes, in "); - text.ShouldContain("\n P/keys (C1): caught, 1 of 2 rows violate; e.g. #1 keys: violation:row 1 is broken\n"); - text.ShouldContain("\n P/nulls (C1): missed, no row violates that did not already (2 rows: pass 2;"); - var last = JsonNode.Parse(File.ReadAllText(Path.Combine(fx.BranchDir, "seeds-last.json")))!["seeds"]!.AsArray() - .Select(s => (s!["probe"]!.GetValue(), s["obligation"]!.GetValue(), s["seed"]!.GetValue(), s["result"]!.GetValue())); - last.ShouldBe([("P", "keys", "SEED_ROW=1,SEED_CHECK=keys", "caught"), ("P", "nulls", "SEED_ROW=7,SEED_CHECK=nulls", "missed")]); - } - - /// - /// The probe P over C1's two rows, without a build: row N violates where SEED_ROW is N, by its check of the - /// obligation SEED_CHECK names where it names one (each row then runs that check), else as a row without checks. - /// - private sealed class FakeAdapter : IProbeAdapter - { - public Ran Build(string worktree, IReadOnlyDictionary environment) => new(0, "Build: OK", ""); - - public Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment) - { - var broken = environment.GetValueOrDefault("SEED_ROW"); - var check = environment.GetValueOrDefault("SEED_CHECK"); - var outDir = environment["PROBE_OUT"]!; - Directory.CreateDirectory(outDir); - var lines = Enumerable.Range(0, 2).Select(idx => - { - var violates = broken == idx.ToString(System.Globalization.CultureInfo.InvariantCulture); - var line = new JsonObject - { - ["idx"] = idx, ["category"] = violates ? "violation" : "pass", - ["reasons"] = violates && check is null ? new JsonArray($"violation:row {idx} is broken") : new JsonArray(), - }; - if (check is not null) - line["checks"] = new JsonArray(new JsonObject - { - ["obligation"] = check, ["baseline"] = "twin", ["category"] = violates ? "violation" : "pass", - ["reasons"] = violates ? new JsonArray($"violation:row {idx} is broken") : new JsonArray(), - }); - return line.ToJsonString(); - }).ToList(); - File.WriteAllText(Path.Combine(outDir, "C1.jsonl"), string.Join('\n', lines) + "\n"); - return new Ran(lines.Any(l => l.Contains("\"violation\"", StringComparison.Ordinal)) ? 1 : 0, "", ""); - } - - public (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory) => (null, "none"); - - public IReadOnlyDictionary Environment(string cacheDirectory) => new Dictionary(); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/PerCheckTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/PerCheckTests.cs deleted file mode 100644 index 6602c34b..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/PerCheckTests.cs +++ /dev/null @@ -1,492 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; -using Probes.Shapes; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// Runs rows through the probe corpus's own harness (harness/Row.cs, compiled in) and reads the lines it writes. -internal static class HarnessRun -{ - /// - /// Runs one row of axes x and y through Rec.Run, writing to : the exception the - /// test ends with (a failure or a skip), if any, and the line the harness wrote for the row. - /// - public static (Exception? Ended, JsonObject Line) Row(string outDir, int idx, Func> check, string x = "1", string y = "1") - { - var previous = Environment.GetEnvironmentVariable("PROBE_OUT"); - Environment.SetEnvironmentVariable("PROBE_OUT", outDir); - Exception? ended = null; - try { Rec.Run("C1", new Row(idx, new Dictionary { ["x"] = x, ["y"] = y }), check).GetAwaiter().GetResult(); } - catch (Exception e) { ended = e; } - finally { Environment.SetEnvironmentVariable("PROBE_OUT", previous); } - var line = File.ReadAllLines(Path.Combine(outDir, "C1.jsonl")).Select(l => JsonNode.Parse(l)!.AsObject()).Last(l => (int)Files.Num(l["idx"]) == idx); - return (ended, line); - } - - public static JsonObject Check(JsonObject line, string obligation) => - line["checks"]!.AsArray().OfType().Single(c => Files.Str(c["obligation"]) == obligation); -} - -/// -/// A check of a row (Row.Check) has one obligation, one baseline, and reads, notes and an outcome of its own: a -/// failure it returns is a violation, an exception a harness error that the other checks of the row survive, and a -/// pass with no baseline a harness error. The row passes when no check failed and one passed, and is skipped, each -/// check's outcome listed, when none passed. -/// -[Collection(nameof(ProbeOut))] -public sealed class CheckHarnessTests : IDisposable -{ - private readonly string outDir = Path.Combine(Path.GetTempPath(), "prober-tests", Guid.NewGuid().ToString("N")); - - public void Dispose() - { - try { Directory.Delete(outDir, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - [Fact] - public void A_failure_a_check_returns_is_its_violation_with_its_own_reads() - { - string? returned = "not run"; - var (ended, line) = HarnessRun.Row(outDir, 0, async r => - { - _ = r["x"]; - returned = await r.Check("keys", Baseline.Twin, c => Task.FromResult(c["y"] == "1" ? "the key differs" : null)); - return returned; - }); - returned.ShouldBe("the key differs", "Check returns the failure, so a probe can stop the row at it"); - ended.ShouldNotBeNull().Message.ShouldContain("keys violation: the key differs"); - Files.Str(line["category"]).ShouldBe("violation"); - Files.StringMap(line["applied"]).ShouldBe(new Dictionary { ["x"] = "1" }, "the row's own reads are those outside any check"); - Files.Strings(line["reasons"]).ShouldBeEmpty("the failure is the check's, not the row's own"); - var check = HarnessRun.Check(line, "keys"); - Files.Str(check["baseline"]).ShouldBe("twin"); - Files.Str(check["category"]).ShouldBe("violation"); - Files.StringMap(check["applied"]).ShouldBe(new Dictionary { ["y"] = "1" }); - Files.Strings(check["reasons"]).ShouldBe(["violation:the key differs"]); - } - - [Fact] - public void An_exception_in_a_check_is_a_harness_error_the_other_checks_survive() - { - string? returned = "not run"; - var (ended, line) = HarnessRun.Row(outDir, 0, async r => - { - returned = await r.Check("keys", Baseline.Twin, (Func>)(_ => throw new InvalidOperationException("the check broke"))); - return r.Check("nulls", Baseline.RoundTrip, c => c.Is("x", "1") ? null : "never"); - }); - returned.ShouldBeNull("an exception never reads as a failure, so a probe returning it makes no violation"); - ended.ShouldNotBeNull().Message.ShouldContain("keys harness-error: UNEXPECTED InvalidOperationException: the check broke"); - Files.Str(line["category"]).ShouldBe("harness-error"); - Files.Str(HarnessRun.Check(line, "keys")["category"]).ShouldBe("harness-error"); - Files.Str(HarnessRun.Check(line, "nulls")["category"]).ShouldBe("pass", "the next check of the row still ran"); - } - - [Fact] - public void The_synchronous_form_keeps_an_exception_from_the_row_too() - { - var (ended, line) = HarnessRun.Row(outDir, 0, r => - { - var returned = r.Check("keys", Baseline.Twin, (Func)(_ => throw new FormatException("bad"))); - return Task.FromResult(returned); - }); - ended.ShouldNotBeNull().Message.ShouldContain("keys harness-error: UNEXPECTED FormatException: bad"); - Files.Strings(line["reasons"]).ShouldBeEmpty("the exception stayed in the check"); - } - - [Fact] - public void A_pass_without_a_baseline_is_a_harness_error() - { - var (ended, line) = HarnessRun.Row(outDir, 0, r => Task.FromResult(r.Check("keys", null, c => c.Is("x", "1") ? null : "differs"))); - ended.ShouldNotBeNull().Message.ShouldContain("keys harness-error: pass-without-baseline"); - Files.Str(HarnessRun.Check(line, "keys")["category"]).ShouldBe("harness-error"); - HarnessRun.Check(line, "keys")["baseline"].ShouldBeNull(); - } - - [Fact] - public void A_row_with_a_passing_check_and_one_that_did_not_apply_passes() - { - var (ended, line) = HarnessRun.Row(outDir, 0, r => - { - r.Check("keys", Baseline.Twin, c => c.Is("x", "1") ? null : "differs"); - r.Check("nulls", Baseline.Twin, c => c.Inapplicable(Cite.NotPromised("closed-set-has-no-null"), "x=1")); - return Task.FromResult(null); - }); - ended.ShouldBeNull("one check compared and passed; the other did not apply, which voids nothing"); - Files.Str(line["status"]).ShouldBe("pass"); - Files.Str(line["category"]).ShouldBe("inapplicable", "the row's category is the highest of its checks'"); - Files.Strings(HarnessRun.Check(line, "nulls")["reasons"]).ShouldBe(["inapplicable:not-promised:closed-set-has-no-null(x=1)"]); - Files.Str(HarnessRun.Check(line, "nulls")["notes"]).ShouldBe("not-promised:closed-set-has-no-null(x=1)"); - } - - [Fact] - public void A_row_where_no_check_passed_is_skipped_listing_each_check() - { - var (ended, line) = HarnessRun.Row(outDir, 0, r => - { - r.Check("smallest-scale", Baseline.Twin, c => c.Inapplicable(Cite.NotPromised("closed-set-has-no-scale"))); - r.Check("number-is-a-number", Baseline.Fact("KB-031"), c => c.Refused(Cite.Fact("KB-031"))); - r.Check("reads-a-number", Baseline.Model, c => c.Inconclusive("both threw, not alike")); - r.Check("covers", Baseline.Docs("docs/json.md#decimals-in-one-spelling"), c => c.Refused(Cite.Docs("docs/json.md#decimals-in-one-spelling"))); - r.Check("keeps", Baseline.Cost, c => c.Inapplicable(Cite.Record("0094"))); - return Task.FromResult(null); - }); - ended.ShouldNotBeNull().GetType().Name.ShouldBe("SkipException"); - ended.Message.ShouldContain("smallest-scale inapplicable: not-promised:closed-set-has-no-scale; number-is-a-number expected-refusal: fact:KB-031; " + - "reads-a-number inconclusive: both threw, not alike; covers expected-refusal: docs:docs/json.md#decimals-in-one-spelling; " + - "keeps inapplicable: record:0094"); - Files.Str(line["status"]).ShouldBe("fail"); - Files.Str(line["category"]).ShouldBe("inconclusive"); - line["checks"]!.AsArray().Select(c => Files.Str(c!["baseline"])).ShouldBe(["twin", "fact:KB-031", "model", "docs:docs/json.md#decimals-in-one-spelling", "cost"]); - } - - [Fact] - public void A_violation_fails_the_row_even_beside_a_passing_check() - { - var (ended, line) = HarnessRun.Row(outDir, 0, r => - { - r.Check("keys", Baseline.Twin, _ => (string?)null); - r.Check("nulls", Baseline.Twin, _ => "a null was written"); - return Task.FromResult(null); - }); - ended.ShouldNotBeNull().GetType().Name.ShouldNotBe("SkipException"); - ended.Message.ShouldContain("nulls violation: a null was written"); - ended.Message.ShouldNotContain("keys pass"); - Files.Str(line["status"]).ShouldBe("fail"); - } - - [Fact] - public void A_row_that_runs_no_check_is_written_as_before() - { - var (ended, line) = HarnessRun.Row(outDir, 0, r => Task.FromResult(r["x"] == "1" ? null : "differs")); - ended.ShouldBeNull(); - line.ContainsKey("checks").ShouldBeFalse(); - line.Select(kv => kv.Key).ShouldBe(["idx", "status", "category", "completed", "applied", "reasons", "findings", "note"]); - } - - [Fact] - public void Reads_notes_and_substitutions_through_the_row_inside_a_check_are_the_checks() - { - var (ended, line) = HarnessRun.Row(outDir, 0, async r => - { - await r.Check("keys", Baseline.Twin, async c => - { - await Task.Yield(); - _ = r["y"]; // a helper handed the row, inside the check - _ = r.Get("x"); - r.Substituted("x", "2", "the options ran as x=2"); - r.Note(NoteKeys.Compared, "write"); - return null; - }); - return null; - }); - ended.ShouldBeNull(); - Files.StringMap(line["applied"]).ShouldBeEmpty("the row read nothing outside the check"); - Files.Str(line["note"]).ShouldBeEmpty(); - var check = HarnessRun.Check(line, "keys"); - Files.StringMap(check["applied"]).OrderBy(kv => kv.Key, StringComparer.Ordinal).Select(kv => $"{kv.Key}={kv.Value}").ShouldBe(["x=2", "y=1"]); - Files.Str(check["notes"]).ShouldBe("substituted(x=2, the options ran as x=2);compared(write)"); - } - - [Fact] - public void A_malformed_citation_or_baseline_is_refused_at_construction() - { - Should.Throw(() => Cite.Fact("KB-31")); - Should.Throw(() => Cite.Record("94")); - Should.Throw(() => Cite.NotPromised("Closed set")); - Should.Throw(() => Cite.Docs("README.md#What TypeKit guarantees")); - Should.Throw(() => Cite.Docs("../outside.md")); - Should.Throw(() => Baseline.Fact("fact:KB-031")); - Should.Throw(() => Baseline.Docs("#anchor-alone")); - new[] { Baseline.Twin, Baseline.Framework, Baseline.RoundTrip, Baseline.Model, Baseline.Refusal, Baseline.Cost, Baseline.Fact("KB-031"), Baseline.Docs("README.md") } - .Select(b => b.Spelling).ShouldBe(["twin", "framework", "round-trip", "model", "refusal", "cost", "fact:KB-031", "docs:README.md"]); - new[] { Cite.NotPromised("a-b"), Cite.Docs("CHANGELOG.md#2.1.0-fixed"), Cite.Fact("KB-001"), Cite.Record("0094") } - .Select(c => c.Spelling).ShouldBe(["not-promised:a-b", "docs:CHANGELOG.md#2.1.0-fixed", "fact:KB-001", "record:0094"]); - - var (ended, line) = HarnessRun.Row(outDir, 0, r => Task.FromResult(r.Check("keys", Baseline.Twin, c => c.Refused(Cite.Fact("KB-31"))))); - ended.ShouldNotBeNull().Message.ShouldContain("keys harness-error: UNEXPECTED ArgumentException:"); - Files.Str(HarnessRun.Check(line, "keys")["category"]).ShouldBe("harness-error"); - } -} - -/// -/// The ledger credits checks, not rows: a passing check earns the pairs of the settings it ran under (the row's, its -/// own laid over them) whatever another check of the row ended as, never a pair put together from two checks' reads, -/// and `status` says per obligation what current checks earned and which no passing check named. The evidence is the -/// harness's own lines, copied as the replay copies them. -/// -[Collection(nameof(ProbeOut))] -public sealed class CheckLedgerTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - /// Runs P's two rows (x=1 y=1, x=2 y=2) through the harness and records the run as the replay would. - private JsonObject Record(Func> check) - { - var outDir = Path.Combine(fx.Root, "rows-out"); - var rows = new JsonObject(); - foreach (var (idx, v) in new[] { (0, "1"), (1, "2") }) - rows[idx.ToString(System.Globalization.CultureInfo.InvariantCulture)] = Replay.EvidenceRow(HarnessRun.Row(outDir, idx, check, v, v).Line); - var path = Path.Combine(fx.Root, "evidence.json"); - File.WriteAllText(path, new JsonObject - { - ["variants"] = new JsonArray(new JsonObject { ["env"] = new JsonObject(), ["expected"] = new JsonArray(0, 1), ["executed"] = new JsonArray(0, 1) }), - ["rows"] = rows, - }.ToJsonString()); - fx.Footprint(); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", path).Exit.ShouldBe(0); - return JsonNode.Parse(File.ReadAllText(Path.Combine(fx.BranchDir, "evidence", "P.json")))!.AsObject(); - } - - private void Obligations() - { - fx.Write("README.md", "# a\n\nThe claim.\n\nA key is written as the wrapped type writes it.\n\nA null is refused.\n"); - fx.Write("tools/probe/corpus/claims.json", """ - [{"id": "C1", "source": "README.md#a", "surfaces": ["s"], "obligations": [ - {"id": "keys", "sentence": "A key is written as the wrapped type writes it."}, - {"id": "nulls", "sentence": "A null is refused."}]}] - """); - fx.Commit("obligations"); - fx.Ledger("universe", "--base", "HEAD").Exit.ShouldBe(0); - } - - private JsonObject Status() - { - var (exit, output, error) = fx.Ledger("status", "--json"); - exit.ShouldBe(0, error); - return JsonNode.Parse(output)!["claims"]!["C1"]!.AsObject(); - } - - [Fact] - public void A_passing_check_earns_its_pairs_beside_a_check_that_did_not_apply() - { - var evidence = Record(r => - { - r.Check("keys", Baseline.Twin, c => c["x"] == c["y"] ? null : "differs"); - r.Check("nulls", Baseline.Twin, c => c.Inapplicable(Cite.NotPromised("no-null-here"))); - return Task.FromResult(null); - }); - Files.Num(evidence["schemaVersion"]).ShouldBe(2); - fx.Covered().ShouldBe(2, "x1y1 and x2y2, each read by the passing check, though every row ended inapplicable"); - } - - [Fact] - public void A_pair_is_never_put_together_from_two_checks_reads() - { - Record(r => - { - r.Check("keys", Baseline.Twin, c => c["x"] is not null ? null : "never"); - if (r.Index == 0) r.Check("nulls", Baseline.Twin, c => c["y"] is not null ? null : "never"); - else r.Check("nulls", Baseline.Twin, c => c["x"] is not null && c["y"] is not null ? null : "never"); - return Task.FromResult(null); - }); - fx.Covered().ShouldBe(1, "row 0's checks read x and y apart, which credits no pair; row 1's second check read both"); - } - - [Fact] - public void The_rows_own_reads_are_what_every_check_ran_under() - { - Record(r => - { - _ = r["x"]; - r.Check("keys", Baseline.Twin, c => c["y"] is not null ? null : "never"); - return Task.FromResult(null); - }); - fx.Covered().ShouldBe(2); - } - - [Fact] - public void A_row_without_checks_is_one_check_of_the_claim() - { - Record(r => Task.FromResult(r["x"] == r["y"] ? null : "differs")); - fx.Covered().ShouldBe(2); - var obligation = Status()["obligations"]!.AsArray().ShouldHaveSingleItem()!; - Files.Str(obligation["id"]).ShouldBe("claim"); - Files.Num(obligation["current"]).ShouldBe(2); - } - - [Fact] - public void Status_gives_each_obligation_its_current_pairs_and_counts_those_no_passing_check_named() - { - Obligations(); - Record(r => - { - r.Check("keys", Baseline.Twin, c => c["x"] == c["y"] ? null : "differs"); - r.Check("nulls", Baseline.Twin, c => c.Inapplicable(Cite.NotPromised("no-null-here"))); - return Task.FromResult(null); - }); - var claim = Status(); - claim["obligations"]!.AsArray().Select(o => $"{Files.Str(o!["id"])} {Files.Num(o["current"])} {o["passed"]!.GetValue()}").ShouldBe(["keys 2 True", "nulls 0 False"]); - claim["check_outcomes"]!.AsObject().Select(kv => $"{kv.Key} {Files.Num(kv.Value)}").ShouldBe(["inapplicable 2", "pass 2"]); - claim["outcomes"]!.AsObject().Select(kv => $"{kv.Key} {Files.Num(kv.Value)}").ShouldBe(["inapplicable 2"]); - fx.Ledger("status").Out.ReplaceLineEndings("\n") - .ShouldContain("\n C1: 2/4 (rows: inapplicable 2; checks: inapplicable 2, pass 2) obligations 2 (2 without a seed: keys, nulls), 1 uncredited\n"); - } - - [Fact] - public void The_replay_keeps_each_checks_fields_and_six_reasons() - { - var line = JsonNode.Parse(""" - {"idx": 0, "category": "inapplicable", "applied": {"x": "1"}, "reasons": [], "findings": [], "note": "", - "checks": [{"obligation": "keys", "baseline": "twin", "category": "inapplicable", "applied": {"y": "1"}, - "reasons": ["inapplicable:1", "inapplicable:2", "inapplicable:3", "inapplicable:4", "inapplicable:5", "inapplicable:6", "inapplicable:7"], "notes": "n"}, - {"obligation": "nulls", "baseline": null, "category": "harness-error", "applied": {}, "reasons": [], "notes": ""}]} - """)!.AsObject(); - var row = Replay.EvidenceRow(line); - var checks = row["checks"]!.AsArray(); - checks.Count.ShouldBe(2); - Files.Str(checks[0]!["obligation"]).ShouldBe("keys"); - Files.Str(checks[0]!["baseline"]).ShouldBe("twin"); - Files.StringMap(checks[0]!["applied"]).ShouldBe(new Dictionary { ["y"] = "1" }); - Files.Strings(checks[0]!["reasons"]).Count().ShouldBe(6); - Files.Str(checks[0]!["notes"]).ShouldBe("n"); - checks[1]!["baseline"].ShouldBeNull(); - Replay.EvidenceRow(JsonNode.Parse("""{"idx": 0, "category": "pass", "applied": {}}""")!.AsObject()).ContainsKey("checks").ShouldBeFalse(); - } -} - -/// -/// `seeds` judges a seed by the checks it fires: a seed naming obligation O is caught only by a clean violation of a -/// check naming O, in a row whose unseeded run had no violation of O; a seed naming none by any check. P's rows run a -/// check of each obligation through the real harness; SEED names the check the seeded build breaks. -/// -[Collection(nameof(ProbeOut))] -public sealed class CheckSeedsTests : IDisposable -{ - private readonly Fixture fx = new(); - private readonly string cache; - - public CheckSeedsTests() - { - cache = fx.Root + "-cache"; - fx.Sh("git", "config", "core.autocrlf", "false"); - fx.Write("README.md", "# a\n\nThe claim.\n\nA key is written as the wrapped type writes it.\n\nA null is refused.\n"); - fx.Write("tools/probe/corpus/claims.json", """ - [{"id": "C1", "source": "README.md#a", "surfaces": ["s"], "obligations": [ - {"id": "keys", "sentence": "A key is written as the wrapped type writes it."}, - {"id": "nulls", "sentence": "A null is refused."}]}] - """); - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["replay"] = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src") }, - }.ToJsonString() + "\n```\n"); - fx.Commit("obligations and the replay configuration"); - } - - public void Dispose() - { - fx.Dispose(); - try { Directory.Delete(cache, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - /// Registers P with one seed per (obligation, SEED value): the seed names the obligation, and its build breaks the check SEED names. - private void Register(params (string? Obligation, string Breaks)[] seeds) - { - fx.Write("tools/probe/corpus/registry.json", new JsonObject - { - ["probes"] = new JsonObject - { - ["P"] = new JsonObject - { - ["claim"] = "C1", ["filter"] = "*P", - ["seeds"] = new JsonArray([.. seeds.Select(s => (JsonNode)new JsonObject - { - ["obligation"] = s.Obligation, ["env"] = new JsonObject { ["SEED"] = s.Breaks }, ["defect"] = $"{s.Breaks} breaks.", - })]), - }, - }, - }.ToJsonString()); - fx.Commit("register"); - } - - private (int Exit, string Out) Seeds(HarnessAdapter adapter) - { - var output = new StringWriter(); - var context = new Context(fx.Root, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, output, output); - int exit; - try { exit = global::Prober.Seeds.Run(context, new Args([], new HashSet(StringComparer.Ordinal)), adapter); } - catch (Refusal refusal) { output.WriteLine(refusal.Message); exit = refusal.ExitCode; } - return (exit, output.ToString().ReplaceLineEndings("\n")); - } - - /// Row 1 runs a check of keys and one of nulls; the one SEED names fails. fails a check of row 1 without a seed. - private static Func> TwoChecks(string? unseeded = null) => (r, seed) => - { - foreach (var obligation in new[] { "keys", "nulls" }) - r.Check(obligation, Baseline.Twin, _ => r.Index == 1 && (seed == obligation || unseeded == obligation) ? $"{obligation} broke" : null); - return Task.FromResult(null); - }; - - [Fact] - public void A_seed_naming_an_obligation_is_caught_only_by_a_check_naming_it() - { - Register(("keys", "nulls"), ("nulls", "nulls")); - var (exit, output) = Seeds(new HarnessAdapter(TwoChecks())); - exit.ShouldBe(1, output); - output.ShouldContain("\n P/keys (C1): missed, no row violates that did not already (2 rows: pass 1, violation 1; 1 violate by a check naming nulls, not keys;"); - output.ShouldContain("\n P/nulls (C1): caught, 1 of 2 rows violate; e.g. #1 nulls: violation:nulls broke\n"); - } - - [Fact] - public void The_baseline_is_per_row_and_obligation() - { - Register(("keys", "keys")); - var (exit, output) = Seeds(new HarnessAdapter(TwoChecks(unseeded: "nulls"))); - exit.ShouldBe(0, output); - output.ShouldContain("\n P/keys (C1): caught, 1 of 2 rows violate; e.g. #1 keys: violation:keys broke\n", - Case.Sensitive, "row 1 violated nulls unseeded, which takes nothing from a violation of keys there"); - } - - [Fact] - public void A_seed_naming_none_is_caught_by_any_check() - { - Register((null, "nulls")); - var (exit, output) = Seeds(new HarnessAdapter(TwoChecks())); - exit.ShouldBe(0, output); - output.ShouldContain("\n P (C1): caught, 1 of 2 rows violate; e.g. #1 nulls: violation:nulls broke\n"); - } - - [Fact] - public void A_harness_error_of_the_row_outside_its_checks_makes_no_check_a_catch() - { - Register(("keys", "keys")); - var (exit, output) = Seeds(new HarnessAdapter((r, seed) => - { - if (seed is not null && r.Index == 1) r.Outcome("harness-error", "the host did not start"); - return TwoChecks()(r, seed); - })); - exit.ShouldBe(1, output); - output.ShouldContain("\n P/keys (C1): missed, no row violates that did not already (2 rows: pass 1, violation 1; 1 ended in a harness error, which is no catch;"); - } - - [Fact] - public void A_row_without_checks_catches_no_seed_naming_an_obligation() - { - Register(("keys", "keys")); - var (exit, output) = Seeds(new HarnessAdapter((r, seed) => Task.FromResult(seed is not null && r.Index == 1 ? "row 1 broke" : null))); - exit.ShouldBe(1, output); - output.ShouldContain("1 violate by a check naming claim, not keys;"); - } - - /// The probe P over two rows, each run through the real harness with the value of SEED (null unseeded). - private sealed class HarnessAdapter(Func> check) : IProbeAdapter - { - public Ran Build(string worktree, IReadOnlyDictionary environment) => new(0, "Build: OK", ""); - - public Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment) - { - var seed = environment.GetValueOrDefault("SEED"); - var outDir = environment["PROBE_OUT"]!; - var failed = false; - for (var idx = 0; idx < 2; idx++) - failed |= HarnessRun.Row(outDir, idx, r => check(r, seed)).Ended is not null; - return new Ran(failed ? 1 : 0, "", ""); - } - - public (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory) => (null, "none"); - - public IReadOnlyDictionary Environment(string cacheDirectory) => new Dictionary(); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/RegisterTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/RegisterTests.cs deleted file mode 100644 index a8e95229..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/RegisterTests.cs +++ /dev/null @@ -1,171 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// `register --obligation O` writes a seed per obligation (decision 104) into the entry's `seeds`, in place of -/// any seed that names O already, and refuses an O the claim does not list; `new` prints that command for each -/// listed obligation, so no seed is written into the registry by hand. C1 lists keys and nulls; C7 lists none. -/// -public sealed class RegisterTests : IDisposable -{ - private readonly Fixture fx = new(); - - public RegisterTests() - { - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["replay"] = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src"), ["families"] = new JsonObject { ["f"] = new JsonArray("s") } }, - }.ToJsonString() + "\n```\n"); - fx.Write("tools/probe/corpus/claims.json", new JsonArray( - new JsonObject - { - ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["obligations"] = new JsonArray( - new JsonObject { ["id"] = "keys", ["sentence"] = "The claim." }, - new JsonObject { ["id"] = "nulls", ["sentence"] = "The claim." }), - }, - new JsonObject { ["id"] = "C7", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s") }).ToJsonString()); - fx.Commit("claims with obligations"); - fx.Ledger("universe", "--base", "HEAD").Exit.ShouldBe(0); - } - - public void Dispose() => fx.Dispose(); - - private string StagedRegistry => Path.Combine(fx.BranchDir, "staging", "registry.json"); - - private JsonObject Entry(string probe = "P") => JsonNode.Parse(File.ReadAllText(StagedRegistry))!["probes"]![probe]!.AsObject(); - - private (int Exit, string Out, string Err) Register(params string[] more) => - fx.Ledger(["register", "--probe", "P", "--claim", "C1", "--filter", "*P", .. more]); - - private static string Seeds(JsonObject entry) => - string.Join("; ", entry["seeds"]!.AsArray().Select(s => $"{s!["obligation"]}={s["patch"]}:{s["defect"]}")); - - [Fact] - public void An_obligation_s_seed_goes_into_seeds() - { - var (exit, output, error) = Register("--obligation", "keys", "--seed", "seeds/C1-keys.patch", "--defect", "A key is written with ToString()."); - exit.ShouldBe(0, output + error); - var entry = Entry(); - entry["seeds"]!.ToJsonString().ShouldBe("""[{"obligation":"keys","patch":"seeds/C1-keys.patch","defect":"A key is written with ToString()."}]"""); - entry.ContainsKey("seed").ShouldBeFalse("a seed naming an obligation is not the probe's one `seed`"); - output.ShouldContain("seed for obligation keys of P"); - } - - [Fact] - public void A_seed_for_an_obligation_that_has_one_replaces_it() - { - Register("--obligation", "keys", "--seed", "seeds/C1-keys.patch", "--defect", "first").Exit.ShouldBe(0); - Register("--obligation", "nulls", "--seed", "seeds/C1-nulls.patch", "--defect", "nulls").Exit.ShouldBe(0); - var (exit, output, _) = Register("--obligation", "keys", "--seed", "seeds/C1-keys-2.patch", "--defect", "second"); - exit.ShouldBe(0); - output.ShouldContain("seed for obligation keys of P, in place of the 1 it had"); - Seeds(Entry()).ShouldBe("nulls=seeds/C1-nulls.patch:nulls; keys=seeds/C1-keys-2.patch:second"); - } - - [Fact] - public void The_one_seed_naming_the_obligation_is_replaced_and_one_naming_another_kept() - { - fx.Write("tools/probe/corpus/registry.json", """ - {"probes": { - "P": {"claim": "C1", "filter": "*P", "seed": {"patch": "seeds/C1.patch", "defect": "old", "obligation": "keys"}}, - "Q": {"claim": "C1", "filter": "*Q", "seed": {"patch": "seeds/C1-q.patch", "defect": "kept", "obligation": "nulls"}}}} - """); - fx.Commit("probes whose one seed names an obligation"); - - Register("--obligation", "keys", "--seed", "seeds/C1-keys.patch", "--defect", "new").Exit.ShouldBe(0); - Entry().ContainsKey("seed").ShouldBeFalse("the seed that named keys is replaced, not kept beside the new one"); - Seeds(Entry()).ShouldBe("keys=seeds/C1-keys.patch:new"); - - fx.Ledger("register", "--probe", "Q", "--claim", "C1", "--filter", "*Q", "--obligation", "keys", "--seed", "seeds/C1-qk.patch", "--defect", "q").Exit.ShouldBe(0); - Entry("Q")["seed"]!["obligation"]!.GetValue().ShouldBe("nulls"); - Seeds(Entry("Q")).ShouldBe("keys=seeds/C1-qk.patch:q"); - } - - [Theory] - [InlineData("C1", "kyes", "claim C1 lists no obligation kyes (it lists keys, nulls)")] - [InlineData("C1", "claim", "claim C1 lists no obligation claim (it lists keys, nulls)")] - [InlineData("C7", "keys", "claim C7 lists no obligation keys (it lists claim)")] - public void An_obligation_the_claim_does_not_list_is_refused_and_nothing_staged(string claim, string obligation, string message) - { - var (exit, _, error) = fx.Ledger("register", "--probe", "P", "--claim", claim, "--filter", "*P", "--obligation", obligation, "--seed", "seeds/x.patch", "--defect", "d"); - exit.ShouldBe(2); - error.ShouldContain(message); - File.Exists(StagedRegistry).ShouldBeFalse(); - } - - [Fact] - public void A_claim_without_obligations_takes_a_seed_for_its_one() - { - fx.Ledger("register", "--probe", "P", "--claim", "C7", "--filter", "*P", "--obligation", "claim", "--seed", "seeds/C7.patch", "--defect", "d").Exit.ShouldBe(0); - Seeds(Entry()).ShouldBe("claim=seeds/C7.patch:d"); - } - - [Fact] - public void A_claim_that_is_not_in_the_claims_is_refused() - { - var (exit, _, error) = fx.Ledger("register", "--probe", "P", "--claim", "C9", "--filter", "*P", "--obligation", "keys", "--seed", "seeds/x.patch", "--defect", "d"); - exit.ShouldBe(1); - error.ShouldContain("no claim C9 in claims.json"); - File.Exists(StagedRegistry).ShouldBeFalse(); - } - - [Fact] - public void An_obligation_without_a_seed_or_a_defect_is_refused() - { - var (exit, _, error) = Register("--obligation", "keys", "--defect", "d"); - exit.ShouldBe(2); - error.ShouldContain("give the seed with --seed PATCH or --seed-env NAME=VALUE"); - Register("--obligation", "keys", "--seed", "seeds/C1-keys.patch").Exit.ShouldBe(2, "a seed without its defect"); - File.Exists(StagedRegistry).ShouldBeFalse(); - } - - [Fact] - public void An_environment_toggle_is_a_seed_too() - { - Register("--obligation", "nulls", "--seed-env", "A=1", "B=x=y", "--defect", "d").Exit.ShouldBe(0); - Entry()["seeds"]!.ToJsonString().ShouldBe("""[{"obligation":"nulls","env":{"A":"1","B":"x=y"},"defect":"d"}]"""); - Register("--seed-env", "C=2", "--defect", "one").Exit.ShouldBe(0); - Entry()["seed"]!.ToJsonString().ShouldBe("""{"env":{"C":"2"},"defect":"one"}"""); - Register("--obligation", "nulls", "--seed-env", "=1", "--defect", "d").Exit.ShouldBe(2, "a toggle without a name"); - } - - [Fact] - public void A_seed_stales_no_result() - { - var before = new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null).Identity("P"); - Register("--obligation", "keys", "--seed", "seeds/C1-keys.patch", "--defect", "d").Exit.ShouldBe(0); - Register("--seed", "seeds/C1.patch", "--defect", "d").Exit.ShouldBe(0); - Entry().ContainsKey("seeds").ShouldBeTrue(); - new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null).Identity("P").ShouldBe(before); - } - - [Fact] - public void The_commands_new_prints_register_each_obligation_s_seed() - { - var (exit, output, _) = fx.Ledger("new", "--claim", "C1", "--probe", "C1KeysProbe"); - exit.ShouldBe(0); - var commands = Regex.Matches(output, @"`prober (register [^`]*)`").Select(m => m.Groups[1].Value).ToList(); - commands.Count.ShouldBe(2, output); - output.ShouldNotContain("in the entry's `seeds`"); - foreach (var command in commands) - { - var args = Regex.Matches(command, @"""([^""]*)""|(\S+)").Select(m => m.Groups[1].Success ? m.Groups[1].Value : m.Groups[2].Value) - .Select(arg => arg == "..." ? "What the seed breaks." : arg).ToArray(); - var (registered, said, error) = fx.Ledger(args); - registered.ShouldBe(0, command + ": " + said + error); - } - - var entry = Entry("C1KeysProbe"); - entry["filter"]!.GetValue().ShouldBe("*C1KeysProbe"); - Seeds(entry).ShouldBe("keys=seeds/C1-keys.patch:What the seed breaks.; nulls=seeds/C1-nulls.patch:What the seed breaks."); - var problems = global::Prober.Seeds.Problems(new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null)); - problems.ShouldNotContain(p => p.StartsWith("C1KeysProbe: no seed", StringComparison.Ordinal)); - problems.ShouldNotContain(p => p.StartsWith("C1: obligation", StringComparison.Ordinal)); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/ReplayTemplateTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/ReplayTemplateTests.cs deleted file mode 100644 index 6c0b723b..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/ReplayTemplateTests.cs +++ /dev/null @@ -1,98 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// The replay's templates name only what the repository has. A tool that moves (a script ported to a tool -/// project) must not leave the replay starting a file that is gone: that fails only on the next replay, far -/// from the commit that caused it. -/// -public sealed class ReplayTemplateTests -{ - private static string RepositoryRoot() - { - for (var dir = new DirectoryInfo(AppContext.BaseDirectory); dir is not null; dir = dir.Parent) - if (File.Exists(Path.Combine(dir.FullName, "CodoMetis.TypeKit.slnx"))) return dir.FullName; - throw new InvalidOperationException("no CodoMetis.TypeKit.slnx above the test output"); - } - - /// - /// Every path a template names: a file must be tracked in the repository; a dll under a project's bin/ must - /// come from a project there that a "prepare" command builds (a fresh worktree has no build output). - /// - internal static List Problems(string root, JsonObject replay) - { - var problems = new List(); - var prepared = (replay["prepare"] as JsonArray ?? []).OfType() - .SelectMany(c => c.Select(a => a?.GetValue() ?? "")).ToHashSet(StringComparer.Ordinal); - var templates = new List<(string Name, IEnumerable Args)>(); - foreach (var name in new[] { "build", "test" }) - templates.Add((name, (replay[name] as JsonArray ?? []).Select(a => a?.GetValue() ?? ""))); - foreach (var (i, command) in (replay["prepare"] as JsonArray ?? []).OfType().Index()) - templates.Add(($"prepare[{i}]", command.Select(a => a?.GetValue() ?? ""))); - if (replay["enumerate"]?["script"]?.GetValue() is { } script) templates.Add(("enumerate", [script])); - - foreach (var (name, args) in templates) - foreach (var arg in args.Where(a => a.Contains('/') && !a.Contains('{'))) - { - var bin = arg.IndexOf("/bin/", StringComparison.Ordinal); - if (bin >= 0) - { - var project = arg[..bin]; - if (!Directory.Exists(Path.Combine(root, project)) || !Directory.EnumerateFiles(Path.Combine(root, project), "*.csproj").Any()) - problems.Add($"{name}: {arg} comes from {project}, which is no project in the repository"); - else if (!prepared.Contains(project)) - problems.Add($"{name}: {arg} is build output of {project}, which no \"prepare\" command builds"); - } - else if (!File.Exists(Path.Combine(root, arg)) && !Directory.Exists(Path.Combine(root, arg))) - problems.Add($"{name}: {arg} is not in the repository"); - } - return problems; - } - - private static JsonObject RepositoryReplay(string root) => - Axes.Block(Path.Combine(root, ".claude", "probe-axes.md"))["replay"] as JsonObject ?? throw new InvalidOperationException("no replay object"); - - [Fact] - public void The_replay_templates_name_only_what_the_repository_has() - { - var root = RepositoryRoot(); - Problems(root, RepositoryReplay(root)).ShouldBeEmpty(); - } - - [Fact] - public void A_template_naming_a_removed_script_is_caught() - { - // The seed: the templates as they were before the verdict script became a tool project. - var root = RepositoryRoot(); - var replay = (JsonObject)RepositoryReplay(root).DeepClone(); - replay["build"] = new JsonArray("tools/build-verdict.sh", "build", "{project}"); - Problems(root, replay).ShouldContain(p => p.Contains("tools/build-verdict.sh is not in the repository", StringComparison.Ordinal)); - } - - [Fact] - public void A_tool_dll_no_prepare_command_builds_is_caught() - { - var root = RepositoryRoot(); - var replay = (JsonObject)RepositoryReplay(root).DeepClone(); - replay.Remove("prepare"); - Problems(root, replay).ShouldContain(p => p.Contains("which no \"prepare\" command builds", StringComparison.Ordinal)); - } - - [Fact] - public void A_bare_command_is_found_on_PATH_and_a_path_must_exist_in_the_worktree() - { - var worktree = Path.Combine(Path.GetTempPath(), "prober-exe-" + Guid.NewGuid().ToString("N")); - Directory.CreateDirectory(Path.Combine(worktree, "tools")); - try - { - DotNetAdapter.Executable(worktree, "dotnet").ShouldBe("dotnet"); - File.WriteAllText(Path.Combine(worktree, "tools", "run.sh"), ""); - DotNetAdapter.Executable(worktree, "tools/run.sh").ShouldBe(Path.Combine(worktree, "tools/run.sh")); - Should.Throw(() => DotNetAdapter.Executable(worktree, "tools/build-verdict.sh")) - .Message.ShouldContain("tools/build-verdict.sh"); - } - finally { Directory.Delete(worktree, recursive: true); } - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/ReplayTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/ReplayTests.cs deleted file mode 100644 index b4502eec..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/ReplayTests.cs +++ /dev/null @@ -1,368 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// The replay runs probes in parallel, by family, with or without a footprint, and `merge` joins the states -/// of a replay split by family (decision 101). The probes here are a fake adapter: P covers C1 (surface s) -/// and Q covers C2 (surface t); a run writes its rows and, when asked for one, a coverage report that names -/// src/a.cs. -/// -public sealed class ReplayTests : IDisposable -{ - private readonly Fixture fx = new(); - private readonly string cache; - private readonly FakeAdapter adapter = new(); - private readonly List states = []; - - public ReplayTests() - { - cache = fx.Root + "-cache"; - fx.Sh("git", "config", "core.autocrlf", "false"); - Axes(new JsonObject { ["a"] = new JsonArray("s"), ["b"] = new JsonArray("t") }); - fx.Write("tools/probe/corpus/claims.json", """[{"id": "C1", "source": "README.md#a", "surfaces": ["s"]}, {"id": "C2", "source": "README.md#a", "surfaces": ["t"]}]"""); - fx.Write("tools/probe/corpus/registry.json", """{"probes": {"P": {"claim": "C1", "filter": "*P"}, "Q": {"claim": "C2", "filter": "*Q"}}}"""); - fx.Write("tools/probe/corpus/harness/Q.cs", "// probe Q\n"); - fx.Write("tools/probe/corpus/rows/Q.json", new JsonObject - { - ["claim"] = "C2", ["rows"] = new JsonArray(new JsonObject { ["row"] = new JsonObject { ["x"] = "1", ["y"] = "1" } }), - }.ToJsonString()); - fx.Commit("two probes of two families"); - } - - public void Dispose() - { - fx.Dispose(); - foreach (var dir in states.Append(cache)) - try { Directory.Delete(dir, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - /// The axes file with a replay configuration; its families are the given ones, or none. - private void Axes(JsonObject? families) - { - var replay = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src") }; - if (families is not null) replay["families"] = families; - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject - { - ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") }, - ["t"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") }, - }, - ["replay"] = replay, - }.ToJsonString() + "\n```\n"); - } - - /// A state directory of its own, as a leg of a matrix or the job that merges them has. - private string State(string name) - { - var dir = fx.Root + "-" + name; - states.Add(dir); - return dir; - } - - private static readonly HashSet Flags = new(StringComparer.Ordinal) { "all", "dry-run", "where", "no-footprint" }; - - private (int Exit, string Out, string Err) Replay(string? state, params string[] args) - { - var output = new StringWriter(); var error = new StringWriter(); - var env = new Dictionary { ["PROBE_CACHE_DIR"] = cache }; - if (state is not null) env["PROBE_STATE_DIR"] = state; - int exit; - try { exit = global::Prober.Replay.Run(new Context(fx.Root, env, output, error), new Args(args, Flags), adapter); } - catch (Refusal refusal) { error.WriteLine(refusal.Message); exit = refusal.ExitCode; } - return (exit, output.ToString(), error.ToString()); - } - - /// A replay in another checkout of the repository, a linked worktree, with the fake adapter. - private (int Exit, string Out, string Err) ReplayIn(string checkout, params string[] args) - { - var output = new StringWriter(); var error = new StringWriter(); - int exit; - try { exit = global::Prober.Replay.Run(new Context(checkout, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, output, error), new Args(args, Flags), adapter); } - catch (Refusal refusal) { error.WriteLine(refusal.Message); exit = refusal.ExitCode; } - return (exit, output.ToString(), error.ToString()); - } - - private string Home(string checkout) => - global::Prober.Replay.Home(new Context(checkout, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, TextWriter.Null, TextWriter.Null), - new global::Prober.Ledger(new Context(checkout, new Dictionary(), TextWriter.Null, TextWriter.Null), null)); - - private (int Exit, string Out, string Err) Ledger(string? state, params string[] args) => - state is null ? fx.Ledger(args) : fx.LedgerWith(new Dictionary { ["PROBE_STATE_DIR"] = state }, args); - - private string BranchDir(string? state) => Path.Combine(state ?? fx.Memory, "branches", "main"); - private bool HasEvidence(string? state, string probe) => File.Exists(Path.Combine(BranchDir(state), "evidence", probe + ".json")); - private JsonObject Last(string? state) => (JsonObject)JsonNode.Parse(File.ReadAllText(Path.Combine(BranchDir(state), "replay-last.json")))!; - private int Lines(string? state, string file) => - File.Exists(Path.Combine(BranchDir(state), file)) ? File.ReadAllLines(Path.Combine(BranchDir(state), file)).Count(l => l.Trim().Length > 0) : 0; - - [Fact] - public void A_family_replays_the_probes_whose_claim_surface_it_holds() - { - var (exit, output, error) = Replay(null, "--all", "--family", "a"); - exit.ShouldBe(0, error + output); - output.ShouldContain("1 of 1 probes to replay (family a)"); - HasEvidence(null, "P").ShouldBeTrue(); - HasEvidence(null, "Q").ShouldBeFalse("Q's claim is of surface t, family b"); - Files.Strings(Last(null)["selected"]).ShouldBe(["P"]); - Files.Str(Last(null)["family"]).ShouldBe("a"); - - Replay(null, "--all", "--family", "b").Exit.ShouldBe(0); - HasEvidence(null, "Q").ShouldBeTrue(); - } - - [Fact] - public void A_probe_whose_surface_is_in_no_family_refuses_the_replay() - { - Axes(new JsonObject { ["a"] = new JsonArray("s") }); - fx.Commit("family b dropped"); - var (exit, _, error) = Replay(null, "--all", "--family", "a"); - exit.ShouldBe(1, "a matrix by family would never run Q"); - error.ShouldContain("Q"); - error.ShouldContain("in no family"); - HasEvidence(null, "P").ShouldBeFalse("nothing ran"); - } - - [Fact] - public void An_unknown_family_and_a_bad_parallel_are_usage_errors() - { - Replay(null, "--all", "--family", "c").Exit.ShouldBe(2); - Replay(null, "--all", "--parallel", "0").Exit.ShouldBe(2); - Replay(null, "--all", "--parallel", "two").Exit.ShouldBe(2); - Axes(null); - fx.Commit("no families"); - var (exit, _, error) = Replay(null, "--all", "--family", "a"); - exit.ShouldBe(2); - error.ShouldContain("names no families"); - } - - [Fact] - public void A_replay_without_a_footprint_records_evidence_and_leaves_the_probes_stale() - { - var (exit, output, error) = Replay(null, "--all", "--no-footprint"); - exit.ShouldBe(0, error + output); - adapter.CoverageAsked.ShouldBeEmpty("no coverage report is asked for without a footprint"); - output.ShouldContain("footprint: not recorded (--no-footprint)"); - HasEvidence(null, "P").ShouldBeTrue(); - HasEvidence(null, "Q").ShouldBeTrue(); - Lines(null, "footprints.jsonl").ShouldBe(0); - Last(null)["footprint"]!.GetValue().ShouldBeFalse(); - Ledger(null, "stale").Out.ShouldStartWith("2 of 2 probes to replay"); - - Replay(null, "--all").Exit.ShouldBe(0); - adapter.CoverageAsked.Count.ShouldBe(2); - Lines(null, "footprints.jsonl").ShouldBe(2); - Ledger(null, "stale").Out.ShouldStartWith("0 of 2 probes to replay"); - } - - [Fact] - public void The_rows_a_probe_is_expected_to_run_are_those_of_its_own_rows_file() - { - Replay(null, "--all", "--no-footprint").Exit.ShouldBe(0); - List Expected(string probe) => - [.. JsonNode.Parse(File.ReadAllText(Path.Combine(BranchDir(null), "evidence", probe + ".json")))!["variants"]![0]!["expected"]!.AsArray().Select(i => i!.GetValue())]; - Expected("P").ShouldBe([0, 1], "rows/P.json holds two rows"); - Expected("Q").ShouldBe([0], "rows/Q.json holds one"); - } - - [Fact] - public void Probes_run_as_many_at_a_time_as_asked_without_a_footprint_and_are_recorded_in_registry_order() - { - var (exit, output, error) = Replay(null, "--all", "--no-footprint", "--parallel", "2"); - exit.ShouldBe(0, error + output); - adapter.MostAtOnce.ShouldBe(2); - output.IndexOf(" P:", StringComparison.Ordinal).ShouldBeLessThan(output.IndexOf(" Q:", StringComparison.Ordinal)); - Last(null)["parallel"]!.GetValue().ShouldBe(2); - - adapter.MostAtOnce = 0; - Replay(null, "--all", "--no-footprint", "--parallel", "1").Exit.ShouldBe(0); - adapter.MostAtOnce.ShouldBe(1); - } - - /// - /// The coverage collector rewrites the assemblies on disk, so two hosts from one output directory corrupt - /// each other (KB-025): a replay that records a footprint runs one host at a time, whatever --parallel says. - /// - [Fact] - public void A_replay_with_a_footprint_runs_one_host_at_a_time() - { - var (exit, output, error) = Replay(null, "--all", "--parallel", "2"); - exit.ShouldBe(0, error + output); - adapter.MostAtOnce.ShouldBe(1); - output.ShouldContain("one at a time (the coverage collector rewrites the assemblies on disk)"); - Last(null)["parallel"]!.GetValue().ShouldBe(1); - } - - [Fact] - public void Merge_joins_the_legs_and_refuses_a_probe_no_leg_saw() - { - var legA = State("leg-a"); var legB = State("leg-b"); var merged = State("merged"); - Replay(legA, "--all", "--family", "a").Exit.ShouldBe(0); - Replay(legB, "--all", "--family", "b").Exit.ShouldBe(0); - - var (exit, output, error) = Ledger(merged, "merge", "--from", legA, legB); - exit.ShouldBe(0, error + output); - output.ShouldContain("2 leg(s)"); - HasEvidence(merged, "P").ShouldBeTrue(); - HasEvidence(merged, "Q").ShouldBeTrue(); - Lines(merged, "runs.jsonl").ShouldBe(2); - Lines(merged, "footprints.jsonl").ShouldBe(2); - Files.Strings(Last(merged)["selected"]).ShouldBe(["P", "Q"]); - Files.Strings(Last(merged)["legs"]).ShouldBe(["a", "b"]); - Ledger(merged, "stale").Out.ShouldStartWith("0 of 2 probes to replay"); - - Ledger(merged, "merge", "--from", legA, legB).Exit.ShouldBe(0); - Lines(merged, "runs.jsonl").ShouldBe(2, "a line a leg carried is added once"); - - var (shortExit, _, shortError) = Ledger(State("short"), "merge", "--from", legA); - shortExit.ShouldBe(1, "Q was in no leg"); - shortError.ShouldContain("Q"); - - var (noneExit, _, noneError) = Ledger(State("none"), "merge", "--from", State("empty")); - noneExit.ShouldBe(1); - noneError.ShouldContain("no replay of main"); - } - - [Fact] - public void Merge_fails_when_a_leg_failed() - { - adapter.Violating.Add("C1"); - var legA = State("leg-a"); var legB = State("leg-b"); var merged = State("merged"); - var (legExit, legOutput, _) = Replay(legA, "--all", "--family", "a"); - legExit.ShouldBe(1, "P violates"); - File.Exists(Path.Combine(BranchDir(legA), "logs", "P-0-host.log")).ShouldBeTrue("a failed run's full log is kept in the state"); - legOutput.ShouldContain(" log: "); - Replay(legB, "--all", "--family", "b").Exit.ShouldBe(0); - Directory.Exists(Path.Combine(BranchDir(legB), "logs")).ShouldBeFalse("a passing run keeps no log"); - - var (exit, output, _) = Ledger(merged, "merge", "--from", legA, legB); - exit.ShouldBe(1); - output.ShouldContain("failed: P"); - Files.Strings(Last(merged)["failed"]).ShouldBe(["P"]); - HasEvidence(merged, "Q").ShouldBeTrue("the merge still joined every leg"); - File.Exists(Path.Combine(BranchDir(merged), "logs", "P-0-host.log")).ShouldBeTrue("the merge carries the legs' logs"); - } - - [Fact] - public void Two_checkouts_of_one_repository_get_scratch_worktrees_of_their_own() - { - var other = fx.Linked("other"); - - var (main, linked) = (Home(fx.Root), Home(other)); - - linked.ShouldNotBe(main); - Path.GetFileName(linked).ShouldStartWith(Path.GetFileName(other) + "-"); - ReplayIn(other, "--where").Out.ShouldContain(Path.Combine(linked, "seeds")); - } - - /// Removing a linked worktree leaves its cache behind; the line `--where` prints removes it, worktrees first. - [Fact] - public void The_remove_line_of_where_removes_the_cache_and_its_worktrees() - { - var other = fx.Linked("other"); - var home = Home(other); - fx.Sh("git", "worktree", "add", "-q", "--detach", Path.Combine(home, "seeds"), "HEAD"); - var line = ReplayIn(other, "--where").Out.Split('\n').Single(l => l.StartsWith("remove:", StringComparison.Ordinal))["remove:".Length..].Trim(); - - static string Slashed(string path) => path.Replace('\\', '/'); - Slashed(line).ShouldContain($"'{Slashed(home)}'"); - fx.Sh("sh", "-c", line); - - Directory.Exists(home).ShouldBeFalse(); - Slashed(fx.Sh("git", "worktree", "list", "--porcelain")).ShouldNotContain(Slashed(home)); - } - - /// The main checkout's cache is keyed as it was before linked worktrees had their own, so its warm copy is kept. - [Fact] - public void The_main_checkout_keeps_the_cache_it_had() - { - var root = Path.GetDirectoryName(fx.Sh("git", "rev-parse", "--path-format=absolute", "--git-common-dir").Trim().TrimEnd('/', '\\'))!; - var key = Convert.ToHexStringLower(System.Security.Cryptography.SHA1.HashData(System.Text.Encoding.UTF8.GetBytes(root)))[..8]; - - Home(fx.Root).ShouldBe(Path.Combine(cache, $"{Path.GetFileName(root)}-{key}")); - } - - [Fact] - public void A_round_on_one_branch_does_not_skip_a_replay_of_another() - { - var other = fx.Linked("other"); - fx.Ledger("lock", "--holder", "round-explore-1", "--mode", "explore").Exit.ShouldBe(0); - - var (exit, output, error) = ReplayIn(other, "--all"); - - exit.ShouldBe(0, error + output); - output.ShouldNotContain("skipped"); - File.Exists(Path.Combine(fx.Memory, "branches", "other", "evidence", "P.json")).ShouldBeTrue(output); - } - - /// - /// The lock is per branch and the replay worktree per checkout: a replay of another branch in this checkout (the - /// branch switched while the post-commit replay runs) skips while one holds the worktree, and never shares it. - /// - [Fact] - public void A_replay_skips_while_another_branchs_replay_in_its_checkout_holds_the_worktree() - { - using (global::Prober.Replay.HoldWorktree(Home(fx.Root))) - { - var (exit, output, error) = Replay(null, "--all", "--branch", "other"); - - exit.ShouldBe(0, error); - output.ShouldContain("skipped, another branch's replay in this checkout"); - File.Exists(Path.Combine(fx.Memory, "branches", "other", "evidence", "P.json")).ShouldBeFalse(); - } - Replay(null, "--all", "--branch", "other").Out.ShouldNotContain("skipped"); - } - - /// P over C1's two rows and Q over C2's one; a run takes a moment, so runs at once can be counted. - private sealed class FakeAdapter : IProbeAdapter - { - private int running; - public int MostAtOnce; - public List CoverageAsked { get; } = []; - public HashSet Violating { get; } = new(StringComparer.Ordinal); - - public Ran Build(string worktree, IReadOnlyDictionary environment) => new(0, "Build: OK", ""); - - public Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment) - { - var now = Interlocked.Increment(ref running); - lock (this) MostAtOnce = Math.Max(MostAtOnce, now); - try - { - Thread.Sleep(300); - var claim = filter.EndsWith('P') ? "C1" : "C2"; - var violates = Violating.Contains(claim); - var outDir = environment["PROBE_OUT"]!; - Directory.CreateDirectory(outDir); - var lines = Enumerable.Range(0, claim == "C1" ? 2 : 1).Select(idx => new JsonObject - { - ["idx"] = idx, ["category"] = violates ? "violation" : "pass", ["applied"] = new JsonObject { ["x"] = "1", ["y"] = "1" }, - ["reasons"] = violates ? new JsonArray("violation:seeded") : new JsonArray(), - }.ToJsonString()); - File.WriteAllText(Path.Combine(outDir, claim + ".jsonl"), string.Join('\n', lines) + "\n"); - if (violates) - { - Directory.CreateDirectory(environment["PROBE_LOG_DIR"]!); - File.WriteAllText(Path.Combine(environment["PROBE_LOG_DIR"]!, "host.log"), "the host's full output"); - } - if (coverageFile.Length > 0) - { - lock (this) CoverageAsked.Add(coverageFile); - Directory.CreateDirectory(results); - File.WriteAllText(Path.Combine(results, coverageFile), - $"{worktree}" + - ""); - } - return new Ran(violates ? 1 : 0, violates ? "TESTS FAILED" : "TESTS OK", ""); - } - finally { Interlocked.Decrement(ref running); } - } - - public (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory) => (null, "none"); - - public IReadOnlyDictionary Environment(string cacheDirectory) => new Dictionary(); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/RowsPerProbeTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/RowsPerProbeTests.cs deleted file mode 100644 index c52e5b5d..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/RowsPerProbeTests.cs +++ /dev/null @@ -1,299 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// Rows are per probe: each probe of a claim runs a rows file of its own, its identity reads that file only, and -/// rows --probe P writes it whole, over the claim's pairs the other probes of the claim have not credited. -/// -public sealed class RowsPerProbeTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private static Dictionary R(string x, string y) => new(StringComparer.Ordinal) { ["x"] = x, ["y"] = y }; - - /// C1 gets a second probe, Q, with a rows file of its own; P credits the pairs of rows 11 and 22. - public RowsPerProbeTests() - { - fx.Write("tools/probe/corpus/registry.json", """{"probes": {"P": {"claim": "C1", "filter": "*P"}, "Q": {"claim": "C1", "filter": "*Q"}}}"""); - QRows("1", "2"); - fx.Commit("a second probe of C1"); - fx.Footprint(); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", fx.Evidence([R("1", "1"), R("2", "2")])).Exit.ShouldBe(0); - } - - private void QRows(string x, string y) => - fx.Write("tools/probe/corpus/rows/Q.json", $$$"""{"claim": "C1", "rows": [{"row": {"x": "{{{x}}}", "y": "{{{y}}}"}}]}"""); - - private string Staged(string relative) => Path.Combine(fx.BranchDir, "staging", relative); - - private List StagedRows(string probe) - { - var staged = JsonNode.Parse(File.ReadAllText(Staged($"rows/{probe}.json")))!; - staged["claim"]!.GetValue().ShouldBe("C1", "a probe's rows file keeps its claim inside"); - return [.. staged["rows"]!.AsArray().Select(r => $"{r!["row"]!["x"]}{r["row"]!["y"]}").Order(StringComparer.Ordinal)]; - } - - private string Identity(string probe) - { - var (exit, output, error) = fx.Ledger("identity", "--probe", probe); - exit.ShouldBe(0, error); - return output.Trim(); - } - - [Fact] - public void Rows_for_a_probe_leave_out_the_pairs_its_siblings_credited() - { - var (exit, output, error) = fx.Ledger("rows", "--probe", "Q"); - exit.ShouldBe(0, output + error); - StagedRows("Q").ShouldBe(["12", "21"], "P credited the pairs of 11 and 22, so Q's rows cover the other two"); - File.Exists(Staged("rows/P.json")).ShouldBeFalse("writing one probe's rows leaves its sibling's as they are"); - } - - [Fact] - public void Rows_for_a_probe_do_not_subtract_its_own_credit() - { - fx.Ledger("rows", "--probe", "P").Exit.ShouldBe(0); - StagedRows("P").ShouldBe(["11", "12", "21", "22"], "P's credit came from the rows the file replaces, so it would end with them"); - } - - [Fact] - public void The_file_is_written_whole_never_appended() - { - fx.Write(Path.GetRelativePath(fx.Root, Staged("rows/Q.json")), """{"claim": "C1", "rows": [{"row": {"x": "2", "y": "1"}}, {"row": {"x": "1", "y": "2"}}, {"row": {"x": "1", "y": "2"}}]}"""); - fx.Ledger("rows", "--probe", "Q").Exit.ShouldBe(0); - StagedRows("Q").ShouldBe(["12", "21"], "the staged rows are regenerated, not kept and added to"); - } - - [Fact] - public void A_probe_that_is_not_registered_is_refused() - { - var (exit, _, error) = fx.Ledger("rows", "--probe", "Nope"); - exit.ShouldBe(2); - error.ShouldContain("no probe Nope in the registry"); - } - - [Fact] - public void Publish_moves_the_staged_rows_by_probe_with_their_claim() - { - fx.Ledger("rows", "--probe", "Q").Exit.ShouldBe(0); - fx.Ledger("publish").Exit.ShouldBe(0); - JsonNode published; - using (var gz = new System.IO.Compression.GZipStream(File.OpenRead(Path.Combine(fx.Root, "tools/probe/corpus/rows/Q.json.gz")), System.IO.Compression.CompressionMode.Decompress)) - published = JsonNode.Parse(gz)!; - published["claim"]!.GetValue().ShouldBe("C1"); - published["rows"]!.AsArray().Select(r => $"{r!["row"]!["x"]}{r["row"]!["y"]}").Order(StringComparer.Ordinal).ShouldBe(["12", "21"]); - File.Exists(Path.Combine(fx.Root, "tools/probe/corpus/rows/C1.json.gz")).ShouldBeFalse("rows are published under the probe's name, not the claim's"); - } - - [Fact] - public void A_sibling_s_rows_changing_leaves_the_probe_s_identity_and_credit() - { - var before = Identity("P"); - QRows("2", "1"); - fx.Commit("Q's rows changed"); - Identity("P").ShouldBe(before, "P's identity reads P's rows, not Q's"); - fx.Covered().ShouldBe(2, "P's pass still counts"); - } - - [Fact] - public void The_overlay_unpacks_each_probe_s_rows_and_drops_a_file_the_corpus_no_longer_has() - { - var worktree = fx.Root + "-overlay"; - try - { - var ledger = new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), "main"); - var config = new JsonObject { ["probeDir"] = "probes" }; - var rows = Path.Combine(global::Prober.Replay.Overlay(ledger, config, worktree), "rows"); - Directory.EnumerateFiles(rows).Select(Path.GetFileName).Order(StringComparer.Ordinal).ShouldBe(["P.json", "Q.json"]); - File.Delete(Path.Combine(fx.Root, "tools/probe/corpus/rows/Q.json")); - global::Prober.Replay.Overlay(ledger, config, worktree); - Directory.EnumerateFiles(rows).Select(Path.GetFileName).ShouldBe(["P.json"], "a rows file left from an earlier overlay would be read by a probe of that name"); - } - finally - { - try { Directory.Delete(worktree, recursive: true); } catch (IOException) { } - } - } - - [Fact] - public void The_probe_s_own_rows_changing_moves_its_identity() - { - var before = Identity("P"); - fx.Rows([("1", "1"), ("2", "2"), ("1", "2")]); - fx.Commit("P's rows changed"); - Identity("P").ShouldNotBe(before); - fx.Covered().ShouldBe(0); - } -} - -/// -/// A registry entry's watched paths stale its footprint: what the probe's result depends on that the footprint, -/// the paths its own process ran under src/, cannot see. -/// -public sealed class WatchedTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private const string Catalogue = "test/Variants/Catalogue.cs"; - - private static Dictionary R(string x, string y) => new(StringComparer.Ordinal) { ["x"] = x, ["y"] = y }; - - private void Registry(bool watched) => - fx.Write("tools/probe/corpus/registry.json", watched - ? $$$$"""{"probes": {"P": {"claim": "C1", "filter": "*P", "watched": ["{{{{Catalogue}}}}"]}}}""" - : """{"probes": {"P": {"claim": "C1", "filter": "*P"}}}"""); - - private void ChangeTheCatalogue() - { - fx.Write(Catalogue, "class Catalogue { int Site; }\n"); - fx.Commit("the catalogue gains a site"); - } - - /// A replay's record of P: the footprint from a coverage report of src/a.cs, through the command the replay runs, and a pass. - private void Replayed() - { - var report = Path.Combine(fx.Root, "coverage.cobertura.xml"); - File.WriteAllText(report, $"""{fx.Root}"""); - var (exit, output, error) = fx.Ledger("footprint", "--probe", "P", "--root", fx.Root, "--cobertura", report); - exit.ShouldBe(0, output + error); - File.Delete(report); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", fx.Evidence([R("1", "1"), R("2", "2")])).Exit.ShouldBe(0); - } - - private bool Stale() - { - var (exit, output, error) = fx.Ledger("stale", "--json"); - exit.ShouldBe(0, error); - return JsonNode.Parse(output)!.AsArray().Single(s => s!["probe"]!.GetValue() == "P")!["stale"]!.GetValue(); - } - - private Ledger Ledger() => new(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), "main"); - - [Fact] - public void The_footprint_records_each_watched_path_with_its_id() - { - Registry(watched: true); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe watching a catalogue"); - Replayed(); - var line = JsonNode.Parse(File.ReadLines(Path.Combine(fx.BranchDir, "footprints.jsonl")).Last())!; - line["watched"]![Catalogue]!.GetValue().ShouldBe(fx.Sh("git", "rev-parse", $"HEAD:{Catalogue}").Trim()); - Stale().ShouldBeFalse(); - fx.Covered().ShouldBe(2); - } - - [Fact] - public void A_change_to_a_watched_path_stales_the_footprint() - { - Registry(watched: true); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe watching a catalogue"); - Replayed(); - ChangeTheCatalogue(); - Stale().ShouldBeTrue("the catalogue P watches changed"); - fx.Covered().ShouldBe(0); - } - - [Fact] - public void Without_watched_the_footprint_does_not_see_the_path() - { - Registry(watched: false); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe watching nothing"); - Replayed(); - ChangeTheCatalogue(); - Stale().ShouldBeFalse("the control: nothing P ran or watches changed, which is the gap watched closes"); - fx.Covered().ShouldBe(2); - } - - [Fact] - public void A_watched_path_the_footprint_did_not_record_is_not_current() - { - Registry(watched: true); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe watching a catalogue"); - fx.Footprint(); - var ledger = Ledger(); - ledger.FootprintCurrent("P", ledger.Footprints()).ShouldBeFalse("the footprint line records nothing of the catalogue P watches"); - } - - [Fact] - public void A_change_to_a_watched_path_changes_the_footprint_since_a_release() - { - Registry(watched: true); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe watching a catalogue"); - Replayed(); - fx.Sh("git", "tag", "v1"); - var ledger = Ledger(); - ledger.FootprintChangedSince("P", "v1", ledger.Footprints()).ShouldBeFalse(); - ChangeTheCatalogue(); - ledger = Ledger(); - ledger.FootprintChangedSince("P", "v1", ledger.Footprints()).ShouldBeTrue(); - } - - /// A replay's record of a probe that runs nothing under src/ in its own process, as C22, which builds its consumer in a child process. - private (int Exit, string Output, string Error) RanNothingUnderSrc() - { - var report = Path.Combine(fx.Root, "coverage.cobertura.xml"); - File.WriteAllText(report, $"""{fx.Root}"""); - var result = fx.Ledger("footprint", "--probe", "P", "--root", fx.Root, "--cobertura", report); - File.Delete(report); - return result; - } - - [Fact] - public void A_probe_that_ran_nothing_under_src_is_recorded_by_what_it_watches() - { - Registry(watched: true); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe watching a catalogue, building in a child process"); - var (exit, output, error) = RanNothingUnderSrc(); - exit.ShouldBe(0, output + error); - var line = JsonNode.Parse(File.ReadLines(Path.Combine(fx.BranchDir, "footprints.jsonl")).Last())!; - line["footprint"]!.AsObject().Count.ShouldBe(0); - line["watched"]![Catalogue]!.GetValue().ShouldBe(fx.Sh("git", "rev-parse", $"HEAD:{Catalogue}").Trim()); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", fx.Evidence([R("1", "1"), R("2", "2")])).Exit.ShouldBe(0); - Stale().ShouldBeFalse("what P watches is unchanged"); - fx.Covered().ShouldBe(2); - ChangeTheCatalogue(); - Stale().ShouldBeTrue("the catalogue P watches changed"); - fx.Covered().ShouldBe(0); - } - - [Fact] - public void A_probe_that_ran_nothing_under_src_and_watches_nothing_is_refused() - { - Registry(watched: false); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe watching nothing, building in a child process"); - var (exit, output, error) = RanNothingUnderSrc(); - exit.ShouldBe(1, output + error); - (output + error).ShouldContain("refusing to record an empty footprint"); - File.Exists(Path.Combine(fx.BranchDir, "footprints.jsonl")).ShouldBeFalse(); - } - - [Theory] - [InlineData(true)] - [InlineData(false)] - public void The_gate_counts_a_change_to_a_watched_path_as_touching_the_claim(bool watched) - { - Registry(watched); - fx.Write(Catalogue, "class Catalogue {}\n"); - fx.Commit("a probe and a catalogue"); - fx.Footprint(); - fx.Sh("git", "tag", "v1"); - ChangeTheCatalogue(); - var (_, output, _) = fx.Ledger("gate", "--since", "v1", "--json"); - var gate = JsonNode.Parse(output)!; - static List Names(JsonNode? list) => [.. list!.AsArray().Select(x => x!.GetValue())]; - Names(gate["touched"]).ShouldBe(watched ? ["C1"] : [], "a change to what a probe of the claim watches touches the claim, and nothing else does here"); - // The conservative rule and the footprint agree: neither selects the claim without the other. - Names(gate["selected_by_footprint_only"]).ShouldBeEmpty(); - Names(gate["missed_by_footprint_alone"]).ShouldBeEmpty(); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/SeedsTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/SeedsTests.cs deleted file mode 100644 index 2fce1dab..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/SeedsTests.cs +++ /dev/null @@ -1,408 +0,0 @@ -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// A probe proves it can fail (decision 100): `seeds` applies the defect its registry entry names in a scratch -/// worktree, runs the probe there, and counts the seed caught only for a violation the unseeded probe does -/// not have. The probe here is a fake adapter: row 1 of C1 violates while the product holds DEFECT. -/// -public sealed class SeedsTests : IDisposable -{ - private readonly Fixture fx = new(); - private readonly string cache; - private readonly FakeAdapter adapter = new(); - - public SeedsTests() - { - cache = fx.Root + "-cache"; - fx.Sh("git", "config", "core.autocrlf", "false"); - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["replay"] = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src") }, - }.ToJsonString() + "\n```\n"); - fx.Commit("replay configuration"); - } - - public void Dispose() - { - fx.Dispose(); - try { Directory.Delete(cache, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - /// Writes the seed for P: a patch made by editing the file and taking the diff, and the registry entry naming it. - private void Seed(string? file = "src/a.cs", string? edit = "class A { /* DEFECT */ }\n", JsonObject? env = null) - { - var seed = new JsonObject { ["defect"] = "A holds the defect." }; - if (file is not null) - { - var original = File.ReadAllText(Path.Combine(fx.Root, file)); - fx.Write(file, edit!); - fx.Write("tools/probe/corpus/seeds/C1.patch", fx.Sh("git", "diff", "--", file)); - fx.Write(file, original); - seed["patch"] = "seeds/C1.patch"; - } - if (env is not null) seed["env"] = env; - fx.Write("tools/probe/corpus/registry.json", new JsonObject - { - ["probes"] = new JsonObject { ["P"] = new JsonObject { ["claim"] = "C1", ["filter"] = "*P", ["seed"] = seed } }, - }.ToJsonString()); - fx.Commit("seed"); - } - - private (int Exit, string Out, string Err) Seeds(params string[] args) - { - var output = new StringWriter(); var error = new StringWriter(); - var context = new Context(fx.Root, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, output, error); - int exit; - try { exit = global::Prober.Seeds.Run(context, new Args(args, new HashSet(StringComparer.Ordinal) { "dry-run", "all" }), adapter); } - catch (Refusal refusal) { error.WriteLine(refusal.Message); exit = refusal.ExitCode; } - return (exit, output.ToString(), error.ToString()); - } - - private string Worktree => Directory.GetDirectories(cache).Single() is var home ? Path.Combine(home, "seeds") : ""; - - [Fact] - public void A_seed_the_probe_catches_passes() - { - Seed(); - var (exit, output, error) = Seeds(); - exit.ShouldBe(0, output + error); - output.ShouldContain("P (C1): caught, 1 of 2 rows violate; e.g. #1: violation:"); - output.ShouldContain("1 caught"); - } - - [Fact] - public void A_seed_the_probe_misses_fails_the_command() - { - Seed(edit: "class A { /* harmless */ }\n"); - var (exit, output, _) = Seeds(); - exit.ShouldBe(1); - output.ShouldContain("P (C1): missed, no row violates that did not already (2 rows: pass 2;"); - } - - [Fact] - public void A_violation_the_probe_has_without_the_seed_is_not_the_seeds() - { - adapter.AlwaysViolates = 1; - Seed(); - var (exit, output, _) = Seeds(); - exit.ShouldBe(1, "row 1 fails unseeded too, so the seed proved nothing"); - output.ShouldContain("1 violations unseeded"); - output.ShouldContain("P (C1): missed"); - } - - [Fact] - public void A_probe_without_a_seed_fails_the_command() - { - var (exit, output, _) = Seeds(); - exit.ShouldBe(1); - output.ShouldContain("P (C1): no seed, its registry entry names none"); - } - - [Fact] - public void An_environment_toggle_is_a_seed() - { - Seed(file: null, env: new JsonObject { ["SEED_DEFECT"] = "1" }); - var (exit, output, error) = Seeds(); - exit.ShouldBe(0, output + error); - output.ShouldContain("P (C1): caught"); - } - - [Fact] - public void A_seed_outside_the_product_files_is_refused() - { - Seed(file: "README.md", edit: "# a\n\nThe claim, DEFECT.\n"); - var (exit, output, _) = Seeds(); - exit.ShouldBe(1); - output.ShouldContain("P (C1): does not apply, it patches README.md, outside the product files (src)"); - } - - [Fact] - public void A_seed_that_no_longer_applies_fails_the_command() - { - Seed(); - fx.Write("src/a.cs", "class B {}\n"); - fx.Commit("the product moved on"); - var (exit, output, _) = Seeds(); - exit.ShouldBe(1); - output.ShouldContain("P (C1): does not apply"); - } - - [Fact] - public void A_seed_that_breaks_the_build_proves_nothing() - { - adapter.BreaksOn = "DEFECT"; - Seed(); - var (exit, output, _) = Seeds(); - exit.ShouldBe(1); - output.ShouldContain("P (C1): build failed"); - } - - [Fact] - public void The_seed_is_applied_only_in_the_scratch_worktree_and_undone() - { - Seed(); - Seeds().Exit.ShouldBe(0); - File.ReadAllText(Path.Combine(fx.Root, "src/a.cs")).ShouldBe("class A {}\n"); - File.ReadAllText(Path.Combine(Worktree, "src/a.cs")).ShouldBe("class A {}\n"); - adapter.Worktrees.ShouldAllBe(w => w == Worktree); - } - - [Fact] - public void A_seeded_run_is_never_evidence() - { - Seed(); - Seeds().Exit.ShouldBe(0); - File.Exists(Path.Combine(fx.BranchDir, "runs.jsonl")).ShouldBeFalse(); - Directory.Exists(Path.Combine(fx.BranchDir, "evidence")).ShouldBeFalse(); - File.Exists(Path.Combine(fx.BranchDir, "seeds-last.json")).ShouldBeTrue(); - } - - [Fact] - public void The_seed_is_not_part_of_the_probe_s_identity() - { - var before = fx.Ledger("identity", "--probe", "P").Out; - Seed(); - fx.Ledger("identity", "--probe", "P").Out.ShouldBe(before, "a seed proves the probe; adding or changing one must not stale its results"); - } - - /// What the replay records for P: the product files it ran, at HEAD. - private void Footprint(params string[] files) => - File.AppendAllText(Path.Combine(fx.BranchDir, "footprints.jsonl"), new JsonObject - { - ["probe"] = "P", - ["footprint"] = new JsonObject(files.Select(f => KeyValuePair.Create(f, (JsonNode?)fx.Sh("git", "rev-parse", "HEAD:" + f).Trim()))), - ["compileTime"] = new JsonObject(), ["toolchain"] = new JsonObject(), - }.ToJsonString() + "\n"); - - /// P seeded and caught once, with a footprint over src/a.cs and src/b.cs or the files given, so the next run has a record to skip by. - private void CaughtOnce(params string[] footprint) - { - fx.Write("src/b.cs", "class B {}\n"); - fx.Commit("a second product file"); - Seed(); - Footprint(footprint.Length > 0 ? footprint : ["src/a.cs", "src/b.cs"]); - var (exit, output, error) = Seeds(); - exit.ShouldBe(0, output + error); - output.ShouldContain(" runs P: no run caught every seed of it yet"); - output.ShouldContain("P (C1): caught"); - } - - private JsonObject Last() => (JsonObject)JsonNode.Parse(File.ReadAllText(Path.Combine(fx.BranchDir, "seeds-last.json")))!; - - [Fact] - public void An_unchanged_probe_is_skipped_and_says_why() - { - CaughtOnce(); - var builds = adapter.Worktrees.Count; - - var (exit, output, error) = Seeds(); - - exit.ShouldBe(0, output + error); - output.ShouldContain(" skips P: every seed caught at " + fx.Sh("git", "rev-parse", "--short=7", "HEAD").Trim() + ", and neither its identity nor its product footprint changed since"); - output.ShouldContain("0 seeds of 0 probes"); - output.ShouldContain("1 probes skipped"); - adapter.Worktrees.Count.ShouldBe(builds, "nothing built or ran"); - Last()["skipped"]!.AsArray().Single()!["probe"]!.GetValue().ShouldBe("P"); - Last()["probes"]!["P"]!["commit"]!.GetValue().ShouldBe(fx.Sh("git", "rev-parse", "HEAD").Trim(), "a skipped probe's record is carried forward"); - } - - [Fact] - public void A_changed_identity_reruns() - { - CaughtOnce(); - fx.Write("tools/probe/corpus/harness/P.cs", "// probe P, changed\n"); - fx.Commit("the harness changed"); - - var (_, output, _) = Seeds(); - - output.ShouldContain(" runs P: its identity or its seeds changed since they were caught at "); - output.ShouldContain("P (C1): caught"); - } - - [Fact] - public void A_changed_seed_reruns() - { - CaughtOnce(); - Seed(edit: "class A { /* DEFECT, another */ }\n"); - - Seeds().Out.ShouldContain(" runs P: its identity or its seeds changed since they were caught at "); - } - - [Fact] - public void A_changed_product_footprint_reruns() - { - CaughtOnce(); - fx.Write("src/b.cs", "class B { int changed; }\n"); - fx.Commit("the product changed where P runs"); - - var (_, output, _) = Seeds(); - - output.ShouldContain(" runs P: its product footprint changed since "); - output.ShouldContain("P (C1): caught"); - } - - /// The seed's own file may be outside what the probe ran; a change there may leave the seed behind, so it reruns. - [Fact] - public void A_change_to_the_file_the_seed_patches_reruns() - { - CaughtOnce("src/b.cs"); - fx.Write("src/a.cs", "class A {}\n// moved on\n"); - fx.Commit("the seeded file changed"); - - Seeds().Out.ShouldContain(" runs P: a file its seeds patch changed since "); - } - - [Fact] - public void A_change_outside_the_footprint_skips() - { - CaughtOnce(); - fx.Write("src/c.cs", "class C {}\n"); - fx.Commit("the product changed where P does not run"); - - Seeds().Out.ShouldContain(" skips P: "); - } - - [Fact] - public void A_probe_without_a_recorded_footprint_runs() - { - Seed(); - Seeds().Exit.ShouldBe(0); - - Seeds().Out.ShouldContain(" runs P: no product footprint recorded for it (a replay records one)"); - } - - [Fact] - public void All_ignores_the_record() - { - CaughtOnce(); - - var (exit, output, _) = Seeds("--all"); - - exit.ShouldBe(0); - output.ShouldNotContain("skips P"); - output.ShouldContain("P (C1): caught"); - Last()["all"]!.GetValue().ShouldBeTrue(); - } - - [Fact] - public void A_probe_named_runs_whatever_its_record_says() - { - CaughtOnce(); - - Seeds("--probe", "P").Out.ShouldContain("P (C1): caught"); - } - - [Fact] - public void A_missed_seed_is_never_skipped_next_time() - { - fx.Write("src/b.cs", "class B {}\n"); - fx.Commit("a second product file"); - Seed(edit: "class A { /* harmless */ }\n"); - Footprint("src/a.cs", "src/b.cs"); - Seeds().Exit.ShouldBe(1); - Last()["probes"]!.AsObject().ShouldBeEmpty(); - - var (exit, output, _) = Seeds(); - - exit.ShouldBe(1); - output.ShouldContain(" runs P: no run caught every seed of it yet"); - output.ShouldContain("P (C1): missed"); - } - - [Fact] - public void A_seed_missed_after_a_catch_drops_the_record() - { - CaughtOnce(); - Seed(edit: "class A { /* harmless */ }\n"); - Seeds().Exit.ShouldBe(1); - - Last()["probes"]!.AsObject().ContainsKey("P").ShouldBeFalse(); - Seeds().Out.ShouldContain(" runs P: no run caught every seed of it yet"); - } - - [Fact] - public void The_record_is_what_its_schema_says() - { - CaughtOnce(); - Seeds(); - var root = AppContext.BaseDirectory; - while (!File.Exists(Path.Combine(root, "CodoMetis.TypeKit.slnx"))) root = Path.GetDirectoryName(root) ?? throw new InvalidOperationException("no CodoMetis.TypeKit.slnx above the test output"); - var schema = (JsonObject)JsonNode.Parse(File.ReadAllText(Path.Combine(root, "tools", "Prober", "Schemas", "seeds-run.schema.json")))!["properties"]!; - - foreach (var field in new[] { "probes", "skipped", "all" }) - SchemaCheck.Problems(Last()[field], (JsonObject)schema[field]!, field).ShouldBeEmpty(field); - Last()["probes"]!["P"]!["identity"]!.GetValue().ShouldBe(global::Prober.Seeds.SeedsIdentity(new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null), "P")); - } - - private List Problems() => global::Prober.Seeds.Problems(new Ledger(new Context(fx.Root, new Dictionary(), TextWriter.Null, TextWriter.Null), null)); - - [Fact] - public void The_check_without_a_build_names_a_probe_without_a_seed() - { - Problems().ShouldBe(["P: no seed, its registry entry names none"]); - Seed(); - Problems().ShouldBeEmpty(); - } - - [Fact] - public void The_check_without_a_build_names_a_seed_the_product_left_behind() - { - Seed(); - fx.Write("src/a.cs", "class B {}\n"); - fx.Commit("the product moved on"); - Problems().ShouldHaveSingleItem().ShouldStartWith("P: does not apply, "); - } - - [Fact] - public void Every_probe_in_the_repository_has_a_seed_that_applies() - { - var root = AppContext.BaseDirectory; - while (!File.Exists(Path.Combine(root, "CodoMetis.TypeKit.slnx"))) root = Path.GetDirectoryName(root) ?? throw new InvalidOperationException("no CodoMetis.TypeKit.slnx above the test output"); - global::Prober.Seeds.Problems(new Ledger(new Context(root, new Dictionary(), TextWriter.Null, TextWriter.Null), null)).ShouldBeEmpty(); - } - - /// The probe P over C1's two rows, without a build: row 1 violates where src/a.cs holds DEFECT or SEED_DEFECT is set. - private sealed class FakeAdapter : IProbeAdapter - { - public int? AlwaysViolates { get; set; } - public string? BreaksOn { get; set; } - public List Worktrees { get; } = []; - - public Ran Build(string worktree, IReadOnlyDictionary environment) - { - Worktrees.Add(worktree); - return BreaksOn is { } marker && File.ReadAllText(Path.Combine(worktree, "src", "a.cs")).Contains(marker, StringComparison.Ordinal) - ? new Ran(1, "Build: FAILED", "") - : new Ran(0, "Build: OK", ""); - } - - public Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment) - { - Worktrees.Add(worktree); - var defect = File.ReadAllText(Path.Combine(worktree, "src", "a.cs")).Contains("DEFECT", StringComparison.Ordinal) - || environment.GetValueOrDefault("SEED_DEFECT") == "1"; - var outDir = environment["PROBE_OUT"]!; - Directory.CreateDirectory(outDir); - var lines = Enumerable.Range(0, 2).Select(idx => - { - var violates = idx == AlwaysViolates || (defect && idx == 1); - return new JsonObject - { - ["idx"] = idx, ["category"] = violates ? "violation" : "pass", - ["reasons"] = violates ? new JsonArray("violation:A holds the defect") : new JsonArray(), - }.ToJsonString(); - }); - File.WriteAllText(Path.Combine(outDir, "C1.jsonl"), string.Join('\n', lines) + "\n"); - return new Ran(lines.Any(l => l.Contains("violation", StringComparison.Ordinal)) ? 1 : 0, "", ""); - } - - public (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory) => (null, "none"); - - public IReadOnlyDictionary Environment(string cacheDirectory) => new Dictionary(); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/SourceLintTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/SourceLintTests.cs deleted file mode 100644 index 7e28922b..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/SourceLintTests.cs +++ /dev/null @@ -1,231 +0,0 @@ -using System.Text.Json.Nodes; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// `register` and `publish` read the staged sources before anything is built (decision 103): a note names a -/// key of the catalogue as NoteKeys.Name, the harness's two-argument skip names an open finding of -/// this ledger as a string literal, and a check's Inapplicable or Refused cites by a Cite -/// factory with a string literal something that exists. Each refusal names the file and the line. -/// -public sealed class SourceLintTests : IDisposable -{ - private readonly Fixture fx = new(); - - public SourceLintTests() - { - fx.Write("tools/probe/corpus/harness/Row.cs", """ - public sealed class NoteKeys - { - // public static readonly NoteKeys InAComment = new("in-a-comment", Pass); - public static readonly NoteKeys Compared = new("compared", Pass); - public static readonly NoteKeys BothThrew = new("both-threw", ExpectedRefusal); - } - """); - // C1's not-promised entry, C2's, and what a citation may name besides: a file of the docs, a knowledge entry, a record. - fx.Write("tools/probe/corpus/claims.json", new JsonArray( - new JsonObject { ["id"] = "C1", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["not-promised"] = new JsonArray(new JsonObject { ["id"] = "c1-limit", ["source"] = "README.md#a", ["sentence"] = "The claim." }) }, - new JsonObject { ["id"] = "C2", ["source"] = "README.md#a", ["surfaces"] = new JsonArray("s"), - ["not-promised"] = new JsonArray(new JsonObject { ["id"] = "c2-limit", ["source"] = "README.md#a", ["sentence"] = "The claim." }) }).ToJsonString()); - fx.Write("design/knowledge/KB-007-a-fact.md", "# KB-007\n"); - fx.Write("design/adr/0094-a-record.md", "# 94\n"); - fx.Commit("the catalogue"); - } - - public void Dispose() => fx.Dispose(); - - private string Staged => Path.Combine(fx.BranchDir, "staging"); - - private void Stage(string body) => - fx.Write(Path.GetRelativePath(fx.Root, Path.Combine(Staged, "probes", "C1Probe.cs")), $$""" - public sealed class C1Probe - { - static string? Check(Row r, string x) - { - var rest = new[] { 1, 2 }.Skip(x.Length); - // r.Note("a comment is no call", x); - var text = "r.Note(\"a string is no call\")"; - {{body}} - return null; - } - } - """); - - /// Stages a source of the probe P (C1's), or a harness source no one probe owns, holding in a check. - private void StageCheck(string body, string file = "probes/P.cs") => - fx.Write(Path.GetRelativePath(fx.Root, Path.Combine(Staged, file)), $$""" - public sealed class P - { - static string? Check(Check c, string x) - { - var refused = new Entry().Refused(); - // c.Inapplicable(Cite.NotPromised("in-a-comment")); - var text = "c.Refused(Cite.Docs(\"in-a-string.md\"))"; - {{body}} - return null; - } - } - """); - - private (int Exit, string Out, string Err) Register() => fx.Ledger("register", "--probe", "P", "--claim", "C1", "--filter", "*C1Probe"); - - private void Finding(string id, string status) => - File.AppendAllText(Path.Combine(fx.BranchDir, "findings.jsonl"), - new JsonObject { ["id"] = id, ["claim"] = "C1", ["status"] = status }.ToJsonString() + "\n"); - - [Fact] - public void A_probe_whose_notes_and_skips_are_written_out_registers() - { - Finding("F7", "open"); - Stage(""" - r.Note(NoteKeys.Compared, $"{x}: {(x.Length > 1 ? "long" : "short")}"); - r.Note(NoteKeys.BothThrew); - r.Skip("F7", "the open finding"); - """); - var (exit, output, error) = Register(); - exit.ShouldBe(0, output + error); - } - - [Fact] - public void A_note_key_built_from_text_is_refused_by_file_and_line() - { - Stage(""" r.Note("msg:" + x, "detail");"""); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("staging/probes/C1Probe.cs:8: Note(\"msg:\" + x, ...) does not name a key of NoteKeys"); - } - - [Fact] - public void A_note_key_the_catalogue_does_not_declare_is_refused() - { - Stage(""" r.Note(NoteKeys.InAComment);"""); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("C1Probe.cs:8: Note(NoteKeys.InAComment, ...) names no key of NoteKeys"); - } - - [Fact] - public void A_finding_built_from_text_is_refused() - { - Stage(""" r.Skip("F" + x, "why");"""); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("C1Probe.cs:8: Skip(\"F\" + x, ...) does not name its finding as a string literal"); - } - - [Fact] - public void A_skip_for_a_finding_that_is_not_open_is_refused() - { - Finding("F7", "open"); - Finding("F7", "withdrawn"); - Stage(""" - r.Skip("F7", "withdrawn since"); - r.Skip("F8", "never filed"); - """); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("C1Probe.cs:8: Skip(\"F7\", ...) names finding F7, which is withdrawn"); - error.ShouldContain("C1Probe.cs:9: Skip(\"F8\", ...) names no finding this ledger has"); - } - - [Fact] - public void Publish_refuses_what_register_refuses_and_moves_nothing() - { - Stage(""" r.Note("msg:" + x, "detail");"""); - var (exit, _, error) = fx.Ledger("publish"); - exit.ShouldBe(1); - error.ShouldContain("publish: "); - error.ShouldContain("C1Probe.cs:8: Note(\"msg:\" + x, ...)"); - File.Exists(Path.Combine(fx.Root, "tools/probe/corpus/probes/C1Probe.cs")).ShouldBeFalse(); - } - - [Fact] - public void A_check_citing_what_exists_registers() - { - StageCheck(""" - c.Inapplicable(Cite.NotPromised("c1-limit"), x); - c.Refused(Cite.Docs("README.md#a")); - c.Refused(Cite.Docs("README.md")); - c.Inapplicable(Cite.Fact("KB-007")); - c.Refused(Cite.Record( "0094" ), "detail"); - """); - var (exit, output, error) = Register(); - exit.ShouldBe(0, output + error); - } - - [Fact] - public void A_citation_that_is_not_a_factory_with_a_string_literal_is_refused() - { - StageCheck(""" - var cite = Cite.NotPromised("c1-limit"); - c.Inapplicable(cite); - c.Refused(Cite.NotPromised("c1-" + x)); - c.Inapplicable(Cite.Text("c1-limit")); - """); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("staging/probes/P.cs:9: Inapplicable(cite, ...) does not cite by a Cite factory with a string literal"); - error.ShouldContain("P.cs:10: Refused(Cite.NotPromised(\"c1-\" + x), ...) does not cite by a Cite factory with a string literal"); - error.ShouldContain("P.cs:11: Inapplicable(Cite.Text(\"c1-limit\"), ...) does not cite by a Cite factory with a string literal"); - } - - [Fact] - public void A_not_promised_id_the_probes_claim_does_not_list_is_refused() - { - StageCheck(""" c.Inapplicable(Cite.NotPromised("c2-limit"));"""); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("P.cs:8: Inapplicable(Cite.NotPromised(\"c2-limit\"), ...) names no not-promised entry of its claim C1"); - } - - [Fact] - public void A_harness_source_cites_any_claims_not_promised_entry_and_none_that_no_claim_lists() - { - StageCheck(""" - c.Inapplicable(Cite.NotPromised("c2-limit")); - c.Inapplicable(Cite.NotPromised("no-claim-lists-it")); - """, "harness/Shared.cs"); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldNotContain("Shared.cs:8:"); - error.ShouldContain("staging/harness/Shared.cs:9: Inapplicable(Cite.NotPromised(\"no-claim-lists-it\"), ...) names no not-promised entry of any claim"); - } - - [Fact] - public void A_docs_source_naming_no_file_is_refused() - { - StageCheck(""" c.Refused(Cite.Docs("docs/gone.md#a"));"""); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("P.cs:8: Refused(Cite.Docs(\"docs/gone.md#a\"), ...) names no file docs/gone.md in the repository"); - } - - [Fact] - public void A_fact_with_no_knowledge_entry_is_refused() - { - StageCheck(""" c.Refused(Cite.Fact("KB-070"));"""); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("P.cs:8: Refused(Cite.Fact(\"KB-070\"), ...) names no entry under design/knowledge/"); - } - - [Fact] - public void A_record_with_no_file_under_design_adr_is_refused() - { - StageCheck(""" c.Inapplicable(Cite.Record("0095"));"""); - var (exit, _, error) = Register(); - exit.ShouldBe(1); - error.ShouldContain("P.cs:8: Inapplicable(Cite.Record(\"0095\"), ...) names no record under design/adr/"); - } - - [Fact] - public void Publish_refuses_a_citation_that_names_nothing() - { - StageCheck(""" c.Inapplicable(Cite.Record("0095"));"""); - var (exit, _, error) = fx.Ledger("publish"); - exit.ShouldBe(1); - error.ShouldContain("publish: "); - error.ShouldContain("P.cs:8: Inapplicable(Cite.Record(\"0095\"), ...) names no record under design/adr/"); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/ToolchainTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/ToolchainTests.cs deleted file mode 100644 index 3c22bc0d..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/ToolchainTests.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System.Text.Json.Nodes; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// A result is bound to what the axes file's toolchain commands print. A command that cannot say it refuses, by -/// name: hashed into the identity, its error text made every result read as stale, so `status` showed 0% current -/// when the dotnet first on PATH had no SDK global.json accepts. -/// -public sealed class ToolchainTests : IDisposable -{ - private readonly Fixture fx = new(); - public void Dispose() => fx.Dispose(); - - private static Dictionary R(string x, string y) => new(StringComparer.Ordinal) { ["x"] = x, ["y"] = y }; - - private void Toolchain(params string[] command) - { - var path = Path.Combine(fx.Root, ".claude/probe-axes.md"); - var text = File.ReadAllText(path); - var block = text[(text.IndexOf('\n') + 1)..text.LastIndexOf("```", StringComparison.Ordinal)]; - var axes = JsonNode.Parse(block)!.AsObject(); - axes["replay"] = new JsonObject { ["toolchain"] = new JsonObject { ["sdk"] = new JsonArray([.. command.Select(c => (JsonNode)c)]) } }; - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + axes.ToJsonString() + "\n```\n"); - fx.Commit("toolchain"); - } - - /// A pass recorded under a toolchain that names itself, then read under one that cannot. - private void RecordedThenBroken(params string[] command) - { - Toolchain("git", "--version"); - RecordPass(); - Toolchain(command); - } - - private void RecordPass() - { - fx.Footprint(); - fx.Ledger("record-run", "--probe", "P", "--result", "pass", "--evidence", fx.Evidence([R("1", "1"), R("2", "2")])).Exit.ShouldBe(0); - } - - /// The ways a toolchain command fails to name the toolchain, each with what the refusal must say. - public static TheoryData Failing() => new() - { - { ["git", "no-such-subcommand"], "exited 1" }, - { ["cmtk-no-such-program"], "did not start" }, - { ["git", "diff", "--quiet"], "printed nothing" }, - { [], "is empty" }, - }; - - [Theory, MemberData(nameof(Failing))] - public void A_toolchain_command_that_cannot_name_the_toolchain_refuses(string[] command, string reason) - { - RecordedThenBroken(command); - var (exit, output, error) = fx.Ledger("status"); - exit.ShouldBe(1, output); - error.ShouldContain("the toolchain command sdk"); - error.ShouldContain(reason); - output.ShouldNotContain("pairs current", Case.Sensitive, "a refusal, never a status of 0% current"); - } - - /// - /// The case that was found: a dotnet with no SDK global.json accepts lists the installed SDKs on stdout, which - /// reads like a version, and says why on stderr. The refusal quotes the why. - /// - [Fact] - public void A_dotnet_without_the_pinned_sdk_refuses_with_its_error_not_its_sdk_list() - { - fx.Write("global.json", """{"sdk": {"version": "0.0.1"}}"""); - RecordedThenBroken("dotnet", "--version"); - var (exit, output, error) = fx.Ledger("status"); - exit.ShouldBe(1, output); - error.ShouldContain("the toolchain command sdk (`dotnet --version`) exited"); - error.ShouldNotContain("sdk]", Case.Insensitive, "the installed SDKs on stdout are not the reason"); - } - - /// Every command that reads an identity, not only status. - [Theory] - [InlineData("status")] - [InlineData("gate")] - [InlineData("stale")] - [InlineData("rows", "--claim", "C1")] - [InlineData("identity", "--probe", "P")] - public void Every_reader_of_an_identity_refuses(params string[] command) - { - RecordedThenBroken("git", "no-such-subcommand"); - var (exit, _, error) = fx.Ledger(command); - exit.ShouldBe(1, error); - error.ShouldContain("the toolchain command sdk"); - } - - /// - /// The case that was found next: Homebrew's dotnet first on PATH (10.0.401 against the 10.0.402 pin) made every - /// `dotnet` the prober starts fail, though the SDK installer's had the pinned SDK. Here the dotnet that runs the - /// built prober has no SDK the pin accepts, and a stub in the installer's place (TOOLS_DOTNET_FALLBACK_DIR) answers: - /// the prober falls back to it, records the pass under what it prints, and keeps the pass current. - /// - [Fact] - public void A_dotnet_without_the_pinned_sdk_falls_back_to_the_installers() - { - Assert.SkipWhen(OperatingSystem.IsWindows(), "the stub installer is a shell script"); - Toolchain("dotnet", "--version"); - fx.Write("global.json", """{"sdk": {"version": "0.0.1", "rollForward": "disable"}}"""); - fx.Commit("a pin no dotnet here has"); - fx.Footprint(); - var evidence = fx.Evidence([R("1", "1"), R("2", "2")]); - - var installer = Directory.CreateTempSubdirectory("prober-installer-").FullName; - try - { - var stub = Path.Combine(installer, "dotnet"); - File.WriteAllText(stub, "#!/bin/sh\necho 0.0.1\n"); - if (!OperatingSystem.IsWindows()) File.SetUnixFileMode(stub, UnixFileMode.UserRead | UnixFileMode.UserWrite | UnixFileMode.UserExecute); - using var sandbox = new ToolSandbox("Prober", "prober"); - // The prober runs as its own process, so the caller's state settings would reach it: under a PROBE_STATE_DIR it - // recorded the run there, not in this repository's state, and status found no claim (#58). None reaches it. - var environment = new Dictionary - { - ["TOOLS_DOTNET_FALLBACK_DIR"] = installer, ["PROBE_STATE_DIR"] = null, ["PROBE_CACHE_DIR"] = null, ["PROBE_HOLDER"] = null, - }; - (int, string, string) Prober(params string[] arguments) => ToolSandbox.RunProcess("dotnet", [sandbox.Dll, .. arguments], fx.Root, environment); - - var (recorded, _, why) = Prober("record-run", "--probe", "P", "--result", "pass", "--evidence", evidence); - recorded.ShouldBe(0, why); - var (exit, output, error) = Prober("status", "--json"); - exit.ShouldBe(0, error); - ((int)JsonNode.Parse(output)!["claims"]!["C1"]!["current"]!.GetValue()).ShouldBe(2, "the pass, recorded under the fallback, is current"); - } - finally - { - Directory.Delete(installer, recursive: true); - } - } - - [Fact] - public void A_toolchain_that_names_itself_keeps_the_result_current() - { - Toolchain("git", "--version"); - RecordPass(); - fx.Covered().ShouldBe(2); - } -} diff --git a/test/CodoMetis.TypeKit.Prober.Tests/TryProbeTests.cs b/test/CodoMetis.TypeKit.Prober.Tests/TryProbeTests.cs deleted file mode 100644 index 7b0076f4..00000000 --- a/test/CodoMetis.TypeKit.Prober.Tests/TryProbeTests.cs +++ /dev/null @@ -1,265 +0,0 @@ -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Nodes; -using Prober; - -namespace CodoMetis.TypeKit.Prober.Tests; - -/// -/// `try` is the writer's loop for one probe: it builds once in the replay's worktree, runs that probe alone, -/// prints how each row ended and exits with the probe's code, and records nothing. The probes here are a fake -/// adapter: P runs C1's three rows (row 1 violates, and the run then exits 7) and Q runs C2's. -/// -public sealed class TryProbeTests : IDisposable -{ - private readonly Fixture fx = new(); - private readonly string cache; - private readonly FakeAdapter adapter = new(); - - public TryProbeTests() - { - cache = fx.Root + "-cache"; - fx.Sh("git", "config", "core.autocrlf", "false"); - fx.Write(".claude/probe-axes.md", "```json probe-axes\n" + new JsonObject - { - ["axes"] = new JsonObject { ["x"] = new JsonArray("1", "2"), ["y"] = new JsonArray("1", "2") }, - ["surfaces"] = new JsonObject { ["s"] = new JsonObject { ["axes"] = new JsonArray("x", "y"), ["watched"] = new JsonArray("src") } }, - ["replay"] = new JsonObject { ["probeDir"] = "probes", ["include"] = new JsonArray("src") }, - }.ToJsonString() + "\n```\n"); - fx.Write("tools/probe/corpus/claims.json", """[{"id": "C1", "source": "README.md#a", "surfaces": ["s"]}, {"id": "C2", "source": "README.md#a", "surfaces": ["s"]}]"""); - fx.Write("tools/probe/corpus/rows/Q.json", """{"claim": "C2", "rows": [{"row": {"x": "1", "y": "1"}}]}"""); - fx.Write("tools/probe/corpus/harness/Q.cs", "// probe Q\n"); - Registry(new JsonObject { ["claim"] = "C1", ["filter"] = "*P" }); - fx.Rows([("1", "1"), ("2", "2"), ("1", "2")]); - fx.Commit("two probes, C1 of three rows"); - } - - public void Dispose() - { - fx.Dispose(); - try { Directory.Delete(cache, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - - private void Registry(JsonObject p) => - fx.Write("tools/probe/corpus/registry.json", new JsonObject - { - ["probes"] = new JsonObject { ["P"] = p, ["Q"] = new JsonObject { ["claim"] = "C2", ["filter"] = "*Q" } }, - }.ToJsonString()); - - private (int Exit, string Out, string Err) Try(params string[] args) => TryEnding(Environment.NewLine, args); - - /// As , with the line end the output writers use, so a test sees Windows' output on any OS. - private (int Exit, string Out, string Err) TryEnding(string newLine, params string[] args) - { - var output = new StringWriter { NewLine = newLine }; var error = new StringWriter { NewLine = newLine }; - var context = new Context(fx.Root, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, output, error); - int exit; - try { exit = TryProbe.Run(context, new Args(args, new HashSet(StringComparer.Ordinal) { "no-build" }), adapter); } - catch (Refusal refusal) { error.WriteLine(refusal.Message); exit = refusal.ExitCode; } - return (exit, output.ToString(), error.ToString()); - } - - /// The output's lines, whatever line end the writer used. - private static string[] Lines(string output) => output.ReplaceLineEndings("\n").Split('\n'); - - [Theory] - [InlineData("\n")] - [InlineData("\r\n")] - public void It_runs_the_one_probe_and_prints_how_each_row_ended(string newLine) - { - var (exit, output, error) = TryEnding(newLine, "--probe", "P"); - exit.ShouldBe(7, output + error); - adapter.Builds.ShouldBe(1); - adapter.Runs.ShouldHaveSingleItem().Filter.ShouldBe("*P"); - adapter.Runs[0].Rows.ShouldBeNull("every row, as the registry runs it"); - var lines = Lines(output); - lines.ShouldContain(l => l.StartsWith(" P: Tests: 1 failed of 3 (exit 7, ", StringComparison.Ordinal)); - lines.ShouldContain(" #0 pass"); - lines.ShouldContain(" #1 violation: row 1 breaks the claim"); - lines.ShouldContain(" #2 pass"); - lines.ShouldContain("prober try: P: pass 2, violation 1; exit 7"); - } - - [Fact] - public void A_probe_that_passes_exits_0() - { - adapter.Violates = null; - var (exit, output, _) = Try("--probe", "P"); - exit.ShouldBe(0, output); - output.ShouldContain("prober try: P: pass 3; exit 0"); - } - - [Fact] - public void It_records_nothing_under_the_state_directory() - { - // A staged probe and a ledger with runs and evidence, as a round leaves them. - fx.Write(".claude/agent-memory-local/prober/branches/main/staging/probes/R.cs", "// staged\n"); - fx.Write(".claude/agent-memory-local/prober/branches/main/runs.jsonl", """{"probe": "P", "result": "pass"}""" + "\n"); - var before = Hash(fx.Memory); - Try("--probe", "P").Exit.ShouldBe(7); - Try("--probe", "P", "--rows", "0").Exit.ShouldBe(0); - Hash(fx.Memory).ShouldBe(before); - } - - [Fact] - public void Rows_runs_the_rows_given_one_stretch_at_a_time() - { - var (exit, output, _) = Try("--probe", "P", "--rows", "0,2"); - exit.ShouldBe(0, output); - adapter.Runs.Select(r => r.Rows).ShouldBe(["0-0", "2-2"]); - output.ShouldContain("#0 pass"); - output.ShouldContain("#2 pass"); - output.ShouldNotContain("#1 "); - Try("--probe", "P", "--rows", "1,2").Exit.ShouldBe(7); - adapter.Runs.Skip(2).Select(r => r.Rows).ShouldBe(["1-2"]); - } - - [Fact] - public void Rows_runs_each_row_in_the_variant_that_selects_it() - { - Registry(new JsonObject - { - ["claim"] = "C1", ["filter"] = "*P", - ["variants"] = new JsonArray(new JsonObject { ["PROBE_ONLY"] = "x=1" }, new JsonObject { ["PROBE_ONLY"] = "x=2" }), - }); - fx.Commit("variants"); - var (exit, output, _) = Try("--probe", "P", "--rows", "1,2"); - exit.ShouldBe(7, output); - adapter.Runs.Select(r => (r.Only, r.Rows)).ShouldBe([("x=1", "2-2"), ("x=2", "1-1")]); - output.ShouldContain(" P [PROBE_ONLY=x=1] rows 2-2: "); - } - - [Fact] - public void A_row_the_claim_does_not_have_is_refused_before_anything_runs() - { - var (exit, _, error) = Try("--probe", "P", "--rows", "3"); - exit.ShouldBe(2); - error.ShouldContain("--rows 3: the claim has 3 row(s), 0 to 2"); - adapter.Builds.ShouldBe(0); - adapter.Runs.ShouldBeEmpty(); - } - - [Fact] - public void A_probe_not_in_the_registry_is_refused() - { - var (exit, _, error) = Try("--probe", "Nope"); - exit.ShouldBe(2); - error.ShouldContain("no probe Nope in the registry"); - } - - [Fact] - public void No_build_runs_without_building() - { - Try("--probe", "P", "--no-build").Exit.ShouldBe(2, "no worktree yet, so nothing was ever built"); - Try("--probe", "P").Exit.ShouldBe(7); - Try("--probe", "P", "--no-build").Exit.ShouldBe(7); - adapter.Builds.ShouldBe(1); - adapter.Runs.Count.ShouldBe(2); - } - - [Fact] - public void A_failed_build_runs_nothing() - { - adapter.BuildFails = true; - var (exit, output, _) = Try("--probe", "P"); - exit.ShouldBe(1); - output.ShouldContain("Build: FAILED"); - adapter.Runs.ShouldBeEmpty(); - } - - [Fact] - public void The_staging_is_overlaid_where_the_probe_runs() - { - fx.Write(".claude/agent-memory-local/prober/branches/main/staging/probes/R.cs", "// staged\n"); - Try("--probe", "P").Exit.ShouldBe(7); - var worktree = Path.Combine(Directory.GetDirectories(cache).Single(), "wt"); - adapter.Worktrees.ShouldAllBe(w => w == worktree); - File.ReadAllText(Path.Combine(worktree, "probes", "R.cs")).ShouldBe("// staged\n"); - File.Exists(Path.Combine(worktree, "probes", "P.cs")).ShouldBeTrue(); - } - - /// Every file and directory under , by relative path and content. - private static string Hash(string dir) => - Convert.ToHexString(SHA256.HashData(Encoding.UTF8.GetBytes(string.Join("\n", - Directory.EnumerateFileSystemEntries(dir, "*", SearchOption.AllDirectories).Order(StringComparer.Ordinal) - .Select(p => Path.GetRelativePath(dir, p) + ":" + (File.Exists(p) ? Convert.ToHexString(SHA256.HashData(File.ReadAllBytes(p))) : "dir")))))); - - [Fact] - public void It_holds_the_replay_worktree_while_it_runs() - { - var (exit, output, error) = Try("--probe", "P"); - exit.ShouldBe(7, output + error); - adapter.WorktreeFreeDuringRun.ShouldBe([false], "a replay of the same checkout must not take the worktree while a try runs in it"); - } - - [Fact] - public void It_refuses_while_a_replay_holds_the_worktree_and_runs_nothing() - { - var context = new Context(fx.Root, new Dictionary { ["PROBE_CACHE_DIR"] = cache }, TextWriter.Null, TextWriter.Null); - var home = global::Prober.Replay.Home(context, new Ledger(context, null)); - using (global::Prober.Replay.HoldWorktree(home).ShouldNotBeNull()) - { - var (exit, _, error) = Try("--probe", "P"); - exit.ShouldBe(1); - error.ShouldContain($"a replay in this checkout is using {Path.Combine(home, "wt")}; run `prober try` when it ends"); - adapter.Runs.ShouldBeEmpty(); - } - Try("--probe", "P").Exit.ShouldBe(7, "the worktree is free again once the replay ends"); - } - - /// - /// Runs the probe the filter names over its claim's rows in the worktree, as the harness does: PROBE_ONLY and - /// PROBE_ROWS select, and each row it ran is one line of PROBE_OUT's jsonl. Row 1 of C1 violates. - /// - private sealed class FakeAdapter : IProbeAdapter - { - public int? Violates { get; set; } = 1; - public bool BuildFails { get; set; } - public int Builds { get; private set; } - public List<(string Filter, string? Only, string? Rows)> Runs { get; } = []; - public List Worktrees { get; } = []; - /// For each run, whether a replay could have taken the worktree (replay.lock beside it) while the probe ran. - public List WorktreeFreeDuringRun { get; } = []; - - public Ran Build(string worktree, IReadOnlyDictionary environment) - { - Builds++; - Worktrees.Add(worktree); - return BuildFails ? new Ran(1, "Build: FAILED\nerror X", "") : new Ran(0, "Build: OK", ""); - } - - public Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment) - { - Worktrees.Add(worktree); - using (var replay = global::Prober.Replay.HoldWorktree(Path.GetDirectoryName(worktree)!)) WorktreeFreeDuringRun.Add(replay is not null); - var only = environment.GetValueOrDefault("PROBE_ONLY"); - var range = environment.GetValueOrDefault("PROBE_ROWS"); - Runs.Add((filter, only, range)); - coverageFile.ShouldBeEmpty("try asks for no coverage"); - // The probe's own rows: P's are C1's, Q's C2's. - var probe = filter.TrimStart('*'); - var rows = JsonNode.Parse(File.ReadAllText(Path.Combine(worktree, "probes", "rows", probe + ".json")))!["rows"]!.AsArray(); - var (lo, hi) = range is null ? (0, int.MaxValue) : (int.Parse(range.Split('-')[0]), int.Parse(range.Split('-')[1])); - var lines = new List(); var failed = 0; - for (var i = 0; i < rows.Count; i++) - { - if (i < lo || i > hi || !global::Prober.Replay.Selected(Files.StringMap(rows[i]!["row"]), only ?? "")) continue; - var violates = probe == "P" && i == Violates; - if (violates) failed++; - lines.Add(new JsonObject - { - ["idx"] = i, ["category"] = violates ? "violation" : "pass", - ["reasons"] = violates ? new JsonArray("violation:row 1 breaks the claim") : new JsonArray(), ["note"] = "compared", - }.ToJsonString()); - } - var outDir = environment["PROBE_OUT"]!; - Directory.CreateDirectory(outDir); - File.WriteAllText(Path.Combine(outDir, probe + ".jsonl"), string.Join('\n', lines) + "\n"); - return failed > 0 ? new Ran(7, $"Tests: {failed} failed of {lines.Count}\n", "") : new Ran(0, $"Tests: {lines.Count} passed\n", ""); - } - - public (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory) => (null, "none"); - - public IReadOnlyDictionary Environment(string cacheDirectory) => new Dictionary(); - } -} diff --git a/test/CodoMetis.TypeKit.Samples/CodoMetis.TypeKit.Samples.csproj b/test/CodoMetis.TypeKit.Samples/CodoMetis.TypeKit.Samples.csproj index 8116a63d..12da029c 100644 --- a/test/CodoMetis.TypeKit.Samples/CodoMetis.TypeKit.Samples.csproj +++ b/test/CodoMetis.TypeKit.Samples/CodoMetis.TypeKit.Samples.csproj @@ -35,27 +35,4 @@ - - - - - - - - - - - - - diff --git a/test/oracles/Candidates.cs b/test/oracles/Candidates.cs new file mode 100644 index 00000000..52aa41ef --- /dev/null +++ b/test/oracles/Candidates.cs @@ -0,0 +1,112 @@ +using System.Text.Json; + +namespace CodoMetis.TypeKit.Oracles; + +/// +/// The known disagreements of an oracle, read from test/oracles/candidates.json, and the verdict a sweep +/// reaches once its failures are matched against them (design/adr/0112). +/// +/// +/// +/// An oracle sweep collects one line per disagreement and hands them here instead of asserting the list empty. +/// A line that matches no candidate fails. A line that matches a candidate whose issue is still empty fails too, +/// labelled [known candidate: family], so nothing is quarantined before it is filed. A line that matches a +/// candidate with an issue is written as SKIPPED [known candidate: family, issue] and does not fail. A +/// candidate that no line matched fails with candidate fixed: remove the entry, so the registry cannot +/// outlive the defect it recorded. +/// +/// +/// A candidate matches a failure line when the line contains the candidate's row text (ordinal): the stable +/// prefix of the sweep's own line, the operands and the operation, never the actual value, which changes with the fix. +/// This file is linked into every test project that holds an oracle rather than referenced. +/// +/// +public static class Candidates +{ + /// + /// One known disagreement: the oracle class, the row's stable text, its family and the issue that tracks it. + /// scopes the row to one test method of the class. + /// + public sealed record Candidate(string Oracle, string Row, string Family, string Issue, string? Method = null); + + private static readonly Lazy> All_ = new(Load); + + /// Every registered candidate. + public static IReadOnlyList All => All_.Value; + + /// Candidates registered for . + public static IReadOnlyList For(string oracle) => + [.. All.Where(candidate => string.Equals(candidate.Oracle, oracle, StringComparison.Ordinal))]; + + /// + /// Passes when every failure line is a filed candidate and every candidate of the oracle still fails; + /// otherwise fails with followed by the offending lines. + /// + /// The oracle class, as registered in test/oracles/oracles.json. + /// The sweep's disagreements, one line each. + /// The first line of the failure message, naming the sweep. + /// How many lines of the failure message to show. + /// The calling test method; a candidate scoped to another method is not this sweep's. + public static void Verdict(string oracle, IReadOnlyList failures, string header, int shown = 25, string? method = null) => + Judge([.. For(oracle).Where(candidate => candidate.Method is null || method is null || string.Equals(candidate.Method, method, StringComparison.Ordinal))], + failures, header, shown); + + /// over the candidates given directly, as SeedVerdictTests drives it. + internal static void Judge(IReadOnlyList known, IReadOnlyList failures, string header, int shown = 25) + { + var failing = new List(); + var unfiled = new List(); + var matched = new HashSet(); + + foreach (var line in failures) + { + var candidate = known.FirstOrDefault(c => line.Contains(c.Row, StringComparison.Ordinal)); + if (candidate is null) + { + failing.Add(OneLine(line)); + continue; + } + + matched.Add(candidate); + if (candidate.Issue.Length == 0) + unfiled.Add($"{SeedVerdict.KnownMarker} {candidate.Family}] {OneLine(line.TrimStart())}"); + else + Console.WriteLine($"SKIPPED [known candidate: {candidate.Family}, {candidate.Issue}] {OneLine(line.TrimStart())}"); + } + + foreach (var fixedCandidate in known.Where(candidate => !matched.Contains(candidate))) + failing.Add($"candidate fixed: remove the entry for '{fixedCandidate.Row}' ({fixedCandidate.Family}) from test/oracles/candidates.json"); + + // Known rows last, so a message cut short hides them first (SeedVerdict). + failing.AddRange(unfiled); + if (failing.Count == 0) return; + + Assert.Fail( + $"{OneLine(header)} ({failing.Count} failures):" + Environment.NewLine + + string.Join(Environment.NewLine, failing.Take(shown)) + + (failing.Count > shown ? $"{Environment.NewLine}{SeedVerdict.MorePrefix}{failing.Count - shown} more" : "")); + } + + /// A row as one line, line breaks shown as ⏎: the message is one row per line, and SeedVerdict reads it so. + private static string OneLine(string text) => + text.Contains('\n') || text.Contains('\r') ? text.Replace("\r\n", "⏎").Replace('\n', '⏎').Replace('\r', '⏎') : text; + + private static IReadOnlyList Load() + { + var path = Path.Combine(RepositoryRoot(), "test", "oracles", "candidates.json"); + using var stream = File.OpenRead(path); + return JsonSerializer.Deserialize>(stream, new JsonSerializerOptions { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }) + ?? throw new InvalidOperationException($"'{path}' holds no array."); + } + + private static string RepositoryRoot() + { + const string marker = "CodoMetis.TypeKit.slnx"; + for (var directory = new DirectoryInfo(AppContext.BaseDirectory); directory is not null; directory = directory.Parent) + { + if (File.Exists(Path.Combine(directory.FullName, marker))) return directory.FullName; + } + + throw new InvalidOperationException($"Could not find '{marker}' above '{AppContext.BaseDirectory}'."); + } +} diff --git a/test/oracles/README.md b/test/oracles/README.md new file mode 100644 index 00000000..4b325bb1 --- /dev/null +++ b/test/oracles/README.md @@ -0,0 +1,38 @@ +# Oracles + +How bug-finding works here as a system rather than one sweep per bug (design/adr/0112): a catalogue of what the +documents promise, an enumeration of what the API offers, oracle tests that check the library against an +independent answer, seeds that prove each oracle can fail, and a registry of the disagreements found but not yet +fixed. It is CodoMetis.ValueRanges' kit, kept to what pays for itself: no covering arrays, no ledger, no replay by +footprint, no agent. The oracles are ordinary tests in the projects that already have their harness, and run with +the suite. + +| File | What it holds | Held by | +|---|---|---| +| `claims.json` | One entry per sentence a consumer-facing document promises: its `source` heading, the `quote` verbatim, the `oracle` kind, the `surfaces` (public members, `TypeName.Member`, `*` a wildcard) and `heldBy`, the test classes that assert it. An empty `heldBy` is the worklist. | `OracleCatalogueTests` | +| `surface-waivers.json` | The public members no claim names, each with why it needs no oracle. A reason that starts `unread:` names a document not yet read for claims (#98). | `OracleSurfaceCoverageTests` | +| `oracles.json` | The test classes that are oracles: checks against an independent answer over many cases, not worked examples. | `OracleSeedTests` | +| `seeds/*.patch` | A defect in `src/` with a `Seed:` line, the `Oracles:` it fails and what `Fails:`. Every oracle has one. | `OracleSeedTests`, and `oracles seeds` | +| `candidates.json` | Disagreements an oracle found that are not fixed yet, each tied to an issue (`Candidates.Verdict`). | `OracleCandidateTests` | +| `axes.md` | What a consumer varies, which tests vary it, and the pairs none varies yet. | read by hand | + +`test/CodoMetis.TypeKit.Oracles.Tests` holds all of it in CI without building anything but itself: a quote its +document no longer holds, a surface or a test class that does not exist, a public member neither claimed nor +waived, an oracle without a seed, and a seed that no longer applies all fail there. + +Whether a seed is still **caught** takes a build, so it is a local tool, run before a release and after changing an +oracle or the code a seed patches: + +```bash +dotnet tools/Oracles/bin/Release/net10.0/oracles.dll seeds [name filter] # each seed: a copy, the patch, one project built, one class run +dotnet tools/Oracles/bin/Release/net10.0/oracles.dll status # the worklist: unheld claims, unread waivers, unseeded oracles, candidates +``` + +`seeds` copies the working tree (without `bin/`, `obj/` and `.git/`, so uncommitted work is included), applies the +patch, builds each named oracle's project and runs that one class. A seed is caught when one of the class's tests +fails with a row that is not a known candidate (`SeedVerdict`). One line per seed and oracle, `CAUGHT`, `MISSED` or +`ERROR`, and exit code 1 unless every pair is caught. + +To add an oracle: write the test class, register it in `oracles.json`, write a seed by planting the defect it +guards against, `git diff` it into `seeds/.patch` under its header lines, undo the edit, and run +`oracles seeds `. diff --git a/test/oracles/SeedVerdict.cs b/test/oracles/SeedVerdict.cs new file mode 100644 index 00000000..4fcd1c85 --- /dev/null +++ b/test/oracles/SeedVerdict.cs @@ -0,0 +1,41 @@ +namespace CodoMetis.TypeKit.Oracles; + +/// +/// Whether a test that failed with a seed planted caught it: the rule oracles seeds applies to each +/// failed test, compiled into tools/Oracles and linked into CodoMetis.TypeKit.Oracles.Tests +/// (SeedVerdictTests), so the two cannot disagree (design/adr/0112). +/// +/// +/// +/// A sweep reports through Candidates.Verdict: a header line, then one line per failing row, where a +/// row that matches an unfiled candidate starts with . A failed test catches the +/// seed when at least one of its rows is not a known candidate. Counting a test as known whenever its +/// message held the marker anywhere read a seed that broke a row beside a known one as missed, as +/// CodoMetis.ValueRanges found (its #55). +/// +/// +/// Candidates.Verdict lists the other rows before the known ones, so a message cut short +/// (… and N more) hides known rows first: when every row shown is known, the hidden ones are too. +/// A message with no known row at all is any other assertion, and catches. +/// +/// +public static class SeedVerdict +{ + /// The prefix Candidates.Verdict gives a row that matches an unfiled candidate. + public const string KnownMarker = "[known candidate:"; + + /// The line Candidates.Verdict ends a cut-short message with. + public const string MorePrefix = "… and "; + + /// Whether a failed test's message holds at least one row that is not a known candidate. + public static bool Catches(string message) + { + var lines = message.Split('\n').Select(line => line.Trim()).Where(line => line.Length > 0).ToList(); + if (!lines.Any(IsKnown)) return true; + + // A Verdict message: the first line is its header, the last may say how many rows were cut. + return lines.Skip(1).Any(line => !IsKnown(line) && !line.StartsWith(MorePrefix, StringComparison.Ordinal)); + } + + private static bool IsKnown(string line) => line.StartsWith(KnownMarker, StringComparison.Ordinal); +} diff --git a/test/oracles/axes.md b/test/oracles/axes.md new file mode 100644 index 00000000..e5851d9e --- /dev/null +++ b/test/oracles/axes.md @@ -0,0 +1,44 @@ +# What a consumer varies + +The claims say what is promised, and the surface says what is offered. Neither says which settings a consumer +combines: the JSON options of the host, the OpenAPI version, MVC or a minimal API, the EF provider, the shape of +their own type. This is the hand-written list (design/adr/0112): one section per surface family, each axis with +its values, the tests that already vary it, and the pairs no test varies yet, which are the next probe class. + +A **probe class** (`*ProbeTests`) is an ordinary test over the full product of a few axes, in the project that +already has the harness. Its oracle is the document's promise, it ends in `Candidates.Verdict`, and it is +registered in `oracles.json` with a seed like every other oracle. It stays under a minute: nothing replays it by +footprint, it runs with the suite. + +## The oracles this repository promises + +- **JSON parity (differential).** A shape with a value object and the same shape with the wrapped type serialize + to the same bytes, as a value, a property and a dictionary key. `JsonParityTests`. +- **Schema parity (differential).** The OpenAPI schema of a value-object property equals the wrapped type's. + `SchemaTests`, `NullablePositionTests`. +- **Column parity (differential).** The EF model gives a value-object property the wrapped type's column, and a + key the wrapped type's generation. `MappingTests`, `KeyGenerationVariantTests`, `ColumnFacetVariantTests`. +- **One set of rules (differential).** `TryFrom`, `Parse`, the JSON converter, the type converter and the + binders refuse what `Create` refuses. `EntryPointValidationTests`. +- **No refusal quotes the input (exception).** A sentinel in refused input never reaches a message. + `UnreadableInputTests`. +- **Off the wire (exception).** `Option` and `Result` refuse System.Text.Json in both directions. `NotWireTypeTests`. +- **Every member, by reflection (completeness).** A null delegate on every branch, an uninitialized result, the + context an asynchronous callback runs on. `NullDelegateTests`, `UninitializedResultTests`, `AsynchronousContextTests`. + +## The axes + +| Family | Axis | Values | Varied by | Pairs no test varies yet | +|---|---|---|---|---| +| JSON | the host's options | defaults, Web, a naming policy, a dictionary-key policy, number handling, a host converter | `JsonParityTests` | reference preservation beside a dictionary key | +| JSON | the wrapped type | every built-in the generators wrap, nullable or not | `JsonParityTests`, `JsonTests` | | +| OpenAPI | the version | 3.1, 3.0, 2.0 | `SchemaTests`, `FaultResponseTests` | a withheld fault name under 2.0 | +| HTTP | the host | minimal API, MVC with `[ApiController]`, MVC without | `MvcBindingTests`, `FaultStatusScopeTests` | | +| EF | the provider | SQLite, Npgsql, SQL Server | `KeyGenerationVariantTests`, `QueryTranslationVariantTests` | | +| EF | where a key sits | `KeyShapes` (`test/CodoMetis.TypeKit.EntityFrameworkCore.Tests/Variants/`) | `KeyGenerationVariantTests` | | +| EF | the query | `QueryShapes` | `QueryTranslationVariantTests` | | +| Analyzers | where a type argument is written, and what it is | `TypeArgumentSites`, `TypeArgumentKinds` (`test/CodoMetis.TypeKit.Analyzers.Tests/Variants/`) | `TypeArgumentConstraintVariantTests` | | +| Generation | the declaration's shape | record struct, record class, struct, class; sealed or not; over each wrapped kind | `BuildOutcomeTests`, `GeneratedSurfaceTests` | | + +An empty last column means nobody has looked yet, not that nothing is missing. Fill it when a feature lands or a +finding names a combination no test varied. diff --git a/test/oracles/candidates.json b/test/oracles/candidates.json new file mode 100644 index 00000000..fe51488c --- /dev/null +++ b/test/oracles/candidates.json @@ -0,0 +1 @@ +[] diff --git a/test/oracles/claims.json b/test/oracles/claims.json new file mode 100644 index 00000000..9859384a --- /dev/null +++ b/test/oracles/claims.json @@ -0,0 +1,2029 @@ +[ + { + "id": "C01-covers-every-decimal", + "source": "docs/json.md#decimals-in-one-spelling", + "quote": "Register either one once, and it covers every decimal: nullable ones, dictionary keys, and every value object over a decimal, since its JSON is the underlying type's.", + "claim": "Register either one once, and it covers every decimal: nullable ones, dictionary keys, and every value object over a decimal, since its JSON is the underlying type's.", + "oracle": "claim", + "surfaces": [ + "DecimalNumberJsonConverter.*", + "DecimalStringJsonConverter.*" + ], + "heldBy": [ + "DecimalJsonConvertersTests" + ] + }, + { + "id": "C01-number-is-a-number", + "source": "docs/json.md#decimals-in-one-spelling", + "quote": "**`DecimalNumberJsonConverter`** writes it as a number.", + "claim": "**`DecimalNumberJsonConverter`** writes it as a number.", + "oracle": "claim", + "surfaces": [ + "DecimalNumberJsonConverter.*", + "DecimalStringJsonConverter.*" + ], + "heldBy": [ + "DecimalJsonConvertersTests" + ] + }, + { + "id": "C01-reads-a-number-or-a-string", + "source": "docs/json.md#decimals-in-one-spelling", + "quote": "Both read a number, or a string holding one, and keep the scale they read.", + "claim": "Both read a number, or a string holding one, and keep the scale they read.", + "oracle": "claim", + "surfaces": [ + "DecimalNumberJsonConverter.*", + "DecimalStringJsonConverter.*" + ], + "heldBy": [ + "DecimalJsonConvertersTests" + ] + }, + { + "id": "C01-smallest-scale", + "source": "docs/json.md#decimals-in-one-spelling", + "quote": "Two converters write the smallest scale instead:", + "claim": "Two converters write the smallest scale instead:", + "oracle": "claim", + "surfaces": [ + "DecimalNumberJsonConverter.*", + "DecimalStringJsonConverter.*" + ], + "heldBy": [ + "DecimalJsonConvertersTests" + ] + }, + { + "id": "C01-string-is-a-string", + "source": "docs/json.md#decimals-in-one-spelling", + "quote": "**`DecimalStringJsonConverter`** writes it as a string.", + "claim": "**`DecimalStringJsonConverter`** writes it as a string.", + "oracle": "claim", + "surfaces": [ + "DecimalNumberJsonConverter.*", + "DecimalStringJsonConverter.*" + ], + "heldBy": [ + "DecimalJsonConvertersTests" + ] + }, + { + "id": "C02-hidden-is-an-error-in-both", + "source": "docs/text.md#required-and-optional", + "quote": "`Hidden` is an error in both cases.", + "claim": "`Hidden` is an error in both cases.", + "oracle": "claim", + "surfaces": [ + "StringTextExtensions.*", + "TextFault.*" + ], + "heldBy": [ + "StringTextExtensionsTests" + ] + }, + { + "id": "C02-null-is-blank", + "source": "docs/text.md#required-and-optional", + "quote": "A null string is `Blank`.", + "claim": "A null string is `Blank`.", + "oracle": "claim", + "surfaces": [ + "StringTextExtensions.*", + "TextFault.*" + ], + "heldBy": [ + "StringTextExtensionsTests" + ] + }, + { + "id": "C02-optional-blank-is-no-value", + "source": "docs/text.md#required-and-optional", + "quote": "For an optional one, a blank means \"no value\", and `AsOptionalText()` and `AsOptionalIdentifier()` return a `Result, TextFault>`.", + "claim": "For an optional one, a blank means \"no value\", and `AsOptionalText()` and `AsOptionalIdentifier()` return a `Result, TextFault>`.", + "oracle": "claim", + "surfaces": [ + "StringTextExtensions.*", + "TextFault.*" + ], + "heldBy": [ + "StringTextExtensionsTests" + ] + }, + { + "id": "C02-required-blank-is-an-error", + "source": "docs/text.md#required-and-optional", + "quote": "For a required field, `Blank` is an error.", + "claim": "For a required field, `Blank` is an error.", + "oracle": "claim", + "surfaces": [ + "StringTextExtensions.*", + "TextFault.*" + ], + "heldBy": [ + "StringTextExtensionsTests" + ] + }, + { + "id": "C03-detail-and-rule-intact", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "`Fault` (from CodoMetis.TypeKit) holds one error of either set, with its detail and rule intact.", + "claim": "`Fault` (from CodoMetis.TypeKit) holds one error of either set, with its detail and rule intact.", + "oracle": "claim", + "surfaces": [ + "TypeKitHttpResultExtensions.ToHttpResult", + "Fault.*" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C03-each-error-own-set", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "Each error is answered as its own set maps it.", + "claim": "Each error is answered as its own set maps it.", + "oracle": "claim", + "surfaces": [ + "TypeKitHttpResultExtensions.ToHttpResult", + "Fault.*" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C03-statuses-ascending", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "So its operation lists the statuses of both sets, in ascending order, with the problem types of both where they share a status.", + "claim": "So its operation lists the statuses of both sets, in ascending order, with the problem types of both where they share a status.", + "oracle": "claim", + "surfaces": [ + "Fault.*", + "TypeKitHttpResultExtensions.ToHttpResult" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C03-unmapped-set-stops-start", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "The host doesn't start while either set is unmapped.", + "claim": "The host doesn't start while either set is unmapped.", + "oracle": "claim", + "surfaces": [ + "TypeKitHttpResultExtensions.ToHttpResult", + "Fault.*" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C04-doc-omits-left-out", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#leaving-out-what-a-route-cannot-answer", + "quote": "The operation then lists no status and no problem type for a member left out.", + "claim": "The operation then lists no status and no problem type for a member left out.", + "oracle": "exception", + "surfaces": [ + "WithoutFaultsAttribute.*", + "TypeKitWithoutFaultsEndpointConventionBuilderExtensions.*" + ], + "heldBy": [ + "FaultExclusionTests" + ] + }, + { + "id": "C04-left-out-everywhere-stops-start", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#leaving-out-what-a-route-cannot-answer", + "quote": "**These stop the host from starting**, as for a status: a value or a name no member has, a status that every endpoint it's set on leaves out (on the endpoint itself, or on a group whose endpoints all leave the member out), and a group or a controller none of whose endpoints answers with the type.", + "claim": "**These stop the host from starting**, as for a status: a value or a name no member has, a status that every endpoint it's set on leaves out (on the endpoint itself, or on a group whose endpoints all leave the member out), and a group or a controller none of whose endpoints answers with the type.", + "oracle": "exception", + "surfaces": [ + "WithoutFaultsAttribute.*", + "TypeKitWithoutFaultsEndpointConventionBuilderExtensions.*" + ], + "heldBy": [ + "FaultExclusionTests" + ] + }, + { + "id": "C04-narrows-wider-status", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#leaving-out-what-a-route-cannot-answer", + "quote": "If a group gives `NotFound` a status and one endpoint inside leaves `NotFound` out, that endpoint drops it, and the group's other endpoints keep the status.", + "claim": "If a group gives `NotFound` a status and one endpoint inside leaves `NotFound` out, that endpoint drops it, and the group's other endpoints keep the status.", + "oracle": "exception", + "surfaces": [ + "WithoutFaultsAttribute.*", + "TypeKitWithoutFaultsEndpointConventionBuilderExtensions.*" + ], + "heldBy": [ + "FaultExclusionTests" + ] + }, + { + "id": "C04-reporting-left-out-throws", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#leaving-out-what-a-route-cannot-answer", + "quote": "Reporting a member left out anyway throws an `InvalidOperationException` naming the endpoint and the member, rather than answering with a status the document doesn't list.", + "claim": "Reporting a member left out anyway throws an `InvalidOperationException` naming the endpoint and the member, rather than answering with a status the document doesn't list.", + "oracle": "exception", + "surfaces": [ + "WithoutFaultsAttribute.*", + "TypeKitWithoutFaultsEndpointConventionBuilderExtensions.*" + ], + "heldBy": [ + "FaultExclusionTests" + ] + }, + { + "id": "C05-nested-chain", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#the-rule-that-refused", + "quote": "A rule of a nested type is named after its nesting chain, whatever `QualifiedFaultNames` says.", + "claim": "A rule of a nested type is named after its nesting chain, whatever `QualifiedFaultNames` says.", + "oracle": "claim", + "surfaces": [ + "FaultRuleExtensions.*", + "FaultClosedSetRuleExtensions.*" + ], + "heldBy": [ + "FaultResponsePartsTests" + ] + }, + { + "id": "C05-no-namespace", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#the-rule-that-refused", + "quote": "**The name carries no namespace**, which would publish your application's structure to every client.", + "claim": "**The name carries no namespace**, which would publish your application's structure to every client.", + "oracle": "claim", + "surfaces": [ + "FaultRuleExtensions.*", + "FaultClosedSetRuleExtensions.*" + ], + "heldBy": [ + "FaultResponsePartsTests" + ] + }, + { + "id": "C05-rule-qualified", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#the-rule-that-refused", + "quote": "The problem carries it as `rule`, qualified with its type's name, so two types' `Blank` can be told apart:", + "claim": "The problem carries it as `rule`, qualified with its type's name, so two types' `Blank` can be told apart:", + "oracle": "claim", + "surfaces": [ + "FaultRuleExtensions.*", + "FaultClosedSetRuleExtensions.*" + ], + "heldBy": [ + "FaultResponsePartsTests" + ] + }, + { + "id": "C05-stays-the-errors", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#the-rule-that-refused", + "quote": "The status, the mapping and the document stay the error's.", + "claim": "The status, the mapping and the document stay the error's.", + "oracle": "claim", + "surfaces": [ + "FaultRuleExtensions.*", + "FaultClosedSetRuleExtensions.*" + ], + "heldBy": [ + "FaultResponsePartsTests" + ] + }, + { + "id": "C05-unnamed-value-throws", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#the-rule-that-refused", + "quote": "A value no member names, such as a combination of flags or a cast number, throws where it's given.", + "claim": "A value no member names, such as a combination of flags or a cast number, throws where it's given.", + "oracle": "claim", + "surfaces": [ + "FaultRuleExtensions.*", + "FaultClosedSetRuleExtensions.*" + ], + "heldBy": [ + "FaultResponsePartsTests" + ] + }, + { + "id": "C06-description-as-detail", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#a-closed-set-as-the-fault-type", + "quote": "The description becomes the problem's `detail`, so a client can show it.", + "claim": "The description becomes the problem's `detail`, so a client can show it.", + "oracle": "claim", + "surfaces": [ + "FaultKind.*", + "IFault.*", + "FaultResponse.For" + ], + "heldBy": [] + }, + { + "id": "C06-kind-gives-status", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#a-closed-set-as-the-fault-type", + "quote": "The kind gives the status:", + "claim": "The kind gives the status:", + "oracle": "claim", + "surfaces": [ + "FaultKind.*", + "IFault.*", + "FaultResponse.For" + ], + "heldBy": [] + }, + { + "id": "C06-member-must-have-kind", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#a-closed-set-as-the-fault-type", + "quote": "**A member must have a kind.** A member whose kind is `default`, which is no kind, fails this call.", + "claim": "**A member must have a kind.** A member whose kind is `default`, which is no kind, fails this call.", + "oracle": "claim", + "surfaces": [ + "FaultKind.*", + "IFault.*", + "FaultResponse.For" + ], + "heldBy": [] + }, + { + "id": "C06-no-description-no-detail", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#a-closed-set-as-the-fault-type", + "quote": "A member without one has no `detail`.", + "claim": "A member without one has no `detail`.", + "oracle": "claim", + "surfaces": [ + "FaultKind.*", + "IFault.*", + "FaultResponse.For" + ], + "heldBy": [] + }, + { + "id": "C07-by-kind", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#every-fault-set-at-once", + "quote": "every fault set an endpoint answers with, by its kind", + "claim": "every fault set an endpoint answers with, by its kind", + "oracle": "claim", + "surfaces": [ + "TypeKitFaultResponsesServiceCollectionExtensions.*", + "TypeKitClosedSetFaultResponsesServiceCollectionExtensions.*" + ], + "heldBy": [ + "FaultSetDefaultTests" + ] + }, + { + "id": "C07-enum-needs-own-mapping", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#every-fault-set-at-once", + "quote": "Neither has a kind, and the host doesn't start while an endpoint answers with one that's unmapped.", + "claim": "Neither has a kind, and the host doesn't start while an endpoint answers with one that's unmapped.", + "oracle": "claim", + "surfaces": [ + "TypeKitFaultResponsesServiceCollectionExtensions.*", + "TypeKitClosedSetFaultResponsesServiceCollectionExtensions.*" + ], + "heldBy": [ + "FaultSetDefaultTests" + ] + }, + { + "id": "C07-member-without-kind-stops-start", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#every-fault-set-at-once", + "quote": "A member with no kind stops the start, and so does one the mapping throws for or gives a status that isn't an error.", + "claim": "A member with no kind stops the start, and so does one the mapping throws for or gives a status that isn't an error.", + "oracle": "claim", + "surfaces": [ + "TypeKitClosedSetFaultResponsesServiceCollectionExtensions.*", + "TypeKitFaultResponsesServiceCollectionExtensions.*" + ], + "heldBy": [ + "FaultSetDefaultTests" + ] + }, + { + "id": "C07-own-mapping", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#every-fault-set-at-once", + "quote": "The mapping can be your own. It reads each member's kind and description through `IFault`:", + "claim": "The mapping can be your own. It reads each member's kind and description through `IFault`:", + "oracle": "claim", + "surfaces": [ + "TypeKitFaultResponsesServiceCollectionExtensions.*", + "TypeKitClosedSetFaultResponsesServiceCollectionExtensions.*" + ], + "heldBy": [ + "FaultSetDefaultTests" + ] + }, + { + "id": "C07-own-set-keeps-mapping", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#every-fault-set-at-once", + "quote": "A set mapped on its own keeps its mapping.", + "claim": "A set mapped on its own keeps its mapping.", + "oracle": "claim", + "surfaces": [ + "TypeKitFaultResponsesServiceCollectionExtensions.*", + "TypeKitClosedSetFaultResponsesServiceCollectionExtensions.*" + ], + "heldBy": [ + "FaultSetDefaultTests" + ] + }, + { + "id": "C08-bare-names-warn", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "With bare names, the default, that's `AccountFault.NotFound` beside an `EditFault.NotFound`.", + "claim": "With bare names, the default, that's `AccountFault.NotFound` beside an `EditFault.NotFound`.", + "oracle": "claim", + "surfaces": [ + "FaultResponseOptions.*" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C08-qualified-chain-warns", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "With qualified names, it's two sets with one nesting chain in two namespaces, since the name carries no namespace.", + "claim": "With qualified names, it's two sets with one nesting chain in two namespaces, since the name carries no namespace.", + "oracle": "claim", + "surfaces": [ + "FaultResponseOptions.*" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C08-qualified-names-fix", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "Fix it with `QualifiedFaultNames`, a rename, or by leaving one out of the route", + "claim": "Fix it with `QualifiedFaultNames`, a rename, or by leaving one out of the route", + "oracle": "claim", + "surfaces": [ + "FaultResponseOptions.*" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C08-warning-names-both", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", + "quote": "A client reads the two errors as one, so the host logs a warning at startup naming both.", + "claim": "A client reads the two errors as one, so the host logs a warning at startup naming both.", + "oracle": "claim", + "surfaces": [ + "FaultResponseOptions.*" + ], + "heldBy": [ + "ComposedFaultResponseTests" + ] + }, + { + "id": "C09-equal-by-the-wrapped-value", + "source": "CHANGELOG.md", + "quote": "Over a base record, a value object's equality is its wrapped value's", + "claim": "Over a base record, a value object's equality is its wrapped value's", + "oracle": "metamorphic", + "surfaces": [ + "IValueObject.*" + ], + "heldBy": [] + }, + { + "id": "C09-in-a-collection-of-the-base", + "source": "CHANGELOG.md", + "quote": "in a collection of the base too, whatever equality the base declares.", + "claim": "in a collection of the base too, whatever equality the base declares.", + "oracle": "metamorphic", + "surfaces": [ + "IValueObject.*" + ], + "heldBy": [] + }, + { + "id": "C10-ignore-cycles-unaffected", + "source": "src/CodoMetis.TypeKit.Generators/README.md#json", + "quote": "a struct of such values, and `ReferenceHandler.IgnoreCycles`.", + "claim": "a struct of such values, and `ReferenceHandler.IgnoreCycles`.", + "oracle": "claim", + "surfaces": [ + "GeneratedJson.*" + ], + "heldBy": [ + "ReferenceHandlingTests" + ] + }, + { + "id": "C10-own-converters-unaffected", + "source": "src/CodoMetis.TypeKit.Generators/README.md#json", + "quote": "anything a converter of your own writes, on the options or on one property", + "claim": "anything a converter of your own writes, on the options or on one property", + "oracle": "claim", + "surfaces": [ + "GeneratedJson.*" + ], + "heldBy": [ + "ReferenceHandlingTests" + ] + }, + { + "id": "C10-refused-where-references-could-be-written", + "source": "src/CodoMetis.TypeKit.Generators/README.md#json", + "quote": "**Preserved references** (`ReferenceHandler.Preserve`) throw a `NotSupportedException` where the serializer could write `$id` or `$ref` inside the underlying value.", + "claim": "**Preserved references** (`ReferenceHandler.Preserve`) throw a `NotSupportedException` where the serializer could write `$id` or `$ref` inside the underlying value.", + "oracle": "claim", + "surfaces": [ + "GeneratedJson.*" + ], + "heldBy": [ + "ReferenceHandlingTests" + ] + }, + { + "id": "C10-value-converters-unaffected", + "source": "src/CodoMetis.TypeKit.Generators/README.md#json", + "quote": "These are unaffected: a value the serializer writes with a value converter (a number, a string, a `Guid`, a `Uri`)", + "claim": "These are unaffected: a value the serializer writes with a value converter (a number, a string, a `Guid`, a `Uri`)", + "oracle": "claim", + "surfaces": [ + "GeneratedJson.*" + ], + "heldBy": [ + "ReferenceHandlingTests" + ] + }, + { + "id": "C11-abstract-string-constructor", + "source": "CHANGELOG.md", + "quote": "or an abstract class's string constructor, no longer breaks the build; the value object has no parsing.", + "claim": "or an abstract class's string constructor, no longer breaks the build; the value object has no parsing.", + "oracle": "claim", + "surfaces": [ + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "C11-implicit-result", + "source": "CHANGELOG.md", + "quote": "A wrapped type's `Parse` whose result can't be assigned to it", + "claim": "A wrapped type's `Parse` whose result can't be assigned to it", + "oracle": "claim", + "surfaces": [ + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "C11-parsing-where-it-can-be-parsed", + "source": "CHANGELOG.md", + "quote": "`IParsable`/`ISpanParsable`/`IUtf8SpanParsable`, formatting in the invariant culture", + "claim": "`IParsable`/`ISpanParsable`/`IUtf8SpanParsable`, formatting in the invariant culture", + "oracle": "claim", + "surfaces": [ + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "C11-ref-text", + "source": "CHANGELOG.md", + "quote": "or that takes its text by `ref`", + "claim": "or that takes its text by `ref`", + "oracle": "claim", + "surfaces": [ + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "C12-every-position", + "source": "docs/json.md#describing-what-a-converter-writes", + "quote": "With CodoMetis.TypeKit.AspNetCore, `AddTypeKit()` writes the same descriptions into the OpenAPI document, in every position, a list's elements and a dictionary's values included.", + "claim": "With CodoMetis.TypeKit.AspNetCore, `AddTypeKit()` writes the same descriptions into the OpenAPI document, in every position, a list's elements and a dictionary's values included.", + "oracle": "claim", + "surfaces": [ + "IDescribedJsonConverter.*", + "JsonSchemaDescriptions.*", + "JsonSchemaDescription.*" + ], + "heldBy": [ + "DescribedSchemaTests" + ] + }, + { + "id": "C12-options-describe", + "source": "docs/json.md#describing-what-a-converter-writes", + "quote": "**A converter someone else wrote**, or the serializer's own enum converter, is described in the options by `JsonSchemaDescriptions`.", + "claim": "**A converter someone else wrote**, or the serializer's own enum converter, is described in the options by `JsonSchemaDescriptions`.", + "oracle": "claim", + "surfaces": [ + "IDescribedJsonConverter.*", + "JsonSchemaDescriptions.*", + "JsonSchemaDescription.*" + ], + "heldBy": [ + "DescribedSchemaTests" + ] + }, + { + "id": "C12-own-converter-describes-itself", + "source": "docs/json.md#describing-what-a-converter-writes", + "quote": "**Your own converter** implements `IDescribedJsonConverter` and describes itself, as both decimal converters do.", + "claim": "**Your own converter** implements `IDescribedJsonConverter` and describes itself, as both decimal converters do.", + "oracle": "claim", + "surfaces": [ + "IDescribedJsonConverter.*", + "JsonSchemaDescriptions.*", + "JsonSchemaDescription.*" + ], + "heldBy": [ + "DescribedSchemaTests" + ] + }, + { + "id": "C12-parameter-keeps-aspnet", + "source": "docs/json.md#describing-what-a-converter-writes", + "quote": "So it keeps the description ASP.NET gives the type without your converters", + "claim": "So it keeps the description ASP.NET gives the type without your converters", + "oracle": "claim", + "surfaces": [ + "IDescribedJsonConverter.*", + "JsonSchemaDescriptions.*", + "JsonSchemaDescription.*" + ], + "heldBy": [ + "DescribedSchemaTests" + ] + }, + { + "id": "C13-absent-is-default", + "source": "docs/merge-patches.md#required-constructor-parameters", + "quote": "a property the request leaves out keeps its default, which is `Absent`.", + "claim": "a property the request leaves out keeps its default, which is `Absent`.", + "oracle": "claim", + "surfaces": [ + "PatchJsonConverterFactory.*", + "PatchState.*" + ], + "heldBy": [ + "PatchSchemaTests" + ] + }, + { + "id": "C13-doc-optional-where-absent", + "source": "docs/merge-patches.md#required-constructor-parameters", + "quote": "`AddTypeKit()` leaves a patch out of `required` where the serializer lets it be absent, and keeps it there where the options reject a body without it.", + "claim": "`AddTypeKit()` leaves a patch out of `required` where the serializer lets it be absent, and keeps it there where the options reject a body without it.", + "oracle": "claim", + "surfaces": [ + "PatchJsonConverterFactory.*", + "PatchState.*" + ], + "heldBy": [ + "PatchSchemaTests" + ] + }, + { + "id": "C13-doc-required-where-rejected", + "source": "docs/merge-patches.md#required-constructor-parameters", + "quote": "So the document never calls a field optional when the endpoint rejects a request without it.", + "claim": "So the document never calls a field optional when the endpoint rejects a request without it.", + "oracle": "claim", + "surfaces": [ + "PatchJsonConverterFactory.*", + "PatchState.*" + ], + "heldBy": [ + "PatchSchemaTests" + ] + }, + { + "id": "C14-application-json-declared", + "source": "CHANGELOG.md", + "quote": "OpenAPI: a problem response an endpoint declares as `application/json`, such as `Produces(404)` or `[ProducesResponseType(404)]`, describes `fault` and `rule` and names its problem types, and keeps its media type", + "claim": "OpenAPI: a problem response an endpoint declares as `application/json`, such as `Produces(404)` or `[ProducesResponseType(404)]`, describes `fault` and `rule` and names its problem types, and keeps its media type", + "oracle": "claim", + "surfaces": [ + "FaultProblem.*", + "TypeKitOpenApiOptionsExtensions.AddTypeKit" + ], + "heldBy": [ + "FaultResponseTests" + ] + }, + { + "id": "C14-ascending-with-declared", + "source": "CHANGELOG.md", + "quote": "OpenAPI: an operation with fault responses lists every status ascending, the ones its endpoint declares itself among them", + "claim": "OpenAPI: an operation with fault responses lists every status ascending, the ones its endpoint declares itself among them", + "oracle": "claim", + "surfaces": [ + "FaultProblem.*", + "TypeKitOpenApiOptionsExtensions.AddTypeKit" + ], + "heldBy": [ + "FaultResponseTests" + ] + }, + { + "id": "C14-declared-describes-fault", + "source": "CHANGELOG.md", + "quote": "OpenAPI: a problem response an endpoint declares itself (`ProducesProblem`, `[ProducesResponseType]`) describes `fault`, and names its problem types.", + "claim": "OpenAPI: a problem response an endpoint declares itself (`ProducesProblem`, `[ProducesResponseType]`) describes `fault`, and names its problem types.", + "oracle": "claim", + "surfaces": [ + "FaultProblem.*", + "TypeKitOpenApiOptionsExtensions.AddTypeKit" + ], + "heldBy": [ + "FaultResponseTests" + ] + }, + { + "id": "C15-builder-reaches-doc", + "source": "CHANGELOG.md", + "quote": "OpenAPI: a `WithFaultStatus` on the builder `MapControllers()` returns reaches the document, as it reached the response.", + "claim": "OpenAPI: a `WithFaultStatus` on the builder `MapControllers()` returns reaches the document, as it reached the response.", + "oracle": "claim", + "surfaces": [ + "FaultStatusAttribute.*", + "TypeKitFaultStatusEndpointConventionBuilderExtensions.*" + ], + "heldBy": [] + }, + { + "id": "C15-status-per-scope", + "source": "CHANGELOG.md", + "quote": "`WithFaultStatus`/`[FaultStatus]` per endpoint, group or controller", + "claim": "`WithFaultStatus`/`[FaultStatus]` per endpoint, group or controller", + "oracle": "claim", + "surfaces": [ + "FaultStatusAttribute.*", + "TypeKitFaultStatusEndpointConventionBuilderExtensions.*" + ], + "heldBy": [] + }, + { + "id": "C15-without-faults-leaves-doc", + "source": "CHANGELOG.md", + "quote": "**`WithoutFaults`** and `[WithoutFaults]`: a route leaves out of its document the members it never answers with, and reporting one throws", + "claim": "**`WithoutFaults`** and `[WithoutFaults]`: a route leaves out of its document the members it never answers with, and reporting one throws", + "oracle": "claim", + "surfaces": [ + "FaultStatusAttribute.*", + "TypeKitFaultStatusEndpointConventionBuilderExtensions.*" + ], + "heldBy": [] + }, + { + "id": "C16-differing-refused", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", + "quote": "Otherwise building the document fails rather than describe JSON the controllers don't write.", + "claim": "Otherwise building the document fails rather than describe JSON the controllers don't write.", + "oracle": "claim", + "surfaces": [ + "TypeKitOpenApiOptionsExtensions.AddTypeKit" + ], + "heldBy": [] + }, + { + "id": "C16-error-names-type-and-descriptions", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", + "quote": "The error names the type and what each set of options says of it, also for a type held only inside a patch.", + "claim": "The error names the type and what each set of options says of it, also for a type held only inside a patch.", + "oracle": "claim", + "surfaces": [ + "TypeKitOpenApiOptionsExtensions.AddTypeKit" + ], + "heldBy": [] + }, + { + "id": "C16-same-converters-build", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", + "quote": "A host with controllers registers the same converters and descriptions on both, in the same order.", + "claim": "A host with controllers registers the same converters and descriptions on both, in the same order.", + "oracle": "claim", + "surfaces": [ + "TypeKitOpenApiOptionsExtensions.AddTypeKit" + ], + "heldBy": [] + }, + { + "id": "C17-a-cast-naming-no-member", + "source": "src/CodoMetis.TypeKit.Analyzers/README.md#cmtk0014", + "quote": "A `FaultKind` or a `ConstructorAccessibility` that names no member: a cast", + "claim": "A `FaultKind` or a `ConstructorAccessibility` that names no member: a cast", + "oracle": "exception", + "surfaces": [ + "FaultKind.*", + "ConstructorAccessibility.*", + "RequireParameterlessConstructorAttribute.*" + ], + "heldBy": [ + "UndefinedEnumValueAnalyzerTests" + ] + }, + { + "id": "C17-a-member-is-silent", + "source": "src/CodoMetis.TypeKit.Analyzers/README.md#cmtk0014", + "quote": "public static readonly ImportFault Duplicate = new(FaultKind.Conflict);", + "claim": "public static readonly ImportFault Duplicate = new(FaultKind.Conflict);", + "oracle": "exception", + "surfaces": [ + "FaultKind.*", + "ConstructorAccessibility.*", + "RequireParameterlessConstructorAttribute.*" + ], + "heldBy": [ + "UndefinedEnumValueAnalyzerTests" + ] + }, + { + "id": "C17-constructor-accessibility-too", + "source": "src/CodoMetis.TypeKit.Analyzers/README.md#cmtk0014", + "quote": "[RequireParameterlessConstructor((ConstructorAccessibility)7)] // CMTK0014: no member, read as public", + "claim": "[RequireParameterlessConstructor((ConstructorAccessibility)7)] // CMTK0014: no member, read as public", + "oracle": "exception", + "surfaces": [ + "FaultKind.*", + "ConstructorAccessibility.*", + "RequireParameterlessConstructorAttribute.*" + ], + "heldBy": [ + "UndefinedEnumValueAnalyzerTests" + ] + }, + { + "id": "C17-default-and-zero", + "source": "src/CodoMetis.TypeKit.Analyzers/README.md#cmtk0014", + "quote": "`default(FaultKind)`, a zero", + "claim": "`default(FaultKind)`, a zero", + "oracle": "exception", + "surfaces": [ + "FaultKind.*", + "ConstructorAccessibility.*", + "RequireParameterlessConstructorAttribute.*" + ], + "heldBy": [ + "UndefinedEnumValueAnalyzerTests" + ] + }, + { + "id": "C17-reported-where-written", + "source": "src/CodoMetis.TypeKit.Analyzers/README.md#cmtk0014", + "quote": "Both are reported where the value is written:", + "claim": "Both are reported where the value is written:", + "oracle": "exception", + "surfaces": [ + "FaultKind.*", + "ConstructorAccessibility.*", + "RequireParameterlessConstructorAttribute.*" + ], + "heldBy": [ + "UndefinedEnumValueAnalyzerTests" + ] + }, + { + "id": "C18-a-warning-and-generated", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "A warning, and the value object is generated.", + "claim": "A warning, and the value object is generated.", + "oracle": "exception", + "surfaces": [ + "IValueObject.*" + ], + "heldBy": [] + }, + { + "id": "C18-generated-equality-never-calls-the-base", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "Over a base record, the generated `Equals(TSelf)` and `GetHashCode()` compare the underlying value alone, and never call the base's.", + "claim": "Over a base record, the generated `Equals(TSelf)` and `GetHashCode()` compare the underlying value alone, and never call the base's.", + "oracle": "exception", + "surfaces": [ + "IValueObject.*" + ], + "heldBy": [] + }, + { + "id": "C18-names-each-base-member", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "So a hand-written `Equals(TBase)`, `GetHashCode()` or `IEquatable.Equals` on a base record isn't used, and a warning names it (CMTK1023).", + "claim": "So a hand-written `Equals(TBase)`, `GetHashCode()` or `IEquatable.Equals` on a base record isn't used, and a warning names it (CMTK1023).", + "oracle": "exception", + "surfaces": [ + "IValueObject.*" + ], + "heldBy": [] + }, + { + "id": "C19-a-pair-once-at-the-later-argument", + "source": "docs/type-constraints.md#the-kind-of-a-type-argument", + "quote": "A breach is reported once, at the later argument.", + "claim": "A breach is reported once, at the later argument.", + "oracle": "exception", + "surfaces": [ + "InterfaceAttribute.*", + "UnsealedAttribute.*", + "ValueObjectAttribute.*", + "FlagsEnumAttribute.*", + "ReadOnlyStructAttribute.*", + "SealedAttribute.*", + "RecordAttribute.*", + "UnrelatedAttribute.*" + ], + "heldBy": [ + "TypeArgumentConstraintKindTests" + ] + }, + { + "id": "C19-flags-enum-and-read-only-struct", + "source": "docs/type-constraints.md#the-kind-of-a-type-argument", + "quote": "**`[FlagsEnum]` and `[ReadOnlyStruct]`** narrow what `where T : Enum` and `where T : struct` admit.", + "claim": "**`[FlagsEnum]` and `[ReadOnlyStruct]`** narrow what `where T : Enum` and `where T : struct` admit.", + "oracle": "exception", + "surfaces": [ + "InterfaceAttribute.*", + "UnsealedAttribute.*", + "ValueObjectAttribute.*", + "FlagsEnumAttribute.*", + "ReadOnlyStructAttribute.*", + "SealedAttribute.*", + "RecordAttribute.*", + "UnrelatedAttribute.*" + ], + "heldBy": [ + "TypeArgumentConstraintKindTests" + ] + }, + { + "id": "C19-interface-and-unsealed", + "source": "docs/type-constraints.md#the-kind-of-a-type-argument", + "quote": "**`[Interface]` and `[Unsealed]`** are what a proxy needs: a contract to implement, or a class to derive from.", + "claim": "**`[Interface]` and `[Unsealed]`** are what a proxy needs: a contract to implement, or a class to derive from.", + "oracle": "exception", + "surfaces": [ + "InterfaceAttribute.*", + "UnsealedAttribute.*", + "ValueObjectAttribute.*", + "FlagsEnumAttribute.*", + "ReadOnlyStructAttribute.*", + "SealedAttribute.*", + "RecordAttribute.*", + "UnrelatedAttribute.*" + ], + "heldBy": [ + "TypeArgumentConstraintKindTests" + ] + }, + { + "id": "C19-methods-the-compiler-calls", + "source": "docs/type-constraints.md#the-kind-of-a-type-argument", + "quote": "**Methods the compiler calls for you:** a `foreach`'s `GetEnumerator`, an `await`'s `GetAwaiter`, a `Deconstruct` (in `var (a, b) = x`, a `foreach`, or a pattern like `x is (1, 2)`), and a collection expression's `Add` or builder method.", + "claim": "**Methods the compiler calls for you:** a `foreach`'s `GetEnumerator`, an `await`'s `GetAwaiter`, a `Deconstruct` (in `var (a, b) = x`, a `foreach`, or a pattern like `x is (1, 2)`), and a collection expression's `Add` or builder method.", + "oracle": "exception", + "surfaces": [ + "InterfaceAttribute.*", + "UnsealedAttribute.*", + "ValueObjectAttribute.*", + "FlagsEnumAttribute.*", + "ReadOnlyStructAttribute.*", + "SealedAttribute.*", + "RecordAttribute.*", + "UnrelatedAttribute.*" + ], + "heldBy": [ + "TypeArgumentConstraintKindTests" + ] + }, + { + "id": "C19-value-object-and-closed-set", + "source": "docs/type-constraints.md#the-kind-of-a-type-argument", + "quote": "**`[ValueObject]` and `[ClosedSet]`** accept their marker interface with any type argument.", + "claim": "**`[ValueObject]` and `[ClosedSet]`** accept their marker interface with any type argument.", + "oracle": "exception", + "surfaces": [ + "InterfaceAttribute.*", + "UnsealedAttribute.*", + "ValueObjectAttribute.*", + "FlagsEnumAttribute.*", + "ReadOnlyStructAttribute.*", + "SealedAttribute.*", + "RecordAttribute.*", + "UnrelatedAttribute.*" + ], + "heldBy": [ + "TypeArgumentConstraintKindTests" + ] + }, + { + "id": "C20-a-failure-says-the-outcome", + "source": "src/CodoMetis.TypeKit.Testing/README.md", + "quote": "**A failure says what the outcome was**", + "claim": "**A failure says what the outcome was**", + "oracle": "claim", + "surfaces": [ + "FaultAssertions.*" + ], + "heldBy": [ + "ComposedFaultAssertionTests" + ] + }, + { + "id": "C20-fails-unless-it-holds-that-reason", + "source": "src/CodoMetis.TypeKit.Testing/README.md", + "quote": "`ShouldHold(reason)` fails unless the fault holds that reason, whichever of its types the reason belongs to.", + "claim": "`ShouldHold(reason)` fails unless the fault holds that reason, whichever of its types the reason belongs to.", + "oracle": "claim", + "surfaces": [ + "FaultAssertions.*" + ], + "heldBy": [ + "ComposedFaultAssertionTests" + ] + }, + { + "id": "C20-hands-back-detail-and-rule", + "source": "src/CodoMetis.TypeKit.Testing/README.md", + "quote": "It hands back the `Fault` with its detail and rule intact.", + "claim": "It hands back the `Fault` with its detail and rule intact.", + "oracle": "claim", + "surfaces": [ + "FaultAssertions.*" + ], + "heldBy": [ + "ComposedFaultAssertionTests" + ] + }, + { + "id": "C20-uninitialized-fails", + "source": "src/CodoMetis.TypeKit.Testing/README.md", + "quote": "**An uninitialized result fails every assertion.**", + "claim": "**An uninitialized result fails every assertion.**", + "oracle": "claim", + "surfaces": [ + "FaultAssertions.*" + ], + "heldBy": [ + "ComposedFaultAssertionTests" + ] + }, + { + "id": "C20-whichever-of-its-types", + "source": "src/CodoMetis.TypeKit.Testing/README.md", + "quote": "whichever of its types the reason belongs to", + "claim": "whichever of its types the reason belongs to", + "oracle": "claim", + "surfaces": [ + "FaultAssertions.*" + ], + "heldBy": [ + "ComposedFaultAssertionTests" + ] + }, + { + "id": "C21-nullable-admits-null-as-the-wrapped-does", + "source": "README.md#what-you-get", + "quote": "Its OpenAPI schema is the one ASP.NET Core publishes for the underlying type.", + "claim": "Its OpenAPI schema is the one ASP.NET Core publishes for the underlying type.", + "oracle": "differential", + "surfaces": [ + "GeneratedJsonPlan.*", + "TypeKitOpenApiOptionsExtensions.AddTypeKit" + ], + "heldBy": [] + }, + { + "id": "C21-the-underlying-types-schema", + "source": "README.md#what-you-get", + "quote": "The JSON, the database column and the OpenAPI schema are the same as the underlying type's.", + "claim": "The JSON, the database column and the OpenAPI schema are the same as the underlying type's.", + "oracle": "differential", + "surfaces": [ + "TypeKitOpenApiOptionsExtensions.AddTypeKit", + "GeneratedJsonPlan.*" + ], + "heldBy": [] + }, + { + "id": "C22-abstract", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "An abstract type with a value-object marker. It would get no member, and a value held as it, of a type derived from it, would have no `Value` and be written as `{}`.", + "claim": "An abstract type with a value-object marker. It would get no member, and a value held as it, of a type derived from it, would have no `Value` and be written as `{}`.", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-closed-set-named-as-generated-member", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "a set itself named as one of them (a member can't have the name of its type)", + "claim": "a set itself named as one of them (a member can't have the name of its type)", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-equality-operator", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "So a hand-written `Equals(TSelf)`, `GetHashCode()`, `IEquatable.Equals`, or `operator ==` or `!=` between two of the type, fails the build (CMTK1011).", + "claim": "So a hand-written `Equals(TSelf)`, `GetHashCode()`, `IEquatable.Equals`, or `operator ==` or `!=` between two of the type, fails the build (CMTK1011).", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-every-error-links-the-table", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "Every error ends with a link to this table.", + "claim": "Every error ends with a link to this table.", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-generic-wrapped", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "A generic underlying type, a tuple included, fails the build (CMTK1005).", + "claim": "A generic underlying type, a tuple included, fails the build (CMTK1005).", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-named-as-generated-member", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "named `Value` or as another member the generators add (`From`, `TryFrom`, `FromKnownGood`, `Revalidate`, `Parse`, `TryParse`, `TryFormat`, `MinValue`, `MaxValue`, `CompareTo`)", + "claim": "named `Value` or as another member the generators add (`From`, `TryFrom`, `FromKnownGood`, `Revalidate`, `Parse`, `TryParse`, `TryFormat`, `MinValue`, `MaxValue`, `CompareTo`)", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-no-type-half-generated", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "A declaration that can't be generated fails the build, so no type is left half-generated.", + "claim": "A declaration that can't be generated fails the build, so no type is left half-generated.", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-over-object", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "or wrapping `object`, `System.Enum`, an array, a pointer, a nullable type, a generic type (a tuple included) or a value object (itself included).", + "claim": "or wrapping `object`, `System.Enum`, an array, a pointer, a nullable type, a generic type (a tuple included) or a value object (itself included).", + "oracle": "exception", + "surfaces": [ + "IClosedSet.*", + "IPlainValueObject.*", + "IValidatedValue.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-over-value-object", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "or a value object (itself included). Wrap what the other value object wraps instead.", + "claim": "or a value object (itself included). Wrap what the other value object wraps instead.", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C22-unsealed-record-class", + "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", + "quote": "A record class not declared `sealed`.", + "claim": "A record class not declared `sealed`.", + "oracle": "exception", + "surfaces": [ + "IPlainValueObject.*", + "IValidatedValue.*", + "IClosedSet.*" + ], + "heldBy": [ + "BuildOutcomeTests" + ] + }, + { + "id": "C23-withheld-host-sees", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-a-client-must-not-tell-apart", + "quote": "**Your host still sees the fault.** `IFaultFeature` carries its name, and `NameWithheld` says the mapping keeps it from the client", + "claim": "**Your host still sees the fault.** `IFaultFeature` carries its name, and `NameWithheld` says the mapping keeps it from the client", + "oracle": "claim", + "surfaces": [ + "IFaultFeature.*" + ], + "heldBy": [ + "WithheldFaultNameTests" + ] + }, + { + "id": "C23-withheld-says-nothing", + "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-a-client-must-not-tell-apart", + "quote": "**The problem says nothing of the member**: no `fault`, no `rule`, and no fault set member's description as its `detail`.", + "claim": "**The problem says nothing of the member**: no `fault`, no `rule`, and no fault set member's description as its `detail`.", + "oracle": "claim", + "surfaces": [ + "FaultResponse.*" + ], + "heldBy": [ + "WithheldFaultNameTests" + ] + }, + { + "id": "C24-content-comes-back", + "source": "src/CodoMetis.TypeKit.Testing/README.md", + "quote": "**The content comes back.** `ShouldBeSome()`, `ShouldBeSuccess()`, `ShouldBeError()` and `ShouldBeValue()` return the value or the error.", + "claim": "**The content comes back.** `ShouldBeSome()`, `ShouldBeSuccess()`, `ShouldBeError()` and `ShouldBeValue()` return the value or the error.", + "oracle": "claim", + "surfaces": [ + "OptionAssertions.*", + "ResultAssertions.*", + "PatchAssertions.*" + ], + "heldBy": [ + "AssertionTests", + "PatchAssertionTests" + ] + }, + { + "id": "E01-column-type", + "source": "docs/ef-core.md#the-model", + "quote": "Every property maps as a column of the underlying type, nullable where the property is.", + "claim": "Every property maps as a column of the underlying type, nullable where the property is.", + "oracle": "differential", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit", + "ValueObjectConverter.*" + ], + "heldBy": [ + "MappingTests" + ] + }, + { + "id": "E01-facet-kept", + "source": "docs/ef-core.md#the-model", + "quote": "A facet you configure on it is kept: a precision, a length, a column type.", + "claim": "A facet you configure on it is kept: a precision, a length, a column type.", + "oracle": "differential", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit", + "ValueObjectConverter.*" + ], + "heldBy": [ + "MappingTests" + ] + }, + { + "id": "E01-nullable", + "source": "docs/ef-core.md#the-model", + "quote": "nullable where the property is", + "claim": "nullable where the property is", + "oracle": "differential", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit", + "ValueObjectConverter.*" + ], + "heldBy": [ + "MappingTests" + ] + }, + { + "id": "E02-column-not-cast", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "A table's column is re-typed, not cast, so an index on it still serves the query.", + "claim": "A table's column is re-typed, not cast, so an index on it still serves the query.", + "oracle": "differential", + "surfaces": [ + "TranslatedAsWrappedValueAttribute.*", + "IValueObject.Value" + ], + "heldBy": [ + "SqlTranslationTests" + ] + }, + { + "id": "E02-get-value", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "db.Orders.Where(o => o.Total.GetValue() > 100m);", + "claim": "db.Orders.Where(o => o.Total.GetValue() > 100m);", + "oracle": "differential", + "surfaces": [ + "TranslatedAsWrappedValueAttribute.*", + "IValueObject.Value" + ], + "heldBy": [ + "SqlTranslationTests" + ] + }, + { + "id": "E02-value", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "`.Value`, `GetValue()` and `ValueOrNull()` translate to the column itself.", + "claim": "`.Value`, `GetValue()` and `ValueOrNull()` translate to the column itself.", + "oracle": "differential", + "surfaces": [ + "TranslatedAsWrappedValueAttribute.*", + "IValueObject.Value" + ], + "heldBy": [ + "SqlTranslationTests" + ] + }, + { + "id": "E02-value-or-null", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "Use `ValueOrNull()` for an optional value object, where `.Value` would first unwrap the `Nullable`.", + "claim": "Use `ValueOrNull()` for an optional value object, where `.Value` would first unwrap the `Nullable`.", + "oracle": "differential", + "surfaces": [ + "TranslatedAsWrappedValueAttribute.*", + "IValueObject.Value" + ], + "heldBy": [ + "SqlTranslationTests" + ] + }, + { + "id": "E03-keys", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#what-maps", + "quote": "Keys, foreign keys, nullable properties, primitive collections and query parameters all map as plain columns of the underlying type.", + "claim": "Keys, foreign keys, nullable properties, primitive collections and query parameters all map as plain columns of the underlying type.", + "oracle": "metamorphic", + "surfaces": [ + "ValueObjectConverter.*" + ], + "heldBy": [ + "PostgreSqlRoundTripTests" + ] + }, + { + "id": "E03-stored-as-underlying", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#what-maps", + "quote": "Every value object is stored in a plain column of its underlying type", + "claim": "Every value object is stored in a plain column of its underlying type", + "oracle": "metamorphic", + "surfaces": [ + "ValueObjectConverter.*" + ], + "heldBy": [ + "PostgreSqlRoundTripTests" + ] + }, + { + "id": "E04-byte-not-generated", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "That's EF's own list: `int`, `long`, `short`, `sbyte`, `uint`, `ulong` and `ushort`, and not `byte`.", + "claim": "That's EF's own list: `int`, `long`, `short`, `sbyte`, `uint`, `ulong` and `ushort`, and not `byte`.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "E04-char-key-yours", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "A key over `char` is yours to assign.", + "claim": "A key over `char` is yours to assign.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "E04-generated-on-add", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "A single-column key over an integer is generated on add, like a key of that integer type.", + "claim": "A single-column key over an integer is generated on add, like a key of that integer type.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "E04-guid-key-yours", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "A key over a `Guid` is yours to assign", + "claim": "A key over a `Guid` is yours to assign", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "E04-own-configuration-wins", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "A configuration you write yourself always wins over these defaults.", + "claim": "A configuration you write yourself always wins over these defaults.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "E05-comparison", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "a comparison (`<`, `>`, `<=`, `>=`, `CompareTo`);", + "claim": "a comparison (`<`, `>`, `<=`, `>=`, `CompareTo`);", + "oracle": "exception", + "surfaces": [ + "HandWrittenOrderingAttribute.*" + ], + "heldBy": [ + "HandWrittenOrderingTests" + ] + }, + { + "id": "E05-equality-allowed", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "Equality isn't ordering, and stays allowed.", + "claim": "Equality isn't ordering, and stays allowed.", + "oracle": "exception", + "surfaces": [ + "HandWrittenOrderingAttribute.*" + ], + "heldBy": [ + "HandWrittenOrderingTests" + ] + }, + { + "id": "E05-ordering", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "an ordering (`OrderBy`, `ThenBy`, their descending forms, `Order`);", + "claim": "an ordering (`OrderBy`, `ThenBy`, their descending forms, `Order`);", + "oracle": "exception", + "surfaces": [ + "HandWrittenOrderingAttribute.*" + ], + "heldBy": [ + "HandWrittenOrderingTests" + ] + }, + { + "id": "E05-query-filter", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "A query filter (`HasQueryFilter`, named or not) that does the same fails when the model is built, naming the entity type, since it applies to every query of that type.", + "claim": "A query filter (`HasQueryFilter`, named or not) that does the same fails when the model is built, naming the entity type, since it applies to every query of that type.", + "oracle": "exception", + "surfaces": [ + "HandWrittenOrderingAttribute.*" + ], + "heldBy": [ + "HandWrittenOrderingTests" + ] + }, + { + "id": "E05-value-order-allowed", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "Compare or order by `.Value` where the underlying value's order is what you mean", + "claim": "Compare or order by `.Value` where the underlying value's order is what you mean", + "oracle": "exception", + "surfaces": [ + "HandWrittenOrderingAttribute.*" + ], + "heldBy": [ + "HandWrittenOrderingTests" + ] + }, + { + "id": "E06-value-not-translated", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "So a filter, a sort, a grouping or an aggregate that reads `.Value` there fails with EF's \"could not be translated\", rather than comparing against what the column really holds.", + "claim": "So a filter, a sort, a grouping or an aggregate that reads `.Value` there fails with EF's \"could not be translated\", rather than comparing against what the column really holds.", + "oracle": "exception", + "surfaces": [ + "ValueObjectConverter.*" + ], + "heldBy": [ + "ForeignConverterTests" + ] + }, + { + "id": "E06-value-object-comparison", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "Comparing the value object itself (`o.Code == code`) goes through that converter and still works.", + "claim": "Comparing the value object itself (`o.Code == code`) goes through that converter and still works.", + "oracle": "exception", + "surfaces": [ + "ValueObjectConverter.*" + ], + "heldBy": [ + "ForeignConverterTests" + ] + }, + { + "id": "E07-in-collections-and-json", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#closed-sets", + "quote": "That holds in a table, in a primitive collection of names (`text[]` on PostgreSQL, a JSON array elsewhere) and in a JSON column alike, with nothing registered per set.", + "claim": "That holds in a table, in a primitive collection of names (`text[]` on PostgreSQL, a JSON array elsewhere) and in a JSON column alike, with nothing registered per set.", + "oracle": "claim", + "surfaces": [ + "ClosedSetConverter.*" + ], + "heldBy": [ + "ClosedSetMappingTests" + ] + }, + { + "id": "E07-non-member-throws", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#closed-sets", + "quote": "An instance that isn't a member throws when it's written.", + "claim": "An instance that isn't a member throws when it's written.", + "oracle": "claim", + "surfaces": [ + "ClosedSetConverter.*" + ], + "heldBy": [ + "ClosedSetMappingTests" + ] + }, + { + "id": "E07-stored-as-name", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#closed-sets", + "quote": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself.", + "claim": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself.", + "oracle": "claim", + "surfaces": [ + "ClosedSetConverter.*" + ], + "heldBy": [ + "ClosedSetMappingTests" + ] + }, + { + "id": "E07-unknown-name-throws", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#closed-sets", + "quote": "A name the set no longer has throws when it's read, rather than reading as null.", + "claim": "A name the set no longer has throws when it's read, rather than reading as null.", + "oracle": "claim", + "surfaces": [ + "ClosedSetConverter.*" + ], + "heldBy": [ + "ClosedSetMappingTests" + ] + }, + { + "id": "E08-error-says-so", + "source": "docs/ef-core.md#option-and-result", + "quote": "building the model fails with an error that says so", + "claim": "building the model fails with an error that says so", + "oracle": "exception", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit" + ], + "heldBy": [] + }, + { + "id": "E08-model-fails", + "source": "docs/ef-core.md#option-and-result", + "quote": "EF can't map an `Option` or a `Result` property", + "claim": "EF can't map an `Option` or a `Result` property", + "oracle": "exception", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit" + ], + "heldBy": [] + }, + { + "id": "E09-element-at-skips", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#an-option-for-nothing-found", + "quote": "skips to its row: `OFFSET @p`.", + "claim": "skips to its row: `OFFSET @p`.", + "oracle": "claim", + "surfaces": [ + "TypeKitQueryableExtensions.*", + "Option.FirstOrNone", + "Option.SingleOrNone" + ], + "heldBy": [ + "QueryOptionTests" + ] + }, + { + "id": "E09-first-reads-one-row", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#an-option-for-nothing-found", + "quote": "read one row: `WHERE ... LIMIT @p`.", + "claim": "read one row: `WHERE ... LIMIT @p`.", + "oracle": "claim", + "surfaces": [ + "TypeKitQueryableExtensions.*", + "Option.FirstOrNone", + "Option.SingleOrNone" + ], + "heldBy": [ + "QueryOptionTests" + ] + }, + { + "id": "E09-single-asks-for-two", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#an-option-for-nothing-found", + "quote": "ask for two rows, and throw for more than one.", + "claim": "ask for two rows, and throw for more than one.", + "oracle": "claim", + "surfaces": [ + "TypeKitQueryableExtensions.*", + "Option.FirstOrNone", + "Option.SingleOrNone" + ], + "heldBy": [ + "QueryOptionTests" + ] + }, + { + "id": "E10-reads-without-create", + "source": "docs/ef-core.md#reading-back-without-validation", + "quote": "EF reads a column back without validation, through the value object's `Materialize` rather than `Create`.", + "claim": "EF reads a column back without validation, through the value object's `Materialize` rather than `Create`.", + "oracle": "claim", + "surfaces": [ + "IValueObjectMaterializer.*", + "ValueObjectConverter.Materialize" + ], + "heldBy": [] + }, + { + "id": "E10-revalidate-finds", + "source": "docs/ef-core.md#reading-back-without-validation", + "quote": "`Revalidate()` finds the rows that today's rules reject, as above.", + "claim": "`Revalidate()` finds the rows that today's rules reject, as above.", + "oracle": "claim", + "surfaces": [ + "IValueObjectMaterializer.*", + "ValueObjectConverter.Materialize" + ], + "heldBy": [] + }, + { + "id": "E11-order-does-not-matter", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#setup", + "quote": "it doesn't matter whether you call it before or after `UseNpgsql` or `UseSqlite`", + "claim": "it doesn't matter whether you call it before or after `UseNpgsql` or `UseSqlite`", + "oracle": "claim", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit" + ], + "heldBy": [] + }, + { + "id": "E12-underlying-type-does-not-reach", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#model-wide-conventions-follow-the-propertys-type", + "quote": "So `configurationBuilder.Properties().HavePrecision(18, 2)` reaches `decimal` properties, and not an `Amount` that wraps one.", + "claim": "So `configurationBuilder.Properties().HavePrecision(18, 2)` reaches `decimal` properties, and not an `Amount` that wraps one.", + "oracle": "claim", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit" + ], + "heldBy": [] + }, + { + "id": "E12-value-object-type-reaches", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#model-wide-conventions-follow-the-propertys-type", + "quote": "Configure the value object instead: `configurationBuilder.Properties().HavePrecision(18, 2)`.", + "claim": "Configure the value object instead: `configurationBuilder.Properties().HavePrecision(18, 2)`.", + "oracle": "claim", + "surfaces": [ + "TypeKitDbContextOptionsBuilderExtensions.UseTypeKit" + ], + "heldBy": [] + }, + { + "id": "E13-contains", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "db.Orders.Where(o => o.Tags.Contains(tag));", + "claim": "db.Orders.Where(o => o.Tags.Contains(tag));", + "oracle": "claim", + "surfaces": [ + "ValueObjectConverter.*" + ], + "heldBy": [] + }, + { + "id": "E13-element-value", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", + "quote": "db.Orders.Where(o => o.Tags.Any(t => t.Value == \"x\"));", + "claim": "db.Orders.Where(o => o.Tags.Any(t => t.Value == \"x\"));", + "oracle": "claim", + "surfaces": [ + "ValueObjectConverter.*" + ], + "heldBy": [] + }, + { + "id": "E14-tpc-assign-on-sqlite", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "SQLite has no such sequence, so there you assign it.", + "claim": "SQLite has no such sequence, so there you assign it.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "E14-tph-tpt-root", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "The root of an inheritance hierarchy follows the same rules under TPH and TPT.", + "claim": "The root of an inheritance hierarchy follows the same rules under TPH and TPT.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "E15-as-for-primitives", + "source": "docs/ef-core.md#keys", + "quote": "A composite key of value objects is `HasKey(x => new { x.Order, x.Line })`, as for primitives.", + "claim": "A composite key of value objects is `HasKey(x => new { x.Order, x.Line })`, as for primitives.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [] + }, + { + "id": "E16-column-type", + "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", + "quote": "A facet you configure on the property, such as a maximum length or a column type, is kept.", + "claim": "A facet you configure on the property, such as a maximum length or a column type, is kept.", + "oracle": "claim", + "surfaces": [ + "WrappedTypeValueGeneratorFactory.*" + ], + "heldBy": [ + "KeyGenerationTests" + ] + }, + { + "id": "L01-read-as-the-underlying-type-reads", + "source": "README.md#what-typekit-guarantees", + "quote": "**Swapping a primitive for a value object keeps the JSON, the column and the schema.**", + "claim": "**Swapping a primitive for a value object keeps the JSON, the column and the schema.**", + "oracle": "differential", + "surfaces": [ + "GeneratedJson.*", + "GeneratedJsonPlan.*", + "StoredJsonConverterFactory.*" + ], + "heldBy": [ + "JsonParityTests" + ] + }, + { + "id": "L01-the-underlying-types-json", + "source": "README.md#what-typekit-guarantees", + "quote": "Its JSON is the underlying type's under your options", + "claim": "Its JSON is the underlying type's under your options", + "oracle": "differential", + "surfaces": [ + "GeneratedJson.*", + "GeneratedJsonPlan.*", + "StoredJsonConverterFactory.*" + ], + "heldBy": [ + "JsonParityTests" + ] + }, + { + "id": "L02-json-never-repeats", + "source": "README.md#what-typekit-guarantees", + "quote": "A JSON or parsing rejection names the value object and the rule, or the underlying type it could not read.", + "claim": "A JSON or parsing rejection names the value object and the rule, or the underlying type it could not read.", + "oracle": "exception", + "surfaces": [ + "GeneratedParsing.*", + "GeneratedFactories.*" + ], + "heldBy": [ + "UnreadableInputTests" + ] + }, + { + "id": "L02-no-inner-that-quotes", + "source": "README.md#what-typekit-guarantees", + "quote": "It carries no inner exception that quotes the text.", + "claim": "It carries no inner exception that quotes the text.", + "oracle": "exception", + "surfaces": [ + "GeneratedParsing.*", + "GeneratedFactories.*" + ], + "heldBy": [ + "UnreadableInputTests" + ] + }, + { + "id": "L02-parsing-never-repeats", + "source": "README.md#what-typekit-guarantees", + "quote": "**Rejections never repeat the input.**", + "claim": "**Rejections never repeat the input.**", + "oracle": "exception", + "surfaces": [ + "GeneratedParsing.*", + "GeneratedFactories.*" + ], + "heldBy": [ + "UnreadableInputTests" + ] + }, + { + "id": "L03-refusals-name-the-fault", + "source": "README.md#what-typekit-guarantees", + "quote": "the `JsonException`, the `FormatException` and the `FromKnownGood` exception all name your `EmailFault.NoAt`", + "claim": "the `JsonException`, the `FormatException` and the `FromKnownGood` exception all name your `EmailFault.NoAt`", + "oracle": "claim", + "surfaces": [ + "IValidatedValue.Create", + "GeneratedFactories.*" + ], + "heldBy": [ + "EntryPointValidationTests" + ] + }, + { + "id": "L03-tryfrom-runs-create", + "source": "README.md#what-typekit-guarantees", + "quote": "The generated `TryFrom`, `FromKnownGood`, JSON converter, parsing and type converter all run it", + "claim": "The generated `TryFrom`, `FromKnownGood`, JSON converter, parsing and type converter all run it", + "oracle": "claim", + "surfaces": [ + "GeneratedFactories.*", + "IValidatedValue.Create" + ], + "heldBy": [ + "EntryPointValidationTests" + ] + }, + { + "id": "L04-invariant-whatever-the-culture", + "source": "src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting", + "quote": "A null `IFormatProvider` means the invariant culture in every generated `Parse`, `TryParse`, `ToString` and `TryFormat`, so the text doesn't depend on the culture the process runs in.", + "claim": "A null `IFormatProvider` means the invariant culture in every generated `Parse`, `TryParse`, `ToString` and `TryFormat`, so the text doesn't depend on the culture the process runs in.", + "oracle": "metamorphic", + "surfaces": [ + "GeneratedFormatting.*", + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "L04-json-refusal-is-a-json-exception", + "source": "src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting", + "quote": "Input it can't read throws a `JsonException` naming the value object and the underlying type", + "claim": "Input it can't read throws a `JsonException` naming the value object and the underlying type", + "oracle": "metamorphic", + "surfaces": [ + "GeneratedFormatting.*", + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "L04-text-reads-back", + "source": "src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting", + "quote": "That text reads back as the same value wherever the underlying type's own does", + "claim": "That text reads back as the same value wherever the underlying type's own does", + "oracle": "metamorphic", + "surfaces": [ + "GeneratedFormatting.*", + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "L04-the-type-converter-writes-the-same", + "source": "src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting", + "quote": "`ToString()` is the underlying type's own default text under the invariant culture, and the type converter and `$\"{x}\"` write the same.", + "claim": "`ToString()` is the underlying type's own default text under the invariant culture, and the type converter and `$\"{x}\"` write the same.", + "oracle": "metamorphic", + "surfaces": [ + "GeneratedFormatting.*", + "GeneratedParsing.*" + ], + "heldBy": [] + }, + { + "id": "L05-equality-is-the-underlying-values", + "source": "src/CodoMetis.TypeKit.Generators/README.md#equality", + "quote": "Equality is the underlying value's, like the ordering, the JSON and the column.", + "claim": "Equality is the underlying value's, like the ordering, the JSON and the column.", + "oracle": "metamorphic", + "surfaces": [ + "IValueObject.op_Equality", + "IValueObject.op_Inequality" + ], + "heldBy": [] + }, + { + "id": "L05-ordering-follows-the-underlying-type", + "source": "src/CodoMetis.TypeKit.Generators/README.md#equality", + "quote": "`CompareTo` and the operators follow the underlying type's ordering, ordinal for a string.", + "claim": "`CompareTo` and the operators follow the underlying type's ordering, ordinal for a string.", + "oracle": "metamorphic", + "surfaces": [ + "IValueObject.op_Equality", + "IValueObject.op_Inequality" + ], + "heldBy": [] + }, + { + "id": "L05-through-every-base", + "source": "src/CodoMetis.TypeKit.Generators/README.md#equality", + "quote": "The value object implements each base's `IEquatable` again (`IReimplementedEquatable`), so a collection of the base compares it the way the value object does.", + "claim": "The value object implements each base's `IEquatable` again (`IReimplementedEquatable`), so a collection of the base compares it the way the value object does.", + "oracle": "metamorphic", + "surfaces": [ + "IValueObject.op_Equality", + "IValueObject.op_Inequality" + ], + "heldBy": [] + }, + { + "id": "L06-never-written", + "source": "README.md#what-typekit-guarantees", + "quote": "System.Text.Json throws `NotSupportedException` for either, in both directions, instead of writing `{}` that reads back as `None`.", + "claim": "System.Text.Json throws `NotSupportedException` for either, in both directions, instead of writing `{}` that reads back as `None`.", + "oracle": "exception", + "surfaces": [ + "NotWireTypeJsonConverterFactory.*" + ], + "heldBy": [ + "NotWireTypeTests" + ] + } +] diff --git a/test/oracles/oracles.json b/test/oracles/oracles.json new file mode 100644 index 00000000..e631ccad --- /dev/null +++ b/test/oracles/oracles.json @@ -0,0 +1,32 @@ +[ + { + "class": "AsynchronousContextTests", + "project": "CodoMetis.TypeKit.Tests", + "kind": "completeness", + "against": "every public member of the base package that returns a task, by reflection: a callback runs where the task it waited for completed, and nothing resumes on the caller's context" + }, + { + "class": "JsonParityTests", + "project": "CodoMetis.TypeKit.Generators.Tests", + "kind": "differential", + "against": "the wrapped type's own JSON under the same options, as a value, a property and a dictionary key" + }, + { + "class": "KeyGenerationVariantTests", + "project": "CodoMetis.TypeKit.EntityFrameworkCore.Tests", + "kind": "differential", + "against": "the same key shape keyed by the wrapped type, on SQLite, Npgsql and SQL Server" + }, + { + "class": "NotWireTypeTests", + "project": "CodoMetis.TypeKit.Tests", + "kind": "completeness", + "against": "every exported struct of the base package, by reflection: System.Text.Json refuses it in both directions" + }, + { + "class": "NullDelegateTests", + "project": "CodoMetis.TypeKit.Tests", + "kind": "completeness", + "against": "every delegate parameter of the base package, by reflection, on every branch of its receiver" + } +] diff --git a/tools/probe/corpus/seeds/L01-json-bytes-as-a-dictionary-key-too.patch b/test/oracles/seeds/json-key-written-by-to-string.patch similarity index 77% rename from tools/probe/corpus/seeds/L01-json-bytes-as-a-dictionary-key-too.patch rename to test/oracles/seeds/json-key-written-by-to-string.patch index 832c2257..0e656c20 100644 --- a/tools/probe/corpus/seeds/L01-json-bytes-as-a-dictionary-key-too.patch +++ b/test/oracles/seeds/json-key-written-by-to-string.patch @@ -1,3 +1,7 @@ +Seed: A value object as a dictionary key is written by the wrapped value's ToString(), not by its converter's WriteAsPropertyName +Oracles: JsonParityTests +Fails: every dictionary-key row whose wrapped type's key is not its ToString(): dates, decimals, a host converter's keys + diff --git a/src/CodoMetis.TypeKit/CompilerServices/GeneratedJsonPlan.cs b/src/CodoMetis.TypeKit/CompilerServices/GeneratedJsonPlan.cs index 8597206..1990a4a 100644 --- a/src/CodoMetis.TypeKit/CompilerServices/GeneratedJsonPlan.cs diff --git a/test/oracles/seeds/patch-traverse-takes-a-null-selector.patch b/test/oracles/seeds/patch-traverse-takes-a-null-selector.patch new file mode 100644 index 00000000..cbe223a7 --- /dev/null +++ b/test/oracles/seeds/patch-traverse-takes-a-null-selector.patch @@ -0,0 +1,17 @@ +Seed: Patch.Traverse checks its selector only where it calls it, so a null passes on a clear and an absent field +Oracles: NullDelegateTests +Fails: Result.Traverse(Patch, ...): selector, on the Null and Absent branches + +diff --git a/src/CodoMetis.TypeKit/ResultExtensions.cs b/src/CodoMetis.TypeKit/ResultExtensions.cs +index a4394be..1568b0a 100644 +--- a/src/CodoMetis.TypeKit/ResultExtensions.cs ++++ b/src/CodoMetis.TypeKit/ResultExtensions.cs +@@ -1568,8 +1568,6 @@ public static class Result + where TResult : notnull + where TError : notnull + { +- ArgumentNullException.ThrowIfNull(selector); +- + if (!patch.TryGetValue(out var value)) + return Result, TError>.Success(patch.State == PatchState.Null ? Patch.Null() : Patch.Absent()); + diff --git a/test/oracles/seeds/result-reads-as-json.patch b/test/oracles/seeds/result-reads-as-json.patch new file mode 100644 index 00000000..2f2b00ad --- /dev/null +++ b/test/oracles/seeds/result-reads-as-json.patch @@ -0,0 +1,16 @@ +Seed: Result loses the converter that refuses System.Text.Json, so a success is written as {} and read back as uninitialized +Oracles: NotWireTypeTests +Fails: the completeness row for Result, and its write and read + +diff --git a/src/CodoMetis.TypeKit/Result.cs b/src/CodoMetis.TypeKit/Result.cs +index b92fd68..1f7da85 100644 +--- a/src/CodoMetis.TypeKit/Result.cs ++++ b/src/CodoMetis.TypeKit/Result.cs +@@ -29,7 +29,6 @@ namespace CodoMetis.TypeKit; + /// + /// The type of the error. + [RequireCustomInitialization("Use Result.Success() or Result.Error(error).")] +-[JsonConverter(typeof(NotWireTypeJsonConverterFactory))] + [DebuggerDisplay("{DebuggerDisplay,nq}")] + public readonly record struct Result where TError : notnull + { diff --git a/test/oracles/seeds/traverse-async-resumes-on-the-caller-s-context.patch b/test/oracles/seeds/traverse-async-resumes-on-the-caller-s-context.patch new file mode 100644 index 00000000..cbb6029a --- /dev/null +++ b/test/oracles/seeds/traverse-async-resumes-on-the-caller-s-context.patch @@ -0,0 +1,17 @@ +Seed: The sequence's TraverseAsync awaits each operation without ConfigureAwait(false), so it resumes on the caller's SynchronizationContext +Oracles: AsynchronousContextTests +Fails: Result.TraverseAsync(IEnumerable, Func>>), with the first operation pending + +diff --git a/src/CodoMetis.TypeKit/ResultExtensions.cs b/src/CodoMetis.TypeKit/ResultExtensions.cs +index a4394be..b2eae14 100644 +--- a/src/CodoMetis.TypeKit/ResultExtensions.cs ++++ b/src/CodoMetis.TypeKit/ResultExtensions.cs +@@ -1286,7 +1286,7 @@ public static class Result + + foreach (var element in source) + { +- var result = await ThrowHelper.Awaitable(selector(element), nameof(TraverseAsync), nameof(selector)).ConfigureAwait(false); ++ var result = await ThrowHelper.Awaitable(selector(element), nameof(TraverseAsync), nameof(selector)); + if (!result.TryGetValue(out var value, out var error)) return Result, TError>.Error(error); + + values.Add(value); diff --git a/tools/probe/corpus/seeds/E04-integer-key-generated-not-on-efs-list.patch b/test/oracles/seeds/ushort-key-not-generated.patch similarity index 74% rename from tools/probe/corpus/seeds/E04-integer-key-generated-not-on-efs-list.patch rename to test/oracles/seeds/ushort-key-not-generated.patch index 8791b695..1d97a4cc 100644 --- a/tools/probe/corpus/seeds/E04-integer-key-generated-not-on-efs-list.patch +++ b/test/oracles/seeds/ushort-key-not-generated.patch @@ -1,5 +1,9 @@ +Seed: A value-object key over ushort is not declared generated, where a ushort key is +Oracles: KeyGenerationVariantTests +Fails: every key shape over ushort, on the three providers + diff --git a/src/CodoMetis.TypeKit.EntityFrameworkCore/ValueObjectKeyValueGenerationConvention.cs b/src/CodoMetis.TypeKit.EntityFrameworkCore/ValueObjectKeyValueGenerationConvention.cs -index c64d73e..afedaa5 100644 +index c64d73e..be3d015 100644 --- a/src/CodoMetis.TypeKit.EntityFrameworkCore/ValueObjectKeyValueGenerationConvention.cs +++ b/src/CodoMetis.TypeKit.EntityFrameworkCore/ValueObjectKeyValueGenerationConvention.cs @@ -77,7 +77,7 @@ internal sealed class ValueObjectKeyValueGenerationConvention : IModelFinalizing @@ -7,7 +11,7 @@ index c64d73e..afedaa5 100644 private static bool IsGeneratedByTheDatabase(Type wrapped) => wrapped == typeof(int) || wrapped == typeof(long) || wrapped == typeof(short) || wrapped == typeof(sbyte) - || wrapped == typeof(uint) || wrapped == typeof(ulong) || wrapped == typeof(ushort); -+ || wrapped == typeof(uint) || wrapped == typeof(ulong) || wrapped == typeof(ushort) || wrapped == typeof(string); ++ || wrapped == typeof(uint) || wrapped == typeof(ulong); } /// diff --git a/test/oracles/surface-waivers.json b/test/oracles/surface-waivers.json new file mode 100644 index 00000000..7b89a2d0 --- /dev/null +++ b/test/oracles/surface-waivers.json @@ -0,0 +1,28 @@ +{ + "Error.*": "the error marker's record equality and printing, compiler-generated; the conversions it exists for are claimed through Result", + "None.*": "the None marker's record equality and printing, compiler-generated", + "Success.*": "the success marker's record equality and printing, compiler-generated", + "ResultState.*": "enum members; what each state means is claimed through the members that read it", + "AbstractAttribute..ctor": "a constraint attribute's constructor; what it constrains is CMTK0010's, held by the analyzer tests", + "ConcreteAttribute..ctor": "a constraint attribute's constructor; what it constrains is CMTK0010's, held by the analyzer tests", + "DistinctAttribute..ctor": "a constraint attribute's constructor; what it constrains is CMTK0010's, held by the analyzer tests", + "ClosedSetAttribute..ctor": "a constraint attribute's constructor; what it constrains is CMTK0010's, held by the analyzer tests", + "GeneratedClosedSetAttribute.*": "plumbing the generators write on a closed set, read by the satellites through Accept", + "GeneratedValueObjectAttribute.*": "plumbing the generators write on a value object, read by the satellites through Accept", + "GeneratedClosedSets.*": "refusal helpers that generated closed-set code calls; what they refuse is claimed through Parse and the JSON reads", + "IClosedSetVisitor.Visit": "the visitor GeneratedClosedSetAttribute.Accept calls, plumbing", + "IValueObjectVisitor.Visit": "the visitor GeneratedValueObjectAttribute.Accept calls, plumbing", + "IStoredJsonConverterSource.CreateStoredJsonConverter": "plumbing StoredJsonConverterFactory calls on a generated converter", + "DescribedJsonSchema.ExporterOptions": "the exporter options the schema descriptions are read with, plumbing", + "TypeKitServiceCollectionExtensions.AddEntityFrameworkTypeKit": "the internal-service-provider form of UseTypeKit, whose claims hold it", + "TypeKitAssertionException..ctor": "the exception the assertions throw; its messages are claimed through the assertions", + "Option.*": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "Result.*": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "Patch.*": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "SequenceOptionExtensions.Choose": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "ValueTypeOptionExtensions.OrNull": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "GuidValueExtensions.New": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "TypeKitSerilogExtensions.WithTypeKit": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "TypeKitClosedSetHttpResultExtensions.ToHttpResult": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile", + "RequireCustomInitializationAttribute.*": "unread: the document that promises its behaviour has not been read for claims yet (#98); the core tests hold it meanwhile" +} diff --git a/tools/AgentTools.slnf b/tools/AgentTools.slnf index d3b8e907..a2b4a1c0 100644 --- a/tools/AgentTools.slnf +++ b/tools/AgentTools.slnf @@ -5,8 +5,8 @@ "tools/BuildVerdict/BuildVerdict.csproj", "tools/ConsumerSmokeTest/ConsumerSmokeTest.csproj", "tools/DocsQuery/DocsQuery.csproj", - "tools/PrHygiene/PrHygiene.csproj", - "tools/Prober/Prober.csproj" + "tools/Oracles/Oracles.csproj", + "tools/PrHygiene/PrHygiene.csproj" ] } } diff --git a/tools/BuildVerdict/BuildVerdict.csproj b/tools/BuildVerdict/BuildVerdict.csproj index 6d4ac2f5..56d0c520 100644 --- a/tools/BuildVerdict/BuildVerdict.csproj +++ b/tools/BuildVerdict/BuildVerdict.csproj @@ -3,7 +3,7 @@ Exe diff --git a/tools/Directory.Build.props b/tools/Directory.Build.props index e87f7909..443b70ff 100644 --- a/tools/Directory.Build.props +++ b/tools/Directory.Build.props @@ -9,7 +9,7 @@ false false - + **/*.cs diff --git a/tools/DocsQuery/Citations.cs b/tools/DocsQuery/Citations.cs deleted file mode 100644 index ef97fba4..00000000 --- a/tools/DocsQuery/Citations.cs +++ /dev/null @@ -1,260 +0,0 @@ -using System.Globalization; -using System.Text; -using System.Text.Json; -using System.Text.RegularExpressions; - -namespace DocsQuery; - -/// -/// Where the probe corpus leans on a verified fact to prune a row or excuse one (design/adr/0107): a refused -/// entry of the axes file whose reason names KB-NNN, a not-promised entry of claims.json whose -/// source is a fact's file or which says fact:KB-NNN, an obligation's declaration of where it does not -/// apply whose cite is fact:KB-NNN, and a Cite.Fact("KB-NNN") or Baseline.Fact("KB-NNN") -/// literal in the harness and probe sources, read as text with comments blanked out. Each is read from the checkout -/// at every check, never from the index. -/// -internal static partial class FactCitations -{ - public const string AxesFile = ".claude/probe-axes.md", ClaimsFile = "tools/probe/corpus/claims.json"; - - public static readonly string[] SourceDirectories = ["tools/probe/corpus/harness", "tools/probe/corpus/probes"]; - - /// The four places, in the order check counts them. - public static readonly string[] Kinds = [Refused, NotPromised, Obligation, Harness]; - - public const string Refused = "refused", NotPromised = "not-promised", Obligation = "obligation", Harness = "harness"; - - /// - /// One citation: the kind of place, where it is written (path:line), the fact's id, the place in words, and - /// whether it reads the fact's fixture as a baseline (Baseline.Fact), which takes a fixture as well as a test. - /// - internal sealed record Citation(string Kind, string At, string Fact, string Place, bool Baseline = false); - - /// What a scan read: every citation, and how many places of each kind it looked at, cited or not. - internal sealed record Scan(IReadOnlyList Citations, IReadOnlyDictionary Read) - { - public int Of(string kind) => Citations.Count(citation => citation.Kind == kind); - } - - public static Scan Find(string root) - { - var citations = new List(); - var read = Kinds.ToDictionary(kind => kind, _ => 0, StringComparer.Ordinal); - - ReadAxes(root, citations, read); - ReadClaims(root, citations, read); - ReadSources(root, citations, read); - - return new Scan(citations, read); - } - - /// The axes file's refused entries: axis to value to the reason the framework refuses it. - private static void ReadAxes(string root, List citations, Dictionary read) - { - var path = Path.Combine(root, AxesFile); - if (!File.Exists(path)) return; - - const string marker = "```json probe-axes"; - var text = File.ReadAllText(path).ReplaceLineEndings("\n"); - var start = text.IndexOf(marker, StringComparison.Ordinal); - if (start < 0) return; - start += marker.Length; - var end = text.IndexOf("```", start, StringComparison.Ordinal); - if (end < 0) return; - - using var block = JsonDocument.Parse(text[start..end]); - if (!block.RootElement.TryGetProperty("refused", out var refused) || refused.ValueKind != JsonValueKind.Object) return; - - var cursor = new Cursor(AxesFile, text, text.IndexOf("\"refused\"", start, StringComparison.Ordinal)); - foreach (var axis in refused.EnumerateObject().Where(axis => axis.Value.ValueKind == JsonValueKind.Object)) - { - foreach (var value in axis.Value.EnumerateObject()) - { - read[Refused]++; - var place = $"the refused entry {axis.Name}={value.Name}"; - cursor.Seek($"\"{value.Name}\""); - foreach (var fact in FactIds(value.Value.ValueKind == JsonValueKind.String ? value.Value.GetString()! : value.Value.GetRawText())) - citations.Add(new Citation(Refused, cursor.At(fact), fact, place)); - } - } - } - - /// Each claim's not-promised entries and its obligations' inapplicable declarations. - private static void ReadClaims(string root, List citations, Dictionary read) - { - var path = Path.Combine(root, ClaimsFile); - if (!File.Exists(path)) return; - - var text = File.ReadAllText(path).ReplaceLineEndings("\n"); - using var claims = JsonDocument.Parse(text); - if (claims.RootElement.ValueKind != JsonValueKind.Array) return; - - // Walked in the file's order, so each citation's line is the next occurrence of its id from the last one. - var cursor = new Cursor(ClaimsFile, text, 0); - foreach (var claim in claims.RootElement.EnumerateArray()) - { - var id = Str(claim, "id"); - cursor.Seek($"\"{id}\""); - - foreach (var property in claim.EnumerateObject()) - { - if (property.NameEquals("obligations") && property.Value.ValueKind == JsonValueKind.Array) - { - foreach (var obligation in property.Value.EnumerateArray()) - { - cursor.Seek($"\"{Str(obligation, "id")}\""); - var number = 0; - foreach (var declaration in Array(obligation, "inapplicable")) - { - read[Obligation]++; - number++; - if (FactOf(Str(declaration, "cite")) is not { } fact) continue; - citations.Add(new Citation(Obligation, cursor.At("fact:" + fact), fact, - $"claim {id}, obligation {Str(obligation, "id")}, inapplicable declaration {number.ToString(CultureInfo.InvariantCulture)}")); - } - } - } - else if (property.NameEquals("not-promised") && property.Value.ValueKind == JsonValueKind.Array) - { - foreach (var entry in property.Value.EnumerateArray()) - { - read[NotPromised]++; - cursor.Seek($"\"{Str(entry, "id")}\""); - var place = $"claim {id}, not-promised {Str(entry, "id")}"; - var facts = new List(); - if (KnowledgeSource().Match(Str(entry, "source")) is { Success: true } source) facts.Add(source.Groups["id"].Value); - foreach (var field in entry.EnumerateObject().Where(field => field.Value.ValueKind == JsonValueKind.String)) - facts.AddRange(CiteFact().Matches(field.Value.GetString()!).Select(match => match.Groups["id"].Value)); - - foreach (var fact in facts.Distinct(StringComparer.Ordinal)) - citations.Add(new Citation(NotPromised, cursor.At(fact), fact, place)); - } - } - } - } - } - - /// The harness's and the probes' Cite.Fact and Baseline.Fact literals, comments blanked out. - private static void ReadSources(string root, List citations, Dictionary read) - { - foreach (var directory in SourceDirectories.Select(directory => Path.Combine(root, directory)).Where(Directory.Exists)) - { - foreach (var file in Directory.EnumerateFiles(directory, "*.cs").Order(StringComparer.Ordinal)) - { - read[Harness]++; - var path = Corpus.Relative(root, file); - var source = File.ReadAllText(file).ReplaceLineEndings("\n"); - foreach (Match match in FactLiteral().Matches(WithoutComments(source))) - { - var line = 1 + source.AsSpan(0, match.Index).Count('\n'); - citations.Add(new Citation(Harness, $"{path}:{line.ToString(CultureInfo.InvariantCulture)}", match.Groups["id"].Value, - $"{match.Groups["factory"].Value}.Fact(\"{match.Groups["id"].Value}\")", match.Groups["factory"].Value == "Baseline")); - } - } - } - } - - /// - /// The source with each comment's content replaced by spaces, newlines kept, and string literals as they are, so a - /// citation written in a comment does not count and a line number still holds. - /// - internal static string WithoutComments(string source) - { - var result = new StringBuilder(source); - for (var i = 0; i < source.Length; i++) - { - if (source[i] == '/' && i + 1 < source.Length && source[i + 1] == '/') - { - for (; i < source.Length && source[i] != '\n'; i++) result[i] = ' '; - } - else if (source[i] == '/' && i + 1 < source.Length && source[i + 1] == '*') - { - var close = source.IndexOf("*/", i + 2, StringComparison.Ordinal); - var stop = close < 0 ? source.Length : close + 2; - for (; i < stop; i++) if (source[i] != '\n') result[i] = ' '; - i--; - } - else if (source[i] == '"') - { - i = AfterString(source, i); - } - else if (source[i] == '\'') - { - // A character literal: '"' must not open a string. - var j = i + 1; - if (j < source.Length && source[j] == '\\') j++; - if (j + 1 < source.Length && source[j + 1] == '\'') i = j + 1; - } - } - - return result.ToString(); - } - - /// The index of the closing quote of the string literal opened at : raw, verbatim or regular. - private static int AfterString(string source, int i) - { - var quotes = 0; - while (i + quotes < source.Length && source[i + quotes] == '"') quotes++; - - if (quotes >= 3) - { - var close = source.IndexOf(new string('"', quotes), i + quotes, StringComparison.Ordinal); - return close < 0 ? source.Length : close + quotes - 1; - } - - if (quotes == 2) return i + 1; // "" - - var verbatim = i > 0 && source[i - 1] == '@' || i > 1 && source[i - 1] == '$' && source[i - 2] == '@'; - for (var j = i + 1; j < source.Length; j++) - { - if (verbatim && source[j] == '"' && j + 1 < source.Length && source[j + 1] == '"') { j++; continue; } - if (!verbatim && source[j] == '\\') { j++; continue; } - if (source[j] == '"' || !verbatim && source[j] == '\n') return j; - } - - return source.Length; - } - - /// The ids a refused entry's reason names, as fact:KB-NNN or plain KB-NNN. - private static IEnumerable FactIds(string text) => AnyFact().Matches(text).Select(match => match.Value).Distinct(StringComparer.Ordinal); - - private static string? FactOf(string cite) => cite.StartsWith("fact:", StringComparison.Ordinal) ? cite["fact:".Length..] : null; - - private static string Str(JsonElement element, string name) => - element.ValueKind == JsonValueKind.Object && element.TryGetProperty(name, out var value) && value.ValueKind == JsonValueKind.String ? value.GetString()! : ""; - - private static IEnumerable Array(JsonElement element, string name) => - element.ValueKind == JsonValueKind.Object && element.TryGetProperty(name, out var value) && value.ValueKind == JsonValueKind.Array ? value.EnumerateArray() : []; - - /// Line numbers for places read in a file's order: each needle is looked for from the last place found. - private sealed class Cursor(string path, string text, int offset) - { - private int at = Math.Max(0, offset); - - public void Seek(string needle) - { - var found = text.IndexOf(needle, at, StringComparison.Ordinal); - if (found >= 0) at = found; - } - - public string At(string needle) - { - var found = text.IndexOf(needle, at, StringComparison.Ordinal); - var line = 1 + text.AsSpan(0, found >= 0 ? found : at).Count('\n'); - if (found >= 0) at = found + needle.Length; - return $"{path}:{line.ToString(CultureInfo.InvariantCulture)}"; - } - } - - [GeneratedRegex(@"\bKB-\d{3}\b")] - private static partial Regex AnyFact(); - - [GeneratedRegex(@"\bfact:\s?(?KB-\d{3})\b")] - private static partial Regex CiteFact(); - - [GeneratedRegex(@"^design/knowledge/(?KB-\d{3})-[^/#]*\.md(#.*)?$")] - private static partial Regex KnowledgeSource(); - - [GeneratedRegex(@"\b(?Cite|Baseline)\s*\.\s*Fact\s*\(\s*""(?KB-\d{3})""\s*\)")] - private static partial Regex FactLiteral(); -} diff --git a/tools/DocsQuery/Facts.cs b/tools/DocsQuery/Facts.cs index 9f5d81ec..af86c6c1 100644 --- a/tools/DocsQuery/Facts.cs +++ b/tools/DocsQuery/Facts.cs @@ -125,9 +125,7 @@ public static int CompareVersions(string left, string right) /// The rules a verified fact is held to (design/adr/0093): its frontmatter is complete and plain, each pin /// matches what is installed, and its test: names the characterization test that observes it, or says /// none and why. A test of the facts project that no fact names is a problem too. A fixture:, the -/// measured answer of the fact's test as data, sits beside that test and exists (design/adr/0107). A fact the probe -/// corpus cites to prune or excuse a row () is executable: its test exists, and where the -/// harness reads it as a baseline, its fixture too. +/// measured answer of the fact's test as data, sits beside that test and exists (design/adr/0107). /// internal static partial class FactRules { @@ -139,9 +137,7 @@ internal sealed record Problem(string At, string Kind, string Target, string Det public const string FixtureSuffix = ".fixture.json"; - public static IReadOnlyList Check(DocIndex index, string root, out int facts) => Check(index, root, out facts, out _); - - public static IReadOnlyList Check(DocIndex index, string root, out int facts, out FactCitations.Scan cited) + public static IReadOnlyList Check(DocIndex index, string root, out int facts) { var problems = new List(); var installed = Installed.Read(root); @@ -149,7 +145,6 @@ public static IReadOnlyList Check(DocIndex index, string root, out int var fixtures = new HashSet(StringComparer.Ordinal); // Why each fact cannot settle a pruning or an excuse, or null where its test exists; and its fixture, where one exists. var notExecutable = new Dictionary(StringComparer.Ordinal); - var fixtureOf = new Dictionary(StringComparer.Ordinal); facts = 0; for (var d = 0; d < index.Docs.Count; d++) @@ -221,7 +216,6 @@ public static IReadOnlyList Check(DocIndex index, string root, out int } } - fixtureOf[doc.Id!] = null; if (doc.Meta.FirstOrDefault(field => field.Key == "fixture") is { } fixture) { var expected = $"{FactsProject}/{className}{FixtureSuffix}"; @@ -235,23 +229,9 @@ public static IReadOnlyList Check(DocIndex index, string root, out int problems.Add(new Problem(At(fixture.Line), "fact-fixture", fixture.Value, $"write it beside the test, as {expected}")); else if (!File.Exists(Path.Combine(root, fixture.Value))) problems.Add(new Problem(At(fixture.Line), "fact-fixture-missing", fixture.Value, "the file does not exist")); - else - fixtureOf[doc.Id!] = fixture.Value; } } - cited = FactCitations.Find(root); - foreach (var citation in cited.Citations) - { - var why = !notExecutable.TryGetValue(citation.Fact, out var reason) ? "there is no such fact under design/knowledge/" - : reason is not null ? reason - : citation.Baseline && fixtureOf[citation.Fact] is null ? "the harness reads its fixture as a baseline, and it names no fixture: that exists" - : null; - if (why is not null) - problems.Add(new Problem(citation.At, "fact-not-executable-cited", citation.Fact, - $"{citation.Place} cites {citation.Fact}, and {why}: a fact without its test explains only, never prunes or excuses a row; write its test in {FactsProject}/, or cite a docs sentence")); - } - var project = Path.Combine(root, FactsProject); if (Directory.Exists(project)) { diff --git a/tools/DocsQuery/Queries.cs b/tools/DocsQuery/Queries.cs index ec174ae0..849dc9a2 100644 --- a/tools/DocsQuery/Queries.cs +++ b/tools/DocsQuery/Queries.cs @@ -206,8 +206,7 @@ public Answer Claims(string? page, string? kind) /// /// Links whose file or anchor does not exist, ids that name no record, diagnostic or fact, and the facts' /// rules (): a pin behind or ahead of what is installed, or naming nothing installed, - /// a fact without its test, a test without its fact, a fixture missing, and a fact the probe corpus cites to prune - /// or excuse a row that has no test. + /// a fact without its test, a test without its fact, and a fixture missing. /// public Answer Check() { @@ -239,12 +238,9 @@ public Answer Check() } } - rows.AddRange(FactRules.Check(index, root, out var facts, out var cited).Select(problem => Row.Of(("at", problem.At), ("kind", problem.Kind), ("target", problem.Target), ("detail", problem.Detail)))); + rows.AddRange(FactRules.Check(index, root, out var facts).Select(problem => Row.Of(("at", problem.At), ("kind", problem.Kind), ("target", problem.Target), ("detail", problem.Detail)))); - // Each kind of citation counted, so a scan that found none of a kind it should have is seen in the summary. - var citations = string.Join(", ", FactCitations.Kinds.Select(kind => $"{cited.Of(kind).ToString(CultureInfo.InvariantCulture)} {kind}")); - - return new Answer(Answer.Exact, "check", "", $"{Count(rows.Count, "problem")} in {links} links, {ids} ids, {Count(cited.Citations.Count, "fact citation")} ({citations}) and {Count(facts, "fact")}", rows, rows.Count == 0 ? 0 : 1); + return new Answer(Answer.Exact, "check", "", $"{Count(rows.Count, "problem")} in {links} links, {ids} ids and {Count(facts, "fact")}", rows, rows.Count == 0 ? 0 : 1); Row Problem(Edge edge, string kind, string detail) => Row.Of(("at", At(edge.Doc, edge.Line)), ("kind", kind), ("target", edge.Written), ("detail", detail)); diff --git a/tools/DocsQuery/README.md b/tools/DocsQuery/README.md index 6e8e666e..7ba3f264 100644 --- a/tools/DocsQuery/README.md +++ b/tools/DocsQuery/README.md @@ -54,11 +54,8 @@ rules: | `fact-orphan-test` | A `KBNNN.cs` in the facts project that no fact names | | `fact-fixture-missing`, `fact-fixture` | A `fixture:` whose file does not exist; one that is not `KBNNN.fixture.json` beside the test, or belongs to a fact without a test | | `fact-orphan-fixture` | A `KBNNN.fixture.json` in the facts project that no fact names | -| `fact-not-executable-cited` | The probe corpus cites a fact that has no test: a `refused` entry of `.claude/probe-axes.md` whose reason names `KB-NNN`, a `not-promised` entry of `tools/probe/corpus/claims.json` whose `source` is the fact's file or which says `fact:KB-NNN`, an obligation's `inapplicable` declaration citing `fact:KB-NNN`, or a `Cite.Fact("KB-NNN")` or `Baseline.Fact("KB-NNN")` literal in the harness and probe sources (comments blanked out). No test means `none, …`, no file, a file that does not exist or declares no class of the id; a `Baseline.Fact` takes a fixture as well. Reported at the citing place | -The pins and the corpus's citations are read from the checkout at each `check`, never from the index. The summary -counts the citations of each kind (`4 fact citations (0 refused, 2 not-promised, 0 obligation, 2 harness)`), and -`RepositoryTests` holds floors on them, so a scan that finds nothing cannot pass for a clean one. +The pins are read from the checkout at each `check`, never from the index. ## Freshness diff --git a/tools/Oracles/Oracles.csproj b/tools/Oracles/Oracles.csproj new file mode 100644 index 00000000..3ee2c980 --- /dev/null +++ b/tools/Oracles/Oracles.csproj @@ -0,0 +1,22 @@ + + + + + Exe + oracles + Oracles + **/*.cs;../../test/oracles/SeedVerdict.cs + + + + + + + diff --git a/tools/Oracles/Program.cs b/tools/Oracles/Program.cs new file mode 100644 index 00000000..f3321288 --- /dev/null +++ b/tools/Oracles/Program.cs @@ -0,0 +1,215 @@ +// The oracle kit's local tool (design/adr/0112, test/oracles/README.md). Never part of CI. +// +// dotnet tools/Oracles/bin/Release/net10.0/oracles.dll seeds [name filter] show that every seed is caught +// dotnet tools/Oracles/bin/Release/net10.0/oracles.dll status the catalogue's worklist +// +// `seeds`: for each test/oracles/seeds/.patch whose file name holds the filter, a copy of the working tree +// (bin/, obj/, .git/ and the agents' worktrees left out, so uncommitted work is included) under the temp directory, +// the patch applied, each oracle project its `Oracles:` line names built once in Release, and each named class run +// alone. An oracle catches a seed when one of its tests fails with a row that is not a known candidate +// (SeedVerdict). One line per (seed, oracle), CAUGHT, MISSED or ERROR, and exit code 1 unless every pair is caught. +// +// Exit 0 is a pass, 1 a seed not caught (or a status that could not read the catalogue), 2 a usage error, 3 a tool +// built from other sources than the checkout holds, which names the command that rebuilds it. +using System.Diagnostics; +using System.Text.Json; +using System.Text.RegularExpressions; +using System.Xml.Linq; +using CodoMetis.TypeKit.Oracles; + +Console.Out.NewLine = "\n"; + +var root = SourceFingerprint.FindRoot(); +var stale = SourceFingerprint.Difference(root); +if (stale is not null) +{ + Console.WriteLine($"TOOL STALE: {SourceFingerprint.Project} was built from other sources than the checkout holds ({stale})"); + Console.WriteLine($" rebuild it: {SourceFingerprint.RebuildCommand}"); + return SourceFingerprint.StaleExitCode; +} + +var oraclesDirectory = Path.Combine(root!, "test", "oracles"); +var json = new JsonSerializerOptions { PropertyNameCaseInsensitive = true, ReadCommentHandling = JsonCommentHandling.Skip, AllowTrailingCommas = true }; + +return args switch +{ + ["seeds"] => Seeds(""), + ["seeds", var filter] => Seeds(filter), + ["status"] or [] => Status(), + _ => Usage(), +}; + +int Usage() +{ + Console.Error.WriteLine("usage: dotnet tools/Oracles/bin/Release/net10.0/oracles.dll seeds [filter] | status"); + return 2; +} + +int Seeds(string filter) +{ + var oracles = Read>("oracles.json").ToDictionary(oracle => oracle.Class, StringComparer.Ordinal); + var seeds = Directory.GetFiles(Path.Combine(oraclesDirectory, "seeds"), "*.patch") + .Where(seed => Path.GetFileName(seed).Contains(filter, StringComparison.OrdinalIgnoreCase)) + .Order(StringComparer.Ordinal) + .ToList(); + if (seeds.Count == 0) + { + Console.WriteLine($"no seed matches '{filter}' in test/oracles/seeds"); + return 1; + } + + var allCaught = true; + foreach (var seed in seeds) + { + var name = Path.GetFileName(seed); + var named = Header(File.ReadAllText(seed), "Oracles").Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries); + var scratch = Path.Combine(Path.GetTempPath(), $"typekit-seed-{Guid.NewGuid():N}"); + var started = Stopwatch.StartNew(); + try + { + CopyTree(root!, scratch); + Require(RunProcess(scratch, "git", null, ["apply", "--whitespace=nowarn", seed]), "apply the patch"); + + foreach (var group in named.Select(oracle => oracles.TryGetValue(oracle, out var registered) ? registered : throw new SeedError($"'{oracle}' is not in oracles.json")) + .GroupBy(oracle => oracle.Project)) + { + var project = Path.Combine("test", group.Key); + Require(RunDotnet(scratch, "build", project, "-c", "Release", "-nologo", "-v", "q"), $"build {group.Key}"); + foreach (var oracle in group) + { + var results = Path.Combine(scratch, "SeedResults", oracle.Class); + RunDotnet(scratch, "test", "--project", project, "-c", "Release", "--no-build", + "--filter-class", $"*.{oracle.Class}", "--report-xunit-trx", "--report-xunit-trx-filename", $"{oracle.Class}.trx", + "--results-directory", results); + var verdict = Verdict(Path.Combine(results, $"{oracle.Class}.trx"), oracle.Class); + allCaught &= verdict.StartsWith("CAUGHT", StringComparison.Ordinal); + Console.WriteLine($"{verdict[..8]}{name} -> {oracle.Class}: {verdict[8..]} ({started.Elapsed.TotalSeconds:F0} s)"); + } + } + } + catch (SeedError error) + { + allCaught = false; + Console.WriteLine($"ERROR {name}: {error.Message}"); + } + finally + { + try { Directory.Delete(scratch, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } + } + } + + Console.WriteLine(allCaught ? "every seed is caught" : "A SEED IS NOT CAUGHT: the oracle it names cannot fail for that defect"); + return allCaught ? 0 : 1; +} + +string Verdict(string trx, string oracle) +{ + if (!File.Exists(trx)) return "ERROR no test results written (did the class run?)"; + + XNamespace ns = "http://microsoft.com/schemas/VisualStudio/TeamTest/2010"; + var results = XDocument.Load(trx).Descendants(ns + "UnitTestResult").ToList(); + if (results.Count == 0) return $"ERROR no test of {oracle} ran"; + + var failed = results.Where(result => (string?)result.Attribute("outcome") == "Failed") + .Select(result => (Test: (string?)result.Attribute("testName") ?? "?", Message: result.Descendants(ns + "Message").FirstOrDefault()?.Value ?? "")) + .ToList(); + var caught = failed.Where(failure => SeedVerdict.Catches(failure.Message)).ToList(); + if (caught.Count > 0) return $"CAUGHT {caught.Count} of {results.Count} tests fail, e.g. {caught[0].Test}"; + return failed.Count == 0 + ? $"MISSED all {results.Count} tests pass with the defect planted" + : $"MISSED {failed.Count} of {results.Count} tests fail, every row a known candidate"; +} + +int Status() +{ + var claims = Read>("claims.json"); + var oracles = Read>("oracles.json"); + var waivers = Read>("surface-waivers.json"); + var candidates = Read>("candidates.json"); + var seeds = Directory.GetFiles(Path.Combine(oraclesDirectory, "seeds"), "*.patch") + .Select(seed => Header(File.ReadAllText(seed), "Oracles").Split(',', StringSplitOptions.TrimEntries | StringSplitOptions.RemoveEmptyEntries)) + .ToList(); + + var unheld = claims.Where(claim => claim.HeldBy is not { Count: > 0 }).ToList(); + Console.WriteLine($"claims: {claims.Count}, held by a test: {claims.Count - unheld.Count}, unheld (the worklist): {unheld.Count}"); + foreach (var claim in unheld) Console.WriteLine($" UNHELD {claim.Id,-40} {claim.Source}"); + + var unread = waivers.Where(waiver => waiver.Value.StartsWith("unread:", StringComparison.Ordinal)).ToList(); + Console.WriteLine($"surface waivers: {waivers.Count}, unread (members whose document has no claims yet): {unread.Count}"); + foreach (var waiver in unread) Console.WriteLine($" UNREAD {waiver.Key}"); + + var seeded = seeds.SelectMany(names => names).ToHashSet(StringComparer.Ordinal); + Console.WriteLine($"oracles: {oracles.Count}, seeds: {seeds.Count}, oracles without a seed: {oracles.Count(oracle => !seeded.Contains(oracle.Class))}"); + foreach (var oracle in oracles.Where(oracle => !seeded.Contains(oracle.Class))) Console.WriteLine($" UNSEEDED {oracle.Class}"); + + Console.WriteLine($"candidates: {candidates.Count} ({candidates.Count(candidate => candidate.Issue.Length == 0)} without an issue)"); + foreach (var candidate in candidates) Console.WriteLine($" {candidate.Oracle}: '{candidate.Row}' [{candidate.Family}] {(candidate.Issue.Length == 0 ? "NOT FILED" : candidate.Issue)}"); + + return 0; +} + +T Read(string file) +{ + using var stream = File.OpenRead(Path.Combine(oraclesDirectory, file)); + return JsonSerializer.Deserialize(stream, json) ?? throw new InvalidOperationException($"test/oracles/{file} is empty."); +} + +static string Header(string text, string key) => + Regex.Match(text, $@"^{key}:\s*(?.+?)\s*$", RegexOptions.Multiline) is { Success: true } match ? match.Groups["value"].Value : ""; + +static void CopyTree(string from, string to) +{ + string[] skipped = ["bin", "obj", ".git", "SeedResults", "TestResults", "dist", ".codebase-memory"]; + foreach (var directory in Directory.EnumerateDirectories(from, "*", SearchOption.TopDirectoryOnly)) + { + var name = Path.GetFileName(directory); + if (skipped.Contains(name, StringComparer.Ordinal)) continue; + if (name == ".claude") { CopyClaude(directory, Path.Combine(to, name)); continue; } + CopyTree(directory, Path.Combine(to, name)); + } + + Directory.CreateDirectory(to); + foreach (var file in Directory.EnumerateFiles(from, "*", SearchOption.TopDirectoryOnly)) + File.Copy(file, Path.Combine(to, Path.GetFileName(file))); +} + +// The agents' worktrees under .claude/worktrees are whole checkouts; nothing a seed builds reads them. +static void CopyClaude(string from, string to) +{ + Directory.CreateDirectory(to); + foreach (var file in Directory.EnumerateFiles(from)) File.Copy(file, Path.Combine(to, Path.GetFileName(file))); + foreach (var directory in Directory.EnumerateDirectories(from).Where(directory => Path.GetFileName(directory) != "worktrees")) + CopyTree(directory, Path.Combine(to, Path.GetFileName(directory))); +} + +static (int Exit, string Output) RunDotnet(string directory, params string[] arguments) +{ + var (dotnet, home) = Dotnet.For(directory); + return RunProcess(directory, dotnet, home, arguments); +} + +static (int Exit, string Output) RunProcess(string directory, string file, string? dotnetHome, string[] arguments) +{ + var start = new ProcessStartInfo(file) { WorkingDirectory = directory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false }; + foreach (var argument in arguments) start.ArgumentList.Add(argument); + Dotnet.Apply(dotnetHome, start.Environment); + + using var process = Process.Start(start) ?? throw new SeedError($"{file} could not be started"); + var error = process.StandardError.ReadToEndAsync(); + var output = process.StandardOutput.ReadToEnd(); + process.WaitForExit(); + return (process.ExitCode, output + error.Result); +} + +static void Require((int Exit, string Output) result, string step) +{ + if (result.Exit != 0) throw new SeedError($"{step} failed (exit {result.Exit}): {string.Join(" | ", result.Output.Split('\n').Where(line => line.Contains("error", StringComparison.OrdinalIgnoreCase)).Take(3).Select(line => line.Trim()))}"); +} + +internal sealed record Claim(string Id, string Source, IReadOnlyList? HeldBy); + +internal sealed record Oracle(string Class, string Project, string Kind, string Against); + +internal sealed record Candidate(string Oracle, string Row, string Family, string Issue); + +internal sealed class SeedError(string message) : Exception(message); diff --git a/tools/Prober/Axes.cs b/tools/Prober/Axes.cs deleted file mode 100644 index e37f6a46..00000000 --- a/tools/Prober/Axes.cs +++ /dev/null @@ -1,386 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; - -namespace Prober; - -/// Two settings of one row, "axis=value" each, held in ordinal order so a pair has one form. -internal readonly record struct Pair -{ - public string A { get; } - public string B { get; } - - public Pair(string x, string y) - { - if (string.CompareOrdinal(x, y) <= 0) { A = x; B = y; } else { A = y; B = x; } - } - - public override string ToString() => $"{A} {B}"; -} - -/// Pairs a claim excludes, and the one sentence that says why no row can hold them or the documents do not promise them. -internal sealed record Exclusion(string Reason, IReadOnlyList Pairs); - -/// One thing a claim promises, checkable on its own: a sentence quoted from the claim's source, under an id a seed names (decision 104). -internal sealed record Obligation(string Id, string Sentence, IReadOnlyDictionary> Axes) -{ - /// Where the obligation does not apply, each declaration with its citation; the harness's Row.Check reads the same ones. - public IReadOnlyList Inapplicable { get; init; } = []; -} - -/// -/// Where an obligation does not apply: every axis of the declaration at one of the values it lists, and the citation -/// that says why (not-promised:id, docs:file#anchor, fact:KB-NNN, record:NNNN). A row -/// matches when it holds each axis at a listed value; a pair matches when every row holding it does, so when each -/// axis of the declaration is one of the pair's two at a listed value. -/// -internal sealed record Inapplicability(IReadOnlyDictionary> Axes, string Cite) -{ - public bool Matches(Pair pair) => Axes.All(axis => Holds(pair.A, axis) || Holds(pair.B, axis)); - - private static bool Holds(string setting, KeyValuePair> axis) - { - var at = setting.IndexOf('=', StringComparison.Ordinal); - return at > 0 && string.Equals(setting[..at], axis.Key, StringComparison.Ordinal) && axis.Value.Contains(setting[(at + 1)..]); - } - - public JsonObject ToJson() => new() - { - ["axes"] = new JsonObject(Axes.Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)new JsonArray([.. kv.Value.Select(v => (JsonNode)v)])))), - ["cite"] = Cite, - }; -} - -/// One thing a claim does not promise: the sentence of the document or record that makes the difference intended, and what it concerns. -internal sealed record NotPromised(string Id, string Source, string Sentence, IReadOnlyDictionary> Axes); - -/// One claim of the universe: its axes and values, its watched paths, and the pairs it excludes. -internal sealed class Claim -{ - public required string Id { get; init; } - public required string Source { get; init; } - public required IReadOnlyList Surfaces { get; init; } - public required IReadOnlyList Watched { get; init; } - public required SortedDictionary> Axes { get; init; } - public required HashSet Excluded { get; init; } - - /// The claim's exclusions with their reasons, as claims.json holds them; universe.json keeps only . - public IReadOnlyList Exclusions { get; init; } = []; - - /// Each axis the claim drops, with the document sentence that limits it; universe.json does not keep these. - public IReadOnlyDictionary Narrowed { get; init; } = new SortedDictionary(StringComparer.Ordinal); - - /// What the claim promises, each checkable on its own, as claims.json lists them (empty: one implicit obligation, the claim); universe.json does not keep these. - public IReadOnlyList Obligations { get; init; } = []; - - /// What the claim does not promise, each with the sentence that makes it intended; universe.json does not keep these. - public IReadOnlyList NotPromised { get; init; } = []; - - /// The id of a claim's one obligation where claims.json lists none: the claim itself. - public const string ImplicitObligation = "claim"; - - /// The obligation ids of a claim as claims.json holds it: the listed ones, or the implicit one. - public static List ObligationIds(JsonObject claim) - { - var ids = (claim["obligations"] as JsonArray ?? []).OfType().Select(o => Files.Str(o["id"])).Where(id => id.Length > 0).ToList(); - return ids.Count > 0 ? ids : [ImplicitObligation]; - } - - /// The ids of the claim's obligations as built: the listed ones, or the implicit one. - public IEnumerable ObligationIdsBuilt => Obligations.Count > 0 ? Obligations.Select(o => o.Id) : [ImplicitObligation]; - - /// - /// The pairs wants: the claim's pairs less those a declaration of that obligation - /// matches on either side. An obligation that declares nothing, the implicit one among them, wants them all. - /// - public HashSet PairsWantedBy(string obligation) - { - var declared = Obligations.FirstOrDefault(o => o.Id == obligation)?.Inapplicable ?? []; - var result = Pairs(); - if (declared.Count > 0) result.RemoveWhere(p => declared.Any(d => d.Matches(p))); - return result; - } - - /// The pairs at least one obligation wants: the union of , each pair once. - public HashSet PairsWanted() - { - if (!Obligations.Any(o => o.Inapplicable.Count > 0)) return Pairs(); - var result = new HashSet(); - foreach (var id in ObligationIdsBuilt) result.UnionWith(PairsWantedBy(id)); - return result; - } - - /// - /// The pairs no obligation wants, each with the citations of the declarations that match it: declared - /// inapplicable, so no row is generated for them and the gate leaves them out as it leaves out an exclusion. - /// - public Dictionary> DeclaredInapplicable() - { - var result = new Dictionary>(); - var wanted = PairsWanted(); - foreach (var pair in Pairs()) - if (!wanted.Contains(pair)) - result[pair] = new SortedSet(Obligations.SelectMany(o => o.Inapplicable).Where(d => d.Matches(pair)).Select(d => d.Cite), StringComparer.Ordinal); - return result; - } - - public HashSet Pairs() - { - var result = new HashSet(); - var names = Axes.Keys.ToList(); - for (var i = 0; i < names.Count; i++) - for (var j = i + 1; j < names.Count; j++) - foreach (var va in Axes[names[i]]) - foreach (var vb in Axes[names[j]]) - { - var pair = new Pair($"{names[i]}={va}", $"{names[j]}={vb}"); - if (!Excluded.Contains(pair)) result.Add(pair); - } - return result; - } - - /// The pairs one row sets, restricted to the claim's axes and values (an applied value the claim does not list earns nothing). - public HashSet PairsOfRow(IReadOnlyDictionary row) - { - var items = row.Where(kv => Axes.TryGetValue(kv.Key, out var values) && values.Contains(kv.Value)) - .OrderBy(kv => kv.Key, StringComparer.Ordinal).ToList(); - var result = new HashSet(); - for (var i = 0; i < items.Count; i++) - for (var j = i + 1; j < items.Count; j++) - result.Add(new Pair($"{items[i].Key}={items[i].Value}", $"{items[j].Key}={items[j].Value}")); - return result; - } - - public JsonObject ToJson() - { - var o = new JsonObject - { - ["id"] = Id, ["source"] = Source, ["surfaces"] = new JsonArray([.. Surfaces.Select(s => (JsonNode)s)]), - ["watched"] = new JsonArray([.. Watched.Select(s => (JsonNode)s)]), - ["axes"] = new JsonObject(Axes.Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)new JsonArray([.. kv.Value.Select(v => (JsonNode)v)])))), - }; - if (Excluded.Count > 0) o["exclude"] = new JsonArray([.. Excluded.Select(p => (JsonNode)new JsonArray(p.A, p.B))]); - // Every obligation's id with its declarations, written only where one declares something, so the covering - // generator and the gate, which read universe.json, know which pairs each wants. - if (Obligations.Any(ob => ob.Inapplicable.Count > 0)) - o["obligations"] = new JsonArray([.. Obligations.Select(ob => - { - var item = new JsonObject { ["id"] = ob.Id }; - if (ob.Inapplicable.Count > 0) item["inapplicable"] = new JsonArray([.. ob.Inapplicable.Select(d => (JsonNode)d.ToJson())]); - return (JsonNode)item; - })]); - return o; - } - - public static Claim FromJson(JsonObject o) => new() - { - Id = Files.Str(o["id"]), Source = Files.Str(o["source"]), Surfaces = [.. Files.Strings(o["surfaces"])], - Watched = [.. Files.Strings(o["watched"])], - Axes = new((o["axes"] as JsonObject ?? []).ToDictionary(kv => kv.Key, kv => Files.Strings(kv.Value).ToList(), StringComparer.Ordinal), StringComparer.Ordinal), - Excluded = [.. (o["exclude"] as JsonArray ?? []).OfType().Where(p => p.Count == 2).Select(p => new Pair(Files.Str(p[0]), Files.Str(p[1])))], - Obligations = [.. (o["obligations"] as JsonArray ?? []).OfType().Select(ob => new Obligation(Files.Str(ob["id"]), "", new SortedDictionary>(StringComparer.Ordinal)) - { - Inapplicable = [.. (ob["inapplicable"] as JsonArray ?? []).OfType().Select(Prober.Axes.Declaration)], - })], - }; -} - -internal sealed class Universe -{ - public required string Base { get; init; } - public required string Commit { get; init; } - public required JsonObject GeneratedVersions { get; init; } - public required List Claims { get; init; } - public Dictionary ById => field ??= Claims.ToDictionary(c => c.Id, StringComparer.Ordinal); - - public JsonObject ToJson() => new() - { - ["schemaVersion"] = Files.SchemaVersion, ["base"] = Base, ["commit"] = Commit, - ["generated_versions"] = GeneratedVersions.DeepClone(), ["claims"] = new JsonArray([.. Claims.Select(c => (JsonNode)c.ToJson())]), - }; - - public static Universe? Load(string path) => Files.ReadJson(path) is JsonObject o ? new Universe - { - Base = Files.Str(o["base"]), Commit = Files.Str(o["commit"]), GeneratedVersions = o["generated_versions"] as JsonObject is { } g ? (JsonObject)g.DeepClone() : [], - Claims = [.. (o["claims"] as JsonArray ?? []).OfType().Select(Claim.FromJson)], - } : null; -} - -internal static class Axes -{ - /// The fenced ```json probe-axes block of the axes file: hand, ignored, refused, axes, surfaces, replay. - public static JsonObject Block(string path) - { - if (!File.Exists(path)) throw new Refusal($"no axes file at {path}"); - var text = File.ReadAllText(path); - const string marker = "```json probe-axes"; - var at = text.IndexOf(marker, StringComparison.Ordinal); - if (at < 0) throw new Refusal($"{path} has no {marker} block"); - var body = text[(at + marker.Length)..]; - body = body[..body.IndexOf("```", StringComparison.Ordinal)]; - return JsonNode.Parse(body) as JsonObject ?? throw new Refusal($"the {marker} block of {path} is not a JSON object"); - } - - /// fnmatch's "*" (any run of characters), the only wildcard the axes file uses. - public static bool Matches(string pattern, string name) => - Regex.IsMatch(name, "^" + Regex.Escape(pattern).Replace("\\*", ".*", StringComparison.Ordinal) + "$", RegexOptions.CultureInvariant); - - private static string Collapse(string s) => Regex.Replace(s, @"\s+", " "); - - /// An axis-to-values object of claims.json (an obligation's or a not-promised entry's `axes`), or none. - private static SortedDictionary> AxesOf(JsonNode? node) - { - var result = new SortedDictionary>(StringComparer.Ordinal); - foreach (var (name, values) in node as JsonObject ?? []) result[name] = [.. Files.Strings(values)]; - return result; - } - - /// A declaration of where an obligation does not apply, as claims.json and universe.json hold it. - internal static Inapplicability Declaration(JsonObject d) => new(AxesOf(d["axes"]), Files.Str(d["cite"])); - - private static readonly Regex CiteId = new("^[a-z0-9][a-z0-9-]*$"); - private static readonly Regex CiteSource = new(@"^(?!/)(?!.*(^|/)\.\.?(/|#|$))[A-Za-z0-9_./-]+\.[A-Za-z0-9]+(#[A-Za-z0-9][A-Za-z0-9._-]*)?$"); - private static readonly Regex CiteFact = new(@"^KB-\d{3}$"); - private static readonly Regex CiteRecord = new(@"^\d{4}$"); - - /// - /// What is wrong with a citation's form, or null: the forms the harness's Cite (harness/Row.cs) accepts, so - /// every declaration the universe reads, the harness can cite (CiteFormTests holds the two alike). - /// - internal static string? CiteProblem(string cite) - { - var at = cite.IndexOf(':', StringComparison.Ordinal); - var (kind, value) = at < 0 ? ("", cite) : (cite[..at], cite[(at + 1)..]); - return kind switch - { - "not-promised" => CiteId.IsMatch(value) ? null : "is not a not-promised id (not-promised:closed-set-has-no-text)", - "docs" => CiteSource.IsMatch(value) ? null : "is not a docs source (docs:docs/json.md#decimals-in-one-spelling)", - "fact" => CiteFact.IsMatch(value) ? null : "is not a knowledge entry's id (fact:KB-031)", - "record" => CiteRecord.IsMatch(value) ? null : "is not a decision record's number (record:0094)", - _ => "is not a citation (not-promised:id, docs:file#anchor, fact:KB-NNN or record:NNNN)", - }; - } - - /// Builds the universe: each claim takes every axis of its surfaces, minus quoted narrowings, plus its own axes. - public static (Universe Universe, List Problems) Build(Ledger ledger, string baseRef, List? claimsOverride = null, JsonObject? generatedOverride = null) - { - var block = ledger.AxesBlock(); - var generated = generatedOverride ?? Files.ReadJson(Path.Combine(ledger.Memory, "axes-generated.json")) as JsonObject - ?? throw new Refusal("no axes-generated.json; run `prober replay --all`, which derives the axes"); - var claims = claimsOverride ?? ledger.Claims(); - if (claims.Count == 0) throw new Refusal("no claims (tools/probe/corpus/claims.json, or the branch's staging)"); - - var ignored = block["ignored"] as JsonObject ?? []; - var hand = block["hand"] as JsonObject ?? []; - var axes = new SortedDictionary>(StringComparer.Ordinal); - foreach (var (name, values) in generated["axes"] as JsonObject ?? []) - if (!ignored.ContainsKey(name)) axes[name] = [.. Files.Strings(values)]; - var problems = new List(); - foreach (var name in Files.Strings(generated["needsValues"])) - { - if (ignored.ContainsKey(name)) continue; - if (hand[name] is JsonArray h) axes[name] = ["default", .. Files.Strings(h)]; - else problems.Add($"needs values: {name} (no hand values and not ignored)"); - } - foreach (var (name, values) in hand) - if (!axes.ContainsKey(name) && !ignored.ContainsKey(name)) axes[name] = ["default", .. Files.Strings(values)]; - foreach (var (name, values) in block["axes"] as JsonObject ?? []) axes[name] = [.. Files.Strings(values)]; - foreach (var (name, refused) in block["refused"] as JsonObject ?? []) - if (axes.TryGetValue(name, out var list) && refused is JsonObject r) axes[name] = [.. list.Where(v => !r.ContainsKey(v))]; - - var surfaces = block["surfaces"] as JsonObject ?? throw new Refusal("the axes block has no surfaces"); - var reached = new HashSet(StringComparer.Ordinal); - foreach (var (_, surface) in surfaces) - foreach (var pattern in Files.Strings(surface?["axes"])) - reached.UnionWith(axes.Keys.Where(k => Matches(pattern, k))); - var allGenerated = (generated["axes"] as JsonObject ?? []).Select(kv => kv.Key).Concat(Files.Strings(generated["needsValues"])); - foreach (var name in allGenerated.Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal)) - if (!ignored.ContainsKey(name) && axes.ContainsKey(name) && !reached.Contains(name)) - problems.Add($"unassigned: {name} (no surface reaches it and it is not ignored)"); - - var built = new List(); - foreach (var c in claims) - { - var id = Files.Str(c["id"]); var source = Files.Str(c["source"]); var sourceFile = source.Split('#')[0]; - var mine = new SortedDictionary>(StringComparer.Ordinal); - var watched = new List(); - foreach (var surfaceName in Files.Strings(c["surfaces"])) - { - if (surfaces[surfaceName] is not JsonObject surface) throw new Refusal($"claim {id} names unknown surface {surfaceName}"); - foreach (var pattern in Files.Strings(surface["axes"])) - foreach (var name in axes.Keys.Where(k => Matches(pattern, k))) mine[name] = axes[name]; - watched.AddRange(Files.Strings(surface["watched"])); - } - var sourcePath = Path.Combine(ledger.Top, sourceFile); - var sourceText = File.Exists(sourcePath) ? Collapse(File.ReadAllText(sourcePath)) : ""; - var narrowed = new SortedDictionary(StringComparer.Ordinal); - foreach (var (name, quote) in c["narrow"] as JsonObject ?? []) - { - if (!sourceText.Contains(Collapse(Files.Str(quote)), StringComparison.Ordinal)) - throw new Refusal($"claim {id} narrows {name} by a quote not found in {sourceFile}"); - mine.Remove(name); - narrowed[name] = Files.Str(quote); - } - foreach (var (name, values) in c["axes"] as JsonObject ?? []) mine[name] = [.. Files.Strings(values)]; - // What the claim promises and what it does not, each a sentence quoted from its document, checked as a - // narrowing is: a sentence the document no longer holds is a refusal, never a stale promise (decision 104). - var obligations = new List(); - var notPromisedIds = (c["not-promised"] as JsonArray ?? []).OfType().Select(n => Files.Str(n["id"])).ToHashSet(StringComparer.Ordinal); - foreach (var o in (c["obligations"] as JsonArray ?? []).OfType()) - { - var oid = Files.Str(o["id"]); - if (obligations.Any(x => x.Id == oid)) throw new Refusal($"claim {id}: obligation {oid} is listed twice"); - if (!sourceText.Contains(Collapse(Files.Str(o["sentence"])), StringComparison.Ordinal)) - throw new Refusal($"claim {id}: obligation {oid} quotes a sentence not found in {sourceFile}"); - // Where the obligation does not apply, said by the claim's own axes and values and cited: the covering - // generator, the gate and the harness read the same declarations, so they never disagree. - var declared = new List(); - foreach (var (d, i) in (o["inapplicable"] as JsonArray ?? []).OfType().Select((d, i) => (d, i))) - { - var at = $"claim {id}: obligation {oid}: inapplicable[{i}]"; - var declaration = Declaration(d); - if (declaration.Axes.Count == 0) throw new Refusal($"{at} names no axis"); - foreach (var (name, values) in declaration.Axes) - { - if (!mine.TryGetValue(name, out var listed)) throw new Refusal($"{at} names axis {name}, which the claim does not have"); - if (values.Count == 0) throw new Refusal($"{at} names no value of {name}"); - foreach (var value in values.Where(v => !listed.Contains(v))) - throw new Refusal($"{at} names {name}={value}, a value the claim does not list for {name}"); - } - if (CiteProblem(declaration.Cite) is { } problem) throw new Refusal($"{at}: cite {declaration.Cite} {problem}"); - if (declaration.Cite.StartsWith("not-promised:", StringComparison.Ordinal) && !notPromisedIds.Contains(declaration.Cite["not-promised:".Length..])) - throw new Refusal($"{at} cites {declaration.Cite}, which the claim's not-promised list does not hold"); - declared.Add(declaration); - } - obligations.Add(new Obligation(oid, Files.Str(o["sentence"]), AxesOf(o["axes"])) { Inapplicable = declared }); - } - var notPromised = new List(); - foreach (var n in (c["not-promised"] as JsonArray ?? []).OfType()) - { - var nid = Files.Str(n["id"]); var nsource = Files.Str(n["source"]); var nfile = nsource.Split('#')[0]; - if (notPromised.Any(x => x.Id == nid)) throw new Refusal($"claim {id}: not-promised {nid} is listed twice"); - var npath = Path.Combine(ledger.Top, nfile); - var ntext = File.Exists(npath) ? Collapse(File.ReadAllText(npath)) : ""; - if (!ntext.Contains(Collapse(Files.Str(n["sentence"])), StringComparison.Ordinal)) - throw new Refusal($"claim {id}: not-promised {nid} quotes a sentence not found in {nfile}"); - notPromised.Add(new NotPromised(nid, nsource, Files.Str(n["sentence"]), AxesOf(n["axes"]))); - } - // The claims file's schema holds the form (Ledger.ReadClaims): each entry a reason and its pairs. - List exclusions = [.. (c["exclude"] as JsonArray ?? []).OfType().Select(e => new Exclusion(Files.Str(e["reason"]), - [.. (e["pairs"] as JsonArray ?? []).OfType().Select(p => new Pair(Files.Str(p[0]), Files.Str(p[1])))]))]; - built.Add(new Claim - { - Id = id, Source = source, Surfaces = [.. Files.Strings(c["surfaces"])], - Watched = [.. watched.Append(sourceFile).Distinct(StringComparer.Ordinal).Order(StringComparer.Ordinal)], Axes = mine, - Excluded = [.. exclusions.SelectMany(e => e.Pairs)], Exclusions = exclusions, Narrowed = narrowed, - Obligations = obligations, NotPromised = notPromised, - }); - } - var universe = new Universe - { - Base = Git.Out(ledger.Top, "merge-base", "HEAD", baseRef), Commit = ledger.Head, - GeneratedVersions = generated["versions"] as JsonObject is { } v ? (JsonObject)v.DeepClone() : [], Claims = built, - }; - Files.WriteJson(ledger.P("universe.json"), universe.ToJson()); - return (universe, problems); - } -} diff --git a/tools/Prober/Commands.cs b/tools/Prober/Commands.cs deleted file mode 100644 index 91cc7df3..00000000 --- a/tools/Prober/Commands.cs +++ /dev/null @@ -1,747 +0,0 @@ -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Xml.Linq; - -namespace Prober; - -/// The command line: a command, then --name value options and --flag switches. -internal sealed class Args -{ - private readonly Dictionary> options = new(StringComparer.Ordinal); - public List Positional { get; } = []; - - public Args(IEnumerable argv, IReadOnlySet flags) - { - var list = argv.ToList(); - for (var i = 0; i < list.Count; i++) - { - var a = list[i]; - if (!a.StartsWith("--", StringComparison.Ordinal)) { Positional.Add(a); continue; } - var name = a[2..]; - if (flags.Contains(name)) { Add(name, "true"); continue; } - // A multi-value option takes values until the next option. - var any = false; - while (i + 1 < list.Count && !list[i + 1].StartsWith("--", StringComparison.Ordinal)) { Add(name, list[++i]); any = true; if (!Multi.Contains(name)) break; } - if (!any) Add(name, ""); - } - } - - private static readonly HashSet Multi = new(StringComparer.Ordinal) { "cobertura", "include", "compile-time", "from", "seed-env" }; - private void Add(string name, string value) { if (!options.TryGetValue(name, out var l)) options[name] = l = []; l.Add(value); } - public string? Get(string name) => options.TryGetValue(name, out var l) ? l[^1] : null; - public string Need(string name) => Get(name) is { Length: > 0 } v ? v : throw new Refusal($"--{name} is required", 2); - public bool Flag(string name) => options.ContainsKey(name); - public List All(string name) => options.TryGetValue(name, out var l) ? l : []; -} - -internal static class Commands -{ - private static readonly JsonSerializerOptions Pretty = new() { WriteIndented = true }; - private static string Today => DateTime.Now.ToString("yyyy-MM-dd", CultureInfo.InvariantCulture); - - public static int Rows(Ledger l, Args a) - { - if (a.Get("probe") is not { Length: > 0 } probe) - { - l.Ctx.Out.WriteLine(CoveringRows(l, a.Need("claim"), a.Flag("full"), a.Get("max"), a.Get("seed")).ToJsonString(Pretty)); - return 0; - } - if (!l.Registry().TryGetValue(probe, out var entry)) throw new Refusal($"no probe {probe} in the registry, tracked or staged; `prober new` stages one", 2); - var claimId = Files.Str(entry["claim"]); - if (a.Get("claim") is { Length: > 0 } named && named != claimId) throw new Refusal($"{probe} is a probe of {claimId}, not of {named}", 2); - var (rows, uncovered) = StageRows(l, probe, claimId, a.Flag("full"), a.Get("max"), a.Get("seed")); - l.Ctx.Out.WriteLine($"rows/{probe}.json: {rows} rows for claim {claimId} over its pairs{(a.Flag("full") ? "" : " its other probes have not covered")}" + - $"{(uncovered > 0 ? $", {uncovered} left uncovered" : "")}; staged in {l.StagingDir}, the file written whole. " + - "`prober publish` moves it into the corpus"); - return 0; - } - - /// - /// Writes a probe's rows file in the staging, whole, never appended: a covering array over its claim's pairs (all of - /// them with ) less those the claim's other probes have credited. The probe's own credit is - /// not subtracted, since it came from the rows this file replaces and ends with them. The stored rows are - /// definitions; the evidence is in the state. - /// - internal static (int Rows, int Uncovered) StageRows(Ledger l, string probe, string claimId, bool full, string? max = null, string? seed = null) - { - var covering = CoveringRows(l, claimId, full, max, seed, except: probe); - var rows = new JsonArray([.. (covering["rows"] as JsonArray ?? []).Select(r => (JsonNode)new JsonObject { ["row"] = r?["row"]?.DeepClone() })]); - Files.WriteJson(Path.Combine(l.StagingDir, "rows", probe + ".json"), new JsonObject { ["claim"] = claimId, ["rows"] = rows }); - return (rows.Count, (int)Files.Num(covering["uncovered_after"])); - } - - /// - /// What `rows` prints: a pairwise covering array over the claim's pairs not yet covered (all of them with - /// ), each row with the pairs it covers. Inside a capped round the claim counts toward - /// the round's cap, and past the deadline or the cap this refuses. - /// - public static JsonObject CoveringRows(Ledger l, string claimId, bool full, string? max = null, string? seed = null, string? except = null) - { - var universe = Universe.Load(l.P("universe.json")) ?? throw new Refusal("no universe.json for this branch yet; run `universe` first"); - if (!universe.ById.TryGetValue(claimId, out var claim)) throw new Refusal($"no claim {claimId} in the universe"); - if (!full && ActiveRound(l) is { } round) - { - if (RoundLock.Now() > Files.Num(round["deadline"])) - throw new Refusal("the round's time is spent; start no new work. Record what ran, hand the corpus off, and report."); - var started = round["claims"] as JsonArray ?? []; - if (!Files.Strings(started).Contains(claimId)) - { - if (round["max_claims"] is JsonValue m && m.TryGetValue(out var cap) && started.Count >= cap) - throw new Refusal($"the round's cap of {cap} claim(s) is reached ({string.Join(", ", Files.Strings(started))}); finish those and report."); - started.Add(claimId); - round["claims"] = started.DeepClone(); - Files.WriteJson(l.P("round.json"), round); - } - } - var covered = full ? [] : l.Covered(universe, claimId, except); - // The pairs some obligation wants: a pair declared inapplicable for every obligation is asked for by no row. - var todo = claim.PairsWanted().Where(p => !covered.Contains((claimId, p))).ToHashSet(); - var rows = Covering.Rows(claim, todo, int.Parse(max ?? "2000", CultureInfo.InvariantCulture), int.Parse(seed ?? "1", CultureInfo.InvariantCulture)); - return new JsonObject - { - ["claim"] = claimId, - ["rows"] = new JsonArray([.. rows.Select(r => (JsonNode)new JsonObject - { - ["row"] = new JsonObject(r.Row.Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)kv.Value))), - ["pairs"] = new JsonArray([.. r.Pairs.Select(p => (JsonNode)new JsonArray(p.A, p.B))]), - })]), - ["uncovered_after"] = todo.Count - rows.Sum(r => r.Pairs.Count), - }; - } - - public static int UniverseCommand(Ledger l, Args a) - { - var claims = a.Get("claims") is { } cp ? Ledger.ReadClaims(cp, cp) : null; - var generated = a.Get("generated") is { } gp ? Files.ReadJson(gp) as JsonObject : null; - var (universe, problems) = Axes.Build(l, a.Get("base") ?? "origin/main", claims, generated); - var total = 0; - foreach (var c in universe.Claims) - { - var n = c.Pairs().Count; total += n; - l.Ctx.Out.WriteLine($"{c.Id}: {c.Axes.Count} axes, {n} pairs"); - // What the claim leaves out and why: a narrowing's quote is checked against its document, an exclusion's reason by a reviewer only. - foreach (var (axis, quote) in c.Narrowed) l.Ctx.Out.WriteLine($" narrow {axis}: \"{quote}\""); - foreach (var e in c.Exclusions) - l.Ctx.Out.WriteLine($" exclude {e.Pairs.Count} pair(s), first {e.Pairs[0]}: {e.Reason}"); - if (Inapplicable(c) is { } line) l.Ctx.Out.WriteLine($" {line}"); - } - l.Ctx.Out.WriteLine($"universe: {universe.Claims.Count} claims, {total} pairs"); - foreach (var p in problems) l.Ctx.Out.WriteLine($" {p}"); - return 0; - } - - /// - /// The pairs of a claim no obligation wants, as one line: their count, the first, and the citations of the - /// declarations that leave them out; null where there are none. - /// - private static string? Inapplicable(Claim c) - { - var dead = c.DeclaredInapplicable(); - if (dead.Count == 0) return null; - var first = dead.Keys.OrderBy(p => p.A, StringComparer.Ordinal).ThenBy(p => p.B, StringComparer.Ordinal).First(); - var cites = new SortedSet(dead.Values.SelectMany(x => x), StringComparer.Ordinal); - return $"inapplicable {dead.Count} pair(s), wanted by no obligation, first {first}: {string.Join(", ", cites)}"; - } - - public static JsonObject ComputeStatus(Ledger l) - { - var universe = Universe.Load(l.P("universe.json")); - if (universe is null) return new JsonObject { ["branch"] = l.Branch, ["done"] = false, ["reason"] = "no universe.json: run `universe` (no round has run here)" }; - var credits = l.Credits(universe); - var done = credits.Select(x => (x.Claim, x.Pair)).ToHashSet(); - // How the rows and the checks of each claim's probes ended at their last replay: only a passing check earns - // pairs, so the rest is what the claim's coverage loses, and a skip for a finding is an open finding the corpus - // records. A row without checks counts as its one check. - var outcomes = new Dictionary>(StringComparer.Ordinal); - var checkOutcomes = new Dictionary>(StringComparer.Ordinal); - var passed = new Dictionary>(StringComparer.Ordinal); - var skips = new SortedDictionary(StringComparer.Ordinal); - foreach (var (probe, entry) in l.Registry()) - { - var evidence = Files.ReadJson(l.EvidencePath(probe)) as JsonObject; - var claim = Files.Str(entry["claim"]); - var sum = outcomes.TryGetValue(claim, out var o) ? o : outcomes[claim] = new(StringComparer.Ordinal); - foreach (var (category, n) in Ledger.Outcomes(evidence)) sum[category] = sum.GetValueOrDefault(category) + n; - var checkSum = checkOutcomes.TryGetValue(claim, out var co) ? co : checkOutcomes[claim] = new(StringComparer.Ordinal); - if (evidence is not null) foreach (var (category, n) in Ledger.CheckOutcomes(evidence)) checkSum[category] = checkSum.GetValueOrDefault(category) + n; - (passed.TryGetValue(claim, out var p) ? p : passed[claim] = new(StringComparer.Ordinal)).UnionWith(Ledger.PassedObligations(evidence)); - foreach (var (finding, n) in Ledger.FindingSkips(evidence)) skips[finding] = skips.GetValueOrDefault(finding) + n; - } - // Each claim's obligations (decision 104), the probes registered for the claim, and the probes whose seeds - // break each obligation: the claim's promise is checked only where a probe can be shown to fail on it. - var listed = l.Claims().ToDictionary(c => Files.Str(c["id"]), Claim.ObligationIds, StringComparer.Ordinal); - var registry = l.Registry(); - var claims = new JsonObject(); var total = 0; var current = 0; var inapplicableTotal = 0; var wantedTotal = 0; var wantedCurrent = 0; - foreach (var c in universe.Claims) - { - var pairs = c.Pairs(); var hit = pairs.Count(p => done.Contains((c.Id, p))); - // The pairs no obligation wants are declared inapplicable: left out of what is still to cover, as the gate leaves them out. - var wanted = c.PairsWanted(); var inapplicable = pairs.Count - wanted.Count; - inapplicableTotal += inapplicable; wantedTotal += wanted.Count; wantedCurrent += wanted.Count(p => done.Contains((c.Id, p))); - var probes = registry.Where(kv => Files.Str(kv.Value["claim"]) == c.Id).ToList(); - var ids = listed.GetValueOrDefault(c.Id) ?? [Claim.ImplicitObligation]; - var obligations = new JsonArray(); - foreach (var id in ids) - { - var seededBy = probes.Where(kv => Seeds.SeedsOf(kv.Value).Any(s => (s.Obligation ?? (ids is [Claim.ImplicitObligation] ? Claim.ImplicitObligation : null)) == id)) - .Select(kv => (JsonNode)kv.Key).ToList(); - // current: the claim's pairs credited by current checks naming this obligation; passed: whether any check - // named it and passed at the last replay, current or not (status's "K uncredited" counts those that did not). - obligations.Add(new JsonObject - { - ["id"] = id, ["seeded_by"] = new JsonArray([.. seededBy]), - ["current"] = pairs.Count(p => credits.Contains((c.Id, id, p))), - ["passed"] = passed.GetValueOrDefault(c.Id)?.Contains(id) ?? false, - }); - } - claims[c.Id] = new JsonObject - { - ["pairs"] = pairs.Count, ["current"] = hit, ["inapplicable_pairs"] = inapplicable, - ["outcomes"] = new JsonObject((outcomes.GetValueOrDefault(c.Id) ?? []).Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)kv.Value))), - ["check_outcomes"] = new JsonObject((checkOutcomes.GetValueOrDefault(c.Id) ?? []).Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)kv.Value))), - ["probes"] = new JsonArray([.. probes.Select(kv => (JsonNode)kv.Key)]), ["obligations"] = obligations, - }; - total += pairs.Count; current += hit; - } - var open = l.OpenFindings(); - return new JsonObject - { - ["branch"] = l.Branch, ["head"] = l.Head, ["universe_commit"] = universe.Commit, ["pairs"] = total, ["current"] = current, - ["inapplicable_pairs"] = inapplicableTotal, - ["share"] = total > 0 ? Math.Round((double)current / total, 4) : 1.0, - ["open_findings"] = new JsonArray([.. open.Select(f => (JsonNode)f)]), - ["skipped_for_findings"] = new JsonObject(skips.Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)kv.Value))), ["claims"] = claims, - ["done"] = total > 0 && wantedCurrent == wantedTotal && open.Count == 0, - }; - } - - public static int Status(Ledger l, Args a) - { - var s = ComputeStatus(l); - if (a.Flag("json")) { l.Ctx.Out.WriteLine(s.ToJsonString(Pretty)); return 0; } - if (s["reason"] is { } reason) { l.Ctx.Out.WriteLine($"{l.Branch}: not done ({Files.Str(reason)})"); return 0; } - var open = Files.Strings(s["open_findings"]).ToList(); - l.Ctx.Out.WriteLine($"{l.Branch} at {Files.Str(s["head"])[..7]}: {Files.Num(s["current"])} of {Files.Num(s["pairs"])} pairs current " + - $"({(Files.Num(s["share"]) * 100).ToString("F1", CultureInfo.InvariantCulture)}%), {open.Count} open finding(s)" + - (open.Count > 0 ? ": " + string.Join(", ", open) : "") + $" -> {(s["done"]!.GetValue() ? "DONE" : "not done")}"); - foreach (var (finding, rows) in (s["skipped_for_findings"] as JsonObject)!) - l.Ctx.Out.WriteLine($" {rows} row(s) skip for finding {finding}"); - foreach (var (id, c) in (s["claims"] as JsonObject)!.OrderBy(kv => kv.Key, StringComparer.Ordinal)) - { - var rows = (c!["outcomes"] as JsonObject)!.Select(kv => $"{kv.Key} {Files.Num(kv.Value)}").ToList(); - var checks = (c["check_outcomes"] as JsonObject ?? []).Select(kv => $"{kv.Key} {Files.Num(kv.Value)}").ToList(); - var obligations = (c["obligations"] as JsonArray ?? []).OfType().ToList(); - var unseeded = obligations.Where(o => (o["seeded_by"] as JsonArray ?? []).Count == 0).Select(o => Files.Str(o["id"])).ToList(); - // K uncredited: the obligations no passing check named at the last replay. - var uncredited = obligations.Count(o => o["passed"] is not JsonValue v || !v.GetValue()); - var promise = obligations.Count == 0 ? "" : $" obligations {obligations.Count}" + (unseeded.Count > 0 ? $" ({unseeded.Count} without a seed: {string.Join(", ", unseeded)})" : ", each seeded") - + $", {uncredited} uncredited"; - var inapplicable = Files.Num(c["inapplicable_pairs"]) is var n and > 0 ? $", {n} inapplicable" : ""; - l.Ctx.Out.WriteLine($" {id}: {Files.Num(c["current"])}/{Files.Num(c["pairs"])}{inapplicable}" + (rows.Count > 0 ? $" (rows: {string.Join(", ", rows)}; checks: {string.Join(", ", checks)})" : "") + promise); - } - return 0; - } - - /// - /// What the corpus leaves out, each with its recorded reason, from the tracked corpus and the generated axes - /// alone: the settings no surface reaches and nothing ignores, the settings without values, the declarations - /// the generators refuse, the values the frameworks refuse on their own, what the claims say they do not - /// promise (their not-promised entries and narrowings), and the surfaces the axes file declares out of reach. - /// A report for the release checklist and a feature-research round; it decides nothing (decision 104). - /// - public static int Gaps(Ledger l, Args a) - { - var (universe, problems) = Axes.Build(l, a.Get("base") ?? "origin/main"); - var block = l.AxesBlock(); - var generated = Files.ReadJson(Path.Combine(l.Memory, "axes-generated.json")) as JsonObject ?? []; - var versions = string.Join(", ", (generated["versions"] as JsonObject ?? []).Select(kv => $"{kv.Key} {Files.Str(kv.Value)}")); - l.Ctx.Out.WriteLine($"prober gaps: {l.Branch} at {l.Head[..7]}: what the corpus leaves out, each with its recorded reason; a report, no decision"); - Section("settings no surface reaches and nothing ignores" + (versions.Length > 0 ? $" (enumerated at {versions})" : ""), - problems.Where(p => p.StartsWith("unassigned: ", StringComparison.Ordinal)).Select(p => p["unassigned: ".Length..])); - Section("settings without values", problems.Where(p => p.StartsWith("needs values: ", StringComparison.Ordinal)).Select(p => p["needs values: ".Length..])); - Section("declarations the generators refuse (shape.refused-kind)", Files.Strings(block["axes"]?["shape.refused-kind"])); - Section("values the frameworks refuse on their own", - (block["refused"] as JsonObject ?? []).SelectMany(axis => (axis.Value as JsonObject ?? []).Select(v => $"{axis.Key}={v.Key}: {Files.Str(v.Value)}"))); - Section("not promised, by what it concerns", - universe.Claims.SelectMany(c => c.NotPromised.Select(n => (Key: Concerns(n.Axes), Line: $"{Concerns(n.Axes)}: {c.Id}/{n.Id} \"{n.Sentence}\" ({n.Source})")) - .Concat(c.Narrowed.Select(kv => (Key: kv.Key, Line: $"{kv.Key}: {c.Id} narrows it, \"{kv.Value}\" ({c.Source})")))) - .OrderBy(x => x.Key, StringComparer.Ordinal).ThenBy(x => x.Line, StringComparer.Ordinal).Select(x => x.Line)); - Section("pairs no obligation wants (declared inapplicable)", - universe.Claims.Select(c => Inapplicable(c) is { } line ? $"{c.Id}: {line}" : null).OfType()); - Section("pairs no passing check read both axes of (the last replay's evidence)", Unread(l, universe)); - Section("out of reach", (block["out-of-reach"] as JsonObject ?? []).Select(kv => $"{kv.Key}: {Files.Str(kv.Value)}")); - return 0; - - void Section(string title, IEnumerable items) - { - var list = items.ToList(); - l.Ctx.Out.WriteLine($"{title}: {(list.Count == 0 ? "none" : list.Count.ToString(CultureInfo.InvariantCulture))}"); - foreach (var item in list) l.Ctx.Out.WriteLine($" {item}"); - } - - static string Concerns(IReadOnlyDictionary> axes) => - axes.Count == 0 ? "the whole claim" : string.Join(" ", axes.Select(kv => $"{kv.Key}={string.Join("|", kv.Value)}")); - } - - /// - /// The wanted pairs of each claim with evidence that no passing check could earn: no obligation that wants the pair - /// has a passing check, in its probes' last replay, that read both of the pair's axes. More rows cannot cover such a - /// pair; a check that reads both axes, an exclusion or a declaration can (#54's four C11 pairs, wanted by obligations - /// whose checks never read shape.wrapped). A claim whose probes have no evidence is left out. - /// - private static IEnumerable Unread(Ledger l, Universe universe) - { - var registry = l.Registry(); - foreach (var claim in universe.Claims.OrderBy(c => c.Id, StringComparer.Ordinal)) - { - // The axes each obligation's passing checks read together, as "a\nb" with a before b. - var read = new Dictionary>(StringComparer.Ordinal); - var any = false; - foreach (var (probe, entry) in registry) - { - if (Files.Str(entry["claim"]) != claim.Id || Files.ReadJson(l.EvidencePath(probe)) is not JsonObject evidence) continue; - any = true; - foreach (var (_, row) in evidence["rows"] as JsonObject ?? []) - if (row is JsonObject r) - foreach (var check in Ledger.ChecksOf(r).Where(c => c.Category == "pass")) - { - if (!read.TryGetValue(check.Obligation, out var axes)) read[check.Obligation] = axes = new(StringComparer.Ordinal); - var names = check.Applied.Keys.Order(StringComparer.Ordinal).ToList(); - for (var i = 0; i < names.Count; i++) - for (var j = i + 1; j < names.Count; j++) axes.Add(names[i] + "\n" + names[j]); - } - } - if (!any) continue; - var wantedBy = claim.ObligationIdsBuilt.Concat(read.Keys).Distinct(StringComparer.Ordinal).ToDictionary(o => o, claim.PairsWantedBy, StringComparer.Ordinal); - foreach (var pair in claim.PairsWanted().OrderBy(p => p.A, StringComparer.Ordinal).ThenBy(p => p.B, StringComparer.Ordinal)) - { - var (a, b) = (Axis(pair.A), Axis(pair.B)); - var both = string.CompareOrdinal(a, b) <= 0 ? a + "\n" + b : b + "\n" + a; - if (!wantedBy.Any(o => o.Value.Contains(pair) && read.TryGetValue(o.Key, out var axes) && axes.Contains(both))) - yield return $"{claim.Id}: {pair.A} {pair.B}"; - } - } - - static string Axis(string setting) => setting[..setting.IndexOf('=', StringComparison.Ordinal)]; - } - - public static int Gate(Ledger l, Args a) => ReleaseGate.Run(l, a.Get("since"), a.Flag("json")); - - public static int Footprint(Ledger l, Args a) - { - var worktree = Path.GetFullPath(a.Need("root")); - var include = a.All("include") is { Count: > 0 } inc ? inc : ["src"]; - var hit = new SortedSet(StringComparer.Ordinal); - foreach (var report in a.All("cobertura")) - { - var doc = XDocument.Load(report); - var sources = doc.Descendants("source").Select(s => Path.GetFullPath(s.Value)).ToList(); - foreach (var cls in doc.Descendants("class")) - { - if (!cls.Descendants("line").Any(line => int.TryParse((string?)line.Attribute("hits"), out var h) && h > 0)) continue; - var name = (string?)cls.Attribute("filename") ?? ""; - var candidates = Path.IsPathRooted(name) ? [name] : sources.Count > 0 ? sources.Select(s => Path.Combine(s, name)).ToList() : [name]; - foreach (var candidate in candidates) - { - var full = Path.GetFullPath(candidate); - if (!full.StartsWith(worktree + Path.DirectorySeparatorChar, StringComparison.Ordinal)) continue; - var rel = Path.GetRelativePath(worktree, full).Replace('\\', '/'); - if (include.Any(p => rel == p || rel.StartsWith(p.TrimEnd('/') + "/", StringComparison.Ordinal))) hit.Add(rel); - break; - } - } - } - // A probe whose own process runs nothing under the include paths (one that builds a consumer in a child process, as C22 - // does) is recorded by what its registry entry watches; with nothing watched either, the footprint would never go stale. - var watchedPaths = l.Watched(a.Need("probe")); - if (hit.Count == 0 && watchedPaths.Count == 0) - throw new Refusal("the coverage report shows no executed file under " + string.Join(", ", include) + - " and the registry entry watches nothing; refusing to record an empty footprint (it would never go stale): name what the probe depends on in its entry's watched"); - var rev = Git.Out(worktree, "rev-parse", "HEAD"); - var footprint = new JsonObject(hit.Select(p => KeyValuePair.Create(p, (JsonNode?)Git.ObjectId(worktree, p)))); - var compileTime = new JsonObject(a.All("compile-time").Select(p => KeyValuePair.Create(p, (JsonNode?)Git.ObjectId(worktree, p)))); - // What the probe's registry entry watches beyond what ran: recorded with the ids it ran at, so a change stales it. - var watched = new JsonObject(watchedPaths.Select(p => KeyValuePair.Create(p, (JsonNode?)Git.ObjectId(worktree, p)))); - Files.AppendLine(l.P("footprints.jsonl"), new JsonObject - { - ["schemaVersion"] = Files.SchemaVersion, ["probe"] = a.Need("probe"), ["commit"] = rev, ["footprint"] = footprint, - ["compileTime"] = compileTime, ["watched"] = watched, ["toolchain"] = l.Toolchain().DeepClone(), ["date"] = a.Get("date") ?? Today, - }); - l.Ctx.Out.WriteLine($"{a.Need("probe")}: {footprint.Count} src files, {compileTime.Count} compile-time paths" + - $"{(watched.Count > 0 ? $", {watched.Count} watched" : "")} at {rev[..Math.Min(7, rev.Length)]}"); - return 0; - } - - public static int Stale(Ledger l, Args a) - { - var states = l.ProbeStates(a.Flag("include-retired")); - if (a.Flag("json")) - { - l.Ctx.Out.WriteLine(new JsonArray([.. states.Select(s => (JsonNode)new JsonObject - { - ["probe"] = s.Probe, ["claim"] = s.Claim, ["filter"] = s.Filter, ["variants"] = s.Variants.DeepClone(), ["stale"] = s.Stale, - ["retired"] = s.Retired, ["passes"] = s.Passes, ["replay"] = s.Replay, - })]).ToJsonString(Pretty)); - return 0; - } - var todo = states.Where(s => s.Replay).ToList(); - l.Ctx.Out.WriteLine($"{todo.Count} of {states.Count} probes to replay{(a.Flag("include-retired") ? " (retired included)" : "")}"); - foreach (var s in todo) l.Ctx.Out.WriteLine($" {s.Probe} {s.Filter}"); - return 0; - } - - public static int RecordRun(Ledger l, Args a) - { - var probe = a.Need("probe"); var result = a.Need("result"); - if (result is not ("pass" or "fail")) throw new Refusal("--result is pass or fail", 2); - var rev = l.Head; var identity = l.Identity(probe); - // A regression is a probe that passed with this very identity and fails now; after any change to its - // corpus, axes, environment or this tool, a failure is the probe's to triage, not the product's. - var passes = Files.ReadLines(l.P("runs.jsonl")).Where(x => Files.Str(x["probe"]) == probe && Files.Str(x["result"]) == "pass").ToList(); - var passedBefore = passes.Any(x => Files.Str(x["identity"]) == identity); - Files.AppendLine(l.P("runs.jsonl"), new JsonObject - { - ["schemaVersion"] = Files.SchemaVersion, ["probe"] = probe, ["result"] = result, ["commit"] = rev, ["date"] = a.Get("date") ?? Today, - ["seconds"] = a.Get("seconds") is { } s ? double.Parse(s, CultureInfo.InvariantCulture) : null, ["identity"] = identity, - }); - if (a.Get("evidence") is { } evidencePath) - { - var evidence = Files.ReadJson(evidencePath) as JsonObject ?? []; - evidence["schemaVersion"] = Files.EvidenceSchemaVersion; evidence["probe"] = probe; evidence["identity"] = identity; - evidence["commit"] = rev; evidence["result"] = result; evidence["date"] = a.Get("date") ?? Today; - Files.WriteJson(l.EvidencePath(probe), evidence); - } - if (result == "fail" && passes.Count > 0 && !passedBefore) - l.Ctx.Out.WriteLine($"{probe}: fails after a change to its corpus, axes, environment or this tool; triage the probe, not the product"); - if (result == "fail" && passedBefore) - Files.AppendLine(l.P("findings.jsonl"), new JsonObject - { - ["schemaVersion"] = Files.SchemaVersion, ["id"] = $"R-{probe}-{rev[..7]}", ["claim"] = Files.Str(l.Registry().GetValueOrDefault(probe)?["claim"]), - ["status"] = "open", ["commit"] = rev, ["probe"] = probe, ["reason"] = "replay: a probe that passed with this identity fails at this commit", - }); - return 0; - } - - public static int IdentityCommand(Ledger l, Args a) { l.Ctx.Out.WriteLine(l.Identity(a.Need("probe"))); return 0; } - - public static int Register(Ledger l, Args a) - { - // A note or a skip built from text fails at run time after a build and a replay; the staged sources are read first. - SourceLint.Refuse(l, "register"); - var path = Path.Combine(l.StagingDir, "registry.json"); - var staged = Files.ReadJson(path) as JsonObject ?? new JsonObject { ["probes"] = new JsonObject() }; - var probes = staged["probes"] as JsonObject ?? []; - var entry = l.Registry().GetValueOrDefault(a.Need("probe")) ?? []; - entry["claim"] = a.Need("claim"); entry["filter"] = a.Need("filter"); - if (a.Get("variants") is { Length: > 0 } v) entry["variants"] = JsonNode.Parse(v); - // The defect `seeds` applies to prove the probe can fail (decision 100): a patch under the corpus, an - // environment toggle, or both, and what it breaks. - var seed = new JsonObject(); - if (a.Get("seed") is { Length: > 0 } patch) seed["patch"] = patch; - if (a.All("seed-env") is { Count: > 0 } toggles) seed["env"] = SeedEnv(toggles); - if (a.Get("obligation") is { } obligation) - { - // A seed per obligation (decision 104): into `seeds`, in place of any seed that names the same obligation. - if (obligation.Length == 0) throw new Refusal("--obligation needs the id of one of the claim's obligations", 2); - if (seed.Count == 0) throw new Refusal("--obligation names the obligation of a seed: give the seed with --seed PATCH or --seed-env NAME=VALUE", 2); - var claim = l.Claims().FirstOrDefault(c => Files.Str(c["id"]) == a.Need("claim")) - ?? throw new Refusal($"no claim {a.Need("claim")} in claims.json (tracked or staged), so obligation {obligation} cannot be checked"); - var ids = Claim.ObligationIds(claim); - if (!ids.Contains(obligation)) - throw new Refusal($"claim {a.Need("claim")} lists no obligation {obligation} (it lists {string.Join(", ", ids)})", 2); - var named = new JsonObject { ["obligation"] = obligation }; - foreach (var (key, value) in seed) named[key] = value?.DeepClone(); - named["defect"] = a.Need("defect"); - var kept = (entry["seeds"] as JsonArray ?? []).Where(s => Files.Str(s?["obligation"]) != obligation).Select(s => s?.DeepClone()).ToList(); - var replaced = (entry["seeds"] as JsonArray ?? []).Count - kept.Count; - entry["seeds"] = new JsonArray([.. kept, named]); - if (entry["seed"] is JsonObject one && Files.Str(one["obligation"]) == obligation) { entry.Remove("seed"); replaced++; } - l.Ctx.Out.WriteLine($"seed for obligation {obligation} of {a.Need("probe")}" + (replaced > 0 ? $", in place of the {replaced} it had" : "")); - } - else if (seed.Count > 0) { seed["defect"] = a.Need("defect"); entry["seed"] = seed; } - probes[a.Need("probe")] = entry; - staged["probes"] = probes.DeepClone(); - Files.WriteJson(path, staged); - l.Ctx.Out.WriteLine($"registered {a.Need("probe")} (staged; `publish` moves it into the corpus)"); - return 0; - } - - /// A seed's environment toggle from --seed-env NAME=VALUE ...; a toggle without a name or an = is refused. - private static JsonObject SeedEnv(List toggles) - { - var env = new JsonObject(); - foreach (var toggle in toggles) - { - var eq = toggle.IndexOf('=', StringComparison.Ordinal); - if (eq <= 0) throw new Refusal($"--seed-env {toggle}: a toggle is NAME=VALUE", 2); - env[toggle[..eq]] = toggle[(eq + 1)..]; - } - return env; - } - - public static int Publish(Ledger l, Args a) - { - if (!Directory.Exists(l.StagingDir)) { l.Ctx.Out.WriteLine("nothing staged"); return 0; } - SourceLint.Refuse(l, "publish"); - var moved = new List(); - foreach (var src in Directory.EnumerateFiles(l.StagingDir, "*", SearchOption.AllDirectories).Order(StringComparer.Ordinal)) - { - var rel = Path.GetRelativePath(l.StagingDir, src).Replace('\\', '/'); - var dst = Path.Combine(l.CorpusDir, rel); - if (rel == "registry.json") - { - var tracked = Files.ReadJson(dst) as JsonObject ?? new JsonObject { ["probes"] = new JsonObject() }; - var probes = tracked["probes"] as JsonObject ?? []; - foreach (var (name, e) in Files.ReadJson(src)?["probes"] as JsonObject ?? []) probes[name] = e?.DeepClone(); - tracked["probes"] = new JsonObject(probes.OrderBy(kv => kv.Key, StringComparer.Ordinal).Select(kv => KeyValuePair.Create(kv.Key, kv.Value?.DeepClone()))); - Files.WriteJson(dst, tracked); - } - else if (rel == "claims.json") - { - var merged = new SortedDictionary(StringComparer.Ordinal); - foreach (var c in Ledger.ReadClaims(dst, "the corpus's claims.json").Concat(Ledger.ReadClaims(src, "the staged claims.json"))) - merged[Files.Str(c["id"])] = c.DeepClone(); - Files.WriteJson(dst, new JsonArray([.. merged.Values])); - } - else if (rel.StartsWith("rows/", StringComparison.Ordinal) && rel.EndsWith(".json", StringComparison.Ordinal)) - { - // A probe's rows, by probe: the file keeps the claim it names, else its probe's registry entry's. - var probe = Path.GetFileNameWithoutExtension(rel); - var claim = Files.Str(Files.ReadJson(src)?["claim"]) is { Length: > 0 } named ? named - : l.Registry().GetValueOrDefault(probe)?["claim"] is { } registered ? Files.Str(registered) - : throw new Refusal($"the staged {rel} names no claim and {probe} is no registered probe; a rows file is a probe's, its claim inside"); - Files.WriteRowsGzipped(dst + ".gz", claim, Files.ReadRows(src).OfType().Select(r => r["row"] as JsonObject ?? r)); - rel += ".gz"; - } - else if (rel.EndsWith(".jsonl", StringComparison.Ordinal)) - { - Directory.CreateDirectory(Path.GetDirectoryName(dst)!); - Files.WriteAtomically(dst, (File.Exists(dst) ? SharedFile.ReadAllText(dst) : "") + SharedFile.ReadAllText(src)); - } - else - { - Directory.CreateDirectory(Path.GetDirectoryName(dst)!); - File.Copy(src, dst, overwrite: true); - } - moved.Add(rel); - } - Directory.Delete(l.StagingDir, recursive: true); - l.Ctx.Out.WriteLine($"published {moved.Count} file(s) into {Path.GetRelativePath(l.Top, l.CorpusDir).Replace('\\', '/')}:"); - foreach (var rel in moved) l.Ctx.Out.WriteLine($" {rel}"); - return 0; - } - - public static int Mark(Ledger l, Args a) - { - if (a.Positional.FirstOrDefault() != "retired") throw new Refusal("mark takes `retired`", 2); - var probe = a.Need("probe"); - if (!l.Registry().TryGetValue(probe, out var entry)) throw new Refusal($"no probe {probe} in the registry"); - var path = Path.Combine(l.StagingDir, "registry.json"); - var staged = Files.ReadJson(path) as JsonObject ?? new JsonObject { ["probes"] = new JsonObject() }; - entry["retired"] = true; - (staged["probes"] as JsonObject)![probe] = entry; - Files.WriteJson(path, staged); - l.Ctx.Out.WriteLine($"{probe} retired (staged)"); - return 0; - } - - public static int Dismiss(Ledger l, Args a) - { - Files.AppendLine(Path.Combine(l.StagingDir, "dismissed.jsonl"), - new JsonObject { ["schemaVersion"] = Files.SchemaVersion, ["id"] = a.Need("id"), ["reason"] = a.Need("reason"), ["date"] = a.Get("date") ?? Today }); - l.Ctx.Out.WriteLine($"{a.Need("id")} dismissed (staged; `publish` puts it in the corpus)"); - return 0; - } - - /// - /// Records an open finding fixed at a commit: the fixed line of findings.jsonl (finding-line.schema.json), with the - /// open line's claim and probe and the commit's full id. It refuses an id this ledger does not hold open and a - /// commit the repository does not have; a commit outside HEAD's history is recorded and counts once it is in it. - /// - public static int Fix(Ledger l, Args a) - { - var id = a.Positional is [var only] ? only : throw new Refusal("fix takes one finding id: fix F --commit ", 2); - var given = a.Need("commit"); - var resolved = Git.Run(l.Top, "rev-parse", "-q", "--verify", given + "^{commit}"); - if (resolved.ExitCode != 0) throw new Refusal($"--commit {given} is no commit of this repository"); - var commit = resolved.Stdout.Trim(); - if (!l.Findings().TryGetValue(id, out var open)) throw new Refusal($"{id} is no finding of this ledger"); - if (Files.Str(open["status"]) is var status and not "open") throw new Refusal($"{id} is {status}, not open"); - var line = new JsonObject - { - ["schemaVersion"] = Files.SchemaVersion, ["id"] = id, ["claim"] = open["claim"]?.DeepClone(), ["status"] = "fixed", ["commit"] = commit, - }; - if (Files.Str(open["probe"]) is { Length: > 0 } probe) line["probe"] = probe; - if (a.Get("reason") is { Length: > 0 } reason) line["reason"] = reason; - Files.AppendLine(l.P("findings.jsonl"), line); - var inHistory = Git.Run(l.Top, "merge-base", "--is-ancestor", commit, "HEAD").ExitCode == 0; - l.Ctx.Out.WriteLine($"{id} fixed at {commit[..7]}" + (inHistory ? "" : "; it counts once that commit is in HEAD's history")); - return 0; - } - - public static int Lock(Ledger l, Args a) - { - var holder = a.Need("holder"); var mode = a.Need("mode"); - if (mode is not ("replay" or "explore" or "full")) throw new Refusal("--mode is replay, explore or full", 2); - var pid = a.Get("pid") is { Length: > 0 } p ? long.Parse(p, CultureInfo.InvariantCulture) : (long?)null; - var (taken, previous) = RoundLock.Take(l, holder, mode, pid); - if (!taken) - { - var since = DateTimeOffset.FromUnixTimeMilliseconds((long)(Files.Num(previous!["since"]) * 1000)).ToLocalTime().ToString("HH:mm", CultureInfo.InvariantCulture); - l.Ctx.Error.WriteLine($"prober: locked by {Files.Str(previous["holder"])} ({Files.Str(previous["mode"])}) since {since}"); - return 1; - } - var over = previous is not null && Files.Str(previous["holder"]) != holder ? $" (took over from {Files.Str(previous["holder"])}, whose heartbeat stopped)" : ""; - l.Ctx.Out.WriteLine($"locked for {mode} by {holder}{over}"); - return 0; - } - - public static int Unlock(Ledger l, Args a) - { - var owed = RoundLock.Release(l, a.Need("holder")); - l.Ctx.Out.WriteLine(owed ? "unlocked; a replay was skipped while locked: run `prober replay` now" : "unlocked"); - return 0; - } - - public static int HeartbeatCommand(Ledger l, Args a) { RoundLock.Beat(l, a.Need("holder")); return 0; } - - public static int OweReplay(Ledger l, Args a) { RoundLock.OweReplay(l); return 0; } - - public static JsonObject? ActiveRound(Ledger l) - { - var round = Files.ReadJson(l.P("round.json")) as JsonObject; - var held = RoundLock.Holder(l); - return round is not null && held is not null && Files.Str(held["holder"]) == Files.Str(round["holder"]) ? round : null; - } - - public static int RoundStart(Ledger l, Args a) - { - var holder = a.Need("holder"); - if (RoundLock.Holder(l) is not { } held || Files.Str(held["holder"]) != holder) - throw new Refusal("take the lock first; round-start records the caps of the lock's holder"); - var minutes = double.Parse(a.Need("minutes"), CultureInfo.InvariantCulture); - var claims = a.Get("claims") is { Length: > 0 } c ? int.Parse(c, CultureInfo.InvariantCulture) : (int?)null; - Files.WriteJson(l.P("round.json"), new JsonObject - { - ["schemaVersion"] = Files.SchemaVersion, ["holder"] = holder, ["started"] = RoundLock.Now(), - ["deadline"] = RoundLock.Now() + minutes * 60, ["max_claims"] = claims, ["claims"] = new JsonArray(), - }); - l.Ctx.Out.WriteLine($"round capped at {minutes.ToString(CultureInfo.InvariantCulture)} min" + (claims is { } n ? $" and {n} claim(s)" : "")); - return 0; - } - - public static int RoundEnd(Ledger l, Args a) - { - if (Files.ReadJson(l.P("round.json")) is JsonObject round && Files.Str(round["holder"]) == a.Need("holder")) - { - File.Delete(l.P("round.json")); - l.Ctx.Out.WriteLine($"round ended: claims {(Files.Strings(round["claims"]).Any() ? string.Join(", ", Files.Strings(round["claims"])) : "none")}"); - } - return 0; - } - - public static int Repair(Ledger l, Args a) - { - var fixedCount = 0; - if (Directory.Exists(l.Dir)) - foreach (var path in Directory.EnumerateFiles(l.Dir, "*.jsonl", SearchOption.AllDirectories)) - { - var data = File.ReadAllText(path); - if (data.Length > 0 && !data.EndsWith('\n')) - { - Files.WriteAtomically(path, data[..(data.LastIndexOf('\n') + 1)]); - fixedCount++; - l.Ctx.Out.WriteLine($" removed a partial last line from {Path.GetRelativePath(l.Dir, path)}"); - } - } - l.Ctx.Out.WriteLine($"repair: {fixedCount} file(s) fixed"); - return 0; - } - - public static int Abandon(Ledger l, Args a) - { - Repair(l, a); - if (Files.ReadJson(l.P("round.json")) is JsonObject round) - { - File.Delete(l.P("round.json")); - l.Ctx.Out.WriteLine($"round caps cleared ({Files.Str(round["holder"])})"); - } - var held = RoundLock.Abandon(l); - l.Ctx.Out.WriteLine(held is null ? "no lock was held" : $"lock of {Files.Str(held["holder"])} ({Files.Str(held["mode"])}) released"); - return 0; - } - - /// - /// Joins the states of a replay split by family (replay --family F, one state directory per leg) into - /// this branch's state: each leg's evidence files are copied in, its run, footprint, finding and ledger lines - /// are added where this state lacks them (a line a leg carried over from a restored state is not added - /// twice), and one replay-last.json sums the legs. It refuses while a probe the registry lists was neither - /// replayed nor skipped by any leg, which is how a matrix that leaves a family out fails instead of passing - /// on what it ran, and it fails (exit 1) when any leg failed, so the job that merges fails as the legs did. - /// - public static int Merge(Ledger l, Args a) - { - var sources = a.All("from").Where(x => x.Length > 0).ToList(); - if (sources.Count == 0) throw new Refusal("--from DIR... is required: the state directories of the legs to join into this one", 2); - var branchDir = Path.GetFileName(l.Dir); - Directory.CreateDirectory(Path.Combine(l.Dir, "evidence")); - var legs = new List(); - var seen = new SortedSet(StringComparer.Ordinal); - foreach (var from in sources) - { - var root = Path.GetFullPath(from); - var dir = Path.Combine(root, "branches", branchDir); - var last = Files.ReadJson(Path.Combine(dir, "replay-last.json")) as JsonObject - ?? throw new Refusal($"{from} holds no replay of {l.Branch}: no branches/{branchDir}/replay-last.json"); - if (legs.Count > 0 && Files.Str(last["commit"]) != Files.Str(legs[0]["commit"])) - throw new Refusal($"{from} replayed {Files.Str(last["commit"])[..7]}, another leg {Files.Str(legs[0]["commit"])[..7]}; the legs of one replay share a commit"); - legs.Add(last); - foreach (var name in new[] { "runs.jsonl", "footprints.jsonl", "findings.jsonl", "ledger.jsonl" }) UnionLines(Path.Combine(dir, name), l.P(name)); - foreach (var sub in new[] { "evidence", "logs" }) - { - var source = Path.Combine(dir, sub); - if (!Directory.Exists(source)) continue; - Directory.CreateDirectory(l.P(sub)); - foreach (var file in Directory.EnumerateFiles(source)) File.Copy(file, l.P(Path.Combine(sub, Path.GetFileName(file))), overwrite: true); - } - foreach (var name in new[] { "universe.json" }) - if (File.Exists(Path.Combine(dir, name)) && !File.Exists(l.P(name))) File.Copy(Path.Combine(dir, name), l.P(name)); - var axes = Path.Combine(root, "axes-generated.json"); - if (File.Exists(axes) && !File.Exists(Path.Combine(l.Memory, "axes-generated.json"))) File.Copy(axes, Path.Combine(l.Memory, "axes-generated.json")); - foreach (var probe in Files.Strings(last["selected"]).Concat(Files.Strings(last["skipped"]))) seen.Add(probe); - } - - var all = legs.Any(x => x["all"] is JsonValue v && v.TryGetValue(out var b) && b); - var selected = new SortedSet(legs.SelectMany(x => Files.Strings(x["selected"])), StringComparer.Ordinal); - var skipped = new SortedSet(legs.SelectMany(x => Files.Strings(x["skipped"])).Where(p => !selected.Contains(p)), StringComparer.Ordinal); - var failed = new SortedSet(legs.SelectMany(x => Files.Strings(x["failed"])), StringComparer.Ordinal); - static JsonArray Names(IEnumerable names) => new([.. names.Select(x => (JsonNode)x)]); - Files.WriteJson(l.P("replay-last.json"), new JsonObject - { - ["branch"] = l.Branch, ["commit"] = Files.Str(legs[0]["commit"]), ["replayed"] = selected.Count, ["failed"] = Names(failed), - ["seconds"] = Math.Round(legs.Max(x => Files.Num(x["seconds"])), 1), ["build_seconds"] = Math.Round(legs.Max(x => Files.Num(x["build_seconds"])), 1), - ["all"] = all, ["legs"] = Names(legs.Select(x => Files.Str(x["family"], "all"))), ["footprint"] = legs.All(x => x["footprint"] is not JsonValue f || !f.TryGetValue(out var ft) || ft), - ["selected"] = Names(selected), ["skipped"] = Names(skipped), - }); - l.Ctx.Out.WriteLine($"prober merge: {legs.Count} leg(s) into {l.Dir}: {selected.Count} replayed, {failed.Count} failed, {skipped.Count} current"); - - var missing = l.Registry().Where(kv => all || kv.Value["retired"] is not JsonValue r || !r.TryGetValue(out var retired) || !retired) - .Select(kv => kv.Key).Where(p => !seen.Contains(p)).ToList(); - if (missing.Count > 0) - throw new Refusal($"no leg replayed or skipped {string.Join(", ", missing)}: every probe's family is a leg of the matrix, or the merged state is short"); - if (failed.Count > 0) { l.Ctx.Out.WriteLine($" failed: {string.Join(", ", failed)}"); return 1; } - return 0; - } - - /// Adds to every line of it does not already hold, in order. - private static void UnionLines(string source, string target) - { - if (!File.Exists(source)) return; - var have = File.Exists(target) ? SharedFile.ReadAllLines(target).Where(x => x.Trim().Length > 0).ToList() : []; - var known = new HashSet(have, StringComparer.Ordinal); - var added = SharedFile.ReadAllLines(source).Where(x => x.Trim().Length > 0 && known.Add(x)).ToList(); - if (added.Count == 0 && File.Exists(target)) return; - Files.WriteAtomically(target, string.Join('\n', have.Concat(added)) + (have.Count + added.Count > 0 ? "\n" : "")); - } -} diff --git a/tools/Prober/Context.cs b/tools/Prober/Context.cs deleted file mode 100644 index 605654a5..00000000 --- a/tools/Prober/Context.cs +++ /dev/null @@ -1,80 +0,0 @@ -using System.Diagnostics; -using System.Text; - -namespace Prober; - -/// -/// Everything a command reads from its surroundings: the directory it runs in, the environment and where -/// it writes. Commands never touch the process's own current directory or environment, so tests run -/// them in-process, side by side, each in a checkout of its own. -/// -internal sealed class Context(string workingDirectory, IReadOnlyDictionary environment, TextWriter output, TextWriter error) -{ - public string WorkingDirectory { get; } = workingDirectory; - public TextWriter Out { get; } = output; - public TextWriter Error { get; } = error; - - public string? Env(string name) => environment.TryGetValue(name, out var value) && !string.IsNullOrEmpty(value) ? value : null; - - public static Context FromProcess() => - new(Directory.GetCurrentDirectory(), - System.Environment.GetEnvironmentVariables().Cast() - .ToDictionary(e => (string)e.Key, e => (string?)e.Value, StringComparer.Ordinal), - Console.Out, Console.Error); - - public IReadOnlyDictionary Environment => environment; -} - -/// A child process's result. -internal sealed record Ran(int ExitCode, string Stdout, string Stderr); - -internal static class Proc -{ - /// - /// Runs a program to completion, capturing its output. Never through a shell. `dotnet` is the one that - /// satisfies the directory's global.json, the installer's when the one on PATH cannot (Dotnet). - /// - public static Ran Run(string fileName, IEnumerable arguments, string workingDirectory, - IReadOnlyDictionary? environment = null, TimeSpan? timeout = null) - { - string? dotnetDirectory = null; - if (fileName == "dotnet") (fileName, dotnetDirectory) = Dotnet.For(workingDirectory); - var start = new ProcessStartInfo(fileName) - { - WorkingDirectory = workingDirectory, RedirectStandardOutput = true, RedirectStandardError = true, UseShellExecute = false, - StandardOutputEncoding = Encoding.UTF8, StandardErrorEncoding = Encoding.UTF8, - }; - foreach (var argument in arguments) start.ArgumentList.Add(argument); - if (environment is not null) - foreach (var (name, value) in environment) - if (value is null) start.Environment.Remove(name); else start.Environment[name] = value; - Dotnet.Apply(dotnetDirectory, start.Environment); - - using var process = Process.Start(start) ?? throw new InvalidOperationException($"could not start {fileName}"); - var stdout = process.StandardOutput.ReadToEndAsync(); - var stderr = process.StandardError.ReadToEndAsync(); - if (timeout is { } limit && !process.WaitForExit(limit)) - { - // The whole tree: a test host or a headless agent starts children of its own. Portable, no signals. - try { process.Kill(entireProcessTree: true); } catch (InvalidOperationException) { } - process.WaitForExit(); - return new Ran(-1, stdout.Result, stderr.Result + "\n(stopped at the time limit)"); - } - process.WaitForExit(); - return new Ran(process.ExitCode, stdout.Result, stderr.Result); - } -} - -internal static class Git -{ - public static Ran Run(string workingDirectory, params string[] arguments) => Proc.Run("git", arguments, workingDirectory); - - public static string Out(string workingDirectory, params string[] arguments) => Run(workingDirectory, arguments).Stdout.Trim(); - - /// The object id of a file (blob) or a directory (tree) at a revision, or null when it does not exist there. - public static string? ObjectId(string workingDirectory, string path, string revision = "HEAD") - { - var ran = Run(workingDirectory, "rev-parse", "-q", "--verify", $"{revision}:{path}"); - return ran.ExitCode == 0 ? ran.Stdout.Trim() : null; - } -} diff --git a/tools/Prober/Covering.cs b/tools/Prober/Covering.cs deleted file mode 100644 index d4cfd13c..00000000 --- a/tools/Prober/Covering.cs +++ /dev/null @@ -1,93 +0,0 @@ -namespace Prober; - -/// -/// A greedy pairwise covering array: each row is the best of a few randomized greedy candidates, and the -/// rows together cover every pair asked for that at least one of the claim's obligations wants. A pair no -/// obligation wants (declared inapplicable) is never asked for, and neither it nor an excluded pair is ever put -/// in a row, not even to fill an axis: every check of such a row would be inapplicable, and an excluded pair is -/// one the claim says no row can run. An axis none of whose values can join a row without one is left out of -/// that row: shape.containing beside shape.position=root, and shape.refused-kind wherever the claim excludes each of -/// its pairs. Deterministic for a seed. -/// -internal static class Covering -{ - public static List<(SortedDictionary Row, List Pairs)> Rows(Claim claim, IReadOnlySet wanted, int max, int seed) - { - var dead = claim.DeclaredInapplicable(); - bool Blocked(Pair pair) => claim.Excluded.Contains(pair) || dead.ContainsKey(pair); - // The pairs asked for, each once however many obligations want it, so a row's gain counts it once. - var todo = new HashSet(wanted); - todo.RemoveWhere(Blocked); - var names = claim.Axes.Keys.ToList(); - var random = new Random(seed); - var rows = new List<(SortedDictionary, List)>(); - while (todo.Count > 0 && rows.Count < max) - { - SortedDictionary? best = null; var bestGain = -1; - for (var attempt = 0; attempt < 30; attempt++) - { - var order = names.OrderBy(_ => random.Next()).ToList(); - var row = new SortedDictionary(StringComparer.Ordinal); - foreach (var name in order) - { - var options = claim.Axes[name].OrderBy(_ => random.Next()).ToList(); - string? pick = null; var pickGain = -1; - foreach (var v in options) - { - var gain = 0; - foreach (var (n, w) in row) - { - var pair = new Pair($"{name}={v}", $"{n}={w}"); - if (Blocked(pair)) { gain = -1; break; } - if (todo.Contains(pair)) gain++; - } - if (gain > pickGain) { pick = v; pickGain = gain; } - } - // Every value of this axis puts a blocked pair in the row: the axis is left out of it. - if (pick is not null) row[name] = pick; - } - var g = Gained(row, todo).Count; - if (g > bestGain) { best = row; bestGain = g; } - } - if (bestGain <= 0) - { - // Seed a row from one uncovered pair so progress never stalls. - best = Seeded(claim, todo.OrderBy(p => p.A, StringComparer.Ordinal).ThenBy(p => p.B, StringComparer.Ordinal).First(), dead); - } - var newly = Gained(best!, todo); - todo.ExceptWith(newly); - rows.Add((best!, newly)); - } - return rows; - } - - static string Axis(string setting) => setting[..setting.IndexOf('=', StringComparison.Ordinal)]; - - /// - /// A row seeded from one pair: the pair's two settings, and each other axis at its first value that puts neither an excluded pair - /// nor a pair no obligation wants () in the row, or left out where none does. - /// - internal static SortedDictionary Seeded(Claim claim, Pair pair, IReadOnlyDictionary> dead) - { - bool Blocked(Pair p) => claim.Excluded.Contains(p) || dead.ContainsKey(p); - var row = new SortedDictionary(StringComparer.Ordinal); - foreach (var item in new[] { pair.A, pair.B }) row[Axis(item)] = item[(item.IndexOf('=', StringComparison.Ordinal) + 1)..]; - foreach (var name in claim.Axes.Keys.Where(n => !row.ContainsKey(n)).ToList()) - if (claim.Axes[name].FirstOrDefault(v => !row.Any(kv => Blocked(new Pair($"{name}={v}", $"{kv.Key}={kv.Value}")))) is { } value) - row[name] = value; - return row; - } - - private static List Gained(SortedDictionary row, HashSet todo) - { - var result = new List(); - var items = row.ToList(); - for (var i = 0; i < items.Count; i++) - for (var j = i + 1; j < items.Count; j++) - { - var pair = new Pair($"{items[i].Key}={items[i].Value}", $"{items[j].Key}={items[j].Value}"); - if (todo.Contains(pair)) result.Add(pair); - } - return result; - } -} diff --git a/tools/Prober/Files.cs b/tools/Prober/Files.cs deleted file mode 100644 index 51f83769..00000000 --- a/tools/Prober/Files.cs +++ /dev/null @@ -1,142 +0,0 @@ -using System.IO.Compression; -using System.Text; -using System.Text.Encodings.Web; -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// Reading and writing the ledger's files. Every write goes to a temp file renamed over the target, so a -/// reader sees the file before or after a change, never half of it, on every OS; reads and the rename go -/// through SharedFile, which Windows needs while another process reads the target. A partial or unreadable -/// line in a JSON-lines file is skipped, never fatal (a writer killed mid-write in an older version). -/// -internal static class Files -{ - /// The schema version this tool writes. A file or line without one is version 1. - public const int SchemaVersion = 1; - - /// - /// The version of evidence/<probe>.json this tool writes: 2 records each row's checks, one entry per check - /// with its own obligation, baseline, outcome and settings. A version-1 file reads as rows without checks, each one - /// check of the obligation claim. - /// - public const int EvidenceSchemaVersion = 2; - - /// - /// Indented, and with only what JSON itself requires escaped: the tracked corpus is read in a diff, where - /// the default encoder's six-character escapes for +, a backtick and < made every publish a - /// rewrite (decision 102). No reader here or in the harness parses these files as - /// anything but JSON, and an identity hashes , not the file's text. - /// - private static readonly JsonSerializerOptions Indented = new() { WriteIndented = true, NewLine = "\n", Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; - - public static JsonNode? ReadJson(string path) - { - if (!File.Exists(path)) return null; - try { return JsonNode.Parse(SharedFile.ReadAllText(path)); } - catch (JsonException) { return null; } // a file another process is creating this instant: not there yet - catch (IOException) { return null; } - catch (UnauthorizedAccessException) { return null; } // Windows: a file being replaced this instant - } - - public static void WriteJson(string path, JsonNode value) => WriteAtomically(path, Format(value)); - - /// The text writes: a tracked file in this form is unchanged by a publish that changes nothing in it. - public static string Format(JsonNode value) => value.ToJsonString(Indented) + "\n"; - - public static List ReadLines(string path) - { - var result = new List(); - if (!File.Exists(path)) return result; - foreach (var line in SharedFile.ReadAllLines(path)) - { - if (line.Trim().Length == 0) continue; - try { if (JsonNode.Parse(line) is JsonObject o) result.Add(o); } - catch (JsonException) { } - } - return result; - } - - /// Appends one line by writing the whole file anew and renaming it over the old one. - public static void AppendLine(string path, JsonObject value) - { - var old = File.Exists(path) ? SharedFile.ReadAllText(path) : ""; - if (old.Length > 0 && !old.EndsWith('\n')) old = old[..(old.LastIndexOf('\n') + 1)]; - WriteAtomically(path, old + value.ToJsonString() + "\n"); - } - - public static void WriteAtomically(string path, string text) - { - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - var tmp = $"{path}.{Environment.ProcessId}.{Guid.NewGuid():N}.tmp"; - File.WriteAllText(tmp, text, new UTF8Encoding(false)); - SharedFile.Replace(tmp, path); - } - - /// A rows file, plain (staging) or gzipped (the corpus). - public static JsonArray ReadRows(string path) - { - if (File.Exists(path)) return ReadJson(path)?["rows"] as JsonArray ?? []; - if (File.Exists(path + ".gz")) - { - using var gz = new GZipStream(SharedFile.OpenRead(path + ".gz"), CompressionMode.Decompress); - using var reader = new StreamReader(gz, Encoding.UTF8); - return JsonNode.Parse(reader.ReadToEnd())?["rows"] as JsonArray ?? []; - } - return []; - } - - public static byte[] Gunzip(string path) - { - using var gz = new GZipStream(SharedFile.OpenRead(path), CompressionMode.Decompress); - using var memory = new MemoryStream(); - gz.CopyTo(memory); - return memory.ToArray(); - } - - /// Writes rows gzipped and reproducibly: one row per line, keys sorted, no timestamp in the header. - public static void WriteRowsGzipped(string path, string claim, IEnumerable rows) - { - var body = new StringBuilder(); - body.Append("{\"claim\": ").Append(JsonSerializer.Serialize(claim)).Append(", \"rows\": [\n"); - body.Append(string.Join(",\n", rows.Select(r => "{\"row\":" + Canonical(r) + "}"))); - body.Append("\n]}\n"); - var tmp = $"{path}.{Environment.ProcessId}.tmp"; - Directory.CreateDirectory(Path.GetDirectoryName(path)!); - using (var file = File.Create(tmp)) - using (var gz = new GZipStream(file, CompressionLevel.SmallestSize)) - gz.Write(Encoding.UTF8.GetBytes(body.ToString())); - SharedFile.Replace(tmp, path); - } - - /// A JSON object with its keys in ordinal order, recursively: the form hashed for an identity. - public static string Canonical(JsonNode? node) => node switch - { - JsonObject o => "{" + string.Join(",", o.OrderBy(kv => kv.Key, StringComparer.Ordinal) - .Select(kv => JsonSerializer.Serialize(kv.Key) + ":" + Canonical(kv.Value))) + "}", - JsonArray a => "[" + string.Join(",", a.Select(Canonical)) + "]", - null => "null", - _ => node.ToJsonString(), - }; - - public static string Str(JsonNode? node, string fallback = "") => node is JsonValue v && v.TryGetValue(out var s) ? s : fallback; - - /// A number, whether parsed from text or built in memory from any numeric type. - public static double Num(JsonNode? node, double fallback = 0) - { - if (node is not JsonValue v) return fallback; - if (v.TryGetValue(out var d)) return d; - if (v.TryGetValue(out var l)) return l; - if (v.TryGetValue(out var i)) return i; - if (v.TryGetValue(out var m)) return (double)m; - return double.TryParse(v.ToJsonString(), System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var p) ? p : fallback; - } - - public static IEnumerable Strings(JsonNode? node) => node is JsonArray a ? a.Select(x => Str(x)) : []; - - public static Dictionary StringMap(JsonNode? node) => - node is JsonObject o ? o.Where(kv => kv.Value is JsonValue).ToDictionary(kv => kv.Key, kv => Str(kv.Value, kv.Value!.ToJsonString()), StringComparer.Ordinal) - : new(StringComparer.Ordinal); -} diff --git a/tools/Prober/Ledger.cs b/tools/Prober/Ledger.cs deleted file mode 100644 index 50673244..00000000 --- a/tools/Prober/Ledger.cs +++ /dev/null @@ -1,431 +0,0 @@ -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// One branch's ledger. Tracked, in the repository: the corpus under tools/probe/corpus (claims, registry, -/// harness and probe sources, rows, waivers, dismissed findings) and the axes file. Local, under the state -/// directory: the universe, evidence, footprints, runs, findings, the round's caps and the lock. A pair is -/// covered only by evidence a replay recorded under an identity that still holds. -/// -internal sealed class Ledger -{ - public Context Ctx { get; } - public string Root { get; } // the main checkout, also from a linked worktree - public string Top { get; } // this checkout - public bool Linked { get; } // a linked worktree (git worktree add), not the main checkout - public string Branch { get; } - public string Memory { get; } // PROBE_STATE_DIR, else the main checkout's prober memory - public string Dir { get; } // the branch's directory under Memory - public string CorpusDir => Path.Combine(Top, "tools", "probe", "corpus"); - public string AxesFile => Path.Combine(Top, ".claude", "probe-axes.md"); - public string StagingDir => Path.Combine(Dir, "staging"); - - public Ledger(Context ctx, string? branch) - { - Ctx = ctx; - var dirs = Git.Run(ctx.WorkingDirectory, "rev-parse", "--path-format=absolute", "--git-common-dir", "--git-dir"); - if (dirs.ExitCode != 0) throw new Refusal("not inside a git repository"); - var lines = dirs.Stdout.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries).Select(d => d.TrimEnd('/', '\\')).ToArray(); - if (lines is not [var common, var own]) throw new Refusal("git rev-parse named no git directory"); - Root = Path.GetDirectoryName(common)!; - Linked = own != common; - Top = Git.Out(ctx.WorkingDirectory, "rev-parse", "--show-toplevel"); - Branch = branch ?? Git.Out(ctx.WorkingDirectory, "rev-parse", "--abbrev-ref", "HEAD"); - Memory = ctx.Env("PROBE_STATE_DIR") ?? Path.Combine(Root, ".claude", "agent-memory-local", "prober"); - Dir = Path.Combine(Memory, "branches", Branch.Replace('/', '-')); - } - - public string Head => Git.Out(Top, "rev-parse", "HEAD"); - public string P(string name) => Path.Combine(Dir, name); - - // ---- the corpus, tracked, overlaid by the branch's unpublished staging ------------------------------ - - public string CorpusPath(string relative) - { - var staged = Path.Combine(StagingDir, relative); - return File.Exists(staged) ? staged : Path.Combine(CorpusDir, relative); - } - - public SortedDictionary Registry() - { - var merged = new SortedDictionary(StringComparer.Ordinal); - foreach (var path in new[] { Path.Combine(CorpusDir, "registry.json"), Path.Combine(StagingDir, "registry.json") }) - if (Files.ReadJson(path)?["probes"] is JsonObject probes) - foreach (var (name, entry) in probes) - if (entry is JsonObject e) merged[name] = (JsonObject)e.DeepClone(); - return merged; - } - - public List Claims() - { - var merged = new SortedDictionary(StringComparer.Ordinal); - foreach (var path in new[] { Path.Combine(CorpusDir, "claims.json"), Path.Combine(StagingDir, "claims.json") }) - foreach (var c in ReadClaims(path, Path.GetRelativePath(Top, path).Replace('\\', '/'))) - merged[Files.Str(c["id"])] = (JsonObject)c.DeepClone(); - return [.. merged.Values]; - } - - /// - /// A claims file, refused unless it matches Schemas/claims.schema.json, each problem named by its claim and - /// field (decision 102). No file is no claims. - /// - public static List ReadClaims(string path, string shown) - { - if (!File.Exists(path)) return []; - if (Files.ReadJson(path) is not JsonArray claims) throw new Refusal($"{shown} is not a JSON list of claims"); - var schema = SchemaCheck.Load("claims.schema.json")["items"] as JsonObject ?? throw new InvalidOperationException("claims.schema.json has no items"); - var problems = new List(); - for (var i = 0; i < claims.Count; i++) - { - var id = claims[i] is JsonObject o && Files.Str(o["id"]) is { Length: > 0 } s ? s : $"#{i}"; - problems.AddRange(SchemaCheck.Problems(claims[i], schema).Select(p => $"claim {id}: {p}")); - } - if (problems.Count > 0) - throw new Refusal($"{shown} does not match tools/Prober/Schemas/claims.schema.json:\n " + string.Join("\n ", problems)); - return [.. claims.OfType()]; - } - - /// - /// A probe's own rows (rows/P.json, staged, else the corpus's rows/P.json.gz). Each probe has a file of its own, its claim - /// inside, so the rows of one probe of a claim never move another's identity. - /// - public JsonArray RowsOf(string probe) - { - var relative = Path.Combine("rows", probe + ".json"); - var staged = Path.Combine(StagingDir, relative); - return File.Exists(staged) ? Files.ReadRows(staged) : Files.ReadRows(Path.Combine(CorpusDir, relative)); - } - - /// - /// The paths a probe's registry entry watches: what its result depends on that the footprint cannot see, such as a - /// variant catalogue under test/ compiled into the probes or a build input a child process reads. They stale its - /// footprint like the paths it ran. - /// - public IReadOnlyList Watched(string probe) => [.. Files.Strings(Registry().GetValueOrDefault(probe)?["watched"])]; - - public JsonObject AxesBlock() => Axes.Block(AxesFile); - - // ---- identity -------------------------------------------------------------------------------------- - - /// - /// What a probe's result depends on besides the product footprint: its registry entry (not its seed), every source - /// compiled with it (the harness and all probes), its own rows (not a sibling's), the axes file, the build environment - /// (SDK, central package versions, build props, global.json), this tool's version and the schema version, and - /// where its claim declares where an obligation does not apply, those declarations, which the harness reads. - /// A result counts only while this is unchanged. - /// - public string Identity(string probe) - { - using var sha = IncrementalHash.CreateHash(HashAlgorithmName.SHA256); - void Add(string s) => sha.AppendData(Encoding.UTF8.GetBytes(s + "\0")); - var entry = Registry().GetValueOrDefault(probe) ?? []; - // The seeds prove the probe; a result of the probe depends on none of them (decisions 100 and 104). - var registered = (JsonObject)entry.DeepClone(); - registered.Remove("seed"); - registered.Remove("seeds"); - Add(Files.Canonical(registered)); - var sources = new SortedDictionary(StringComparer.Ordinal); - foreach (var baseDir in new[] { CorpusDir, StagingDir }) - foreach (var sub in new[] { "harness", "probes" }) - { - var folder = Path.Combine(baseDir, sub); - if (Directory.Exists(folder)) - foreach (var file in Directory.EnumerateFiles(folder, "*.cs")) sources[Path.GetFileName(file)] = file; - } - foreach (var (name, file) in sources) { Add(name); sha.AppendData(File.ReadAllBytes(file)); } - Add(Files.Canonical(RowsOf(probe))); - // The declarations Row.Check applies before a check's body runs: a change to one changes what the probe's rows - // check. Added only where the claim declares something, so a claim without declarations leaves the identity as it was. - if (Declarations(Files.Str(entry["claim"])) is { } declared) Add("inapplicable:" + Files.Canonical(declared)); - try { Add(Files.Canonical(AxesBlock())); } catch (Refusal) { Add("no axes file"); } - foreach (var path in new[] { "Directory.Packages.props", "Directory.Build.props", "global.json" }) - Add(path + (Git.ObjectId(Top, path) ?? "-")); - Add(Files.Canonical(Toolchain())); - Add("tool:" + SourceFingerprint.ToolVersion[..16]); - Add("schema:" + Files.SchemaVersion); - return Convert.ToHexStringLower(sha.GetHashAndReset())[..16]; - } - - private Dictionary? declarations; - - /// A claim's declarations of where its obligations do not apply, as claims.json holds them, by obligation; null where it declares none. - private JsonArray? Declarations(string claim) - { - declarations ??= Claims().ToDictionary(c => Files.Str(c["id"]), c => new JsonArray([.. (c["obligations"] as JsonArray ?? []).OfType() - .Where(o => o["inapplicable"] is JsonArray { Count: > 0 }) - .Select(o => (JsonNode)new JsonObject { ["obligation"] = Files.Str(o["id"]), ["inapplicable"] = o["inapplicable"]!.DeepClone() })]), StringComparer.Ordinal); - return declarations.TryGetValue(claim, out var declared) && declared.Count > 0 ? declared : null; - } - - private JsonObject? toolchain; - - /// - /// What no file in the repository records: the SDK a global.json roll-forward picked, say. A command that cannot - /// say it (it does not start, exits non-zero or prints nothing) is a refusal: its error text in every identity - /// would read as a toolchain change, and every result as stale, without a word why. - /// - public JsonObject Toolchain() - { - if (toolchain is not null) return toolchain; - JsonObject? commands; - try { commands = AxesBlock()["replay"]?["toolchain"] as JsonObject; } catch (Refusal) { commands = null; } - var result = new JsonObject(); - foreach (var (name, argv) in (commands ?? []).OrderBy(kv => kv.Key, StringComparer.Ordinal)) - { - var parts = Files.Strings(argv).ToArray(); - var shown = $"the toolchain command {name} (`{string.Join(' ', parts)}`)"; - if (parts.Length == 0) throw new Refusal($"{shown} is empty"); - Ran ran; - try { ran = Proc.Run(parts[0], parts[1..], Top); } - catch (System.ComponentModel.Win32Exception e) { throw new Refusal($"{shown} did not start: {e.Message}"); } - static string FirstLine(string s) => s.Trim().Split('\n')[0].Trim(); - // A failing `dotnet --version` lists the installed SDKs on stdout ("10.0.401 [path]", a version to look at) - // and says why on stderr. - if (ran.ExitCode != 0) - { - var why = FirstLine(ran.Stderr) is { Length: > 0 } err ? err : FirstLine(ran.Stdout); - throw new Refusal($"{shown} exited {ran.ExitCode}: {(why.Length > 0 ? why : "no output")}. A result is bound to what it prints, " + - "so none could be current; put the toolchain global.json asks for first on PATH, or install it where " + - $"the SDK installer puts it ({Dotnet.InstallerDirectory}), which `dotnet` falls back to"); - } - var first = FirstLine(ran.Stdout.Trim().Length > 0 ? ran.Stdout : ran.Stderr); - if (first.Length == 0) throw new Refusal($"{shown} printed nothing, so a result could be bound to no toolchain"); - result[name] = first; - } - return toolchain = result; - } - - // ---- footprints, evidence, coverage ------------------------------------------------------------------ - - public Dictionary Footprints() => - Files.ReadLines(P("footprints.jsonl")).GroupBy(l => Files.Str(l["probe"]), StringComparer.Ordinal) - .ToDictionary(g => g.Key, g => g.Last(), StringComparer.Ordinal); - - private readonly Dictionary currentCache = new(StringComparer.Ordinal); - - /// - /// A probe's footprint is current when every path it ran, every compile-time path, every path its registry entry - /// watches and the toolchain are unchanged at HEAD. A watched path the footprint did not record (one added to the - /// entry since) is not current. A footprint that ran nothing under src/ is current only by watched paths it recorded. - /// - public bool FootprintCurrent(string probe, Dictionary footprints) - { - if (currentCache.TryGetValue(probe, out var cached)) return cached; - var ok = false; - if (footprints.TryGetValue(probe, out var r) && r["footprint"] is JsonObject fp && (fp.Count > 0 || r["watched"] is JsonObject { Count: > 0 })) - { - var watched = r["watched"] as JsonObject ?? []; - ok = Watched(probe).All(watched.ContainsKey) - && fp.Concat(r["compileTime"] as JsonObject ?? []).Concat(watched).All(kv => Git.ObjectId(Top, kv.Key) == Files.Str(kv.Value, null!)); - if (ok) - { - var now = Toolchain(); - ok = (r["toolchain"] as JsonObject ?? []).All(kv => Files.Str(now[kv.Key]) == Files.Str(kv.Value)); - } - } - return currentCache[probe] = ok; - } - - public bool FootprintChangedSince(string probe, string since, Dictionary footprints) - { - if (!footprints.TryGetValue(probe, out var r)) return true; - var paths = (r["footprint"] as JsonObject ?? []).Select(kv => kv.Key).Concat((r["compileTime"] as JsonObject ?? []).Select(kv => kv.Key)) - .Concat((r["watched"] as JsonObject ?? []).Select(kv => kv.Key)).Concat(Watched(probe)) - .Distinct(StringComparer.Ordinal).ToArray(); - if (paths.Length == 0) return true; - return Git.Run(Top, ["diff", "--quiet", since, "HEAD", "--", .. paths]).ExitCode != 0; - } - - public string EvidencePath(string probe) => P(Path.Combine("evidence", probe + ".json")); - - /// A check that earned credit: the obligation it named and the settings it ran under. - public sealed record Credit(string Obligation, Dictionary Applied); - - /// - /// The checks of one probe's evidence that earn credit, each with its obligation and settings: only while the - /// probe's identity and product footprint are current, and only checks that ended as a pass in rows that were - /// expected and executed. A check's settings are the row's (read outside any check, so every check ran under them) - /// with its own laid over them; a row without checks is one check of the obligation claim, credited when - /// the row passed. - /// - public List CreditedChecks(string probe, JsonObject? evidence, Dictionary? footprints) - { - var result = new List(); - if (evidence is null || Files.Str(evidence["identity"]) != Identity(probe)) return result; - if (footprints is not null && !FootprintCurrent(probe, footprints)) return result; - var expected = new HashSet(); var executed = new HashSet(); - foreach (var v in (evidence["variants"] as JsonArray ?? []).OfType()) - { - foreach (var i in v["expected"] as JsonArray ?? []) expected.Add((int)Files.Num(i)); - foreach (var i in v["executed"] as JsonArray ?? []) executed.Add((int)Files.Num(i)); - } - foreach (var (key, row) in evidence["rows"] as JsonObject ?? []) - { - var i = int.Parse(key, System.Globalization.CultureInfo.InvariantCulture); - if (row is not JsonObject r || !expected.Contains(i) || !executed.Contains(i)) continue; - foreach (var check in ChecksOf(r)) - if (check.Category == "pass" && check.Applied.Count > 0) result.Add(new Credit(check.Obligation, check.Applied)); - } - return result; - } - - /// One check of an evidence row as the ledger reads it. - public sealed record CheckRead(string Obligation, string Category, Dictionary Applied, string? Baseline); - - /// - /// The checks of one evidence row: each with its obligation, category and settings (the row's, the check's laid - /// over them). A row without checks, every row of a version-1 file among them, is one check of the implicit - /// obligation claim with the row's category and settings and no baseline. - /// - public static List ChecksOf(JsonObject row) - { - var applied = row["applied"] is JsonObject a ? Files.StringMap(a) : new Dictionary(StringComparer.Ordinal); - if (row["checks"] is not JsonArray { Count: > 0 } checks) - return [new CheckRead(Claim.ImplicitObligation, Files.Str(row["category"], "violation"), applied, null)]; - var result = new List(); - foreach (var c in checks.OfType()) - { - var own = new Dictionary(applied, StringComparer.Ordinal); - if (c["applied"] is JsonObject ca) foreach (var (axis, value) in Files.StringMap(ca)) own[axis] = value; - result.Add(new CheckRead(Files.Str(c["obligation"]), Files.Str(c["category"], "violation"), own, c["baseline"] is JsonValue b ? Files.Str(b) : null)); - } - return result; - } - - /// - /// (claim, obligation, pair) for every pair a current check earned, leaving out the probe - /// names. Each check's settings alone make its pairs: a pair is never put together from two checks' settings. - /// - public HashSet<(string Claim, string Obligation, Pair Pair)> Credits(Universe universe, string? claimId = null, string? except = null) - { - var footprints = Footprints(); - var done = new HashSet<(string, string, Pair)>(); - foreach (var (name, entry) in Registry()) - { - var cid = Files.Str(entry["claim"]); - if (name == except || (claimId is not null && cid != claimId) || !universe.ById.TryGetValue(cid, out var claim)) continue; - var evidence = Files.ReadJson(EvidencePath(name)) as JsonObject; - foreach (var credit in CreditedChecks(name, evidence, footprints)) - foreach (var pair in claim.PairsOfRow(credit.Applied)) done.Add((cid, credit.Obligation, pair)); - } - return done; - } - - /// - /// (claim, pair) for every pair current evidence earned, leaving out the probe names. A round's - /// own ledger lines earn nothing. - /// - public HashSet<(string Claim, Pair Pair)> Covered(Universe universe, string? claimId = null, string? except = null) => - [.. Credits(universe, claimId, except).Select(x => (x.Claim, x.Pair))]; - - /// The last event per finding; a fixed line counts once its commit is in HEAD's history, a dismissal in the corpus everywhere. - public Dictionary Findings() - { - var last = new Dictionary(StringComparer.Ordinal); - foreach (var f in Files.ReadLines(P("findings.jsonl"))) - { - if (Files.Str(f["status"]) == "fixed" && Files.Str(f["commit"]) is { Length: > 0 } c - && Git.Run(Top, "merge-base", "--is-ancestor", c, "HEAD").ExitCode != 0) continue; - last[Files.Str(f["id"])] = f; - } - foreach (var f in Files.ReadLines(CorpusPath("dismissed.jsonl"))) - { - var d = (JsonObject)f.DeepClone(); d["status"] = "dismissed"; - last[Files.Str(f["id"])] = d; - } - return last; - } - - /// The findings one probe's evidence steps around, with the number of rows that skip for each. - public static SortedDictionary FindingSkips(JsonObject? evidence) - { - var skips = new SortedDictionary(StringComparer.Ordinal); - foreach (var (_, row) in evidence?["rows"] as JsonObject ?? []) - foreach (var f in Files.Strings(row?["findings"])) skips[f] = skips.GetValueOrDefault(f) + 1; - return skips; - } - - /// - /// The skips of one probe's evidence that no longer stand: a finding this ledger has fixed (in HEAD's history) - /// or the corpus has dismissed. Such a skip would hide the defect coming back, so the replay fails the probe. - /// A finding this ledger has never seen (a fresh state, as in CI) is left to the gate, which refuses any skip. - /// - public List StaleSkips(JsonObject? evidence) - { - var findings = Findings(); - var stale = new List(); - foreach (var (id, rows) in FindingSkips(evidence)) - { - if (!findings.TryGetValue(id, out var f)) continue; - var status = Files.Str(f["status"]); - if (status == "fixed" && Files.Str(f["commit"]) is { Length: >= 7 } commit) status += " at " + commit[..7]; - if (status != "open") stale.Add($"{rows} row(s) skip for {id}, which is {status}: remove the skip"); - } - return stale; - } - - /// How the rows of one probe's evidence ended, by category. - public static SortedDictionary Outcomes(JsonObject? evidence) - { - var outcomes = new SortedDictionary(StringComparer.Ordinal); - foreach (var (_, row) in evidence?["rows"] as JsonObject ?? []) - { - var category = Files.Str(row?["category"], "violation"); - outcomes[category] = outcomes.GetValueOrDefault(category) + 1; - } - return outcomes; - } - - /// How the checks of one probe's evidence ended, by category; a row without checks counts as its one check claim. - public static SortedDictionary CheckOutcomes(JsonObject? evidence) - { - var outcomes = new SortedDictionary(StringComparer.Ordinal); - foreach (var (_, row) in evidence?["rows"] as JsonObject ?? []) - if (row is JsonObject r) - foreach (var check in ChecksOf(r)) outcomes[check.Category] = outcomes.GetValueOrDefault(check.Category) + 1; - return outcomes; - } - - /// The obligations a check of one probe's evidence passed for at its last replay, current or not. - public static HashSet PassedObligations(JsonObject? evidence) - { - var passed = new HashSet(StringComparer.Ordinal); - foreach (var (_, row) in evidence?["rows"] as JsonObject ?? []) - if (row is JsonObject r) - foreach (var check in ChecksOf(r).Where(c => c.Category == "pass")) passed.Add(check.Obligation); - return passed; - } - - public List OpenFindings() => [.. Findings().Where(kv => Files.Str(kv.Value["status"]) == "open").Select(kv => kv.Key).Order(StringComparer.Ordinal)]; - - public sealed record ProbeState(string Probe, string Claim, string Filter, JsonArray Variants, bool Stale, bool Retired, int Passes, bool Replay); - - /// A registry entry's process variants: its own, or one that sets nothing. - public static JsonArray VariantsOf(JsonObject entry) => entry["variants"] is JsonArray va && va.Count > 0 ? (JsonArray)va.DeepClone() : [new JsonObject()]; - - public List ProbeStates(bool includeRetired) - { - var footprints = Footprints(); - var runs = Files.ReadLines(P("runs.jsonl")); - var states = new List(); - foreach (var (probe, r) in Registry()) - { - var passes = runs.Count(x => Files.Str(x["probe"]) == probe && Files.Str(x["result"]) == "pass"); - var retired = r["retired"] is JsonValue rv && rv.TryGetValue(out var b) && b; - var evidence = Files.ReadJson(EvidencePath(probe)); - var stale = !FootprintCurrent(probe, footprints) || evidence is null || Files.Str(evidence["identity"]) != Identity(probe); - var variants = VariantsOf(r); - states.Add(new ProbeState(probe, Files.Str(r["claim"]), Files.Str(r["filter"]), variants, stale, retired, passes, stale && (includeRetired || !retired))); - } - return states; - } -} - -/// A refusal the user acts on: printed as "prober: ...", exit 1. Never a stack trace. -internal sealed class Refusal(string message, int exitCode = 1) : Exception(message) -{ - public int ExitCode { get; } = exitCode; -} diff --git a/tools/Prober/NewProbe.cs b/tools/Prober/NewProbe.cs deleted file mode 100644 index 97047dcd..00000000 --- a/tools/Prober/NewProbe.cs +++ /dev/null @@ -1,166 +0,0 @@ -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; - -namespace Prober; - -/// -/// new: the mechanical half of writing a probe, done the same way every time, into the branch's staging. -/// It writes the probe's skeleton in the harness's form (probes/P.cs), headed by the claim's source and -/// every obligation's id and sentence, with one check per obligation that throws until it is written; stages the -/// probe's own rows (a covering array over its claim's pairs the claim's other probes have not covered, as -/// rows --probe P writes); -/// and registers the probe with an empty seeds, so seeds and Seeds.Problems name each -/// obligation's seed as owed. The skeleton builds, so the other probes keep building beside it, and every row of -/// it ends as a harness error, which fails the row, earns nothing and is never a seed's catch (decision 103). -/// It refuses, writing nothing, a claim that is not in the claims, a name a probe has already, and a claim -/// whose first surface is in no family of the replay configuration (or not in the one --family names). -/// -internal static partial class NewProbe -{ - [GeneratedRegex("^[A-Za-z_][A-Za-z0-9_]*Probe$")] - private static partial Regex ProbeName(); - - [GeneratedRegex("^([A-Za-z]+[0-9]+)(?:-(.*))?$")] - private static partial Regex ClaimPrefix(); - - [GeneratedRegex("[^A-Za-z0-9]+")] - private static partial Regex NotAlphanumeric(); - - public static int Run(Ledger l, Args a) - { - var claimId = a.Need("claim"); - var claim = l.Claims().FirstOrDefault(c => Files.Str(c["id"]) == claimId) - ?? throw new Refusal($"no claim {claimId} in claims.json (tracked or staged)"); - var probe = a.Get("probe") is { Length: > 0 } named ? named : DefaultName(claimId); - if (!ProbeName().IsMatch(probe)) throw new Refusal($"{probe} is not a probe's name: a C# class name ending in Probe", 2); - if (Taken(l, probe) is { } where) throw new Refusal($"a probe {probe} exists already ({where}); name another with --probe"); - var family = Family(l, claim, a.Get("family")); - var obligations = Obligations(claim); - - var source = Path.Combine(l.StagingDir, "probes", probe + ".cs"); - Files.WriteAtomically(source, Skeleton(claim, probe, family, obligations)); - // The probe's own rows, its claim's other probes' untouched: a covering array over the pairs they have not covered. - var (rows, _) = Commands.StageRows(l, probe, claimId, full: false); - var registry = Path.Combine(l.StagingDir, "registry.json"); - var staged = Files.ReadJson(registry) as JsonObject ?? new JsonObject { ["probes"] = new JsonObject() }; - var probes = staged["probes"] as JsonObject ?? []; - probes[probe] = new JsonObject { ["claim"] = claimId, ["filter"] = "*" + probe, ["seeds"] = new JsonArray() }; - staged["probes"] = probes.DeepClone(); - Files.WriteJson(registry, staged); - - var o = l.Ctx.Out; - o.WriteLine($"prober new: {probe} for claim {claimId}{(family is null ? "" : $" (family {family})")}, staged in {l.StagingDir}"); - o.WriteLine($" probes/{probe}.cs: the skeleton; it builds, and every row ends as a harness error until each obligation's check is written"); - o.WriteLine($" rows/{probe}.json: {rows} rows over the claim's pairs its other probes have not covered" + - (rows == 0 ? "; with no row the probe has no test to run" : "")); - o.WriteLine($" registry.json: {probe}, claim {claimId}, filter *{probe}, no seed yet"); - o.WriteLine("still owed, per obligation:"); - foreach (var (id, sentence, method) in obligations) - o.WriteLine($" {id}: the check {method} ({(sentence is null ? "the claim as a whole" : $"\"{sentence}\"")}), and a seed that breaks it " + - (id == Claim.ImplicitObligation && sentence is null - ? $"(`prober register --probe {probe} --claim {claimId} --filter \"*{probe}\" --seed seeds/{claimId}.patch --defect \"...\"`)" - : $"(`prober register --probe {probe} --claim {claimId} --filter \"*{probe}\" --obligation {id} --seed seeds/{claimId}-{id}.patch --defect \"...\"`)")); - o.WriteLine($"then: `prober try --probe {probe}` until each row ends as it should, `prober seeds --probe {probe}`, `prober register` (it lints the staged sources), `prober publish`"); - return 0; - } - - /// The claim id's letter-digit prefix, then its words run together, then Probe: C20-testing-shouldhold is C20TestingShouldholdProbe. - internal static string DefaultName(string claimId) - { - var m = ClaimPrefix().Match(claimId); - var (prefix, rest) = m.Success ? (m.Groups[1].Value, m.Groups[2].Value) : ("", claimId); - return Pascal(prefix) + Pascal(rest) + "Probe"; - } - - private static string Pascal(string text) => - string.Concat(NotAlphanumeric().Split(text).Where(w => w.Length > 0).Select(w => char.ToUpperInvariant(w[0]) + w[1..])); - - /// Where a probe of that name is already: the registry, a source file of that name, or a class of that name in any corpus source. - private static string? Taken(Ledger l, string probe) - { - if (l.Registry().ContainsKey(probe)) return "in the registry"; - var declares = new Regex($@"\b(class|record|struct)\s+{probe}\b"); - foreach (var dir in new[] { l.CorpusDir, l.StagingDir }) - foreach (var sub in new[] { "harness", "probes" }) - { - var folder = Path.Combine(dir, sub); - if (!Directory.Exists(folder)) continue; - foreach (var file in Directory.EnumerateFiles(folder, "*.cs")) - if (Path.GetFileNameWithoutExtension(file) == probe || declares.IsMatch(File.ReadAllText(file))) return file; - } - return null; - } - - /// - /// The family the replay runs the probe in, its claim's first surface's (decision 101), checked against - /// --family. Null where the replay configuration names no families and none was asked for. - /// - private static string? Family(Ledger l, JsonObject claim, string? asked) - { - var families = (l.AxesBlock()["replay"] as JsonObject)?["families"] as JsonObject; - if (families is null) - return asked is null ? null : throw new Refusal($"--family {asked}: the replay configuration names no families", 2); - var surface = Files.Strings(claim["surfaces"]).FirstOrDefault() ?? ""; - var family = families.FirstOrDefault(kv => Files.Strings(kv.Value).Contains(surface)).Key - ?? throw new Refusal($"the first surface of claim {Files.Str(claim["id"])} ({surface}) is in no family of the replay configuration, so a replay by family would never run its probe"); - if (asked is not null && asked != family) - throw new Refusal($"claim {Files.Str(claim["id"])} is of family {family} (its first surface, {surface}), not {asked}", 2); - return family; - } - - /// Each obligation of the claim with its sentence and the check method the skeleton gives it; a claim without obligations has one, the claim itself, with no sentence. - internal static List<(string Id, string? Sentence, string Method)> Obligations(JsonObject claim) - { - var listed = (claim["obligations"] as JsonArray ?? []).OfType() - .Select(o => (Id: Files.Str(o["id"]), Sentence: (string?)OneLine(Files.Str(o["sentence"])))).ToList(); - if (listed.Count == 0) listed = [(Claim.ImplicitObligation, null)]; - var methods = new HashSet(StringComparer.Ordinal); - var result = new List<(string, string?, string)>(); - foreach (var (id, sentence) in listed) - { - var method = "Check" + Pascal(id); - for (var n = 2; !methods.Add(method); n++) method = "Check" + Pascal(id) + n; - result.Add((id, sentence, method)); - } - return result; - } - - /// The probe's source: the form of the corpus's probes (Rows.Data, [Theory, MemberData(nameof(Data))], Rec.Run), one throwing check per obligation. - internal static string Skeleton(JsonObject claim, string probe, string? family, List<(string Id, string? Sentence, string Method)> obligations) - { - var id = Files.Str(claim["id"]); - var literal = JsonSerializer.Serialize(probe); - var b = new StringBuilder(); - void Line(string text = "") => b.Append(text).Append('\n'); - Line($"// Claim {id} ({Files.Str(claim["source"])}), surfaces {string.Join(", ", Files.Strings(claim["surfaces"]))}{(family is null ? "" : $", family {family}")}."); - Line("// Obligations (claims.json), each with a check below and a seed of its own in the registry entry (decision 104):"); - foreach (var (oid, sentence, _) in obligations) - Line($"// {oid}: {(sentence is null ? "the claim as a whole (claims.json lists no obligations for it)" : $"\"{sentence}\"")}"); - Line("// Oracle: TODO: what each row is compared against, and the result it expects."); - Line("// Written by `prober new`: it builds, and every row ends as a harness error until each obligation's check is written."); - Line("using Probes.Shapes;"); - Line("namespace Probes;"); - Line($"public sealed class {probe}"); - Line("{"); - Line($" public static TheoryData Data() => Rows.Data({literal});"); - Line(" [Theory, MemberData(nameof(Data))]"); - Line(" public Task Row(string probe, int idx) => Rec.Run(probe, idx, Check);"); - Line(); - Line(" static async Task Check(Row r) =>"); - for (var i = 0; i < obligations.Count; i++) - Line($" {(i == 0 ? "" : "?? ")}await {obligations[i].Method}(r){(i == obligations.Count - 1 ? ";" : "")}"); - foreach (var (oid, sentence, method) in obligations) - { - Line(); - Line($" // TODO {oid}: {(sentence is null ? "the claim as a whole" : $"\"{sentence}\"")}"); - Line(" // Return the failure text, or null where the row holds it; say how it was compared with r.Note(NoteKeys.Compared, detail)."); - Line($" static Task {method}(Row r) => throw new NotImplementedException({JsonSerializer.Serialize($"{id}/{oid}: its check is not written yet")});"); - } - Line("}"); - return b.ToString(); - } - - private static string OneLine(string text) => string.Join(" ", text.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)); -} diff --git a/tools/Prober/ProbeAdapter.cs b/tools/Prober/ProbeAdapter.cs deleted file mode 100644 index fc993e87..00000000 --- a/tools/Prober/ProbeAdapter.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; - -namespace Prober; - -/// -/// What the core needs from a repository's toolchain, and nothing more: build the probes, run one probe in -/// one process with an environment (writing its rows' outcomes and a coverage report), and derive the -/// generated axes. The core holds nothing of .NET's; a repository on another stack plugs in its own. -/// -internal interface IProbeAdapter -{ - /// Builds what the probes need in the worktree. The first line of the output is shown. - Ran Build(string worktree, IReadOnlyDictionary environment); - - /// Runs one probe (by its filter) with the environment given; the coverage report goes to under . - Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment); - - /// The generated axes (settings of the option types the features read), or null with a reason. - (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory); - - /// The environment every build and run of the probes needs (for .NET: the coverage extension). - IReadOnlyDictionary Environment(string cacheDirectory); -} - -/// -/// The .NET adapter, configured by the "replay" object of the axes file: argv templates for the build and -/// the test run (through the repository's verdict tool), the commands that prepare the worktree first (building -/// that tool), the coverage extension referenced from a targets file outside the worktree, and the reflection -/// enumerator run with the repository's package versions. -/// -internal sealed class DotNetAdapter(JsonObject config) : IProbeAdapter -{ - private string Project => Files.Str(config["project"]); - - private static string[] Expand(JsonNode? template, Dictionary values) => - [.. Files.Strings(template).Select(arg => Regex.Replace(arg, @"\{(\w+)\}", m => values.TryGetValue(m.Groups[1].Value, out var v) ? v : m.Value))]; - - /// - /// The program a template starts: a bare name (no path separator, dotnet) is found on PATH; a path is - /// relative to the replay worktree and must exist there, so a template naming a file the repository no longer - /// has fails by name, never as a puzzling exit code. - /// - internal static string Executable(string worktree, string program) - { - if (program.IndexOfAny(['/', '\\']) < 0) return program; - var path = Path.Combine(worktree, program); - return File.Exists(path) ? path : throw new Refusal($"the replay template names {program}, which the worktree does not have"); - } - - private static Ran Start(string worktree, string[] argv, IReadOnlyDictionary environment) => - Proc.Run(Executable(worktree, argv[0]), argv[1..], worktree, environment); - - /// Prepares the worktree (the "prepare" commands: building the verdict tool, which a fresh or moved worktree lacks or holds stale), then builds the probes. - public Ran Build(string worktree, IReadOnlyDictionary environment) - { - foreach (var command in (config["prepare"] as JsonArray ?? []).OfType()) - { - var prepared = Start(worktree, Expand(command, new() { ["project"] = Project }), environment); - if (prepared.ExitCode != 0) - return prepared with { Stdout = $"PREPARE FAILED: {string.Join(' ', Files.Strings(command))}\n" + prepared.Stdout + prepared.Stderr }; - } - return Start(worktree, Expand(config["build"], new() { ["project"] = Project }), environment); - } - - /// - /// The "test" template, then the "coverage" arguments when a coverage file is asked for. A replay without a - /// footprint asks for none, so a template that carries the coverage arguments itself is refused rather than - /// run with an empty file name. - /// - public Ran RunProbe(string worktree, string filter, string results, string coverageFile, IReadOnlyDictionary environment) - { - var values = new Dictionary { ["project"] = Project, ["filter"] = filter, ["results"] = results, ["cobertura"] = coverageFile }; - if (coverageFile.Length == 0 && Files.Strings(config["test"]).Any(arg => arg.Contains("{cobertura}", StringComparison.Ordinal))) - throw new Refusal("the replay's test template carries the coverage arguments itself; move them to a \"coverage\" array of the replay configuration, so a run without a footprint can leave them out"); - string[] argv = coverageFile.Length > 0 ? [.. Expand(config["test"], values), .. Expand(config["coverage"], values)] : Expand(config["test"], values); - // The template runs the verdict tool, which keeps its full log where BUILD_VERDICT_LOG_DIR says: the - // directory the replay offers as PROBE_LOG_DIR, whose files it keeps for a failed run. - if (environment.TryGetValue("PROBE_LOG_DIR", out var logDir) && logDir is { Length: > 0 }) - environment = new Dictionary(environment, StringComparer.Ordinal) { ["BUILD_VERDICT_LOG_DIR"] = logDir }; - return Start(worktree, argv, environment); - } - - public IReadOnlyDictionary Environment(string cacheDirectory) - { - var result = new Dictionary(StringComparer.Ordinal); - if (Files.Str(config["coverageTargets"]) is { Length: > 0 } targets) - { - var path = Path.Combine(cacheDirectory, "coverage.targets"); - if (!File.Exists(path) || SharedFile.ReadAllText(path) != targets) Files.WriteAtomically(path, targets); - result["CustomAfterMicrosoftCommonTargets"] = path; - } - return result; - } - - public (JsonObject? Axes, string? Error) DeriveAxes(string checkout, string workDirectory) - { - if (config["enumerate"] is not JsonObject enumerate) return (null, "no enumerator configured"); - // The enumerator pins its packages in #:package directives; each version comes from the repository's - // central package versions, so the axes follow a dependency update without anyone editing the script. - var script = File.ReadAllText(Path.Combine(checkout, Files.Str(enumerate["script"]))); - var props = Path.Combine(checkout, "Directory.Packages.props"); - var versions = File.Exists(props) - ? Regex.Matches(File.ReadAllText(props), " m.Groups[1].Value, m => m.Groups[2].Value, StringComparer.Ordinal) - : new Dictionary(StringComparer.Ordinal); - script = Regex.Replace(script, @"^#:package ([^@\s]+)@(\S+)", m => $"#:package {m.Groups[1].Value}@{(versions.TryGetValue(m.Groups[1].Value, out var v) ? v : m.Groups[2].Value)}", RegexOptions.Multiline); - Directory.CreateDirectory(workDirectory); - var file = Path.Combine(workDirectory, Path.GetFileName(Files.Str(enumerate["script"]))); - Files.WriteAtomically(file, script); - var ran = Proc.Run("dotnet", ["run", file], workDirectory); - if (ran.ExitCode != 0) return (null, "the enumerator failed: " + ran.Stderr.Trim().Split('\n')[^1]); - var json = ran.Stdout[ran.Stdout.IndexOf('{')..]; - return (JsonNode.Parse(json) as JsonObject, null); - } -} diff --git a/tools/Prober/ProbeRun.cs b/tools/Prober/ProbeRun.cs deleted file mode 100644 index 484fc507..00000000 --- a/tools/Prober/ProbeRun.cs +++ /dev/null @@ -1,107 +0,0 @@ -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// What replay, seeds and try share to run a probe: the worktree moved to HEAD, the -/// environment every build and run gets, one run of a probe in one process, and how each row of it ended, read -/// from the jsonl the harness writes under PROBE_OUT. -/// -internal static class ProbeRun -{ - /// - /// A row's outcome under one run: its category, the first reason given, whether any reason of the row or of a check - /// of it was a harness error, and its checks. A row that ran no check is one check of the obligation claim. - /// - internal sealed record RowOutcome(string Category, string Reason, bool HarnessError, IReadOnlyList Checks); - - /// - /// One check of a row under one run: its obligation, category and first reason, and whether a harness error broke it - /// or its row outside any check. Only a clean violation, one with no harness error, says the check fired (decision 103). - /// - internal sealed record CheckOutcome(string Obligation, string Category, string Reason, bool HarnessError) - { - public bool CleanViolation => Category == "violation" && !HarnessError; - } - - /// - /// Adds the worktree at ("cold"), or moves it there without force ("warm"): files the - /// commits did not change keep their mtimes, so the build stays incremental. - /// - public static string Ready(Ledger ledger, string worktree, string head) - { - if (!Directory.Exists(worktree)) - { - var added = Git.Run(ledger.Root, "worktree", "add", "--detach", worktree, head); - if (added.ExitCode != 0) throw new Refusal("could not add the worktree: " + added.Stderr.Trim(), 2); - return "cold"; - } - if (Git.Run(worktree, "checkout", "--detach", head).ExitCode != 0) Git.Run(worktree, "reset", "--hard", head); - return "warm"; - } - - /// The process's environment with what the adapter adds for every build and run of the probes. - public static Dictionary Environment(Context ctx, IProbeAdapter adapter, string home) - { - var environment = new Dictionary(ctx.Environment, StringComparer.Ordinal); - foreach (var (k, v) in adapter.Environment(home)) environment[k] = v; - return environment; - } - - /// The environment of one variant: the base, then the variant's own settings, then the extra ones (a seed's toggle). - public static Dictionary Variant(IReadOnlyDictionary environment, JsonObject variant, JsonObject? extra = null) - { - var env = new Dictionary(environment, StringComparer.Ordinal); - foreach (var (k, v) in variant) env[k] = Files.Str(v); - foreach (var (k, v) in extra ?? []) env[k] = Files.Str(v); - return env; - } - - /// - /// Runs the probe the filter selects once, with its rows written to {results}/{name}-rows, and reads how - /// each row ended. empty asks for no coverage. - /// - public static (Ran Ran, Dictionary Rows) Once(IProbeAdapter adapter, string worktree, string filter, string results, string name, - string coverageFile, Dictionary env) - { - var outDir = Path.Combine(results, name + "-rows"); - env["PROBE_OUT"] = outDir; - var ran = adapter.RunProbe(worktree, filter, results, coverageFile, env); - return (ran, Outcomes(outDir)); - } - - /// How each row ended, from every jsonl under ; a line without a category is a violation. - public static Dictionary Outcomes(string outDir) - { - var rows = new Dictionary(); - if (!Directory.Exists(outDir)) return rows; - foreach (var file in Directory.EnumerateFiles(outDir, "*.jsonl")) - foreach (var line in Files.ReadLines(file)) - { - var category = Files.Str(line["category"], "violation"); - var reasons = Files.Strings(line["reasons"]).ToList(); - var reason = reasons.FirstOrDefault(r => r.StartsWith(category + ":", StringComparison.Ordinal)) ?? Files.Str(line["note"]); - static bool Broken(IEnumerable reasons) => reasons.Any(r => r.StartsWith("harness-error:", StringComparison.Ordinal)); - var checks = new List(); - if (line["checks"] is JsonArray { Count: > 0 } ran) - { - // The row's own harness error (outside any check) breaks every check of it; a check's breaks that check. - var rowBroken = Broken(reasons); - foreach (var c in ran.OfType()) - { - var cc = Files.Str(c["category"], "violation"); - var cr = Files.Strings(c["reasons"]).ToList(); - checks.Add(new CheckOutcome(Files.Str(c["obligation"]), cc, cr.FirstOrDefault(r => r.StartsWith(cc + ":", StringComparison.Ordinal)) ?? Files.Str(c["notes"]), - rowBroken || cc == "harness-error" || Broken(cr))); - } - } - else checks.Add(new CheckOutcome(Claim.ImplicitObligation, category, reason, category == "harness-error" || Broken(reasons))); - var harnessError = Broken(reasons) || checks.Any(c => c.HarnessError); - rows[(int)Files.Num(line["idx"])] = new RowOutcome(category, reason, harnessError, checks); - } - return rows; - } - - /// The first line of a run's output (the verdict's), or its exit code where it printed nothing. - public static string FirstLine(Ran ran) => ran.Stdout.Split('\n')[0].Trim() is { Length: > 0 } line ? line : $"exit {ran.ExitCode}"; -} diff --git a/tools/Prober/Prober.csproj b/tools/Prober/Prober.csproj deleted file mode 100644 index ba12505a..00000000 --- a/tools/Prober/Prober.csproj +++ /dev/null @@ -1,37 +0,0 @@ - - - - - Exe - prober - Prober - - false - $(ToolSourcePatterns);Schemas/*.json - - - - - - - - - - - - diff --git a/tools/Prober/Program.cs b/tools/Prober/Program.cs deleted file mode 100644 index 7b62b3ae..00000000 --- a/tools/Prober/Program.cs +++ /dev/null @@ -1,135 +0,0 @@ -namespace Prober; - -internal static class Program -{ - private const string Usage = - """ - prober: the probing workflow's core. No command starts a model; only `round` does, locally. - - Ledger (any checkout of the repository; --branch B, default the checked-out branch): - universe [--base REF] each claim's axes, from the claims, the axes file and the generated axes - rows --claim C [--full] a pairwise covering array over the claim's pairs not yet covered; - inside a capped round it refuses past the deadline or the claim cap - rows --probe P [--full] writes P's rows file whole into the staging: a covering array over its - claim's pairs less those the claim's other probes have credited - status [--json] coverage from current evidence, open findings, done or not; per claim - its obligations and which have a seed - gaps [--base REF] what the corpus leaves out, each with its reason: unassigned settings, - refused shapes and values, what claims do not promise, out of reach, and - the wanted pairs no passing check of the last replay read both axes of - gate [--since REF] [--json] the release gate (needs only the tracked corpus and a replay) - stale [--json] [--include-retired] the probes a replay would run - identity --probe P what a result of P is bound to - record-run --probe P --result pass|fail [--evidence F] [--seconds S] - footprint --probe P --cobertura F... --root WT [--include src] [--compile-time PATH...] - register --probe P --claim C --filter F [--variants JSON] [--seed PATCH] [--seed-env NAME=VALUE...] [--defect TEXT] - [--obligation O]: the seed (a patch, a toggle or both) into `seed`, or with - --obligation into `seeds`, in place of a seed naming O; refuses an O that - claim C does not list - publish | dismiss --id F --reason TEXT | mark retired --probe P - fix F --commit SHA [--reason TEXT] records open finding F fixed at the commit (findings.jsonl); refuses - an id this ledger does not hold open and a commit the repository lacks - lock --holder H --mode replay|explore|full [--pid N] | unlock --holder H | heartbeat --holder H - round-start --holder H --minutes M [--claims N] | round-end --holder H | repair | abandon - merge --from DIR... joins the states of a replay split by family (one DIR per leg) into - this one; refuses while a registered probe was in no leg - - Writing a probe (into the branch's staging): - new --claim C [--probe P] [--family F] stages P's skeleton (the claim's source and obligations quoted, one - check per obligation that throws until written), the claim's rows and P's - registry entry with no seed yet; P defaults to the claim's prefix and words - (C20-testing-shouldhold: C20TestingShouldholdProbe); refuses a claim not in - the claims, a name taken, a family that is not the claim's - try --probe P [--rows i,j,...] [--no-build] builds once in the replay's worktree and runs P alone, every - variant or the rows given, printing how each row ended; records nothing, - holds the worktree from a replay, exits with the probe's exit code - - Running probes: - replay [--all] [--family F] [--parallel N] [--no-footprint] [--holder H] [--dry-run] [--where] - replays the corpus with no model: the stale probes, or all; one family - of the replay configuration; N probe processes at a time (default: - the cores); without coverage or footprint, for a state nobody keeps - round explore|full [--minutes M] [--claims N] [--usd X] a capped exploration round (local only) - seeds [--all] [--probe P] [--dry-run] applies each probe's seeded defect in a scratch worktree, replays the - probe there and requires a violation it causes (local only, a build per - seed): the probes whose identity or footprint changed since every seed of - theirs was caught, or all of them, as a release runs it - - State goes under PROBE_STATE_DIR when set (CI), else the main checkout's prober memory. - Exit codes: 0 done, 1 refused or failed, 2 usage, 3 this dll is stale; `try` exits with the probe's own. - """; - - private static readonly HashSet Flags = new(StringComparer.Ordinal) - { "json", "full", "all", "include-retired", "dry-run", "where", "no-footprint", "no-build" }; - - private static int Main(string[] args) - { - Console.OutputEncoding = new System.Text.UTF8Encoding(false); - if (args.Length == 0 || args[0] is "help" or "-h" or "--help") { Console.Write(Usage); return args.Length == 0 ? 2 : 0; } - var home = FindRoot(AppContext.BaseDirectory); - if (home is not null && SourceFingerprint.Difference(home) is { } stale) - { - Console.Error.WriteLine($"prober: this dll was built from other sources than the checkout holds ({stale}). Run `{SourceFingerprint.RebuildCommand}`."); - return SourceFingerprint.StaleExitCode; - } - return Run(Context.FromProcess(), args); - } - - /// Runs one command against a context: the process's, or a test's. - public static int Run(Context ctx, string[] args) - { - try - { - var command = args[0]; - var a = new Args(args[1..], Flags); - if (command == "replay") return Replay.Run(ctx, a); - if (command == "round") return Round.Run(ctx, a); - if (command == "seeds") return Seeds.Run(ctx, a); - if (command == "try") return TryProbe.Run(ctx, a); - var ledger = new Ledger(ctx, a.Get("branch")); - // Commands run under a round keep its lock alive. - if (command is not ("lock" or "unlock" or "abandon" or "heartbeat") && ctx.Env("PROBE_HOLDER") is { } holder) - RoundLock.Beat(ledger, holder); - return command switch - { - "universe" => Commands.UniverseCommand(ledger, a), - "rows" => Commands.Rows(ledger, a), - "status" => Commands.Status(ledger, a), - "gate" => Commands.Gate(ledger, a), - "stale" => Commands.Stale(ledger, a), - "identity" => Commands.IdentityCommand(ledger, a), - "record-run" => Commands.RecordRun(ledger, a), - "footprint" => Commands.Footprint(ledger, a), - "new" => NewProbe.Run(ledger, a), - "register" => Commands.Register(ledger, a), - "publish" => Commands.Publish(ledger, a), - "dismiss" => Commands.Dismiss(ledger, a), - "fix" => Commands.Fix(ledger, a), - "mark" => Commands.Mark(ledger, a), - "lock" => Commands.Lock(ledger, a), - "unlock" => Commands.Unlock(ledger, a), - "heartbeat" => Commands.HeartbeatCommand(ledger, a), - "owe-replay" => Commands.OweReplay(ledger, a), - "round-start" => Commands.RoundStart(ledger, a), - "round-end" => Commands.RoundEnd(ledger, a), - "repair" => Commands.Repair(ledger, a), - "abandon" => Commands.Abandon(ledger, a), - "merge" => Commands.Merge(ledger, a), - "gaps" => Commands.Gaps(ledger, a), - _ => throw new Refusal($"unknown command {command}; see `prober help`", 2), - }; - } - catch (Refusal refusal) - { - ctx.Error.WriteLine("prober: " + refusal.Message); - return refusal.ExitCode; - } - } - - private static string? FindRoot(string start) - { - for (var dir = new DirectoryInfo(start); dir is not null; dir = dir.Parent) - if (File.Exists(Path.Combine(dir.FullName, "CodoMetis.TypeKit.slnx"))) return dir.FullName; - return null; - } -} diff --git a/tools/Prober/README.md b/tools/Prober/README.md deleted file mode 100644 index 3abf10f2..00000000 --- a/tools/Prober/README.md +++ /dev/null @@ -1,181 +0,0 @@ -# prober - -The probing workflow's core: the ledger of what the documents claim and what the probes have shown, the -replay of the probe corpus, the release gate, and the capped exploration round. Repository tooling, not -a package. It references the base class library only and is built with the solution. - -```text -dotnet tools/Prober/bin/Release/net10.0/prober.dll help -``` - -## What runs where - -| Command | Model | Where | -|---|---|---| -| `replay [--all] [--family F] [--parallel N] [--no-footprint]` | none | anywhere, CI included: the probes whose identity or footprint changed, or all of them; one family of the replay configuration; N probe processes at a time (the cores by default); without coverage or a footprint, for a state nobody keeps (decision 101) | -| `merge --from DIR...` | none | anywhere, CI included: joins the states of a replay split by family, and refuses while a registered probe was in no leg | -| `gate [--since REF]` | none | anywhere, CI included: needs only the tracked corpus and a replay | -| `status`, `stale`, `universe`, `rows [--claim C \| --probe P]`, … | none | anywhere | -| `gaps [--base REF]` | none | anywhere: what the corpus leaves out, each with its recorded reason, and, from the last replay's evidence, each wanted pair no obligation wanting it has a passing check that read both its axes of (more rows cannot cover it); a report that decides nothing (decision 104) | -| `fix F --commit SHA [--reason TEXT]` | none | anywhere: records an open finding fixed at a commit, the fixed line of the state's `findings.jsonl` as `finding-line.schema.json` wants it; it counts once the commit is in HEAD's history | -| `new --claim C [--probe P] [--family F]` | none | anywhere: stages a probe's skeleton, its own rows and its registry entry with no seed yet, and prints what the writer still owes | -| `try --probe P [--rows i,j,...] [--no-build]` | none | locally: builds once in the replay's worktree and runs that one probe, every row or the rows given; records nothing, and holds the worktree from a replay while it runs | -| `round explore\|full` | the `prober` agent | locally only, under caps it cannot overrun | -| `seeds [--all] [--probe P]` | none | locally only: one build per seed, in a scratch worktree (decision 100); the probes whose seeds identity or product footprint changed since every seed was caught, or all of them, as a release runs it (decision 107) | - -No command in the gate's path needs a login, an API key or a Claude executable. A gate obligation is met -by a probe in the corpus, written by hand or by a round, or by a reviewed waiver in -`tools/probe/corpus/waivers.json`. - -## Files - -The corpus is tracked under `tools/probe/corpus/`: claims, the probe registry, harness and probe sources, -rows (gzipped), waivers and dismissed findings. State goes under `PROBE_STATE_DIR` when set (CI sets it), -else the main checkout's `.claude/agent-memory-local/prober/`. Every file's shape is in `Schemas/`, versioned; -a file or line without `schemaVersion` is version 1. `claims.json` is checked against its schema wherever it is -read, and a field the schema does not have is refused with the claim and the field named; each `exclude` entry -is a reason and its pairs, which `universe` prints beside the claim's narrowings. The JSON files the prober -writes keep `+`, backticks and `<` literal, so a `publish` that changes nothing in a tracked file leaves it as -it is (decision 102). The harness's generated declarations, `harness/*.g.cs`, are written by the file-based -apps of `tools/probe/generators/` from the rows and the axes file, and `GeneratedHarnessTests` in the -conventions tests holds each to its app's output (that directory's README). - -Rows are per probe: `rows/.json.gz`, one file for each registered probe, its claim inside (`claim`), so -two probes of one claim (C07's `BaseProbe` and `C07BlanketMappingProbe`) each run rows of their own, and a -probe's identity reads its own file only: a sibling's rows changing leaves it as it was. The probe's -`Rows.Data("P")` names the file, and the replay, `try` and the identity read it. `rows --probe P` writes P's file -whole into the staging, never appended: a covering array over its claim's pairs less those the claim's other -probes have credited (P's own credit is not subtracted, since it came from the rows the file replaces), and -`publish` moves it into the corpus by probe; `rows --claim C` only prints such an array. The stored rows are -definitions, the evidence is in the state. `ProbeRowsTests` in the conventions tests refuses two rows of one -file with the same axis map, a file whose `claim` is not in `claims.json` or is not its probe's, a file that -names no registered probe, and a registered probe without a file. - -A registry entry may list `watched`: paths, files or directories, that the probe's result depends on and its -footprint cannot see, since the footprint is what ran in the probe's own process under `src/` beside the -`compileTimeFootprint` paths. A variant catalogue under `test/` compiled into the probes is one (C19 builds -over the analyzer tests' `TypeArgumentSites` and `TypeArgumentKinds`), a build input a child process reads is -another (C18's consumer build compiles against the base package and evaluates `src/Directory.Build.*`). The -replay records each with its object id in the footprint line, a change to one stales the footprint like a path -the probe ran, and the gate counts it as touching the probe's claim. A probe whose own process runs nothing under `src/` -(C22 builds its consumer in a child process) is recorded by what its entry watches alone; where nothing ran and nothing -is watched, `footprint` refuses to record an empty footprint, which would never go stale. - -The round lock, `round.lock` and its mutex, sits in the branch's directory (`branches//`), beside -everything a round or a replay writes, so rounds and replays of two branches run at once and two of one branch -never do. The scratch worktrees, `wt` (the replay's), `seeds`, `explore` (the `prober` agent's) and `enumerate`, -are per checkout: under `PROBE_CACHE_DIR` when set, else `~/.cache/probe-replay/-`, -and `replay --where` prints them. So a linked worktree (`git worktree add`, as each session under -`.claude/worktrees/` is) builds a warm copy of its own, about a gigabyte of disk, and two sessions never reset -each other's copy; the main checkout keeps the directory it had before. Since the lock is per branch and the -worktree per checkout, a replay also holds `replay.lock` beside them (an OS lock, gone with its process), and a -replay of another branch in the same checkout, after a branch switch, is skipped while one runs. Removing a -linked worktree leaves its cache behind: before you remove it, run there the `remove:` line that `replay --where` -prints, which removes the cache's worktrees (`git worktree remove`) and then its directory, every path written out. - -A claim lists what it promises in `obligations`, each an id and a sentence quoted verbatim from the claim's -source, and what it does not promise in `not-promised`, each an id and the sentence of the document or record -that makes the difference intended, quoted from that source. `universe` checks every quote as it checks a -narrowing's and refuses a sentence its document does not hold, or an id listed twice. A claim without -`obligations` has one, the claim itself, under the id `claim`. `status` shows per claim its obligations and the -ones no seed names, and `gaps` prints every not-promised entry and narrowing beside the settings no surface -reaches, the refused shapes and values, and the axes file's `out-of-reach` surfaces (decision 104). - -An obligation may declare where it does not apply: `inapplicable`, each entry the claim's own axis values -(`axes`) and a citation (`cite`: `not-promised:id` of the claim's list, `docs:file#anchor`, `fact:KB-NNN` or -`record:NNNN`). A row holding every axis of a declaration at one of its values runs no check of that -obligation: `Row.Check` records the check inapplicable with the cite before its body runs, for that check only. -The replay stages the claims beside the rows (`claims.json` in the probe directory) for the harness's -`Rows.Claim`, and a probe's identity takes its claim's declarations. A pair a declaration matches is not wanted -by that obligation (`Claim.PairsWantedBy`); a pair no obligation wants is declared inapplicable: `universe` -prints each claim's count with the citations, `gaps` lists them under a heading of their own, `status --json` -gives `inapplicable_pairs` per claim, the covering generator never puts one in a row, and the gate leaves them -out as it leaves out an exclusion. A claim's pairs stay its pairs. `universe` refuses a declaration whose axis -or value the claim does not list, whose cite is malformed, or whose not-promised id the claim lacks. - -A pair of a claim's axes counts as covered only by evidence a replay recorded: a check that ended as a pass, in a -row that was expected and executed, for the settings that actually applied, while the result's identity holds -(the probe, every compiled corpus source, the rows, the axes file, the build environment, this tool's -version and the schema version) and the product files the probe ran are unchanged. A row's checks -(`Row.Check` in the harness) each name an obligation and a baseline and record the axes they read; a check is -credited with those and the row's own (read outside any check), and a pair is never put together from two -checks' axes. A row without checks is one check of the obligation `claim` with the row's category. Evidence is -version 2 (`Schemas/evidence.schema.json`): the replay copies each row's `checks` beside its `applied` and -`reasons`. `status --json` gives each obligation `current`, the pairs current checks naming it earned, and -`passed`; the text line counts rows and checks by outcome and ends with the obligations no passing check named -at the last replay (`K uncredited`). - -## Seeds - -Each probe's registry entry names a seeded product defect: a patch under `seeds/` against the product -files (the replay's `include`), an environment toggle, or both, and the sentence `defect`. Where its claim -lists obligations, the entry's `seeds` holds one such seed per obligation the probe checks, each naming it in -`obligation`, and every listed obligation of a claim with probes has a seed in one of them (decision 104). -`seeds` applies each seed on its own in the `seeds` worktree beside the replay's, builds, runs the probe -and counts the seed caught only for a check that ends as a violation, with no harness error in it or in its -row outside its checks (decision 103), in a row whose unseeded run had no violation of the same obligation; a -seed naming an obligation is caught only by a check naming it, so a probe whose rows run no checks (each the -one check `claim`) catches only seeds that name none. The unseeded rows come from the ledger where the probe's -evidence is current, else from an unseeded run first. Nothing it runs is evidence; it -writes `seeds-last.json` (`Schemas/seeds-run.schema.json`), one outcome per seed, reported as -`P/` where the seed names one. `register --obligation O --seed PATCH --defect TEXT` (or -`--seed-env NAME=VALUE...` for a toggle) writes a seed into `seeds`, in place of any seed of the entry that -names O, `seed` included, and refuses an O that the claim's entry in `claims.json` does not list; without -`--obligation`, `--seed` and `--seed-env` set the one `seed`. No seed is written into the registry by hand. -Without `--all`, `seeds` runs only the probes that changed since the run that caught every one of their seeds -(design/adr/0107), as `replay` runs only the stale ones: a probe runs when its seeds identity (the probe's identity, -its seeds and the content of their patches) differs from the one recorded, when the product footprint its last -replay recorded or a file its seeds patch changed since the recorded commit, when no footprint is recorded, and when -no run caught all its seeds. It prints each probe it runs or skips with the reason. `seeds-last.json` keeps, per probe, -the identity and the commit of the run that caught them all, carried forward for the probes a run skips; a missed seed -drops the probe's record, so it is never skipped next time. `--all` runs every probe whatever the record says, and a -release runs it before the tag; `--probe P` runs P. -A seed is not part of a probe's identity, neither `seed` nor `seeds`. Without a build, -`Seeds.Problems`, which the prober tests run over the repository, refuses a probe without a seed, a seed that -does not apply, a seed naming an obligation its claim does not list, and a listed obligation no seed names. - -## Writing a probe - -`new --claim C` starts one in the branch's staging, whose path it prints. It writes `probes/P.cs` in the form -the corpus's probes have (`Rows.Data`, `[Theory, MemberData(nameof(Data))]`, `Rec.Run`), headed by the claim's -source and each obligation's id and sentence, with one check per obligation that throws until it is written: -the probe builds beside the others, and every row of it ends as a harness error, which fails the row, earns -nothing and is never a seed's catch. It stages the probe's own rows, a covering array over its claim's pairs the -claim's other probes have not covered, as `rows --probe P` writes them, and the registry entry with an empty -`seeds`, so `seeds` and `Seeds.Problems` name the seed each obligation owes. P defaults to the claim id's -prefix and words (`C20-testing-shouldhold` is `C20TestingShouldholdProbe`); it refuses a claim that is not in -the claims, a name a probe has, and a claim whose first surface is in no family, or not in `--family`'s. - -Then fill each check and run `try --probe P` until each row ends as it should. It moves the replay's worktree -to HEAD, overlays the corpus and the staging as the replay does, builds once through the adapter (not with -`--no-build`), and runs P alone in each of its registry variants, or only the rows `--rows` names, through the -harness's `PROBE_ROWS`, within the rows each variant's `PROBE_ONLY` selects. It prints each run's verdict line -and how each row ended, and exits with the probe's code (1 when the build fails). It records no evidence, -footprint or run and takes no round lock, but holds the replay's worktree (`replay.lock`) while it runs, so a try -refuses while a replay of the checkout runs, and a replay skips while a try does. Then write a seed per -obligation and register it with the command `new` printed for that obligation (`register --obligation O`, -under "Seeds"), show each caught with `seeds --probe P`, and `publish`. `register` lints the staged sources -first (below). A writer needs no build or sync script of their own. - -## Register and publish - -`register` and `publish` read the staged harness and probe sources first and refuse, by file and line, a -`Note` whose first argument is not a `NoteKeys` member the corpus declares, a harness `Skip` whose finding -is not a string literal naming a finding this ledger holds open (decision 103), and a check's `Inapplicable` or -`Refused` whose first argument is not a `Cite` factory with a string literal, or names what does not exist: a -not-promised entry the probe's claim does not list (any claim's, for a harness source no one probe owns), a docs -file, a knowledge entry under `design/knowledge/`, a record under `design/adr/`. `ProbeCiteTests` in the -conventions tests holds the same over the tracked corpus. The scan blanks out comments -and string literals and reads text, since this tool references the base class library only. - -## The adapter - -Building the probes, running one probe in one process, and deriving the generated axes go through -`IProbeAdapter` (`ProbeAdapter.cs`), configured by the `replay` object of `.claude/probe-axes.md`. The .NET -adapter is the only one; the core holds nothing of .NET's, so another stack can plug in its own. - -## Freshness - -The dll hashes its own sources, the schemas among them, at build time and checks them at startup, as every -tool does (`tools/Directory.Build.targets`): a changed, added or removed source makes it refuse with exit 3 -and name the command that rebuilds it, `dotnet build tools/Prober -c Release`. Content hashes, never times. diff --git a/tools/Prober/ReleaseGate.cs b/tools/Prober/ReleaseGate.cs deleted file mode 100644 index 42caea16..00000000 --- a/tools/Prober/ReleaseGate.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// The release gate. It needs nothing but the tracked corpus and a replay: no model, no login, no local -/// memory of a developer's machine. It refuses while the staging is unpublished, a finding is open or a row -/// skips for one, a probe has not passed at its current identity and footprint, an expected row did not run, or a claim touched -/// since the release has pairs no probe covers and no reviewed waiver. Touched is chosen conservatively: the -/// claim's source document, its surfaces' source areas, the paths its probes watch or the build inputs changed; footprints only add. -/// -internal static class ReleaseGate -{ - private static readonly string[] BuildInputs = ["Directory.Packages.props", "Directory.Build.props", "global.json"]; - - public static int Run(Ledger l, string? sinceArg, bool json) - { - var since = sinceArg ?? Git.Out(l.Top, "describe", "--tags", "--abbrev=0"); - var (universe, _) = Axes.Build(l, since); - var footprints = l.Footprints(); - var reasons = new List(); - if (Directory.Exists(l.StagingDir) && Directory.EnumerateFiles(l.StagingDir, "*", SearchOption.AllDirectories).Any()) - reasons.Add("the staging corpus is not published (run `publish` and commit tools/probe/corpus)"); - var open = l.OpenFindings(); - if (open.Count > 0) reasons.Add($"open findings: {string.Join(", ", open)}"); - - var runs = Files.ReadLines(l.P("runs.jsonl")).GroupBy(r => Files.Str(r["probe"]), StringComparer.Ordinal).ToDictionary(g => g.Key, g => g.Last(), StringComparer.Ordinal); - foreach (var s in l.ProbeStates(includeRetired: true)) - { - if (s.Stale) { reasons.Add($"{s.Probe}: not replayed at its current identity and footprint (run `prober replay --all`)"); continue; } - if (Files.Str(runs.GetValueOrDefault(s.Probe)?["result"]) != "pass") { reasons.Add($"{s.Probe}: its last replay failed"); continue; } - var evidence = Files.ReadJson(l.EvidencePath(s.Probe)) as JsonObject; - var expected = new HashSet(); var executed = new HashSet(); - foreach (var v in (evidence?["variants"] as JsonArray ?? []).OfType()) - { - foreach (var i in v["expected"] as JsonArray ?? []) expected.Add((int)Files.Num(i)); - foreach (var i in v["executed"] as JsonArray ?? []) executed.Add((int)Files.Num(i)); - } - expected.ExceptWith(executed); - if (expected.Count > 0) reasons.Add($"{s.Probe}: {expected.Count} expected row(s) did not run"); - // A skip is an open finding the corpus itself records, so it binds where the ledger has none (CI). - foreach (var (finding, rows) in Ledger.FindingSkips(evidence)) - reasons.Add($"{s.Probe}: {rows} row(s) skip for finding {finding}; fix it and remove the skip, or dismiss it"); - } - - var done = l.Covered(universe); - var byClaim = l.Registry().GroupBy(kv => Files.Str(kv.Value["claim"]), StringComparer.Ordinal).ToDictionary(g => g.Key, g => g.Select(kv => kv.Key).ToList(), StringComparer.Ordinal); - var changed = Git.Out(l.Top, "diff", "--name-only", since, "HEAD").Split('\n', StringSplitOptions.RemoveEmptyEntries); - bool Under(IEnumerable paths) => paths.Any(w => changed.Any(f => f == w || f.StartsWith(w.TrimEnd('/') + "/", StringComparison.Ordinal))); - - // A reviewed waiver meets an obligation without a probe; one without a reason or a reviewer meets nothing. - var waivers = (Files.ReadJson(l.CorpusPath("waivers.json")) as JsonArray ?? []).OfType() - .Where(w => Files.Str(w["claim"]).Length > 0 && Files.Str(w["reason"]).Length > 0 && Files.Str(w["reviewed_by"]).Length > 0) - .Select(w => Files.Str(w["claim"])).ToHashSet(StringComparer.Ordinal); - - var required = new List(); var waived = new List(); var missedByFootprint = new List(); var footprintOnly = new List(); - var missingTotal = 0; var inapplicableTotal = 0; - foreach (var claim in universe.Claims) - { - var probes = byClaim.GetValueOrDefault(claim.Id) ?? []; - // What the claim's surfaces watch, and what its probes' registry entries watch beyond their footprints. - var conservative = Under([claim.Source.Split('#')[0]]) || Under(claim.Watched) || Under(probes.SelectMany(l.Watched)) || Under(BuildInputs); - var footprint = probes.Count > 0 && probes.Any(p => l.FootprintChangedSince(p, since, footprints)); - if (probes.Count > 0 && conservative && !footprint) missedByFootprint.Add(claim.Id); - if (footprint && !conservative) footprintOnly.Add(claim.Id); - if (!(conservative || footprint)) continue; - if (waivers.Contains(claim.Id)) { waived.Add(claim.Id); continue; } - // The pairs some obligation wants; one no obligation wants is declared inapplicable and left out, as an exclusion is. - var pairs = claim.PairsWanted(); - inapplicableTotal += claim.Pairs().Count - pairs.Count; - var missing = pairs.Count(p => !done.Contains((claim.Id, p))); - required.Add(claim.Id); missingTotal += missing; - if (missing > 0) reasons.Add($"{claim.Id}: {missing} of {pairs.Count} pairs uncovered (touched since {since})"); - } - - if (json) - { - l.Ctx.Out.WriteLine(new JsonObject - { - ["since"] = since, ["touched"] = new JsonArray([.. required.Select(x => (JsonNode)x)]), ["uncovered_pairs"] = missingTotal, ["inapplicable_pairs"] = inapplicableTotal, - ["reasons"] = new JsonArray([.. reasons.Select(x => (JsonNode)x)]), - ["selected_by_footprint_only"] = new JsonArray([.. footprintOnly.Select(x => (JsonNode)x)]), - ["waived"] = new JsonArray([.. waived.Select(x => (JsonNode)x)]), - ["missed_by_footprint_alone"] = new JsonArray([.. missedByFootprint.Select(x => (JsonNode)x)]), ["passes"] = reasons.Count == 0, - }.ToJsonString(new JsonSerializerOptions { WriteIndented = true })); - return reasons.Count == 0 ? 0 : 1; - } - if (reasons.Count > 0) - { - l.Ctx.Error.WriteLine($"probe gate: refused for {l.Branch} since {since} ({required.Count} claim(s) touched, {missingTotal} pair(s) uncovered):"); - foreach (var r in reasons) l.Ctx.Error.WriteLine($" {r}"); - return 1; - } - l.Ctx.Out.WriteLine($"probe gate: {l.Branch} passes since {since}: {required.Count} claim(s) touched and fully covered" + - (waived.Count > 0 ? $", {waived.Count} waived ({string.Join(", ", waived)})" : "") + - ", every probe replayed and passing, no open finding."); - return 0; - } -} diff --git a/tools/Prober/Replay.cs b/tools/Prober/Replay.cs deleted file mode 100644 index eba97a0f..00000000 --- a/tools/Prober/Replay.cs +++ /dev/null @@ -1,440 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Security.Cryptography; -using System.Text; -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// Replays the written probes with no model in the loop. Per commit it runs only the probes whose identity or -/// footprint changed; --all runs every probe, retired ones included (the release run). Each probe runs -/// once per process variant, and the replay records each probe in registry order whatever order the runs -/// finished in: per-row evidence (rows expected, rows executed, each row's outcome and applied settings), the -/// result, and the fresh footprint. --no-footprint runs without coverage and records no footprint, for a -/// state nobody keeps (a pull request's); only then do the variants of every selected probe run -/// --parallel N at a time (the machine's cores by default), since the coverage collector rewrites the -/// assemblies on disk and two hosts from one output directory corrupt each other (KB-025). -/// --family F replays the probes whose claim's first surface the -/// replay configuration's families puts under F, so a matrix of jobs can split the corpus and -/// merge join their states; it refuses while any probe's surface is in no family, so a matrix cannot -/// quietly leave a probe out. It works in one warm worktree kept between runs and moved to HEAD without force, -/// so only what changed is rebuilt. A dependency input that changed stales every probe, and the axes are -/// derived again. Only one round runs at a time. -/// -internal static class Replay -{ - /// How this replay was asked to run, recorded in replay-last.json for merge to read. - internal sealed record Options(bool All, string? Family, int Parallel, bool Footprint); - - public static int Run(Context ctx, Args a, IProbeAdapter? adapter = null) - { - var ledger = new Ledger(ctx, a.Get("branch")); - var config = ledger.AxesBlock()["replay"] as JsonObject ?? throw new Refusal("the axes file's block has no \"replay\" configuration"); - adapter ??= new DotNetAdapter(config); - var home = Home(ctx, ledger); - if (a.Flag("where")) - { - ctx.Out.WriteLine($"replay: {Path.Combine(home, "wt")}\nexplore: {Path.Combine(home, "explore")}\nseeds: {Path.Combine(home, "seeds")}"); - ctx.Out.WriteLine($"remove: {RemoveCommand(home)}"); - return 0; - } - - // With a footprint the coverage collector instruments the assemblies on disk, so two hosts from one - // output directory corrupt each other (KB-025): one at a time, whatever --parallel says. - var footprint = !a.Flag("no-footprint"); - var asked = Parallelism(a.Get("parallel")); - var options = new Options(a.Flag("all"), a.Get("family"), footprint ? 1 : asked, footprint); - var states = ledger.ProbeStates(options.All); - var scope = options.Family is null ? states : OfFamily(config, ledger, states, options.Family); - var todo = options.All ? scope : [.. scope.Where(s => s.Replay)]; - var skipped = scope.Where(s => !todo.Contains(s)).Select(s => s.Probe).ToList(); - var head = ledger.Head; - ctx.Out.WriteLine($"prober replay: {ledger.Branch} at {head[..7]}: {todo.Count} of {scope.Count} probes to replay" + - $"{(options.Family is null ? "" : $" (family {options.Family})")}, " + - (footprint ? "one at a time (the coverage collector rewrites the assemblies on disk)" : $"{options.Parallel} at a time, no footprint")); - if (a.Flag("dry-run")) { foreach (var s in todo) ctx.Out.WriteLine($" {s.Probe}"); return 0; } - - Directory.CreateDirectory(home); - var holder = a.Get("holder") ?? $"replay-{System.Environment.ProcessId}"; - // Under a round's lock (--holder) the replay takes nothing: re-locking would tie the round's liveness to - // this shorter-lived process. On its own it locks with its own process as the owner. - if (a.Get("holder") is null && !RoundLock.Take(ledger, holder, "replay", System.Environment.ProcessId).Taken) - { - RoundLock.OweReplay(ledger); - ctx.Out.WriteLine($"prober replay: skipped, the ledger is locked by another round; its unlock will say a replay is owed"); - return 0; - } - try - { - using var beat = new RoundLock.Heartbeat(ledger, holder); - using var held = HoldWorktree(home); - if (held is null) - { - ctx.Out.WriteLine($"prober replay: skipped, another branch's replay in this checkout is using {home}; run `prober replay` when it ends"); - return 0; - } - var axes = Rederive(ledger, adapter, config, Path.Combine(home, "enumerate"), force: options.All); - if (axes is not null) - { - if (axes["error"] is { } error) ctx.Out.WriteLine($" axes: {Files.Str(error)}"); - else - { - ctx.Out.WriteLine($" axes: re-derived ({(axes["added"] as JsonArray)!.Count} added, {(axes["removed"] as JsonArray)!.Count} removed)"); - foreach (var x in Files.Strings(axes["added"])) ctx.Out.WriteLine($" + {x}"); - foreach (var x in Files.Strings(axes["removed"])) ctx.Out.WriteLine($" - {x}"); - } - } - if (todo.Count == 0) - { - Summary(ledger, head, warmth: null, buildSeconds: 0, todo, skipped, failed: [], seconds: 0, axes, options); - return 0; - } - return Probes(ctx, ledger, adapter, config, home, Path.GetFullPath(a.Get("worktree") ?? Path.Combine(home, "wt")), head, todo, skipped, axes, options); - } - finally - { - if (a.Get("holder") is null) RoundLock.Release(ledger, holder); - } - } - - /// How many probe processes run at once: --parallel N, else one per core. - internal static int Parallelism(string? given) - { - if (given is null) return Math.Max(1, System.Environment.ProcessorCount); - if (!int.TryParse(given, NumberStyles.Integer, CultureInfo.InvariantCulture, out var n) || n < 1) throw new Refusal("--parallel takes a whole number of 1 or more", 2); - return n; - } - - /// - /// The probes of one family: those whose claim's first surface the replay configuration's families - /// (name to surfaces) puts under it. Every probe of the registry must have a family, or a matrix of jobs by - /// family would never run the one that has none; so this refuses for the whole registry, not only the family - /// asked for. - /// - internal static List OfFamily(JsonObject config, Ledger ledger, List states, string family) - { - var families = config["families"] as JsonObject ?? throw new Refusal("the replay configuration names no families; add \"families\" (a name to the surfaces it holds) to the axes file's replay block", 2); - if (families[family] is not JsonArray) throw new Refusal($"no family {family}; the replay configuration has {string.Join(", ", families.Select(kv => kv.Key))}", 2); - var bySurface = new Dictionary(StringComparer.Ordinal); - foreach (var (name, surfaces) in families) - foreach (var surface in Files.Strings(surfaces)) - { - if (bySurface.TryGetValue(surface, out var other) && other != name) throw new Refusal($"the surface {surface} is in two families, {other} and {name}", 2); - bySurface[surface] = name; - } - var firstSurface = ledger.Claims().ToDictionary(c => Files.Str(c["id"]), c => Files.Strings(c["surfaces"]).FirstOrDefault() ?? "", StringComparer.Ordinal); - var result = new List(); - foreach (var s in states) - { - var surface = firstSurface.GetValueOrDefault(s.Claim) ?? ""; - if (!bySurface.TryGetValue(surface, out var f)) - throw new Refusal($"{s.Probe}: the first surface of its claim {s.Claim} ({(surface.Length > 0 ? surface : "none")}) is in no family of the replay configuration, so a replay by family would never run it"); - if (f == family) result.Add(s); - } - return result; - } - - /// Derives the generated axes again when a dependency input or the toolchain changed, or always when forced. - private static JsonObject? Rederive(Ledger ledger, IProbeAdapter adapter, JsonObject config, string work, bool force) - { - if (config["enumerate"] is not JsonObject enumerate) return null; - var stampInput = Files.Canonical(new JsonArray( - new JsonArray([.. Files.Strings(enumerate["inputs"]).Select(p => (JsonNode?)(Git.ObjectId(ledger.Top, p) ?? ""))]), - ledger.Toolchain().DeepClone())); - var stamp = Convert.ToHexStringLower(SHA256.HashData(Encoding.UTF8.GetBytes(stampInput))); - var path = Path.Combine(ledger.Memory, "axes-generated.json"); - var old = Files.ReadJson(path) as JsonObject ?? []; - if (!force && Files.Str(old["inputsStamp"]) == stamp) return null; - var (generated, error) = adapter.DeriveAxes(ledger.Top, work); - if (generated is null) return new JsonObject { ["error"] = error }; - generated["inputsStamp"] = stamp; - static SortedSet Settings(JsonObject o) => - new((o["axes"] as JsonObject ?? []).SelectMany(kv => Files.Strings(kv.Value).Select(v => $"{kv.Key}={v}")).Concat(Files.Strings(o["needsValues"])), StringComparer.Ordinal); - var before = Settings(old); var after = Settings(generated); - Files.WriteJson(path, generated); - return new JsonObject - { - ["added"] = new JsonArray([.. after.Except(before, StringComparer.Ordinal).Select(x => (JsonNode)x)]), - ["removed"] = new JsonArray([.. before.Except(after, StringComparer.Ordinal).Select(x => (JsonNode)x)]), - ["versions"] = generated["versions"]?.DeepClone(), - }; - } - - /// One process variant of a probe, run: what it was asked, what it answered, and where its rows, coverage and logs went. - private sealed record VariantRun(JsonObject Variant, int Index, Ran Ran, double Seconds, string OutDir, string Coverage, string LogDir); - - /// - /// Starts every variant of a probe, each on a thread of its own that waits its turn at the gate, so the - /// variants of all selected probes share the --parallel slots in the order they were started. - /// - private static List> Start(IProbeAdapter adapter, string worktree, string results, IReadOnlyDictionary environment, - Ledger.ProbeState s, SemaphoreSlim gate, bool coverage) - { - var pending = new List>(); - var index = 0; - foreach (var variant in s.Variants.OfType()) - { - var i = index++; - var coverageFile = coverage ? $"{s.Probe}-{i}.cobertura.xml" : ""; - var outDir = Path.Combine(results, $"{s.Probe}-{i}-rows"); - // PROBE_LOG_DIR: where the run may leave its full logs; a failed run's are kept in the state. - var logDir = Path.Combine(results, $"{s.Probe}-{i}-log"); - var env = new Dictionary(environment, StringComparer.Ordinal) { ["PROBE_OUT"] = outDir, ["PROBE_LOG_DIR"] = logDir }; - foreach (var (k, v) in variant) env[k] = Files.Str(v); - pending.Add(Task.Factory.StartNew(() => - { - gate.Wait(); - try - { - var clock = Stopwatch.StartNew(); - var ran = adapter.RunProbe(worktree, s.Filter, results, coverageFile, env); - return new VariantRun(variant, i, ran, clock.Elapsed.TotalSeconds, outDir, coverageFile, logDir); - } - finally { gate.Release(); } - }, CancellationToken.None, TaskCreationOptions.LongRunning, TaskScheduler.Default)); - } - return pending; - } - - private static int Probes(Context ctx, Ledger ledger, IProbeAdapter adapter, JsonObject config, string home, string worktree, string head, - List todo, List skipped, JsonObject? axes, Options options) - { - var clock = Stopwatch.StartNew(); - var warmth = ProbeRun.Ready(ledger, worktree, head); - var probeDir = Overlay(ledger, config, worktree); - var environment = ProbeRun.Environment(ctx, adapter, home); - var results = Directory.CreateTempSubdirectory("prober-replay-").FullName; - - var buildClock = Stopwatch.StartNew(); - var build = adapter.Build(worktree, environment); - var buildSeconds = buildClock.Elapsed.TotalSeconds; - ctx.Out.WriteLine($" build ({warmth}): {buildSeconds.ToString("F1", CultureInfo.InvariantCulture)} s, {FirstLine(build)}"); - if (build.ExitCode != 0) { ctx.Out.WriteLine(build.Stdout); return 2; } - - // Every variant of every probe is started now and runs when a slot frees; the probes are then read, - // reported and recorded one by one in registry order, each once all its variants have ended. - using var gate = new SemaphoreSlim(options.Parallel); - var started = todo.Select(s => Start(adapter, worktree, results, environment, s, gate, options.Footprint)).ToList(); - - var failed = new List(); - foreach (var (s, pending) in todo.Zip(started)) - { - var variants = pending.Select(t => t.GetAwaiter().GetResult()).OrderBy(v => v.Index).ToList(); - var probeRows = Files.ReadRows(Path.Combine(probeDir, "rows", s.Probe + ".json")); - var evidence = new JsonObject { ["variants"] = new JsonArray(), ["rows"] = new JsonObject() }; - var result = "pass"; var seconds = 0.0; var reports = new List(); - foreach (var v in variants) - { - seconds += v.Seconds; - var label = v.Variant.Count > 0 ? " [" + string.Join(", ", v.Variant.Select(kv => $"{kv.Key}={Files.Str(kv.Value)}")) + "]" : ""; - ctx.Out.WriteLine($" {s.Probe}{label}: {FirstLine(v.Ran)} ({v.Seconds.ToString("F1", CultureInfo.InvariantCulture)} s)"); - if (v.Ran.ExitCode != 0) { result = "fail"; ctx.Out.WriteLine(string.Join('\n', v.Ran.Stdout.Split('\n').Skip(1).Take(11))); } - // A failed run's full logs, kept under the state's logs/ for triage: in CI the state is the artifact. - if (v.Ran.ExitCode != 0 && Directory.Exists(v.LogDir)) - foreach (var file in Directory.EnumerateFiles(v.LogDir)) - { - var kept = ledger.P(Path.Combine("logs", $"{s.Probe}-{v.Index}-{Path.GetFileName(file)}")); - Directory.CreateDirectory(Path.GetDirectoryName(kept)!); - File.Copy(file, kept, overwrite: true); - ctx.Out.WriteLine($" log: {kept}"); - } - if (v.Coverage.Length > 0) reports.AddRange(Directory.EnumerateFiles(results, v.Coverage, SearchOption.AllDirectories)); - - // Execution evidence: the rows this variant should run, the rows it did, how each ended and the - // settings that applied. A row missing here, or recorded without its settings, earns nothing. - var only = Files.Str(v.Variant["PROBE_ONLY"]); - var expected = new JsonArray(); var executed = new SortedSet(); - for (var i = 0; i < probeRows.Count; i++) - if (Selected(Files.StringMap(probeRows[i]?["row"] ?? probeRows[i]), only)) expected.Add(i); - if (Directory.Exists(v.OutDir)) - foreach (var file in Directory.EnumerateFiles(v.OutDir, "*.jsonl")) - foreach (var line in Files.ReadLines(file)) - { - var idx = (int)Files.Num(line["idx"]); - executed.Add(idx); - (evidence["rows"] as JsonObject)![idx.ToString(CultureInfo.InvariantCulture)] = EvidenceRow(line); - } - (evidence["variants"] as JsonArray)!.Add(new JsonObject - { - ["env"] = v.Variant.DeepClone(), ["expected"] = expected, ["executed"] = new JsonArray([.. executed.Select(x => (JsonNode)x)]), - }); - var missing = expected.Select(x => (int)Files.Num(x)).Where(x => !executed.Contains(x)).ToList(); - if (missing.Count > 0) ctx.Out.WriteLine($" {missing.Count} expected row(s) did not run: [{string.Join(", ", missing.Take(10))}]"); - } - foreach (var stale in ledger.StaleSkips(evidence)) { result = "fail"; ctx.Out.WriteLine($" {stale}"); } - if (result == "fail") failed.Add(s.Probe); - var evidencePath = Path.Combine(results, $"{s.Probe}-evidence.json"); - Files.WriteJson(evidencePath, evidence); - var categories = (evidence["rows"] as JsonObject)!.GroupBy(kv => Files.Str(kv.Value!["category"]), StringComparer.Ordinal) - .OrderBy(g => g.Key, StringComparer.Ordinal).Select(g => $"{g.Key} {g.Count()}"); - // Checks beside rows, once a row of the probe ran any: until then every row is its one check and the line says nothing more. - var checks = (evidence["rows"] as JsonObject)!.Any(kv => kv.Value?["checks"] is JsonArray { Count: > 0 }) - ? "; checks: " + string.Join(", ", Ledger.CheckOutcomes(evidence).Select(kv => $"{kv.Key} {kv.Value}")) : ""; - ctx.Out.WriteLine(" outcomes: " + string.Join(", ", categories) + checks); - var record = new StringWriter(); - Program.Run(new Context(ledger.Top, ctx.Environment, record, ctx.Error), - ["record-run", "--branch", ledger.Branch, "--probe", s.Probe, "--result", result, - "--seconds", seconds.ToString("F1", CultureInfo.InvariantCulture), "--evidence", evidencePath]); - if (record.ToString().Trim() is { Length: > 0 } said) ctx.Out.WriteLine(" " + said); - // The footprint is what ran, pass or fail: a probe failing for an open finding is skipped like any - // other until its footprint changes (a fix touches it). Without one the probe stays stale, which is - // right for a state nobody keeps. - if (!options.Footprint) ctx.Out.WriteLine(" footprint: not recorded (--no-footprint)"); - else if (reports.Count > 0) - { - var fp = new StringWriter(); var fpError = new StringWriter(); - Program.Run(new Context(ledger.Top, ctx.Environment, fp, fpError), - ["footprint", "--branch", ledger.Branch, "--probe", s.Probe, "--root", worktree, "--cobertura", .. reports, - "--include", .. (Files.Strings(config["include"]).ToArray() is { Length: > 0 } inc ? inc : ["src"]), - "--compile-time", .. Files.Strings(config["compileTimeFootprint"])]); - ctx.Out.WriteLine($" footprint: {(fp.ToString() + fpError.ToString()).Trim()}"); - } - else ctx.Error.WriteLine(" footprint: no coverage report found; the probe stays stale"); - } - - Summary(ledger, head, warmth, buildSeconds, todo, skipped, failed, clock.Elapsed.TotalSeconds, axes, options); - ctx.Out.WriteLine($"prober replay: {todo.Count - failed.Count} passed, {failed.Count} failed, {clock.Elapsed.TotalSeconds.ToString("F1", CultureInfo.InvariantCulture)} s in all"); - try { Directory.Delete(results, recursive: true); } catch (IOException) { } - return failed.Count > 0 ? 1 : 0; - } - - /// - /// replay-last.json: what this replay did, and how it was asked to run. selected and skipped - /// together are the probes in its scope (its family, or all), so merge can tell a probe no leg saw. - /// - private static void Summary(Ledger ledger, string head, string? warmth, double buildSeconds, List todo, List skipped, - List failed, double seconds, JsonObject? axes, Options options) - { - static JsonArray Names(IEnumerable names) => new([.. names.Select(x => (JsonNode)x)]); - Files.WriteJson(ledger.P("replay-last.json"), new JsonObject - { - ["branch"] = ledger.Branch, ["commit"] = head, ["build"] = warmth, ["build_seconds"] = Math.Round(buildSeconds, 1), - ["replayed"] = todo.Count, ["failed"] = Names(failed), ["seconds"] = Math.Round(seconds, 1), ["axes"] = axes?.DeepClone(), - ["all"] = options.All, ["family"] = options.Family, ["parallel"] = options.Parallel, ["footprint"] = options.Footprint, - ["selected"] = Names(todo.Select(s => s.Probe)), ["skipped"] = Names(skipped), - }); - } - - /// - /// The cache directory of this checkout's scratch worktrees (wt, seeds, explore, enumerate) under - /// PROBE_CACHE_DIR, else ~/.cache/probe-replay: the checkout's directory name and a hash of its path. A linked worktree - /// has one of its own, so sessions in two checkouts never reset each other's copy; the main checkout is keyed on - /// , as it was before, so its warm copy stays where it is. - /// - internal static string Home(Context ctx, Ledger ledger) - { - var checkout = ledger.Linked ? ledger.Top : ledger.Root; - var key = Convert.ToHexStringLower(SHA1.HashData(Encoding.UTF8.GetBytes(checkout)))[..8]; - return Path.Combine(ctx.Env("PROBE_CACHE_DIR") ?? Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), ".cache", "probe-replay"), - $"{Path.GetFileName(checkout)}-{key}"); - } - - /// - /// The command that removes this checkout's cache, its worktrees first (for git worktree list) and then the - /// directory, every path literal: removing a linked worktree leaves its cache behind. - /// - internal static string RemoveCommand(string home) - { - // Forward slashes on Windows, which git and the sh of Git for Windows both read. - static string Quoted(string path) => "'" + (Path.DirectorySeparatorChar == '\\' ? path.Replace('\\', '/') : path).Replace("'", "'\\''") + "'"; - var worktrees = new[] { "wt", "seeds", "explore" }.Select(w => Path.Combine(home, w)).Where(w => File.Exists(Path.Combine(w, ".git"))); - return string.Join("; ", worktrees.Select(w => $"git worktree remove --force {Quoted(w)}").Append($"rm -rf {Quoted(home)}")); - } - - /// - /// Holds this checkout's replay worktree for one replay: the lock is per branch, the worktree per checkout, so two - /// branches' replays in one checkout (a branch switched while the post-commit replay runs) would otherwise share it. - /// An OS lock on replay.lock beside it, released by the OS when its process ends; null when another holds it. - /// - internal static FileStream? HoldWorktree(string home) - { - Directory.CreateDirectory(home); - try { return new FileStream(Path.Combine(home, "replay.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None); } - catch (IOException) { return null; } - } - - /// - /// Copies the corpus into the worktree's probe directory: tracked, overlaid by the branch's unpublished staging; rows - /// unpacked, one file per probe, and a rows file the corpus no longer has removed; and the claims as claims.json, - /// whose declarations of where an obligation does not apply the harness reads. - /// - internal static string Overlay(Ledger ledger, JsonObject config, string worktree) - { - var probeDir = Path.Combine(worktree, Files.Str(config["probeDir"])); - Directory.CreateDirectory(Path.Combine(probeDir, "rows")); - var wanted = new SortedDictionary(StringComparer.Ordinal); - var rows = new SortedDictionary(StringComparer.Ordinal); - foreach (var source in new[] { ledger.CorpusDir, ledger.StagingDir }) - { - foreach (var sub in new[] { "harness", "probes" }) - if (Directory.Exists(Path.Combine(source, sub))) - foreach (var f in Directory.EnumerateFiles(Path.Combine(source, sub), "*.cs")) wanted[Path.GetFileName(f)] = f; - if (Directory.Exists(Path.Combine(source, "rows"))) - foreach (var f in Directory.EnumerateFiles(Path.Combine(source, "rows")).Where(f => f.EndsWith(".json", StringComparison.Ordinal) || f.EndsWith(".json.gz", StringComparison.Ordinal))) - rows[Path.GetFileName(f).Replace(".gz", "", StringComparison.Ordinal)] = f; - } - foreach (var existing in Directory.EnumerateFiles(probeDir)) if (!wanted.ContainsKey(Path.GetFileName(existing)) && Path.GetFileName(existing) != "claims.json") File.Delete(existing); - foreach (var existing in Directory.EnumerateFiles(Path.Combine(probeDir, "rows"))) if (!rows.ContainsKey(Path.GetFileName(existing))) File.Delete(existing); - foreach (var (name, src) in wanted) - { - var dst = Path.Combine(probeDir, name); - if (!File.Exists(dst) || !File.ReadAllBytes(dst).AsSpan().SequenceEqual(File.ReadAllBytes(src))) File.Copy(src, dst, overwrite: true); - } - foreach (var (name, src) in rows) - { - var data = src.EndsWith(".gz", StringComparison.Ordinal) ? Files.Gunzip(src) : File.ReadAllBytes(src); - var dst = Path.Combine(probeDir, "rows", name); - if (!File.Exists(dst) || !File.ReadAllBytes(dst).AsSpan().SequenceEqual(data)) File.WriteAllBytes(dst, data); - } - // The claims beside the rows, the corpus's with the staging's over them: the harness's Rows.Claim reads each - // obligation's declarations from here, so Row.Check skips a check where its claim declares it inapplicable. - var claims = Path.Combine(probeDir, "claims.json"); - var text = new JsonArray([.. ledger.Claims().Select(c => (JsonNode)c)]).ToJsonString(); - if (!File.Exists(claims) || File.ReadAllText(claims) != text) File.WriteAllText(claims, text); - return probeDir; - } - - /// - /// One row of the harness's jsonl as evidence keeps it: its category, the settings that applied, six reasons, the - /// findings it skipped for, and each check it ran with the same fields (obligation, baseline, category, its own - /// settings, six reasons, notes). A row that ran no check keeps no checks. - /// - internal static JsonObject EvidenceRow(JsonObject line) - { - static JsonArray Six(JsonNode? reasons) => new([.. Files.Strings(reasons).Take(6).Select(x => (JsonNode)x)]); - var row = new JsonObject - { - ["category"] = Files.Str(line["category"], "violation"), ["applied"] = line["applied"]?.DeepClone() ?? new JsonObject(), - ["reasons"] = Six(line["reasons"]), - ["findings"] = new JsonArray([.. Files.Strings(line["findings"]).Select(x => (JsonNode)x)]), - }; - if (line["checks"] is JsonArray checks && checks.Count > 0) - row["checks"] = new JsonArray([.. checks.OfType().Select(c => (JsonNode)new JsonObject - { - ["obligation"] = Files.Str(c["obligation"]), ["baseline"] = c["baseline"] is JsonValue b ? Files.Str(b) : null, - ["category"] = Files.Str(c["category"], "violation"), ["applied"] = c["applied"]?.DeepClone() ?? new JsonObject(), - ["reasons"] = Six(c["reasons"]), ["notes"] = Files.Str(c["notes"]), - })]); - return row; - } - - /// The harness's PROBE_ONLY filter, replicated: the rows a variant is expected to run. - internal static bool Selected(IReadOnlyDictionary row, string only) - { - if (only.Length == 0) return true; - foreach (var condition in only.Split(',')) - { - var neq = condition.IndexOf("!=", StringComparison.Ordinal); - if (neq >= 0) { if (row.TryGetValue(condition[..neq], out var v) && v == condition[(neq + 2)..]) return false; } - else - { - var eq = condition.IndexOf('='); - if (!row.TryGetValue(condition[..eq], out var v) || v != condition[(eq + 1)..]) return false; - } - } - return true; - } - - private static string FirstLine(Ran ran) => ProbeRun.FirstLine(ran); -} diff --git a/tools/Prober/Round.cs b/tools/Prober/Round.cs deleted file mode 100644 index 9bbf4057..00000000 --- a/tools/Prober/Round.cs +++ /dev/null @@ -1,123 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// One exploration round (explore or full) under caps the model cannot overrun, run locally only: never in -/// CI, which needs no model. The wall clock is enforced here (the process tree is stopped at the deadline -/// plus a grace), the claim cap and the deadline by `rows`, which refuses new work, and the spend by -/// `--max-budget-usd`. The round always ends cleanly: partial lines repaired, caps cleared, lock released. -/// -internal static class Round -{ - public static int Run(Context ctx, Args a) - { - var mode = a.Positional.FirstOrDefault() ?? throw new Refusal("round takes explore or full", 2); - if (mode is not ("explore" or "full")) throw new Refusal("round takes explore or full", 2); - var minutes = a.Get("minutes") is { Length: > 0 } m ? double.Parse(m, CultureInfo.InvariantCulture) : mode == "explore" ? 10 : 30; - int? claims = a.Get("claims") is { Length: > 0 } c ? int.Parse(c, CultureInfo.InvariantCulture) : mode == "explore" ? 2 : null; - var grace = double.Parse(ctx.Env("PROBE_GRACE") ?? "60", CultureInfo.InvariantCulture); - var ledger = new Ledger(ctx, a.Get("branch")); - var holder = $"round-{mode}-{System.Environment.ProcessId}"; - - if (!RoundLock.Take(ledger, holder, mode, System.Environment.ProcessId).Taken) - { - ctx.Out.WriteLine("prober round: the ledger is locked by another round"); - return 1; - } - var clock = Stopwatch.StartNew(); - string outcome = "completed"; int? exit = null; JsonObject usage = []; var report = ""; - string roundEnded = "", repaired = "", unlocked; - try - { - Program.Run(new Context(ledger.Top, ctx.Environment, TextWriter.Null, ctx.Error), - ["round-start", "--branch", ledger.Branch, "--holder", holder, "--minutes", minutes.ToString(CultureInfo.InvariantCulture), - .. (claims is { } n ? new[] { "--claims", n.ToString(CultureInfo.InvariantCulture) } : [])]); - var caps = $"{minutes.ToString(CultureInfo.InvariantCulture)} minutes" + (claims is { } k ? $" and {k} claim(s)" : ""); - var prompt = - $"Mode: {mode}. Repository {ledger.Top}, branch {ledger.Branch}, base {a.Get("base") ?? "main"}.\n" + - $"This round runs under caps enforced outside you ({caps}). The round runner holds the lock as `{holder}`: do not lock, " + - $"unlock or start a round yourself, and pass `--holder {holder}` to `prober replay`. `prober rows` refuses new work once " + - "the time is spent or the claim cap is reached; when it refuses, start nothing new: record what ran, hand the corpus off " + - "to staging, and report. The process is stopped at the deadline whatever you are doing, so record each claim's results " + - "as soon as its rows have run."; - var argv = new List { "-p", prompt, "--agent", "prober", "--permission-mode", "auto", "--output-format", "json" }; - if (a.Get("usd") is { Length: > 0 } usd) argv.AddRange(["--max-budget-usd", usd]); - var env = new Dictionary(ctx.Environment, StringComparer.Ordinal) { ["PROBE_HOLDER"] = holder }; - - using (new RoundLock.Heartbeat(ledger, holder)) - { - var ran = Proc.Run(FindClaude(ctx), argv, ledger.Top, env, TimeSpan.FromSeconds(minutes * 60 + grace)); - exit = ran.ExitCode; - if (ran.ExitCode == -1) outcome = "stopped at the cap"; - else - { - try - { - if (JsonNode.Parse(ran.Stdout) is JsonObject result) - { - foreach (var key in new[] { "total_cost_usd", "num_turns", "duration_ms", "usage", "subtype" }) usage[key] = result[key]?.DeepClone(); - report = Files.Str(result["result"]); - } - } - catch (JsonException) { report = ran.Stdout.Length > 4000 ? ran.Stdout[^4000..] : ran.Stdout; } - if (ran.ExitCode != 0) outcome = "failed"; - if ((ran.Stdout + ran.Stderr).Contains("Not logged in", StringComparison.Ordinal)) - { - outcome = "not logged in"; - report = "The headless claude CLI is not authenticated. Log it in once (`claude`, then /login, or ANTHROPIC_API_KEY), or start the round from a Claude session with the /probe skill, which caps it with `prober round-start` and stops it at the deadline."; - } - } - } - } - catch (System.ComponentModel.Win32Exception) - { - outcome = "no claude executable"; report = "No claude executable was found; set CLAUDE_BIN. Exploration needs a model and runs locally only."; - } - finally - { - var w = new StringWriter(); - Program.Run(new Context(ledger.Top, ctx.Environment, w, ctx.Error), ["repair", "--branch", ledger.Branch]); repaired = w.ToString().Trim(); - w = new StringWriter(); - Program.Run(new Context(ledger.Top, ctx.Environment, w, ctx.Error), ["round-end", "--branch", ledger.Branch, "--holder", holder]); roundEnded = w.ToString().Trim(); - w = new StringWriter(); - Program.Run(new Context(ledger.Top, ctx.Environment, w, ctx.Error), ["unlock", "--branch", ledger.Branch, "--holder", holder]); unlocked = w.ToString().Trim(); - } - if (unlocked.Contains("replay was skipped", StringComparison.Ordinal)) - Replay.Run(new Context(ledger.Top, ctx.Environment, TextWriter.Null, ctx.Error), new Args(["--branch", ledger.Branch], new HashSet(StringComparer.Ordinal))); - var status = new StringWriter(); - Program.Run(new Context(ledger.Top, ctx.Environment, status, ctx.Error), ["status", "--branch", ledger.Branch]); - var summary = new JsonObject - { - ["mode"] = mode, ["branch"] = ledger.Branch, - ["caps"] = new JsonObject { ["minutes"] = minutes, ["claims"] = claims, ["usd"] = a.Get("usd") }, - ["outcome"] = outcome, ["exit"] = exit, ["seconds"] = Math.Round(clock.Elapsed.TotalSeconds, 1), ["usage"] = usage, - ["round"] = roundEnded, ["repair"] = repaired, ["status"] = status.ToString().Split('\n')[0].Trim(), - }; - var full = (JsonObject)summary.DeepClone(); full["report"] = report; - Files.WriteJson(ledger.P("round-last.json"), full); - ctx.Out.WriteLine(summary.ToJsonString(new JsonSerializerOptions { WriteIndented = true })); - if (report.Length > 0) ctx.Out.WriteLine("\n" + report); - return outcome == "completed" ? 0 : 1; - } - - /// $CLAUDE_BIN, else claude on PATH, else the desktop app's bundled copy. - private static string FindClaude(Context ctx) - { - if (ctx.Env("CLAUDE_BIN") is { } bin) return bin; - foreach (var dir in (ctx.Env("PATH") ?? "").Split(Path.PathSeparator)) - foreach (var name in new[] { "claude", "claude.exe" }) - if (dir.Length > 0 && File.Exists(Path.Combine(dir, name))) return Path.Combine(dir, name); - var bundled = Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Claude", "claude-code"); - if (Directory.Exists(bundled)) - { - var found = Directory.EnumerateFiles(bundled, "claude", SearchOption.AllDirectories) - .Where(p => p.Contains("Contents/MacOS", StringComparison.Ordinal)).Order(StringComparer.Ordinal).LastOrDefault(); - if (found is not null) return found; - } - throw new System.ComponentModel.Win32Exception("no claude executable"); - } -} diff --git a/tools/Prober/RoundLock.cs b/tools/Prober/RoundLock.cs deleted file mode 100644 index e3e16831..00000000 --- a/tools/Prober/RoundLock.cs +++ /dev/null @@ -1,133 +0,0 @@ -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// One round at a time per branch. The lock file sits in the branch's directory (branches/<branch>/round.lock), -/// beside everything a round or a replay writes, so two branches' rounds may run at once and two of one branch never do. -/// It is created exclusively, with its holder's owner and a heartbeat inside; a holder is alive while its heartbeat is -/// younger than . Liveness is the heartbeat alone (no process is probed, no signal sent), -/// which is the same on every OS. Every read-judge-write of the lock runs under an exclusive file create (the mutex), so -/// two takers can never both see it free, or both take over a holder whose heartbeat stopped. -/// -internal static class RoundLock -{ - public const int HeartbeatMinutes = 20; - public static readonly TimeSpan BeatEvery = TimeSpan.FromSeconds(30); - private const int MutexStaleSeconds = 60; - - public static string LockPath(Ledger l) => Path.Combine(l.Dir, "round.lock"); - - public static double Now() => DateTimeOffset.UtcNow.ToUnixTimeMilliseconds() / 1000.0; - - public static bool Alive(JsonObject held) => - Now() - Files.Num(held["heartbeat"], Files.Num(held["since"])) < HeartbeatMinutes * 60; - - public static JsonObject? Holder(Ledger l) => Files.ReadJson(LockPath(l)) is JsonObject held && Alive(held) ? held : null; - - /// - /// The mutex: a file created with , which fails when it exists, atomically - /// on every OS. A crashed writer's mutex goes stale after a minute and is removed. - /// - public static T WithMutex(Ledger l, Func body) - { - Directory.CreateDirectory(l.Dir); - var path = LockPath(l) + ".mutex"; - var deadline = DateTime.UtcNow.AddSeconds(10); - while (true) - { - try - { - using (new FileStream(path, FileMode.CreateNew, FileAccess.Write, FileShare.None)) { } - break; - } - catch (Exception busy) when (busy is IOException or UnauthorizedAccessException) - { - // It exists (or vanished an instant ago, when its holder released it): either way, look again. - // Windows says UnauthorizedAccess, not IOException, for a name whose file is being deleted. - try - { - if (File.Exists(path) && (DateTime.UtcNow - File.GetLastWriteTimeUtc(path)).TotalSeconds > MutexStaleSeconds) File.Delete(path); - } - catch (Exception gone) when (gone is IOException or UnauthorizedAccessException) { } - if (DateTime.UtcNow > deadline) throw new Refusal("the lock's mutex is held for over 10 s; another command is stuck"); - Thread.Sleep(Random.Shared.Next(5, 25)); - } - } - try { return body(); } - finally { try { File.Delete(path); } catch (Exception e) when (e is IOException or UnauthorizedAccessException) { } } - } - - public static void WithMutex(Ledger l, Action body) => WithMutex(l, () => { body(); return 0; }); - - public static (bool Taken, JsonObject? Previous) Take(Ledger l, string holder, string mode, long? pid) - { - var now = Now(); - var record = new JsonObject - { - ["schemaVersion"] = Files.SchemaVersion, ["holder"] = holder, ["mode"] = mode, - ["owner"] = new JsonObject { ["pid"] = pid ?? Environment.ProcessId, ["host"] = Environment.MachineName }, - ["since"] = now, ["heartbeat"] = now, ["pending_replay"] = false, - }; - return WithMutex(l, () => - { - var held = Files.ReadJson(LockPath(l)) as JsonObject; - if (held is not null && Files.Str(held["holder"]) != holder && Alive(held)) return (false, held); - if (held is not null) - { - record["pending_replay"] = held["pending_replay"] is JsonValue p && p.TryGetValue(out var b) && b; - Files.WriteJson(LockPath(l), record); // a holder taking it again, or one whose heartbeat stopped - } - else - { - // No lock: an exclusive create, so even a writer that bypasses the mutex cannot overwrite one. - using var stream = new FileStream(LockPath(l), FileMode.CreateNew, FileAccess.Write, FileShare.None); - using var writer = new StreamWriter(stream); - writer.Write(record.ToJsonString()); - } - return (true, held); - }); - } - - public static void Beat(Ledger l, string holder) => WithMutex(l, () => - { - if (Files.ReadJson(LockPath(l)) is JsonObject held && Files.Str(held["holder"]) == holder) - { - held["heartbeat"] = Now(); - Files.WriteJson(LockPath(l), held); - } - }); - - public static void OweReplay(Ledger l) => WithMutex(l, () => - { - if (Files.ReadJson(LockPath(l)) is JsonObject held) - { - held["pending_replay"] = true; - Files.WriteJson(LockPath(l), held); - } - }); - - /// Releases the lock; returns whether a replay was owed. Refuses another holder's lock. - public static bool Release(Ledger l, string holder) => WithMutex(l, () => - { - var held = Files.ReadJson(LockPath(l)) as JsonObject; - if (held is not null && Files.Str(held["holder"]) != holder) - throw new Refusal($"the lock is {Files.Str(held["holder"])}'s, not {holder}'s"); - if (held is not null) File.Delete(LockPath(l)); - return held?["pending_replay"] is JsonValue p && p.TryGetValue(out var b) && b; - }); - - public static JsonObject? Abandon(Ledger l) => WithMutex(l, () => - { - var held = Files.ReadJson(LockPath(l)) as JsonObject; - if (held is not null) File.Delete(LockPath(l)); - return held; - }); - - /// Keeps a lock alive while a long step runs: a timer beats every 30 s. - public sealed class Heartbeat(Ledger ledger, string holder) : IDisposable - { - private readonly Timer timer = new(_ => { try { Beat(ledger, holder); } catch (Exception) { } }, null, BeatEvery, BeatEvery); - public void Dispose() => timer.Dispose(); - } -} diff --git a/tools/Prober/SchemaCheck.cs b/tools/Prober/SchemaCheck.cs deleted file mode 100644 index 56725a7d..00000000 --- a/tools/Prober/SchemaCheck.cs +++ /dev/null @@ -1,88 +0,0 @@ -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; - -namespace Prober; - -/// -/// The part of JSON Schema the tracked files' schemas use, checked where a file is read, so a field nobody -/// reads is refused instead of kept (decision 102: three invented fields for an exclusion's reason sat in -/// claims.json unread). A keyword outside this part throws: a schema that says more than is checked would be -/// a check that silently does less than it reads. -/// -internal static class SchemaCheck -{ - private static readonly HashSet Annotations = ["$schema", "$id", "title", "description"]; - - /// The schema of Schemas/{name}, embedded in the dll, so it is the one the dll's fingerprint covers. - public static JsonObject Load(string name) - { - using var stream = typeof(SchemaCheck).Assembly.GetManifestResourceStream(name) - ?? throw new InvalidOperationException($"no embedded schema {name}"); - return JsonNode.Parse(stream) as JsonObject ?? throw new InvalidOperationException($"the schema {name} is not a JSON object"); - } - - /// What in the schema refuses, each at its path (exclude[0].reason). - public static List Problems(JsonNode? node, JsonObject schema, string path = "") - { - var problems = new List(); - Check(node, schema, path, problems); - return problems; - } - - private static void Check(JsonNode? node, JsonObject schema, string path, List problems) - { - var at = path.Length == 0 ? "the value" : path; - foreach (var (keyword, _) in schema) - if (!Annotations.Contains(keyword) && keyword is not ("type" or "const" or "required" or "properties" or "additionalProperties" - or "items" or "minItems" or "maxItems" or "minLength" or "pattern")) - throw new InvalidOperationException($"a schema uses {keyword}, which SchemaCheck does not check"); - - if (schema["type"] is JsonValue type && !IsType(node, Files.Str(type))) - { - problems.Add($"{at} is not {Article(Files.Str(type))}"); - return; - } - if (schema["const"] is { } constant && !JsonNode.DeepEquals(node, constant)) problems.Add($"{at} is not {constant.ToJsonString()}"); - - if (node is JsonObject o) - { - foreach (var name in Files.Strings(schema["required"])) - if (!o.ContainsKey(name)) problems.Add($"{Join(path, name)} is required"); - var properties = schema["properties"] as JsonObject ?? []; - foreach (var (name, value) in o) - { - if (properties[name] is JsonObject property) Check(value, property, Join(path, name), problems); - else if (schema["additionalProperties"] is JsonObject rest) Check(value, rest, Join(path, name), problems); - else if (schema["additionalProperties"] is JsonValue allowed && allowed.TryGetValue(out var b) && !b) - problems.Add($"{Join(path, name)} is not a field the schema has"); - } - } - if (node is JsonArray a) - { - if (schema["minItems"] is { } min && a.Count < Files.Num(min)) problems.Add($"{at} has {a.Count} item(s), fewer than {Files.Num(min)}"); - if (schema["maxItems"] is { } max && a.Count > Files.Num(max)) problems.Add($"{at} has {a.Count} item(s), more than {Files.Num(max)}"); - if (schema["items"] is JsonObject items) - for (var i = 0; i < a.Count; i++) Check(a[i], items, $"{path}[{i}]", problems); - } - if (node is JsonValue v && v.TryGetValue(out var s)) - { - if (schema["minLength"] is { } length && s.Length < Files.Num(length)) problems.Add($"{at} is shorter than {Files.Num(length)}"); - if (schema["pattern"] is JsonValue pattern && !Regex.IsMatch(s, Files.Str(pattern), RegexOptions.CultureInvariant)) - problems.Add($"{at} does not match {Files.Str(pattern)}"); - } - } - - private static string Join(string path, string name) => path.Length == 0 ? name : $"{path}.{name}"; - - private static string Article(string type) => type is "object" or "array" or "integer" ? $"an {type}" : $"a {type}"; - - private static bool IsType(JsonNode? node, string type) => type switch - { - "object" => node is JsonObject, - "array" => node is JsonArray, - "string" => node is JsonValue v && v.TryGetValue(out _), - "integer" => node is JsonValue v && v.TryGetValue(out _), - "boolean" => node is JsonValue v && v.TryGetValue(out _), - _ => throw new InvalidOperationException($"a schema names type {type}, which SchemaCheck does not check"), - }; -} diff --git a/tools/Prober/Schemas/claims.schema.json b/tools/Prober/Schemas/claims.schema.json deleted file mode 100644 index 3c944f19..00000000 --- a/tools/Prober/Schemas/claims.schema.json +++ /dev/null @@ -1,189 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/claims.schema.json", - "title": "Claims", - "description": "Tracked (corpus/claims.json): the claims the documents make, each naming the surfaces whose axes it takes. The prober refuses a file that does not match this schema when it reads one (decision 102).", - "type": "array", - "items": { - "type": "object", - "required": [ - "id", - "source", - "surfaces" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "id": { - "type": "string", - "minLength": 1 - }, - "source": { - "type": "string", - "description": "file#heading of the document that states it" - }, - "surfaces": { - "type": "array", - "items": { - "type": "string" - }, - "minItems": 1 - }, - "axes": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - }, - "description": "claim-local axes" - }, - "narrow": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "axis dropped, with the source sentence that limits it (checked verbatim)" - }, - "obligations": { - "type": "array", - "minItems": 1, - "description": "what the claim promises, each checkable on its own: a sentence quoted verbatim from the claim's source document (checked as a narrowing is), under an id a probe's seed names (decision 104); a claim without the field has one obligation, the claim itself, under the id `claim`", - "items": { - "type": "object", - "required": [ - "id", - "sentence" - ], - "properties": { - "id": { - "type": "string", - "pattern": "^[a-z0-9][a-z0-9-]*$" - }, - "sentence": { - "type": "string", - "pattern": "\\S" - }, - "axes": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - }, - "description": "the axis values the obligation concerns, where it is not the whole claim" - }, - "inapplicable": { - "type": "array", - "minItems": 1, - "description": "where the obligation does not apply: a row holding every axis of a declaration at one of its values runs no check of the obligation (Row.Check records it inapplicable with the cite), a pair matched by a declaration is not wanted by the obligation, and a pair no obligation wants is declared inapplicable, never put in a generated row and left out of the gate's count as an exclusion is. `Axes.Build` refuses an axis or value the claim does not list, a malformed cite, and a not-promised id the claim lacks", - "items": { - "type": "object", - "required": [ - "axes", - "cite" - ], - "properties": { - "axes": { - "type": "object", - "additionalProperties": { - "type": "array", - "minItems": 1, - "items": { - "type": "string" - } - }, - "description": "axis to the values where the obligation does not apply; every axis must hold at one of its values" - }, - "cite": { - "type": "string", - "pattern": "^(not-promised|docs|fact|record):", - "description": "why: not-promised:, docs:, fact:KB-NNN or record:NNNN, in the forms the harness's Cite accepts" - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - } - }, - "not-promised": { - "type": "array", - "minItems": 1, - "description": "what the claim does not promise, one entry per intended difference: the sentence of the document or record that makes it intended, quoted verbatim from `source` (checked as a narrowing is), and the axis values it concerns; a note key of outcome expected-refusal, a narrowing or an applicability condition cites an entry by its id (decision 104)", - "items": { - "type": "object", - "required": [ - "id", - "source", - "sentence" - ], - "properties": { - "id": { - "type": "string", - "pattern": "^[a-z0-9][a-z0-9-]*$" - }, - "source": { - "type": "string", - "description": "file#heading of the document or record that states it" - }, - "sentence": { - "type": "string", - "pattern": "\\S" - }, - "axes": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - }, - "description": "the axis values or shapes the difference concerns" - } - }, - "additionalProperties": false - } - }, - "exclude": { - "type": "array", - "description": "pairs no row can hold, each group with the reason why; `prober universe` prints the reasons, since nothing checks them mechanically", - "items": { - "type": "object", - "required": [ - "reason", - "pairs" - ], - "properties": { - "reason": { - "type": "string", - "pattern": "\\S", - "description": "one sentence saying why the pairs cannot exist or are not promised, citing a docs sentence, a record or a KB fact where one applies" - }, - "pairs": { - "type": "array", - "minItems": 1, - "items": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[^=]+=.*$" - }, - "minItems": 2, - "maxItems": 2 - } - } - }, - "additionalProperties": false - } - } - }, - "additionalProperties": false - } -} diff --git a/tools/Prober/Schemas/dismissed-line.schema.json b/tools/Prober/Schemas/dismissed-line.schema.json deleted file mode 100644 index 1556681b..00000000 --- a/tools/Prober/Schemas/dismissed-line.schema.json +++ /dev/null @@ -1,27 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/dismissed-line.schema.json", - "title": "Dismissed finding (one line)", - "description": "Tracked (corpus/dismissed.jsonl): a finding the maintainers dismissed.", - "type": "object", - "required": [ - "id", - "reason" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "id": { - "type": "string" - }, - "reason": { - "type": "string" - }, - "date": { - "type": "string" - } - } -} diff --git a/tools/Prober/Schemas/evidence.schema.json b/tools/Prober/Schemas/evidence.schema.json deleted file mode 100644 index 7d86b9db..00000000 --- a/tools/Prober/Schemas/evidence.schema.json +++ /dev/null @@ -1,170 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/evidence.schema.json", - "title": "Evidence", - "description": "State (branches//evidence/.json): what one replay of a probe did. Only checks that ended as a pass, in rows expected and executed, earn credit, for the settings that applied (the row's, the check's laid over them), while the identity holds. A row without checks is one check of the obligation `claim` with the row's category.", - "type": "object", - "required": [ - "identity", - "variants", - "rows" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "enum": [ - 1, - 2 - ], - "description": "The schema version: 2 records each row's checks. A file without it is version 1, whose rows have no checks." - }, - "probe": { - "type": "string" - }, - "identity": { - "type": "string" - }, - "commit": { - "type": "string" - }, - "result": { - "enum": [ - "pass", - "fail" - ] - }, - "date": { - "type": "string" - }, - "variants": { - "type": "array", - "items": { - "type": "object", - "required": [ - "expected", - "executed" - ], - "properties": { - "env": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Axis name to value, both compared ordinally." - }, - "expected": { - "type": "array", - "items": { - "type": "integer" - } - }, - "executed": { - "type": "array", - "items": { - "type": "integer" - } - } - } - } - }, - "rows": { - "type": "object", - "additionalProperties": { - "type": "object", - "required": [ - "category", - "applied" - ], - "properties": { - "category": { - "enum": [ - "pass", - "violation", - "expected-refusal", - "inapplicable", - "harness-error", - "inconclusive", - "known-finding" - ], - "description": "The highest of the row's own outcome and its checks'." - }, - "applied": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Axis name to value, both compared ordinally: the axes the row read outside any check, which every check of it ran under." - }, - "reasons": { - "type": "array", - "items": { - "type": "string" - } - }, - "findings": { - "type": "array", - "items": { - "type": "string" - }, - "description": "The open findings the row stepped around (Row.Skip in the harness); the row ends as known-finding." - }, - "checks": { - "type": "array", - "description": "Version 2: one entry per check the row ran (Row.Check in the harness), in the order they ended. Absent for a row that ran none.", - "items": { - "type": "object", - "required": [ - "obligation", - "category", - "applied" - ], - "properties": { - "obligation": { - "type": "string", - "description": "An id of the claim's obligations, or `claim` for a claim without them." - }, - "baseline": { - "type": [ - "string", - "null" - ], - "description": "What the check compared against: twin, framework, round-trip, model, refusal, cost, fact:KB-NNN or docs:file#anchor. A pass always has one." - }, - "category": { - "enum": [ - "pass", - "violation", - "expected-refusal", - "inapplicable", - "harness-error", - "inconclusive", - "known-finding" - ] - }, - "applied": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "The axes this check read, at the value that applied, and none another check read." - }, - "reasons": { - "type": "array", - "items": { - "type": "string" - }, - "description": "Six at most, as the row's." - }, - "notes": { - "type": "string", - "description": "How the check was compared, and the citations of what did not apply or was refused, separated by ;." - } - }, - "additionalProperties": false - } - } - } - } - } - } -} diff --git a/tools/Prober/Schemas/finding-line.schema.json b/tools/Prober/Schemas/finding-line.schema.json deleted file mode 100644 index 317c24f6..00000000 --- a/tools/Prober/Schemas/finding-line.schema.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/finding-line.schema.json", - "title": "Finding event (one line)", - "description": "State (findings.jsonl): open, fixed or withdrawn. The last line per id wins; a fixed line counts once its commit is in HEAD's history.", - "type": "object", - "required": [ - "id", - "status" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "id": { - "type": "string" - }, - "claim": { - "type": [ - "string", - "null" - ] - }, - "status": { - "enum": [ - "open", - "fixed", - "withdrawn", - "dismissed" - ] - }, - "commit": { - "type": "string" - }, - "probe": { - "type": "string" - }, - "reason": { - "type": "string" - } - } -} diff --git a/tools/Prober/Schemas/footprint-line.schema.json b/tools/Prober/Schemas/footprint-line.schema.json deleted file mode 100644 index f5e41f2e..00000000 --- a/tools/Prober/Schemas/footprint-line.schema.json +++ /dev/null @@ -1,60 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/footprint-line.schema.json", - "title": "Footprint (one line)", - "description": "State (footprints.jsonl): the source files a run of a probe executed, the compile-time paths and the toolchain, each with its id. The last line per probe wins.", - "type": "object", - "required": [ - "probe", - "footprint" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "probe": { - "type": "string" - }, - "commit": { - "type": "string" - }, - "footprint": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Axis name to value, both compared ordinally." - }, - "compileTime": { - "type": "object", - "additionalProperties": { - "type": [ - "string", - "null" - ] - } - }, - "watched": { - "type": "object", - "additionalProperties": { - "type": [ - "string", - "null" - ] - }, - "description": "The paths the probe's registry entry watches, each with its object id at the commit the run built (null where it does not exist). A footprint line without one of the entry's watched paths is not current." - }, - "toolchain": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Axis name to value, both compared ordinally." - }, - "date": { - "type": "string" - } - } -} diff --git a/tools/Prober/Schemas/ledger-line.schema.json b/tools/Prober/Schemas/ledger-line.schema.json deleted file mode 100644 index 188d2a8f..00000000 --- a/tools/Prober/Schemas/ledger-line.schema.json +++ /dev/null @@ -1,44 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/ledger-line.schema.json", - "title": "Ledger line (legacy, one line)", - "description": "State (ledger.jsonl): what a round recorded per pair. Informational: it earns no credit; only evidence does.", - "type": "object", - "required": [ - "claim", - "pair", - "commit", - "result" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "claim": { - "type": "string" - }, - "pair": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[^=]+=.*$" - }, - "minItems": 2, - "maxItems": 2 - }, - "commit": { - "type": "string" - }, - "result": { - "type": "string" - }, - "probe": { - "type": "string" - }, - "date": { - "type": "string" - } - } -} diff --git a/tools/Prober/Schemas/lock.schema.json b/tools/Prober/Schemas/lock.schema.json deleted file mode 100644 index 5e9e290d..00000000 --- a/tools/Prober/Schemas/lock.schema.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/lock.schema.json", - "title": "Round lock", - "description": "State (branches//round.lock): one round at a time per branch. Created exclusively; alive while its heartbeat is recent.", - "type": "object", - "required": [ - "holder", - "mode", - "since", - "heartbeat" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "holder": { - "type": "string" - }, - "mode": { - "enum": [ - "replay", - "explore", - "full" - ] - }, - "owner": { - "type": "object", - "properties": { - "pid": { - "type": [ - "integer", - "null" - ] - }, - "host": { - "type": "string" - } - } - }, - "since": { - "type": "number" - }, - "heartbeat": { - "type": "number" - }, - "pending_replay": { - "type": "boolean" - } - } -} diff --git a/tools/Prober/Schemas/registry.schema.json b/tools/Prober/Schemas/registry.schema.json deleted file mode 100644 index 3be5eb0a..00000000 --- a/tools/Prober/Schemas/registry.schema.json +++ /dev/null @@ -1,143 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/registry.schema.json", - "title": "Probe registry", - "description": "Tracked (corpus/registry.json): every probe, the claim it covers, how to select it, and the process variants it runs in.", - "type": "object", - "required": [ - "probes" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "probes": { - "type": "object", - "additionalProperties": { - "type": "object", - "required": [ - "claim", - "filter" - ], - "properties": { - "claim": { - "type": "string" - }, - "filter": { - "type": "string" - }, - "variants": { - "type": "array", - "items": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Axis name to value, both compared ordinally." - }, - "description": "one process per environment map" - }, - "retired": { - "type": "boolean" - }, - "watched": { - "type": "array", - "items": { - "type": "string", - "minLength": 1 - }, - "description": "Paths, files or directories relative to the repository root, that the probe's result depends on and its footprint cannot see: a variant catalogue under test/ compiled into the probes, a build input a child process reads. The replay records each with its object id in the probe's footprint line; a change to one stales the footprint (Ledger.FootprintCurrent, FootprintChangedSince), and the release gate counts a change to one as touching the probe's claim. Part of the probe's identity, as the whole entry but its seeds is." - }, - "seeds": { - "type": "array", - "description": "One seeded defect per obligation of the probe's claim that this probe checks (decision 104): each as `seed` below, naming the obligation it breaks. A probe may carry `seed` and `seeds` both. Empty in the entry `prober new` stages, until the writer registers each with `prober register --obligation O`, which replaces a seed naming O; `seeds` and Seeds.Problems refuse a probe with none. Not part of the probe's identity.", - "items": { - "type": "object", - "required": [ - "obligation", - "defect" - ], - "anyOf": [ - { - "required": [ - "patch" - ] - }, - { - "required": [ - "env" - ] - } - ], - "properties": { - "obligation": { - "type": "string", - "description": "The id of the claim's obligation this seed breaks (claims.json `obligations[].id`, or `claim` for a claim without the field)." - }, - "patch": { - "type": "string" - }, - "env": { - "type": "object", - "additionalProperties": { - "type": "string" - } - }, - "defect": { - "type": "string", - "minLength": 1 - } - }, - "additionalProperties": false - } - }, - "seed": { - "type": "object", - "required": [ - "defect" - ], - "anyOf": [ - { - "required": [ - "patch" - ] - }, - { - "required": [ - "env" - ] - } - ], - "properties": { - "patch": { - "type": "string", - "description": "A unified diff against the product files (the replay's \"include\"), relative to the corpus directory: seeds/.patch." - }, - "env": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Environment variables set for the seeded build and every variant's run." - }, - "defect": { - "type": "string", - "minLength": 1, - "description": "What the seed breaks, in one sentence." - }, - "obligation": { - "type": "string", - "description": "The id of the claim's obligation this seed breaks (decision 104); absent, the seed proves the probe and counts for the claim's one implicit obligation only where the claim lists none." - } - }, - "additionalProperties": false, - "description": "The product defect `prober seeds` applies in a scratch worktree to prove the probe can fail (decision 100). Not part of the probe's identity." - } - }, - "additionalProperties": false - } - } - } -} diff --git a/tools/Prober/Schemas/round.schema.json b/tools/Prober/Schemas/round.schema.json deleted file mode 100644 index dda984fe..00000000 --- a/tools/Prober/Schemas/round.schema.json +++ /dev/null @@ -1,40 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/round.schema.json", - "title": "Round caps", - "description": "State (branches//round.json): the caps `rows` enforces for the round that holds the lock.", - "type": "object", - "required": [ - "holder", - "deadline", - "claims" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "holder": { - "type": "string" - }, - "started": { - "type": "number" - }, - "deadline": { - "type": "number" - }, - "max_claims": { - "type": [ - "integer", - "null" - ] - }, - "claims": { - "type": "array", - "items": { - "type": "string" - } - } - } -} diff --git a/tools/Prober/Schemas/rows.schema.json b/tools/Prober/Schemas/rows.schema.json deleted file mode 100644 index 290f9a7b..00000000 --- a/tools/Prober/Schemas/rows.schema.json +++ /dev/null @@ -1,52 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/rows.schema.json", - "title": "Rows", - "description": "Tracked (corpus/rows/.json.gz, gzipped; rows/.json in the staging): the rows one probe runs, a pairwise covering array over its claim's axes. One file per registered probe, written whole by `prober rows --probe P`, never appended; no two rows of a file have the same axis map (ProbeRowsTests).", - "type": "object", - "required": [ - "claim", - "rows" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "claim": { - "type": "string", - "description": "The id of the probe's claim in claims.json; the file is named for the probe, so the claim is kept inside." - }, - "rows": { - "type": "array", - "items": { - "type": "object", - "required": [ - "row" - ], - "properties": { - "row": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Axis name to value, both compared ordinally." - }, - "pairs": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[^=]+=.*$" - }, - "minItems": 2, - "maxItems": 2 - } - } - } - } - } - } -} diff --git a/tools/Prober/Schemas/run-line.schema.json b/tools/Prober/Schemas/run-line.schema.json deleted file mode 100644 index 5aca7f82..00000000 --- a/tools/Prober/Schemas/run-line.schema.json +++ /dev/null @@ -1,43 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/run-line.schema.json", - "title": "Run (one line)", - "description": "State (runs.jsonl): one replay result of one probe, with the identity it ran under.", - "type": "object", - "required": [ - "probe", - "result", - "commit" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "probe": { - "type": "string" - }, - "result": { - "enum": [ - "pass", - "fail" - ] - }, - "commit": { - "type": "string" - }, - "date": { - "type": "string" - }, - "seconds": { - "type": [ - "number", - "null" - ] - }, - "identity": { - "type": "string" - } - } -} diff --git a/tools/Prober/Schemas/seeds-run.schema.json b/tools/Prober/Schemas/seeds-run.schema.json deleted file mode 100644 index 824b9919..00000000 --- a/tools/Prober/Schemas/seeds-run.schema.json +++ /dev/null @@ -1,131 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/seeds-run.schema.json", - "title": "Seeds run", - "description": "State (seeds-last.json): the last `prober seeds` run on the branch, one outcome per seed it ran (a probe without one has a single outcome, `no seed`), the probes it skipped and why, and per probe the run that last caught every one of its seeds, which decides what the next run without --all skips (design/adr/0107). Never evidence: a seeded run proves the probe, not the product.", - "type": "object", - "required": [ - "branch", - "commit", - "seeds" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "branch": { - "type": "string" - }, - "commit": { - "type": "string" - }, - "seconds": { - "type": "number" - }, - "all": { - "type": "boolean", - "description": "Whether the run was `--all`, every probe whatever its record says, as a release runs it. Absent in a file written before footprint selection." - }, - "probes": { - "type": "object", - "description": "Per probe, the run that last caught every seed it owes; a probe with a seed missed, or that never ran, has no entry, so it runs next time. Carried forward for the probes a run skipped.", - "additionalProperties": { - "type": "object", - "required": [ - "identity", - "commit" - ], - "properties": { - "identity": { - "type": "string", - "description": "The seeds identity it was caught under: the probe's identity, its seeds and the content of their patches." - }, - "commit": { - "type": "string", - "description": "HEAD of the run that caught them; the product footprint and the patched files are compared with it." - }, - "seeds": { - "type": "integer", - "description": "How many seeds were caught." - } - }, - "additionalProperties": false - } - }, - "skipped": { - "type": "array", - "description": "The probes this run skipped, unchanged since every seed of theirs was caught, each with the reason printed.", - "items": { - "type": "object", - "required": [ - "probe", - "reason" - ], - "properties": { - "probe": { - "type": "string" - }, - "reason": { - "type": "string" - } - }, - "additionalProperties": false - } - }, - "seeds": { - "type": "array", - "items": { - "type": "object", - "required": [ - "probe", - "claim", - "result" - ], - "properties": { - "probe": { - "type": "string" - }, - "claim": { - "type": "string" - }, - "obligation": { - "type": [ - "string", - "null" - ], - "description": "The claim's obligation the seed breaks (decision 104), or null for a seed that names none." - }, - "seed": { - "type": "string", - "description": "The patch and the environment toggle applied, or (none)." - }, - "result": { - "enum": [ - "caught", - "missed", - "no seed", - "does not apply", - "build failed" - ] - }, - "violations": { - "type": "integer", - "description": "Rows the seed made violations that the unseeded probe does not have." - }, - "rows": { - "type": "integer", - "description": "Rows that ran under the seed." - }, - "detail": { - "type": "string" - } - }, - "additionalProperties": false - }, - "description": "One outcome per seed this run ran; the probes it skipped have none." - } - }, - "additionalProperties": false -} diff --git a/tools/Prober/Schemas/universe.schema.json b/tools/Prober/Schemas/universe.schema.json deleted file mode 100644 index 776635f3..00000000 --- a/tools/Prober/Schemas/universe.schema.json +++ /dev/null @@ -1,120 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/universe.schema.json", - "title": "Universe", - "description": "State (branches//universe.json): each claim's axes and values, derived from the claims, the axes file and the generated axes.", - "type": "object", - "required": [ - "claims" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "base": { - "type": "string" - }, - "commit": { - "type": "string" - }, - "generated_versions": { - "type": "object", - "additionalProperties": { - "type": "string" - }, - "description": "Axis name to value, both compared ordinally." - }, - "claims": { - "type": "array", - "items": { - "type": "object", - "required": [ - "id", - "axes" - ], - "properties": { - "id": { - "type": "string" - }, - "source": { - "type": "string" - }, - "surfaces": { - "type": "array", - "items": { - "type": "string" - } - }, - "watched": { - "type": "array", - "items": { - "type": "string" - } - }, - "axes": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "exclude": { - "type": "array", - "items": { - "type": "array", - "items": { - "type": "string", - "pattern": "^[^=]+=.*$" - }, - "minItems": 2, - "maxItems": 2 - } - }, - "obligations": { - "type": "array", - "description": "written only where an obligation of the claim declares where it does not apply: every obligation's id with its declarations, as claims.json holds them, so the covering generator, status and the gate know the pairs each wants", - "items": { - "type": "object", - "required": [ - "id" - ], - "properties": { - "id": { - "type": "string" - }, - "inapplicable": { - "type": "array", - "items": { - "type": "object", - "required": [ - "axes", - "cite" - ], - "properties": { - "axes": { - "type": "object", - "additionalProperties": { - "type": "array", - "items": { - "type": "string" - } - } - }, - "cite": { - "type": "string" - } - } - } - } - } - } - } - } - } - } - } -} diff --git a/tools/Prober/Schemas/waivers.schema.json b/tools/Prober/Schemas/waivers.schema.json deleted file mode 100644 index 1b0d99c1..00000000 --- a/tools/Prober/Schemas/waivers.schema.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "$schema": "https://json-schema.org/draft/2020-12/schema", - "$id": "https://codometis.typekit/probe/schemas/waivers.schema.json", - "title": "Waivers", - "description": "Tracked (corpus/waivers.json): a claim's gate obligation met without a probe. Counts only with a reason and a reviewer.", - "type": "array", - "items": { - "type": "object", - "required": [ - "claim", - "reason", - "reviewed_by" - ], - "properties": { - "schemaVersion": { - "type": "integer", - "const": 1, - "description": "The schema version. A file or line without it is version 1 (the Python baseline)." - }, - "claim": { - "type": "string" - }, - "reason": { - "type": "string", - "minLength": 1 - }, - "reviewed_by": { - "type": "string", - "minLength": 1 - }, - "date": { - "type": "string" - } - } - } -} diff --git a/tools/Prober/Seeds.cs b/tools/Prober/Seeds.cs deleted file mode 100644 index 92e5fe86..00000000 --- a/tools/Prober/Seeds.cs +++ /dev/null @@ -1,396 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// Proves that each probe can fail (decision 100). A probe's registry entry names a seeded product defect: -/// a patch against the product files (the replay's include, src here), an environment toggle, -/// or both, and a sentence saying what it breaks. This applies each seed in a scratch worktree of its own, -/// builds, replays that probe there, and counts the seed caught only where a check ends as a violation that the -/// unseeded probe does not have in the same row for the same obligation and that no harness error raised, and, for a -/// seed naming an obligation, only a check naming that obligation (a row without checks is one check, claim). -/// Nothing it runs is recorded as evidence: a seeded run proves the probe, -/// not the product. Never in the checkout and never in the replay's worktree; one build per seed, so it runs -/// locally, not in CI. A missed seed, a probe without one, a seed that no longer applies and a seeded build -/// that fails all fail the command. Without --all it runs only the probes whose seeds identity or product -/// footprint changed since the run that last caught every seed of the probe (design/adr/0107), and says why it skips -/// each of the others; the release runs --all. -/// -internal static class Seeds -{ - /// How one seed ended; is the claim's obligation it breaks, or null for a seed that proves the probe alone. - internal sealed record Outcome(string Probe, string Claim, string? Obligation, string Seed, string Result, int Violations, int Rows, string Detail); - - /// - /// Every seed of a registry entry, each with the obligation it names: the one `seed` (its `obligation`, if - /// any) and each of `seeds` (decision 104). An entry without either has none. - /// - internal static List<(string? Obligation, JsonObject Seed)> SeedsOf(JsonObject entry) - { - var result = new List<(string?, JsonObject)>(); - if (entry["seed"] is JsonObject one) result.Add((Files.Str(one["obligation"]) is { Length: > 0 } o ? o : null, one)); - foreach (var seed in (entry["seeds"] as JsonArray ?? []).OfType()) - result.Add((Files.Str(seed["obligation"]) is { Length: > 0 } o ? o : null, seed)); - return result; - } - - private static string Named(string probe, string? obligation) => obligation is null ? probe : $"{probe}/{obligation}"; - - public static int Run(Context ctx, Args a, IProbeAdapter? adapter = null) - { - var ledger = new Ledger(ctx, a.Get("branch")); - var config = ledger.AxesBlock()["replay"] as JsonObject ?? throw new Refusal("the axes file's block has no \"replay\" configuration"); - adapter ??= new DotNetAdapter(config); - var registry = ledger.Registry(); - var states = ledger.ProbeStates(includeRetired: false).Where(s => !s.Retired).ToList(); - if (a.Get("probe") is { } only) - { - states = [.. states.Where(s => s.Probe == only)]; - if (states.Count == 0) throw new Refusal($"no registered probe {only} that is not retired", 2); - } - var head = ledger.Head; - var home = Replay.Home(ctx, ledger); - var worktree = Path.Combine(home, "seeds"); - var include = Files.Strings(config["include"]).ToArray() is { Length: > 0 } inc ? inc : ["src"]; - // The probes whose every seed the last runs caught, each at the commit and the identity it was caught at; a probe - // named by --probe, and every probe under --all, runs whatever its record says. - var last = Files.ReadJson(ledger.P(LastFile)) as JsonObject; - var record = (last?["probes"] as JsonObject ?? []).Where(kv => kv.Value is JsonObject).ToDictionary(kv => kv.Key, kv => (JsonObject)kv.Value!.DeepClone(), StringComparer.Ordinal); - var identities = states.ToDictionary(s => s.Probe, s => SeedsIdentity(ledger, s.Probe), StringComparer.Ordinal); - var skipped = new List<(string Probe, string Why)>(); - if (!a.Flag("all") && a.Get("probe") is null) - { - var footprints = ledger.Footprints(); - var runs = new List(); - foreach (var s in states) - { - var why = Stale(ledger, s.Probe, registry[s.Probe], record.GetValueOrDefault(s.Probe), identities[s.Probe], footprints); - if (why.Run) runs.Add(s); - else skipped.Add((s.Probe, why.Reason)); - ctx.Out.WriteLine($" {(why.Run ? "runs" : "skips")} {s.Probe}: {why.Reason}"); - } - states = runs; - } - var seedsOf = states.ToDictionary(s => s.Probe, s => SeedsOf(registry[s.Probe]), StringComparer.Ordinal); - // A probe without a seed owes one per obligation its claim lists (decision 104), each an outcome of its own; - // under a claim without obligations, one. - var listed = ledger.Claims().ToDictionary(c => Files.Str(c["id"]), Claim.ObligationIds, StringComparer.Ordinal); - List<(string? Obligation, JsonNode? Seed)> Owed(Ledger.ProbeState s) => - seedsOf[s.Probe].Count > 0 ? [.. seedsOf[s.Probe].Select(x => (x.Obligation, (JsonNode?)x.Seed))] - : listed.GetValueOrDefault(s.Claim) is { } ids && ids is not [Claim.ImplicitObligation] ? [.. ids.Select(id => ((string?)id, (JsonNode?)null))] - : [(null, null)]; - ctx.Out.WriteLine($"prober seeds: {ledger.Branch} at {head[..7]}: {seedsOf.Values.Sum(x => x.Count)} seeds of {states.Count} probes, in {worktree}" + - (skipped.Count > 0 ? $"; {skipped.Count} probes skipped, unchanged since every seed of theirs was caught (--all runs them)" : "")); - if (a.Flag("dry-run")) - { - foreach (var s in states) - { - foreach (var (obligation, seed) in Owed(s)) ctx.Out.WriteLine($" {Named(s.Probe, obligation)}: {Describe(seed)}"); - } - return 0; - } - - var clock = Stopwatch.StartNew(); - var outcomes = new List(); - // Nothing to run: no worktree and no build, only the record carried forward. - if (states.Count > 0) - { - Directory.CreateDirectory(home); - if (!Directory.Exists(worktree)) - { - var added = Git.Run(ledger.Root, "worktree", "add", "--detach", worktree, head); - if (added.ExitCode != 0) throw new Refusal("could not add the scratch worktree: " + added.Stderr.Trim()); - } - var environment = ProbeRun.Environment(ctx, adapter, home); - var results = Directory.CreateTempSubdirectory("prober-seeds-").FullName; - try - { - // The baseline: the checks each probe already fails without a seed, by row and obligation. From the ledger - // where its evidence is current, else from an unseeded run here, so a seed is credited only with violations - // of its own. - var baseline = new Dictionary>(StringComparer.Ordinal); - var unseeded = new List(); - foreach (var s in states) - { - if (!s.Stale && Files.ReadJson(ledger.EvidencePath(s.Probe)) is JsonObject evidence) - baseline[s.Probe] = [.. (evidence["rows"] as JsonObject ?? []).Where(kv => kv.Value is JsonObject) - .SelectMany(kv => Ledger.ChecksOf((JsonObject)kv.Value!).Where(c => c.Category == "violation") - .Select(c => (int.Parse(kv.Key, CultureInfo.InvariantCulture), c.Obligation)))]; - else unseeded.Add(s); - } - ctx.Out.WriteLine($" baseline: {states.Count - unseeded.Count} from the ledger, {unseeded.Count} to run unseeded"); - if (unseeded.Count > 0) - { - Reset(worktree, head); - Replay.Overlay(ledger, config, worktree); - var build = Timed(() => adapter.Build(worktree, environment), out var seconds); - ctx.Out.WriteLine($" unseeded build: {Seconds(seconds)}, {FirstLine(build)}"); - if (build.ExitCode != 0) { ctx.Out.WriteLine(build.Stdout); throw new Refusal("the unseeded build failed; no seed can be judged"); } - foreach (var s in unseeded) - { - var rows = RunProbe(adapter, worktree, s, environment, null, results, "base"); - baseline[s.Probe] = Violating(rows); - ctx.Out.WriteLine($" {s.Probe}: {rows.Count} rows, {baseline[s.Probe].Select(x => x.Row).Distinct().Count()} violations unseeded"); - } - } - - foreach (var s in states) - { - // A seed missing is an outcome "no seed"; a probe with several runs each under its obligation. - foreach (var (obligation, seed) in Owed(s)) - { - var outcome = One(ledger, config, adapter, worktree, head, include, environment, results, s, obligation, seed, baseline[s.Probe]); - outcomes.Add(outcome); - ctx.Out.WriteLine($" {Named(s.Probe, obligation)} ({s.Claim}): {outcome.Result}{(outcome.Detail.Length > 0 ? ", " + outcome.Detail : "")}"); - } - } - } - finally - { - // The worktree is left at HEAD, every seed undone, for the next run to build incrementally. - Reset(worktree, head); - try { Directory.Delete(results, recursive: true); } catch (IOException) { } - } - } - - // A probe is recorded only where every seed it owes was caught, so a missed one runs again next time, whatever changed. - foreach (var s in states) - { - var own = outcomes.Where(o => o.Probe == s.Probe).ToList(); - if (own.Count > 0 && own.All(o => o.Result == "caught")) - record[s.Probe] = new JsonObject { ["identity"] = identities[s.Probe], ["commit"] = head, ["seeds"] = own.Count }; - else record.Remove(s.Probe); - } - foreach (var gone in record.Keys.Where(probe => !registry.ContainsKey(probe)).ToList()) record.Remove(gone); - - Files.WriteJson(ledger.P(LastFile), new JsonObject - { - ["schemaVersion"] = Files.SchemaVersion, ["branch"] = ledger.Branch, ["commit"] = head, - ["seconds"] = Math.Round(clock.Elapsed.TotalSeconds, 1), - ["all"] = a.Flag("all"), - ["probes"] = new JsonObject(record.OrderBy(kv => kv.Key, StringComparer.Ordinal).Select(kv => KeyValuePair.Create(kv.Key, (JsonNode?)kv.Value))), - ["skipped"] = new JsonArray([.. skipped.Select(x => (JsonNode)new JsonObject { ["probe"] = x.Probe, ["reason"] = x.Why })]), - ["seeds"] = new JsonArray([.. outcomes.Select(o => (JsonNode)new JsonObject - { - ["probe"] = o.Probe, ["claim"] = o.Claim, ["obligation"] = o.Obligation, ["seed"] = o.Seed, ["result"] = o.Result, - ["violations"] = o.Violations, ["rows"] = o.Rows, ["detail"] = o.Detail, - })]), - }); - var caught = outcomes.Count(o => o.Result == "caught"); - var counts = outcomes.GroupBy(o => o.Result, StringComparer.Ordinal).OrderBy(g => g.Key, StringComparer.Ordinal).Select(g => $"{g.Count()} {g.Key}"); - ctx.Out.WriteLine($"prober seeds: {string.Join(", ", counts)}, {Seconds(clock.Elapsed.TotalSeconds)} in all"); - return caught == outcomes.Count ? 0 : 1; - } - - public const string LastFile = "seeds-last.json"; - - /// - /// What a probe's seeded runs depend on besides the product: the probe's identity (: - /// its registry entry, the harness and probe sources, its rows, the axes file, the build environment, the tool), - /// its seeds, which that identity leaves out, and the content of each patch they apply. - /// - internal static string SeedsIdentity(Ledger ledger, string probe) - { - using var sha = System.Security.Cryptography.IncrementalHash.CreateHash(System.Security.Cryptography.HashAlgorithmName.SHA256); - void Add(string text) => sha.AppendData(System.Text.Encoding.UTF8.GetBytes(text + "\0")); - Add(ledger.Identity(probe)); - foreach (var (obligation, seed) in SeedsOf(ledger.Registry().GetValueOrDefault(probe) ?? [])) - { - Add(obligation ?? "-"); - Add(Files.Canonical(seed)); - var patch = Files.Str(seed["patch"]); - if (patch.Length > 0 && File.Exists(ledger.CorpusPath(patch))) sha.AppendData(File.ReadAllBytes(ledger.CorpusPath(patch))); - } - return Convert.ToHexStringLower(sha.GetHashAndReset())[..16]; - } - - /// - /// Whether a probe's seeds run without --all, and why: they run unless every seed was caught at a recorded - /// commit, under the seeds identity the probe has now, and neither the product footprint the last replay recorded for - /// it nor a file its seeds patch changed since that commit. A probe without a recorded footprint runs. - /// - internal static (bool Run, string Reason) Stale(Ledger ledger, string probe, JsonObject entry, JsonObject? caught, string identity, Dictionary footprints) - { - if (caught is null) return (true, "no run caught every seed of it yet"); - var commit = Files.Str(caught["commit"]); - var at = commit.Length >= 7 ? commit[..7] : commit; - if (Files.Str(caught["identity"]) != identity) return (true, $"its identity or its seeds changed since they were caught at {at}"); - if (!footprints.ContainsKey(probe)) return (true, "no product footprint recorded for it (a replay records one)"); - if (ledger.FootprintChangedSince(probe, commit, footprints)) return (true, $"its product footprint changed since {at}"); - var patched = SeedsOf(entry).Select(x => Files.Str(x.Seed["patch"])).Where(p => p.Length > 0 && File.Exists(ledger.CorpusPath(p))) - .SelectMany(p => Patched(File.ReadAllText(ledger.CorpusPath(p)))).Distinct(StringComparer.Ordinal).ToArray(); - if (patched.Length > 0 && Git.Run(ledger.Top, ["diff", "--quiet", commit, "HEAD", "--", .. patched]).ExitCode != 0) - return (true, $"a file its seeds patch changed since {at}"); - return (false, $"every seed caught at {at}, and neither its identity nor its product footprint changed since"); - } - - /// The files a patch changes, as its +++ b/ lines name them. - private static IEnumerable Patched(string patch) => - patch.Split('\n').Where(line => line.StartsWith("+++ b/", StringComparison.Ordinal)).Select(line => line["+++ b/".Length..].TrimEnd('\r')); - - /// Applies one probe's seed, builds, runs the probe, and undoes the seed. - private static Outcome One(Ledger ledger, JsonObject config, IProbeAdapter adapter, string worktree, string head, string[] include, - Dictionary environment, string results, Ledger.ProbeState s, string? obligation, JsonNode? seed, - HashSet<(int Row, string Obligation)> baseline) - { - Outcome End(string result, string detail, int violations = 0, int rows = 0) => new(s.Probe, s.Claim, obligation, Describe(seed), result, violations, rows, detail); - - Reset(worktree, head); - Replay.Overlay(ledger, config, worktree); - var (refused, detail, patch, env) = Validate(ledger, include, seed, worktree); - if (refused is not null) return End(refused, detail); - if (patch is not null) - { - var ran = Git.Run(worktree, "apply", "--whitespace=nowarn", patch); - if (ran.ExitCode != 0) return End("does not apply", FirstLine(ran.Stderr)); - } - try - { - var seeded = new Dictionary(environment, StringComparer.Ordinal); - foreach (var (k, v) in env ?? []) seeded[k] = Files.Str(v); - var build = Timed(() => adapter.Build(worktree, seeded), out var buildSeconds); - if (build.ExitCode != 0) return End("build failed", $"the seeded build: {FirstLine(build)}"); - var rows = RunProbe(adapter, worktree, s, environment, env, results, "seed"); - // A check that broke the harness says nothing about the claim, even where it also returned a failure; a - // seed naming an obligation is caught only by a check naming it (decision 104), a seed naming none by any. - var fresh = rows.SelectMany(kv => kv.Value.Checks.Select(c => (Row: kv.Key, Check: c))) - .Where(x => x.Check.CleanViolation && !baseline.Contains((x.Row, x.Check.Obligation))).OrderBy(x => x.Row).ToList(); - var own = fresh.Where(x => obligation is null || x.Check.Obligation == obligation).ToList(); - var caughtRows = own.Select(x => x.Row).Distinct().Count(); - var tally = string.Join(", ", rows.Values.GroupBy(r => r.Category, StringComparer.Ordinal).OrderBy(g => g.Key, StringComparer.Ordinal).Select(g => $"{g.Key} {g.Count()}")); - var broken = rows.Values.Count(r => r.HarnessError); - var harness = broken > 0 ? $"; {broken} ended in a harness error, which is no catch" : ""; - // Violations by checks of other obligations are no catch of this one; say whose they were, a row without checks being `claim`'s. - var others = fresh.Count > own.Count - ? $"; {fresh.Select(x => x.Row).Distinct().Count()} violate by a check naming {string.Join(", ", fresh.Select(x => x.Check.Obligation).Distinct().Order(StringComparer.Ordinal))}, not {obligation}" - : ""; - if (own.Count == 0) return End("missed", $"no row violates that did not already ({rows.Count} rows: {(tally.Length > 0 ? tally : "none ran")}{harness}{others}; build {Seconds(buildSeconds)})", 0, rows.Count); - var example = own[0]; - var named = example.Check.Obligation == Claim.ImplicitObligation && rows[example.Row].Checks.Count == 1 ? "" : " " + example.Check.Obligation; - return End("caught", $"{caughtRows} of {rows.Count} rows violate; e.g. #{example.Row}{named}: {Cut(example.Check.Reason)}", caughtRows, rows.Count); - } - finally - { - if (patch is not null) Git.Run(worktree, "apply", "-R", "--whitespace=nowarn", patch); - } - } - - /// - /// Why a seed cannot be applied in (a result and a detail), or a null result with - /// the patch's full path and the environment toggle to apply. A seed names its defect and a patch, an - /// environment toggle or both; a patch exists in the corpus, touches only the product files and applies. - /// - private static (string? Refused, string Detail, string? Patch, JsonObject? Env) Validate(Ledger ledger, string[] include, JsonNode? seed, string worktree) - { - if (seed is not JsonObject entry) return ("no seed", "its registry entry names none", null, null); - var patch = Files.Str(entry["patch"]); - var env = entry["env"] as JsonObject; - if (Files.Str(entry["defect"]).Length == 0 || (patch.Length == 0 && env is not { Count: > 0 })) - return ("no seed", "a seed names its defect and a patch or an environment toggle", null, null); - if (patch.Length == 0) return (null, "", null, env); - - var file = Path.GetFullPath(ledger.CorpusPath(patch)); - if (!File.Exists(file)) return ("does not apply", $"{patch} is not in the corpus", null, null); - var touched = Git.Run(worktree, "apply", "--numstat", file); - if (touched.ExitCode != 0) return ("does not apply", FirstLine(touched.Stderr), null, null); - var outside = touched.Stdout.Split('\n', StringSplitOptions.RemoveEmptyEntries).Select(line => line.Split('\t')[^1].Trim()) - .Where(path => !include.Any(p => path.StartsWith(p.TrimEnd('/') + "/", StringComparison.Ordinal))).ToList(); - if (outside.Count > 0) return ("does not apply", $"it patches {string.Join(", ", outside)}, outside the product files ({string.Join(", ", include)})", null, null); - var check = Git.Run(worktree, "apply", "--check", file); - return check.ExitCode != 0 ? ("does not apply", FirstLine(check.Stderr), null, null) : (null, "", file, env); - } - - /// - /// What `seeds` would refuse before building anything, against this checkout: a probe without a seed, or a - /// seed that does not apply. No build, so the tests run it on every build and a probe registered without a - /// seed, or a product change that leaves a seed behind, fails there and not only at the next local run. - /// - internal static List Problems(Ledger ledger) - { - var config = ledger.AxesBlock()["replay"] as JsonObject ?? []; - var include = Files.Strings(config["include"]).ToArray() is { Length: > 0 } inc ? inc : ["src"]; - var problems = new List(); - // The obligations each claim lists (or its one implicit obligation), and the seeds that name each. - var obligations = ledger.Claims().ToDictionary(c => Files.Str(c["id"]), Claim.ObligationIds, StringComparer.Ordinal); - var seeded = new Dictionary>(StringComparer.Ordinal); - foreach (var (probe, entry) in ledger.Registry()) - { - if (entry["retired"] is JsonValue retired && retired.TryGetValue(out var r) && r) continue; - var claim = Files.Str(entry["claim"]); - var seeds = SeedsOf(entry); - if (seeds.Count == 0) { problems.Add($"{probe}: no seed, its registry entry names none"); continue; } - foreach (var (obligation, seed) in seeds) - { - if (Validate(ledger, include, seed, ledger.Top) is { Refused: { } refused, Detail: var detail }) - problems.Add($"{Named(probe, obligation)}: {refused}, {detail}"); - var ids = obligations.GetValueOrDefault(claim) ?? [Claim.ImplicitObligation]; - // A seed naming no obligation proves the probe; it counts for the claim's one implicit obligation only. - var counts = obligation ?? (ids is [Claim.ImplicitObligation] ? Claim.ImplicitObligation : null); - if (counts is null) continue; - if (!ids.Contains(counts)) { problems.Add($"{Named(probe, obligation)}: names obligation {counts}, which claim {claim} does not list"); continue; } - (seeded.TryGetValue(claim, out var set) ? set : seeded[claim] = new(StringComparer.Ordinal)).Add(counts); - } - } - // Every listed obligation of a claim that has a probe is seeded by some probe of that claim (decision 104); - // the implicit obligation is covered by the rule above that every probe has a seed. - var claimed = ledger.Registry().Values.Select(e => Files.Str(e["claim"])).ToHashSet(StringComparer.Ordinal); - foreach (var (claim, ids) in obligations.OrderBy(kv => kv.Key, StringComparer.Ordinal)) - { - if (ids is [Claim.ImplicitObligation] || !claimed.Contains(claim)) continue; - foreach (var id in ids.Where(id => !(seeded.GetValueOrDefault(claim)?.Contains(id) ?? false))) - problems.Add($"{claim}: obligation {id} has no seed in any of its probes"); - } - return problems; - } - - /// The (row, obligation) of every check that ended as a violation, clean or not: what a seed's violations are measured against. - private static HashSet<(int Row, string Obligation)> Violating(Dictionary rows) => - [.. rows.SelectMany(kv => kv.Value.Checks.Where(c => c.Category == "violation").Select(c => (kv.Key, c.Obligation)))]; - - /// Runs one probe in every one of its variants and reads how each row ended. - private static Dictionary RunProbe(IProbeAdapter adapter, string worktree, Ledger.ProbeState s, Dictionary environment, - JsonObject? seedEnv, string results, string tag) - { - var rows = new Dictionary(); - var index = 0; - foreach (var variant in s.Variants.OfType()) - { - var name = $"{s.Probe}-{tag}-{index}"; - var (_, outcomes) = ProbeRun.Once(adapter, worktree, s.Filter, results, name, name + ".cobertura.xml", ProbeRun.Variant(environment, variant, seedEnv)); - foreach (var (idx, outcome) in outcomes) rows[idx] = outcome; - index++; - } - return rows; - } - - /// Puts the scratch worktree's tracked files back at HEAD; the copied corpus, untracked, stays. - private static void Reset(string worktree, string head) - { - if (Git.Run(worktree, "checkout", "-q", "--detach", head).ExitCode != 0 && Git.Run(worktree, "reset", "-q", "--hard", head).ExitCode != 0) - throw new Refusal($"could not move the scratch worktree {worktree} to {head[..7]}"); - Git.Run(worktree, "reset", "-q", "--hard", head); - } - - private static string Describe(JsonNode? seed) => - seed is JsonObject o - ? string.Join(" + ", new[] { Files.Str(o["patch"]), o["env"] is JsonObject env ? string.Join(",", env.Select(kv => $"{kv.Key}={Files.Str(kv.Value)}")) : "" }.Where(x => x.Length > 0)) - : "(none)"; - - private static Ran Timed(Func run, out double seconds) - { - var clock = Stopwatch.StartNew(); - var ran = run(); - seconds = clock.Elapsed.TotalSeconds; - return ran; - } - - private static string Seconds(double seconds) => seconds.ToString("F1", CultureInfo.InvariantCulture) + " s"; - - private static string Cut(string s) => string.Join(" ", s.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)) is var line && line.Length > 160 ? line[..160] + "..." : line; - - private static string FirstLine(Ran ran) => ProbeRun.FirstLine(ran); - - private static string FirstLine(string text) => text.Trim().Split('\n')[0].Trim(); -} diff --git a/tools/Prober/SourceLint.cs b/tools/Prober/SourceLint.cs deleted file mode 100644 index 6b34502b..00000000 --- a/tools/Prober/SourceLint.cs +++ /dev/null @@ -1,277 +0,0 @@ -using System.Text; -using System.Text.Json.Nodes; -using System.Text.RegularExpressions; - -namespace Prober; - -/// -/// What `register` and `publish` refuse in the staged probe sources before anything is built (decision 103): -/// a Note whose first argument is not a key of the catalogue (NoteKeys.Name, a member the -/// corpus or the staging declares), and a two-argument Skip (the harness's, not LINQ's) whose first -/// argument is not a string literal naming a finding this ledger holds open. The compiler refuses a key -/// outside the catalogue too; this says so by file and line before a build, and holds the finding ids, -/// which stay strings. A check's Inapplicable and Refused cite their reason by a Cite -/// factory with a string literal, and what it names exists: a not-promised entry of the probe's claim (of any -/// claim, for a harness source no one probe owns), a file of the docs, a knowledge entry under design/knowledge/, -/// a record under design/adr/. It reads code with comments and string literals blanked out, so a call written in -/// either does not count. -/// -internal static partial class SourceLint -{ - /// The catalogue's members, as harness/Row.cs declares them. - [GeneratedRegex(@"public\s+static\s+readonly\s+NoteKeys\s+(\w+)\s*=")] - private static partial Regex CatalogueMember(); - - [GeneratedRegex(@"\.\s*(Note|Skip|Inapplicable|Refused)\s*\(")] - private static partial Regex Call(); - - [GeneratedRegex("^Cite\\s*\\.\\s*(NotPromised|Docs|Fact|Record)\\s*\\(\\s*\"([^\"\\\\]*)\"\\s*\\)$")] - private static partial Regex CiteFactory(); - - [GeneratedRegex(@"^NoteKeys\s*\.\s*(\w+)$")] - private static partial Regex CatalogueKey(); - - [GeneratedRegex("^\"([^\"\\\\]*)\"$")] - private static partial Regex PlainLiteral(); - - /// Refuses, naming each file and line, while a staged source breaks a rule; does nothing without a staging. - public static void Refuse(Ledger l, string command) - { - var problems = Problems(l); - if (problems.Count > 0) - throw new Refusal($"{command}: the staged sources make a note, a skip or a citation that is not written out (decision 103):\n " + string.Join("\n ", problems)); - } - - /// Every problem in the staged harness and probe sources, with the finding ids checked against this ledger. - public static List Problems(Ledger l) - { - var staged = Sources(l.StagingDir).ToList(); - if (staged.Count == 0) return []; - var catalogue = new HashSet(StringComparer.Ordinal); - foreach (var file in Sources(l.CorpusDir).Concat(staged)) - foreach (Match m in CatalogueMember().Matches(Mask(File.ReadAllText(file)))) catalogue.Add(m.Groups[1].Value); - var findings = l.Findings(); - // The not-promised ids of each claim, and the claim of each registered probe: a probe's source cites its own - // claim's entries, a harness source that no one probe owns any claim's. - var notPromised = l.Claims().ToDictionary(c => Files.Str(c["id"]), - c => (IReadOnlySet)(c["not-promised"] as JsonArray ?? []).OfType().Select(n => Files.Str(n["id"])).ToHashSet(StringComparer.Ordinal), StringComparer.Ordinal); - // A probe's source is named for it, or for the class its filter selects (*C1Probe). - var claimOf = new Dictionary(StringComparer.Ordinal); - foreach (var (name, entry) in l.Registry()) - { - claimOf[name] = Files.Str(entry["claim"]); - if (Files.Str(entry["filter"]).TrimStart('*') is { Length: > 0 } cls) claimOf.TryAdd(cls, Files.Str(entry["claim"])); - } - var problems = new List(); - foreach (var file in staged) - { - // Relative to the checkout where the state lives in it, else in full (PROBE_STATE_DIR elsewhere). - var path = Path.GetRelativePath(l.Top, file) is var rel && !rel.StartsWith("..", StringComparison.Ordinal) ? rel.Replace('\\', '/') : file; - var cites = new Citations(l.Top, claimOf.TryGetValue(Path.GetFileNameWithoutExtension(file), out var claim) ? claim : null, notPromised); - foreach (var (line, problem, finding) in Scan(File.ReadAllText(file), catalogue, cites.Problem)) - { - if (problem is not null) problems.Add($"{path}:{line}: {problem}"); - else if (finding is not null && Status(findings, finding) is { } status) - problems.Add($"{path}:{line}: Skip(\"{finding}\", ...) names {status}; a skip steps around an open finding only"); - } - } - return problems; - } - - private static string? Status(Dictionary findings, string id) => - !findings.TryGetValue(id, out var f) ? "no finding this ledger has" - : Files.Str(f["status"]) is var status && status != "open" ? $"finding {id}, which is {status}" - : null; - - private static IEnumerable Sources(string root) => - new[] { "harness", "probes" }.Select(sub => Path.Combine(root, sub)).Where(Directory.Exists) - .SelectMany(dir => Directory.EnumerateFiles(dir, "*.cs")).Order(StringComparer.Ordinal); - - /// - /// What a citation of one source may name, and why one does not: takes the factory and its - /// literal and says what is missing, or null. is the claim of the probe the source is, or - /// null for a source that no one probe owns, which may cite any claim's not-promised entry. - /// - internal sealed record Citations(string Top, string? Claim, IReadOnlyDictionary> NotPromised) - { - public string? Problem(string factory, string value) - { - switch (factory) - { - case "NotPromised" when Claim is not null: - return NotPromised.GetValueOrDefault(Claim)?.Contains(value) ?? false ? null : $"names no not-promised entry of its claim {Claim}"; - case "NotPromised": - return NotPromised.Values.Any(ids => ids.Contains(value)) ? null : "names no not-promised entry of any claim"; - case "Docs": - var file = value.Split('#')[0]; - return file.Length > 0 && !file.Contains("..", StringComparison.Ordinal) && File.Exists(Path.Combine(Top, file)) ? null : $"names no file {file} in the repository"; - case "Fact": - return Named(Path.Combine(Top, "design", "knowledge"), value) ? null : "names no entry under design/knowledge/"; - default: - return Named(Path.Combine(Top, "design", "adr"), value) ? null : "names no record under design/adr/"; - } - } - - /// A file of the directory named for the id: KB-031-...md, 0094-...md. - private static bool Named(string directory, string id) => - id.Length > 0 && Directory.Exists(directory) - && Directory.EnumerateFiles(directory, "*.md").Any(f => Path.GetFileName(f).StartsWith(id + "-", StringComparison.Ordinal) || Path.GetFileName(f) == id + ".md"); - } - - /// - /// Each Note, two-argument Skip, and Inapplicable or Refused with an argument, of one - /// source: its line, the rule it breaks (or null), and for a skip written out, the finding it names. - /// says what a citation's factory and literal fail to name; without it only the form is held. - /// - internal static IEnumerable<(int Line, string? Problem, string? Finding)> Scan(string source, IReadOnlySet catalogue, Func? cite = null) - { - var masked = Mask(source); - foreach (Match m in Call().Matches(masked)) - { - var open = m.Index + m.Length - 1; - var arguments = Arguments(masked, source, open); - if (arguments is null) continue; - var line = 1 + masked.AsSpan(0, m.Index).Count('\n'); - var first = arguments.Count > 0 ? arguments[0] : ""; - if (m.Groups[1].Value is "Inapplicable" or "Refused") - { - if (arguments.Count == 0) continue; // an entry's own Refused() of the harness, not a check's - var call = m.Groups[1].Value; - var factory = CiteFactory().Match(first); - if (!factory.Success) yield return (line, $"{call}({Shown(first)}, ...) does not cite by a Cite factory with a string literal", null); - else if (cite?.Invoke(factory.Groups[1].Value, factory.Groups[2].Value) is { } missing) - yield return (line, $"{call}({Shown(first)}, ...) {missing}", null); - } - else if (m.Groups[1].Value == "Note") - { - var key = CatalogueKey().Match(first); - if (!key.Success) yield return (line, $"Note({Shown(first)}, ...) does not name a key of NoteKeys", null); - else if (!catalogue.Contains(key.Groups[1].Value)) yield return (line, $"Note({Shown(first)}, ...) names no key of NoteKeys", null); - } - else if (arguments.Count == 2) - { - var id = PlainLiteral().Match(first); - yield return id.Success ? (line, null, id.Groups[1].Value) : (line, $"Skip({Shown(first)}, ...) does not name its finding as a string literal", null); - } - } - } - - private static string Shown(string argument) => Regex.Replace(argument, @"\s+", " ") is var s && s.Length > 60 ? s[..60] + "..." : s; - - /// The top-level arguments of the call whose parenthesis opens at , from the original text; null if it never closes. - private static List? Arguments(string masked, string source, int open) - { - var arguments = new List(); - var depth = 0; var start = open + 1; - for (var i = open + 1; i < masked.Length; i++) - { - switch (masked[i]) - { - case '(' or '[' or '{': depth++; break; - case ')' or ']' or '}' when depth > 0: depth--; break; - case ',' when depth == 0: arguments.Add(source[start..i].Trim()); start = i + 1; break; - case ')': - var last = source[start..i].Trim(); - if (last.Length > 0 || arguments.Count > 0) arguments.Add(last); - return arguments; - } - } - return null; - } - - /// - /// The source with every comment and the content of every string and character literal blanked out (line - /// breaks kept, so lines still count), and the code of an interpolation hole kept as code. - /// - internal static string Mask(string s) - { - var m = new StringBuilder(s); - Code(s, m, 0, hole: false); - return m.ToString(); - } - - private static void Blank(StringBuilder m, int from, int to) - { - for (var i = from; i < to && i < m.Length; i++) if (m[i] != '\n') m[i] = ' '; - } - - /// Scans code from ; in a hole, returns the index after its closing brace. - private static int Code(string s, StringBuilder m, int i, bool hole) - { - var depth = 0; - while (i < s.Length) - { - var c = s[i]; - if (c == '/' && i + 1 < s.Length && s[i + 1] == '/') - { - var end = s.IndexOf('\n', i); end = end < 0 ? s.Length : end; - Blank(m, i, end); i = end; - } - else if (c == '/' && i + 1 < s.Length && s[i + 1] == '*') - { - var end = s.IndexOf("*/", i + 2, StringComparison.Ordinal); end = end < 0 ? s.Length : end + 2; - Blank(m, i, end); i = end; - } - else if (c == '\'') - { - var j = i + 1; - while (j < s.Length && s[j] != '\'' && s[j] != '\n') j += s[j] == '\\' ? 2 : 1; - Blank(m, i + 1, j); i = j + 1; - } - else if (c is '"' or '$' or '@' && StringStart(s, i) is { } literal) i = String(s, m, i, literal); - else if (hole && c == '{') { depth++; i++; } - else if (hole && c == '}') { if (depth-- == 0) return i + 1; i++; } - else i++; - } - return i; - } - - private sealed record Literal(int Dollars, bool Verbatim, int Quotes, int QuoteAt); - - /// The literal that starts at : its $ count, @, and run of quotes. - private static Literal? StringStart(string s, int i) - { - if (i > 0 && (char.IsLetterOrDigit(s[i - 1]) || s[i - 1] == '_') && s[i] != '"') return null; - var j = i; var dollars = 0; var verbatim = false; - while (j < s.Length && (s[j] == '$' || s[j] == '@')) { if (s[j] == '$') dollars++; else verbatim = true; j++; } - if (j >= s.Length || s[j] != '"') return null; - var quotes = 0; - while (j + quotes < s.Length && s[j + quotes] == '"') quotes++; - return new Literal(dollars, verbatim, quotes >= 3 ? quotes : 1, j); - } - - /// Blanks one literal's content, keeping its holes as code; returns the index after it. - private static int String(string s, StringBuilder m, int i, Literal literal) - { - var j = literal.QuoteAt + literal.Quotes; - if (literal.Quotes >= 3) - { - // A raw literal ends at a run of as many quotes as it opened with. Holes in it hold no note or skip - // (both return nothing), so it is blanked whole. - var close = new string('"', literal.Quotes); - var end = s.IndexOf(close, j, StringComparison.Ordinal); end = end < 0 ? s.Length : end; - Blank(m, j, end); - return end + literal.Quotes; - } - var from = j; - while (j < s.Length) - { - var c = s[j]; - if (literal.Verbatim && c == '"' && j + 1 < s.Length && s[j + 1] == '"') { j += 2; continue; } - if (!literal.Verbatim && c == '\\') { j += 2; continue; } - if (c == '"') { Blank(m, from, j); return j + 1; } - if (literal.Dollars > 0 && c == '{') - { - if (j + 1 < s.Length && s[j + 1] == '{') { j += 2; continue; } - Blank(m, from, j + 1); - j = Code(s, m, j + 1, hole: true); - from = j - 1; - continue; - } - if (!literal.Verbatim && c == '\n') break; - j++; - } - Blank(m, from, j); - return j; - } -} diff --git a/tools/Prober/TryProbe.cs b/tools/Prober/TryProbe.cs deleted file mode 100644 index ba6e36cc..00000000 --- a/tools/Prober/TryProbe.cs +++ /dev/null @@ -1,151 +0,0 @@ -using System.Diagnostics; -using System.Globalization; -using System.Text.Json.Nodes; - -namespace Prober; - -/// -/// try: the writer's loop for one probe. It moves the replay's worktree to HEAD, overlays the corpus and -/// the branch's staging there as the replay does, builds once through the adapter (not with --no-build), -/// and runs the one probe in each of its registry variants; --rows keeps the rows given, run through the -/// harness's PROBE_ROWS one stretch of consecutive rows at a time, within the rows each variant's -/// PROBE_ONLY selects. It prints each run's first line (the verdict's) and how each row ended, from the -/// jsonl the harness writes. It records nothing, no evidence, footprint or run, so nothing under the state -/// directory changes, and takes no round lock, but it holds the replay's worktree () -/// while it runs: a try and a replay of the same checkout never build in it at once, and a try refuses while a -/// replay holds it. Its exit code is the probe's: the first run's that failed, else 0; 1 when the build fails. -/// -internal static class TryProbe -{ - public static int Run(Context ctx, Args a, IProbeAdapter? adapter = null) - { - var ledger = new Ledger(ctx, a.Get("branch")); - var config = ledger.AxesBlock()["replay"] as JsonObject ?? throw new Refusal("the axes file's block has no \"replay\" configuration"); - adapter ??= new DotNetAdapter(config); - var probe = a.Need("probe"); - if (!ledger.Registry().TryGetValue(probe, out var entry)) - throw new Refusal($"no probe {probe} in the registry, tracked or staged; `prober new` stages one", 2); - var claim = Files.Str(entry["claim"]); - var filter = Files.Str(entry["filter"]); - - // What each variant runs: the rows its PROBE_ONLY selects, those of --rows among them, in stretches. - var probeRows = ledger.RowsOf(probe); - var asked = Indices(a.Get("rows"), probeRows.Count); - var plan = new List<(JsonObject Variant, List Rows, List Stretches)>(); - foreach (var variant in Ledger.VariantsOf(entry).OfType()) - { - var only = Files.Str(variant["PROBE_ONLY"]); - var selected = Enumerable.Range(0, probeRows.Count).Where(i => Replay.Selected(Files.StringMap(probeRows[i]?["row"] ?? probeRows[i]), only)).ToList(); - if (asked is null) { plan.Add((variant, selected, [null])); continue; } - var rows = selected.Where(asked.Contains).ToList(); - if (rows.Count > 0) plan.Add((variant, rows, Stretches(rows))); - } - if (asked is not null && asked.Except(plan.SelectMany(p => p.Rows)).ToList() is { Count: > 0 } unrun) - throw new Refusal($"no variant of {probe} runs row(s) {string.Join(", ", unrun)}: each variant's PROBE_ONLY leaves them out", 2); - - var home = Replay.Home(ctx, ledger); - var worktree = Path.Combine(home, "wt"); - var noBuild = a.Flag("no-build"); - if (noBuild && !Directory.Exists(worktree)) throw new Refusal($"--no-build: the replay worktree {worktree} does not exist yet, so nothing is built; run without it", 2); - // The worktree is the replay's: held for the whole try, so neither moves it or builds in it under the other. - using var held = Replay.HoldWorktree(home) - ?? throw new Refusal($"a replay in this checkout is using {worktree}; run `prober try` when it ends"); - var head = ledger.Head; - var warmth = ProbeRun.Ready(ledger, worktree, head); - Replay.Overlay(ledger, config, worktree); - var environment = ProbeRun.Environment(ctx, adapter, home); - ctx.Out.WriteLine($"prober try: {probe} ({claim}) at {head[..7]} in {worktree}; nothing is recorded"); - if (!noBuild) - { - var clock = Stopwatch.StartNew(); - var build = adapter.Build(worktree, environment); - ctx.Out.WriteLine($" build ({warmth}): {Seconds(clock.Elapsed.TotalSeconds)}, {ProbeRun.FirstLine(build)}"); - if (build.ExitCode != 0) { ctx.Out.WriteLine(build.Stdout); return 1; } - } - - // The full logs of each run, kept until the next try; the results and the rows' jsonl are thrown away. - var logs = Path.Combine(home, "try-logs"); - try { if (Directory.Exists(logs)) Directory.Delete(logs, recursive: true); } catch (IOException) { } - var results = Directory.CreateTempSubdirectory("prober-try-").FullName; - var exit = 0; - var tally = new SortedDictionary(StringComparer.Ordinal); - try - { - var index = 0; - foreach (var (variant, rows, stretches) in plan) - { - var label = variant.Count > 0 ? " [" + string.Join(", ", variant.Select(kv => $"{kv.Key}={Files.Str(kv.Value)}")) + "]" : ""; - foreach (var stretch in stretches) - { - var name = $"{probe}-try-{index++}"; - var env = ProbeRun.Variant(environment, variant); - if (stretch is not null) env["PROBE_ROWS"] = stretch; - env["PROBE_LOG_DIR"] = Path.Combine(logs, name); - var clock = Stopwatch.StartNew(); - var (ran, outcomes) = ProbeRun.Once(adapter, worktree, filter, results, name, "", env); - ctx.Out.WriteLine($" {probe}{label}{(stretch is null ? "" : $" rows {stretch}")}: {ProbeRun.FirstLine(ran)} (exit {ran.ExitCode}, {Seconds(clock.Elapsed.TotalSeconds)})"); - foreach (var i in rows.Where(i => stretch is null || Within(i, stretch))) - { - var outcome = outcomes.GetValueOrDefault(i); - var category = outcome?.Category ?? "did not run"; - tally[category] = tally.GetValueOrDefault(category) + 1; - ctx.Out.WriteLine($" #{i} {category}{(outcome is { Category: not "pass", Reason.Length: > 0 } ? ": " + Cut(Unprefixed(outcome.Reason, category)) : "")}"); - } - if (ran.ExitCode != 0 && Directory.Exists(env["PROBE_LOG_DIR"])) ctx.Out.WriteLine($" log: {env["PROBE_LOG_DIR"]}"); - if (ran.ExitCode != 0 && exit == 0) exit = ran.ExitCode; - } - } - } - finally - { - try { Directory.Delete(results, recursive: true); } catch (IOException) { } - } - ctx.Out.WriteLine($"prober try: {probe}: {(tally.Count == 0 ? "no rows" : string.Join(", ", tally.Select(kv => $"{kv.Key} {kv.Value}")))}; exit {exit}"); - return exit; - } - - /// The row indices of --rows (i,j,..., a-b for a stretch), or null for every row; refuses one the claim does not have. - internal static SortedSet? Indices(string? given, int count) - { - if (given is null) return null; - var result = new SortedSet(); - foreach (var part in given.Split(',', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) - { - var bounds = part.Split('-'); - if (bounds.Length > 2 || !bounds.All(x => int.TryParse(x, NumberStyles.None, CultureInfo.InvariantCulture, out _))) - throw new Refusal($"--rows takes row indices, i,j,... or a-b: not {part}", 2); - var lo = int.Parse(bounds[0], CultureInfo.InvariantCulture); var hi = int.Parse(bounds[^1], CultureInfo.InvariantCulture); - for (var i = lo; i <= hi; i++) result.Add(i); - } - if (result.Count == 0) throw new Refusal("--rows takes row indices, i,j,... or a-b", 2); - if (result.Max >= count) throw new Refusal($"--rows {result.Max}: the claim has {count} row(s), 0 to {count - 1}", 2); - return result; - } - - /// The consecutive stretches of sorted rows, as the harness's PROBE_ROWS spells one: lo-hi. - internal static List Stretches(List rows) - { - var result = new List(); - for (var i = 0; i < rows.Count;) - { - var j = i; - while (j + 1 < rows.Count && rows[j + 1] == rows[j] + 1) j++; - result.Add(string.Create(CultureInfo.InvariantCulture, $"{rows[i]}-{rows[j]}")); - i = j + 1; - } - return result; - } - - private static bool Within(int row, string stretch) - { - var bounds = stretch.Split('-'); - return row >= int.Parse(bounds[0], CultureInfo.InvariantCulture) && row <= int.Parse(bounds[1], CultureInfo.InvariantCulture); - } - - /// A reason without the category the harness puts in front of it (violation:...), which the line already names. - private static string Unprefixed(string reason, string category) => reason.StartsWith(category + ":", StringComparison.Ordinal) ? reason[(category.Length + 1)..] : reason; - - private static string Seconds(double seconds) => seconds.ToString("F1", CultureInfo.InvariantCulture) + " s"; - - private static string Cut(string s) => string.Join(" ", s.Split((char[]?)null, StringSplitOptions.RemoveEmptyEntries)) is var line && line.Length > 160 ? line[..160] + "..." : line; -} diff --git a/tools/probe/corpus/claims.json b/tools/probe/corpus/claims.json deleted file mode 100644 index 301b96a8..00000000 --- a/tools/probe/corpus/claims.json +++ /dev/null @@ -1,16178 +0,0 @@ -[ - { - "id": "C01-decimal-spelling", - "source": "docs/json.md#decimals-in-one-spelling", - "surfaces": [ - "json" - ], - "axes": { - "converter": [ - "number", - "string" - ], - "input": [ - "7.80", - "7.8000", - "0.00", - "100", - "1E+2", - "max28digits", - "1e-28" - ] - }, - "narrow": { - "shape.wrapped": "Register either one once, and it covers every decimal: nullable ones, dictionary keys, and every value object over a decimal, since its JSON is the underlying type's.", - "input.json": "Both read a number, or a string holding one, and keep the scale they read." - }, - "obligations": [ - { - "id": "smallest-scale", - "sentence": "Two converters write the smallest scale instead:" - }, - { - "id": "number-is-a-number", - "sentence": "**`DecimalNumberJsonConverter`** writes it as a number.", - "axes": { - "converter": [ - "number" - ] - } - }, - { - "id": "string-is-a-string", - "sentence": "**`DecimalStringJsonConverter`** writes it as a string.", - "axes": { - "converter": [ - "string" - ] - } - }, - { - "id": "reads-a-number-or-a-string", - "sentence": "Both read a number, or a string holding one, and keep the scale they read." - }, - { - "id": "keeps-the-scale-it-reads", - "sentence": "Both read a number, or a string holding one, and keep the scale they read." - }, - { - "id": "covers-every-decimal", - "sentence": "Register either one once, and it covers every decimal: nullable ones, dictionary keys, and every value object over a decimal, since its JSON is the underlying type's." - } - ], - "exclude": [ - { - "reason": "A closed set wraps nothing, so it holds no decimal for a decimal converter to write (docs/json.md#decimals-in-one-spelling covers decimals, nullable ones, dictionary keys and value objects over a decimal).", - "pairs": [ - [ - "converter=number", - "shape.kind=closed-set" - ], - [ - "converter=string", - "shape.kind=closed-set" - ], - [ - "input=0.00", - "shape.kind=closed-set" - ], - [ - "input=100", - "shape.kind=closed-set" - ], - [ - "input=1E+2", - "shape.kind=closed-set" - ], - [ - "input=1e-28", - "shape.kind=closed-set" - ], - [ - "input=7.80", - "shape.kind=closed-set" - ], - [ - "input=7.8000", - "shape.kind=closed-set" - ], - [ - "input=max28digits", - "shape.kind=closed-set" - ], - [ - "json.AllowDuplicateProperties=default", - "shape.kind=closed-set" - ], - [ - "json.AllowDuplicateProperties=false", - "shape.kind=closed-set" - ], - [ - "json.AllowOutOfOrderMetadataProperties=default", - "shape.kind=closed-set" - ], - [ - "json.AllowOutOfOrderMetadataProperties=true", - "shape.kind=closed-set" - ], - [ - "json.AllowTrailingCommas=default", - "shape.kind=closed-set" - ], - [ - "json.AllowTrailingCommas=true", - "shape.kind=closed-set" - ], - [ - "json.Converters=JsonStringEnumConverter", - "shape.kind=closed-set" - ], - [ - "json.Converters=StoredJsonConverterFactory", - "shape.kind=closed-set" - ], - [ - "json.Converters=default", - "shape.kind=closed-set" - ], - [ - "json.Converters=own-for-value-object", - "shape.kind=closed-set" - ], - [ - "json.Converters=own-for-wrapped", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenReading", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenWriting", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenWritingDefault", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenWritingNull", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=default", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=CamelCase", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=KebabCaseUpper", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=SnakeCaseLower", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=default", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=own-upper-case", - "shape.kind=closed-set" - ], - [ - "json.Encoder=UnsafeRelaxedJsonEscaping", - "shape.kind=closed-set" - ], - [ - "json.Encoder=default", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyFields=default", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyFields=true", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyProperties=default", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyProperties=true", - "shape.kind=closed-set" - ], - [ - "json.IncludeFields=default", - "shape.kind=closed-set" - ], - [ - "json.IncludeFields=true", - "shape.kind=closed-set" - ], - [ - "json.MaxDepth=3", - "shape.kind=closed-set" - ], - [ - "json.MaxDepth=default", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=AllowNamedFloatingPointLiterals", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=AllowReadingFromString", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=WriteAsString", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=default", - "shape.kind=closed-set" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "shape.kind=closed-set" - ], - [ - "json.PreferredObjectCreationHandling=default", - "shape.kind=closed-set" - ], - [ - "json.PropertyNameCaseInsensitive=default", - "shape.kind=closed-set" - ], - [ - "json.PropertyNameCaseInsensitive=true", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=CamelCase", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=KebabCaseLower", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=KebabCaseUpper", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=SnakeCaseLower", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=SnakeCaseUpper", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=default", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=own-upper-case", - "shape.kind=closed-set" - ], - [ - "json.ReadCommentHandling=Skip", - "shape.kind=closed-set" - ], - [ - "json.ReadCommentHandling=default", - "shape.kind=closed-set" - ], - [ - "json.ReferenceHandler=IgnoreCycles", - "shape.kind=closed-set" - ], - [ - "json.ReferenceHandler=Preserve", - "shape.kind=closed-set" - ], - [ - "json.ReferenceHandler=default", - "shape.kind=closed-set" - ], - [ - "json.RespectNullableAnnotations=default", - "shape.kind=closed-set" - ], - [ - "json.RespectNullableAnnotations=true", - "shape.kind=closed-set" - ], - [ - "json.RespectRequiredConstructorParameters=default", - "shape.kind=closed-set" - ], - [ - "json.RespectRequiredConstructorParameters=true", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolver=default", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolver=default-with-renaming-modifier", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolver=source-generated-context", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolverChain=context-then-default", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolverChain=default", - "shape.kind=closed-set" - ], - [ - "json.UnknownTypeHandling=JsonNode", - "shape.kind=closed-set" - ], - [ - "json.UnknownTypeHandling=default", - "shape.kind=closed-set" - ], - [ - "json.UnmappedMemberHandling=Disallow", - "shape.kind=closed-set" - ], - [ - "json.UnmappedMemberHandling=default", - "shape.kind=closed-set" - ], - [ - "shape.containing=class", - "shape.kind=closed-set" - ], - [ - "shape.containing=generic-type", - "shape.kind=closed-set" - ], - [ - "shape.containing=record-class", - "shape.kind=closed-set" - ], - [ - "shape.containing=record-struct", - "shape.kind=closed-set" - ], - [ - "shape.containing=struct", - "shape.kind=closed-set" - ], - [ - "shape.kind=closed-set", - "shape.position=array-element" - ], - [ - "shape.kind=closed-set", - "shape.position=dictionary-key" - ], - [ - "shape.kind=closed-set", - "shape.position=dictionary-value" - ], - [ - "shape.kind=closed-set", - "shape.position=field" - ], - [ - "shape.kind=closed-set", - "shape.position=immutable-array-element" - ], - [ - "shape.kind=closed-set", - "shape.position=in-patch" - ], - [ - "shape.kind=closed-set", - "shape.position=list-element" - ], - [ - "shape.kind=closed-set", - "shape.position=nullable-property" - ], - [ - "shape.kind=closed-set", - "shape.position=polymorphic-derived-property" - ], - [ - "shape.kind=closed-set", - "shape.position=property" - ], - [ - "shape.kind=closed-set", - "shape.position=read-only-list-element" - ], - [ - "shape.kind=closed-set", - "shape.position=record-ctor-parameter" - ], - [ - "shape.kind=closed-set", - "shape.position=required-init" - ], - [ - "shape.kind=closed-set", - "shape.position=root" - ] - ] - }, - { - "reason": "A value at the root is not held by a containing type, so shape.containing has nothing to apply to.", - "pairs": [ - [ - "shape.containing=class", - "shape.position=root" - ], - [ - "shape.containing=generic-type", - "shape.position=root" - ], - [ - "shape.containing=record-class", - "shape.position=root" - ], - [ - "shape.containing=record-struct", - "shape.position=root" - ], - [ - "shape.containing=struct", - "shape.position=root" - ] - ] - }, - { - "reason": "System.Text.Json serializes a field only under IncludeFields (or [JsonInclude]), so without it the member is in no document and no converter runs in either direction, for the plain type alike.", - "pairs": [ - [ - "json.IncludeFields=default", - "shape.position=field" - ] - ] - }, - { - "reason": "System.Text.Json refuses JsonObjectCreationHandling.Populate together with a reference handler (InvalidOperationException when the options are used; harness/JsonRow.cs runs such a row without Populate), so no row can set both.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ] - ] - } - ] - }, - { - "id": "C02-optional-text", - "source": "docs/text.md#required-and-optional", - "surfaces": [ - "text" - ], - "axes": { - "entry": [ - "AsOptionalText", - "AsOptionalIdentifier", - "AsText", - "AsIdentifier" - ], - "input": [ - "null", - "empty", - "spaces", - "tab-newline", - "word", - "word+ZWSP", - "nbsp-only", - "ZWSP-only" - ] - }, - "narrow": { - "shape.kind": "`AsOptionalText()` and `AsOptionalIdentifier()` return a `Result, TextFault>`", - "shape.wrapped": "`AsOptionalText()` and `AsOptionalIdentifier()` return a `Result, TextFault>`", - "text.entry": "`AsOptionalText()` and `AsOptionalIdentifier()` return a `Result, TextFault>`" - }, - "obligations": [ - { - "id": "required-blank-is-an-error", - "sentence": "For a required field, `Blank` is an error.", - "axes": { - "entry": [ - "AsText", - "AsIdentifier" - ], - "input": [ - "null", - "empty", - "spaces", - "tab-newline", - "nbsp-only" - ] - } - }, - { - "id": "optional-blank-is-no-value", - "sentence": "For an optional one, a blank means \"no value\", and `AsOptionalText()` and `AsOptionalIdentifier()` return a `Result, TextFault>`.", - "axes": { - "entry": [ - "AsOptionalText", - "AsOptionalIdentifier" - ], - "input": [ - "null", - "empty", - "spaces", - "tab-newline", - "nbsp-only" - ] - } - }, - { - "id": "hidden-is-an-error-in-both", - "sentence": "`Hidden` is an error in both cases.", - "axes": { - "input": [ - "word+ZWSP", - "ZWSP-only" - ] - } - }, - { - "id": "null-is-blank", - "sentence": "A null string is `Blank`.", - "axes": { - "input": [ - "null" - ] - } - } - ], - "not-promised": [ - { - "id": "as-text-refused-in-globalization-invariant-mode", - "source": "docs/text.md#globalization-invariant-mode", - "sentence": "So `AsText()` throws `PlatformNotSupportedException` there, on its first call, whatever the string.", - "axes": { - "text.culture": [ - "globalization-invariant" - ], - "entry": [ - "AsText", - "AsOptionalText" - ] - } - } - ], - "exclude": [ - { - "reason": "Not promised (as-text-refused-in-globalization-invariant-mode): `AsText()` throws `PlatformNotSupportedException` in globalization-invariant mode (docs/text.md#globalization-invariant-mode), and `AsOptionalText()`, which calls it, throws with it, so C02OptionalTextProbe checks that refusal without credit and runs the row on the identifier counterpart; no row holds the pair.", - "pairs": [ - [ - "entry=AsText", - "text.culture=globalization-invariant" - ], - [ - "entry=AsOptionalText", - "text.culture=globalization-invariant" - ] - ] - } - ] - }, - { - "id": "C03-composed-faults", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", - "surfaces": [ - "http" - ], - "axes": { - "arity": [ - "2", - "3" - ], - "error": [ - "first-set", - "last-set", - "with-detail", - "with-rule" - ] - }, - "obligations": [ - { - "id": "each-error-own-set", - "sentence": "Each error is answered as its own set maps it." - }, - { - "id": "detail-and-rule-intact", - "sentence": "`Fault` (from CodoMetis.TypeKit) holds one error of either set, with its detail and rule intact.", - "axes": { - "error": [ - "with-detail", - "with-rule" - ] - } - }, - { - "id": "statuses-ascending", - "sentence": "So its operation lists the statuses of both sets, in ascending order, with the problem types of both where they share a status." - }, - { - "id": "shared-status-types", - "sentence": "So its operation lists the statuses of both sets, in ascending order, with the problem types of both where they share a status." - }, - { - "id": "unmapped-set-stops-start", - "sentence": "The host doesn't start while either set is unmapped." - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C04-without-faults", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#leaving-out-what-a-route-cannot-answer", - "surfaces": [ - "http" - ], - "axes": { - "scope": [ - "endpoint", - "group", - "controller" - ], - "combined": [ - "alone", - "with-WithFaultStatus-on-group" - ] - }, - "obligations": [ - { - "id": "doc-omits-left-out", - "sentence": "The operation then lists no status and no problem type for a member left out." - }, - { - "id": "reporting-left-out-throws", - "sentence": "Reporting a member left out anyway throws an `InvalidOperationException` naming the endpoint and the member, rather than answering with a status the document doesn't list." - }, - { - "id": "narrows-wider-status", - "sentence": "If a group gives `NotFound` a status and one endpoint inside leaves `NotFound` out, that endpoint drops it, and the group's other endpoints keep the status.", - "axes": { - "scope": [ - "endpoint", - "controller" - ], - "combined": [ - "with-WithFaultStatus-on-group" - ] - } - }, - { - "id": "left-out-everywhere-stops-start", - "sentence": "**These stop the host from starting**, as for a status: a value or a name no member has, a status that every endpoint it's set on leaves out (on the endpoint itself, or on a group whose endpoints all leave the member out), and a group or a controller none of whose endpoints answers with the type.", - "axes": { - "scope": [ - "group" - ], - "combined": [ - "with-WithFaultStatus-on-group" - ] - } - } - ], - "not-promised": [ - { - "id": "controller-scope-is-mvc", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#leaving-out-what-a-route-cannot-answer", - "sentence": "**In MVC**, an action or a controller says it with `[WithoutFaults(nameof(AccountFault.NotFound))]`.", - "axes": { - "scope": [ - "controller" - ], - "http.host": [ - "minimal", - "route-group" - ] - } - } - ], - "exclude": [ - { - "reason": "A minimal-API host has no controller to leave a member out on: the controller scope is MVC's (not-promised controller-scope-is-mvc).", - "pairs": [ - [ - "scope=controller", - "http.host=minimal" - ], - [ - "scope=controller", - "http.host=route-group" - ] - ] - }, - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C05-with-rule", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#the-rule-that-refused", - "surfaces": [ - "http" - ], - "axes": { - "ruletype": [ - "enum", - "closed-set", - "nested-enum", - "flags-combination", - "undefined-cast" - ], - "qualified": [ - "bare", - "QualifiedFaultNames" - ] - }, - "obligations": [ - { - "id": "rule-qualified", - "sentence": "The problem carries it as `rule`, qualified with its type's name, so two types' `Blank` can be told apart:", - "axes": { - "ruletype": [ - "enum", - "closed-set" - ] - } - }, - { - "id": "nested-chain", - "sentence": "A rule of a nested type is named after its nesting chain, whatever `QualifiedFaultNames` says.", - "axes": { - "ruletype": [ - "nested-enum" - ] - } - }, - { - "id": "stays-the-errors", - "sentence": "The status, the mapping and the document stay the error's.", - "axes": { - "ruletype": [ - "enum", - "closed-set", - "nested-enum" - ] - } - }, - { - "id": "unnamed-value-throws", - "sentence": "A value no member names, such as a combination of flags or a cast number, throws where it's given.", - "axes": { - "ruletype": [ - "flags-combination", - "undefined-cast" - ] - } - }, - { - "id": "no-namespace", - "sentence": "**The name carries no namespace**, which would publish your application's structure to every client.", - "axes": { - "ruletype": [ - "enum", - "closed-set", - "nested-enum" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C06-fault-kinds", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#a-closed-set-as-the-fault-type", - "surfaces": [ - "http" - ], - "axes": { - "kind": [ - "PreconditionFailed", - "Malformed", - "Invalid", - "Unavailable" - ] - }, - "obligations": [ - { - "id": "kind-gives-status", - "sentence": "The kind gives the status:" - }, - { - "id": "description-as-detail", - "sentence": "The description becomes the problem's `detail`, so a client can show it." - }, - { - "id": "no-description-no-detail", - "sentence": "A member without one has no `detail`." - }, - { - "id": "member-must-have-kind", - "sentence": "**A member must have a kind.** A member whose kind is `default`, which is no kind, fails this call." - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C07-fault-responses-blanket", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#every-fault-set-at-once", - "surfaces": [ - "http" - ], - "axes": { - "mapping": [ - "by-kind", - "own-IFault-mapping" - ], - "set": [ - "fault-set", - "own-mapped-set", - "enum-unmapped", - "member-without-kind", - "mapping-throws", - "status-not-error" - ] - }, - "obligations": [ - { - "id": "by-kind", - "sentence": "every fault set an endpoint answers with, by its kind", - "axes": { - "mapping": [ - "by-kind" - ] - } - }, - { - "id": "own-mapping", - "sentence": "The mapping can be your own. It reads each member's kind and description through `IFault`:", - "axes": { - "mapping": [ - "own-IFault-mapping" - ] - } - }, - { - "id": "own-set-keeps-mapping", - "sentence": "A set mapped on its own keeps its mapping.", - "axes": { - "set": [ - "own-mapped-set" - ] - } - }, - { - "id": "enum-needs-own-mapping", - "sentence": "Neither has a kind, and the host doesn't start while an endpoint answers with one that's unmapped.", - "axes": { - "set": [ - "enum-unmapped" - ] - } - }, - { - "id": "member-without-kind-stops-start", - "sentence": "A member with no kind stops the start, and so does one the mapping throws for or gives a status that isn't an error.", - "axes": { - "set": [ - "member-without-kind" - ] - } - }, - { - "id": "mapping-refusal-stops-start", - "sentence": "A member with no kind stops the start, and so does one the mapping throws for or gives a status that isn't an error.", - "axes": { - "set": [ - "mapping-throws", - "status-not-error" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C08-same-name-warning", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types", - "surfaces": [ - "http" - ], - "axes": { - "naming": [ - "bare", - "qualified" - ], - "arity": [ - "2", - "3" - ], - "leave-out": [ - "none", - "WithoutFaults-one" - ] - }, - "obligations": [ - { - "id": "bare-names-warn", - "sentence": "With bare names, the default, that's `AccountFault.NotFound` beside an `EditFault.NotFound`.", - "axes": { - "naming": [ - "bare" - ] - } - }, - { - "id": "qualified-chain-warns", - "sentence": "With qualified names, it's two sets with one nesting chain in two namespaces, since the name carries no namespace.", - "axes": { - "naming": [ - "qualified" - ] - } - }, - { - "id": "warning-names-both", - "sentence": "A client reads the two errors as one, so the host logs a warning at startup naming both.", - "axes": { - "leave-out": [ - "none" - ] - } - }, - { - "id": "qualified-names-fix", - "sentence": "Fix it with `QualifiedFaultNames`, a rename, or by leaving one out of the route", - "axes": { - "naming": [ - "qualified" - ] - } - }, - { - "id": "leave-one-out-fixes", - "sentence": "Fix it with `QualifiedFaultNames`, a rename, or by leaving one out of the route", - "axes": { - "leave-out": [ - "WithoutFaults-one" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C09-base-record-equality", - "source": "CHANGELOG.md#2.1.0-fixed", - "surfaces": [ - "generation" - ], - "axes": { - "base-equality": [ - "none", - "override-Equals", - "override-GetHashCode", - "explicit-IEquatable" - ], - "collection": [ - "HashSet", - "Dictionary-key", - "Distinct" - ] - }, - "narrow": { - "shape.position": "Over a base record, a value object's equality is its wrapped value's, in a collection of the base too, whatever equality the base declares.", - "shape.containing": "Over a base record, a value object's equality is its wrapped value's, in a collection of the base too, whatever equality the base declares." - }, - "obligations": [ - { - "id": "equal-by-the-wrapped-value", - "sentence": "Over a base record, a value object's equality is its wrapped value's" - }, - { - "id": "in-a-collection-of-the-base", - "sentence": "in a collection of the base too, whatever equality the base declares." - } - ], - "exclude": [ - { - "reason": "A record struct or a closed set has no base record: C# gives a struct no base type, and a closed set is a sealed class that is never a record (CMTK1020).", - "pairs": [ - [ - "base-equality=explicit-IEquatable", - "shape.kind=closed-set" - ], - [ - "base-equality=explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "base-equality=explicit-IEquatable", - "shape.kind=validated-struct" - ], - [ - "base-equality=none", - "shape.kind=closed-set" - ], - [ - "base-equality=none", - "shape.kind=record-struct" - ], - [ - "base-equality=none", - "shape.kind=validated-struct" - ], - [ - "base-equality=override-Equals", - "shape.kind=closed-set" - ], - [ - "base-equality=override-Equals", - "shape.kind=record-struct" - ], - [ - "base-equality=override-Equals", - "shape.kind=validated-struct" - ], - [ - "base-equality=override-GetHashCode", - "shape.kind=closed-set" - ], - [ - "base-equality=override-GetHashCode", - "shape.kind=record-struct" - ], - [ - "base-equality=override-GetHashCode", - "shape.kind=validated-struct" - ], - [ - "collection=Dictionary-key", - "shape.kind=closed-set" - ], - [ - "collection=Dictionary-key", - "shape.kind=record-struct" - ], - [ - "collection=Dictionary-key", - "shape.kind=validated-struct" - ], - [ - "collection=Distinct", - "shape.kind=closed-set" - ], - [ - "collection=Distinct", - "shape.kind=record-struct" - ], - [ - "collection=Distinct", - "shape.kind=validated-struct" - ], - [ - "collection=HashSet", - "shape.kind=closed-set" - ], - [ - "collection=HashSet", - "shape.kind=record-struct" - ], - [ - "collection=HashSet", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=none", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=none", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=none", - "shape.kind=validated-struct" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=closed-set", - "shape.site=in-generic-type" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-in-class" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-in-struct" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-two-levels" - ], - [ - "shape.kind=closed-set", - "shape.site=top-level" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=record-struct", - "shape.site=in-generic-type" - ], - [ - "shape.kind=record-struct", - "shape.site=nested-in-class" - ], - [ - "shape.kind=record-struct", - "shape.site=nested-in-struct" - ], - [ - "shape.kind=record-struct", - "shape.site=nested-two-levels" - ], - [ - "shape.kind=record-struct", - "shape.site=top-level" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=Guid" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=Uri" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=bool" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=byte" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=char" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=decimal" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=double" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=enum" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=int" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=long" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=string" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=ulong" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=validated-struct", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-struct", - "shape.site=nested-in-class" - ], - [ - "shape.kind=validated-struct", - "shape.site=nested-in-struct" - ], - [ - "shape.kind=validated-struct", - "shape.site=nested-two-levels" - ], - [ - "shape.kind=validated-struct", - "shape.site=top-level" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=Guid" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=Uri" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=bool" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=byte" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=char" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=decimal" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=double" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=enum" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=int" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=long" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=string" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=ulong" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ] - ] - }, - { - "reason": "A value object nested in a generic type fails the build with CMTK1005 (Generators README, Build errors), so it has no generated equality or parsing to check.", - "pairs": [ - [ - "base-equality=explicit-IEquatable", - "shape.site=in-generic-type" - ], - [ - "base-equality=none", - "shape.site=in-generic-type" - ], - [ - "base-equality=override-Equals", - "shape.site=in-generic-type" - ], - [ - "base-equality=override-GetHashCode", - "shape.site=in-generic-type" - ], - [ - "collection=Dictionary-key", - "shape.site=in-generic-type" - ], - [ - "collection=Distinct", - "shape.site=in-generic-type" - ], - [ - "collection=HashSet", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-1", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-2", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-3", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=none", - "shape.site=in-generic-type" - ], - [ - "shape.kind=sealed-record-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-class", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=in-generic-type" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTime" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Guid" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeSpan" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Uri" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=bool" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=byte" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=char" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=decimal" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=double" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=flags-enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=int" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=long" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=own-record-class" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=string" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=in-generic-type" - ] - ] - }, - { - "reason": "The claim is about a value object over a base record; with no base record it states nothing.", - "pairs": [ - [ - "base-equality=explicit-IEquatable", - "shape.inheritance=none" - ], - [ - "base-equality=none", - "shape.inheritance=none" - ], - [ - "base-equality=override-Equals", - "shape.inheritance=none" - ], - [ - "base-equality=override-GetHashCode", - "shape.inheritance=none" - ], - [ - "collection=Dictionary-key", - "shape.inheritance=none" - ], - [ - "collection=Distinct", - "shape.inheritance=none" - ], - [ - "collection=HashSet", - "shape.inheritance=none" - ], - [ - "shape.inheritance=none", - "shape.kind=sealed-record-class" - ], - [ - "shape.inheritance=none", - "shape.kind=validated-class" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=none", - "shape.site=nested-in-class" - ], - [ - "shape.inheritance=none", - "shape.site=nested-in-struct" - ], - [ - "shape.inheritance=none", - "shape.site=nested-two-levels" - ], - [ - "shape.inheritance=none", - "shape.site=top-level" - ], - [ - "shape.inheritance=none", - "shape.wrapped=DateOnly" - ], - [ - "shape.inheritance=none", - "shape.wrapped=DateTime" - ], - [ - "shape.inheritance=none", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.inheritance=none", - "shape.wrapped=Guid" - ], - [ - "shape.inheritance=none", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.inheritance=none", - "shape.wrapped=TimeOnly" - ], - [ - "shape.inheritance=none", - "shape.wrapped=TimeSpan" - ], - [ - "shape.inheritance=none", - "shape.wrapped=Uri" - ], - [ - "shape.inheritance=none", - "shape.wrapped=bool" - ], - [ - "shape.inheritance=none", - "shape.wrapped=byte" - ], - [ - "shape.inheritance=none", - "shape.wrapped=char" - ], - [ - "shape.inheritance=none", - "shape.wrapped=decimal" - ], - [ - "shape.inheritance=none", - "shape.wrapped=double" - ], - [ - "shape.inheritance=none", - "shape.wrapped=enum" - ], - [ - "shape.inheritance=none", - "shape.wrapped=flags-enum" - ], - [ - "shape.inheritance=none", - "shape.wrapped=int" - ], - [ - "shape.inheritance=none", - "shape.wrapped=long" - ], - [ - "shape.inheritance=none", - "shape.wrapped=own-record-class" - ], - [ - "shape.inheritance=none", - "shape.wrapped=string" - ], - [ - "shape.inheritance=none", - "shape.wrapped=ulong" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=equality-operator (CMTK1011)" - ] - ] - }, - { - "reason": "A declaration of a refused kind fails the build (CMTK1005, CMTK1006), so it is generated into nothing; .claude/probe-axes.md: shape.refused-kind is taken only where the refusal is the claim.", - "pairs": [ - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "base-equality=none", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "base-equality=none", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "base-equality=none", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "base-equality=none", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "collection=Distinct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "collection=Distinct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "collection=Distinct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "collection=Distinct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "collection=HashSet", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "collection=HashSet", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "collection=HashSet", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "collection=HashSet", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=ulong" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "base-equality=explicit-IEquatable", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "base-equality=none", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "base-equality=none", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "base-equality=none", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "base-equality=none", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "base-equality=none", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "base-equality=override-Equals", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "base-equality=override-GetHashCode", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "collection=Dictionary-key", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "collection=Distinct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "collection=Distinct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "collection=Distinct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "collection=Distinct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "collection=Distinct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "collection=HashSet", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "collection=HashSet", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "collection=HashSet", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "collection=HashSet", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "collection=HashSet", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=ulong" - ] - ] - } - ] - }, - { - "id": "C10-preserve-guard", - "source": "src/CodoMetis.TypeKit.Generators/README.md#json", - "surfaces": [ - "json" - ], - "axes": { - "handler": [ - "Preserve", - "IgnoreCycles", - "none" - ] - }, - "obligations": [ - { - "id": "refused-where-references-could-be-written", - "sentence": "**Preserved references** (`ReferenceHandler.Preserve`) throw a `NotSupportedException` where the serializer could write `$id` or `$ref` inside the underlying value.", - "axes": { - "handler": [ - "Preserve" - ], - "shape.wrapped": [ - "own-record-class" - ] - } - }, - { - "id": "value-converters-unaffected", - "sentence": "These are unaffected: a value the serializer writes with a value converter (a number, a string, a `Guid`, a `Uri`)", - "axes": { - "handler": [ - "Preserve" - ] - } - }, - { - "id": "own-converters-unaffected", - "sentence": "anything a converter of your own writes, on the options or on one property", - "axes": { - "json.Converters": [ - "own-for-wrapped", - "own-for-value-object" - ] - } - }, - { - "id": "ignore-cycles-unaffected", - "sentence": "a struct of such values, and `ReferenceHandler.IgnoreCycles`.", - "axes": { - "handler": [ - "IgnoreCycles" - ] - } - } - ], - "exclude": [ - { - "reason": "handler is this claim's name for JsonSerializerOptions.ReferenceHandler (Preserve, IgnoreCycles, none = default), the same setting as json.ReferenceHandler; one setting cannot hold two values in one row.", - "pairs": [ - [ - "handler=IgnoreCycles", - "json.ReferenceHandler=Preserve" - ], - [ - "handler=IgnoreCycles", - "json.ReferenceHandler=default" - ], - [ - "handler=Preserve", - "json.ReferenceHandler=IgnoreCycles" - ], - [ - "handler=Preserve", - "json.ReferenceHandler=default" - ], - [ - "handler=none", - "json.ReferenceHandler=IgnoreCycles" - ], - [ - "handler=none", - "json.ReferenceHandler=Preserve" - ] - ] - }, - { - "reason": "A closed set wraps nothing (Generators README#closed-sets: a sealed class whose instances are its members), so it has no shape.wrapped.", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "A value at the root is not held by a containing type, so shape.containing has nothing to apply to.", - "pairs": [ - [ - "shape.containing=class", - "shape.position=root" - ], - [ - "shape.containing=generic-type", - "shape.position=root" - ], - [ - "shape.containing=record-class", - "shape.position=root" - ], - [ - "shape.containing=record-struct", - "shape.position=root" - ], - [ - "shape.containing=struct", - "shape.position=root" - ] - ] - }, - { - "reason": "System.Text.Json refuses JsonObjectCreationHandling.Populate together with a reference handler (InvalidOperationException when the options are used; harness/JsonRow.cs runs such a row without Populate), so no row can set both.", - "pairs": [ - [ - "handler=IgnoreCycles", - "json.PreferredObjectCreationHandling=Populate" - ], - [ - "handler=Preserve", - "json.PreferredObjectCreationHandling=Populate" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ] - ] - } - ] - }, - { - "id": "C11-parse-shapes-build", - "source": "CHANGELOG.md#2.1.0-fixed", - "surfaces": [ - "text", - "generation" - ], - "axes": { - "parse-shape": [ - "implicit-result", - "ref-text", - "abstract-string-ctor", - "ordinary" - ] - }, - "narrow": { - "shape.position": "A wrapped type's `Parse` whose result can't be assigned to it, or that takes its text by `ref`, or an abstract class's string constructor, no longer breaks the build; the value object has no parsing.", - "shape.containing": "A wrapped type's `Parse` whose result can't be assigned to it, or that takes its text by `ref`, or an abstract class's string constructor, no longer breaks the build; the value object has no parsing." - }, - "obligations": [ - { - "id": "implicit-result", - "sentence": "A wrapped type's `Parse` whose result can't be assigned to it", - "axes": { - "parse-shape": [ - "implicit-result" - ] - } - }, - { - "id": "ref-text", - "sentence": "or that takes its text by `ref`", - "axes": { - "parse-shape": [ - "ref-text" - ] - } - }, - { - "id": "abstract-string-constructor", - "sentence": "or an abstract class's string constructor, no longer breaks the build; the value object has no parsing.", - "axes": { - "parse-shape": [ - "abstract-string-ctor" - ] - } - }, - { - "id": "parsing-where-it-can-be-parsed", - "sentence": "`IParsable`/`ISpanParsable`/`IUtf8SpanParsable`, formatting in the invariant culture", - "axes": { - "parse-shape": [ - "ordinary" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.wrapped": [ - "Uri", - "enum", - "flags-enum", - "NodaTime.Instant" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "not-promised:no-span-parsing-without-the-underlying-types" - } - ] - } - ], - "not-promised": [ - { - "id": "no-span-parsing-without-the-underlying-types", - "source": "src/CodoMetis.TypeKit.Generators/README.md#what-is-generated", - "sentence": "A `Uri`, an enum and a NodaTime type have neither.", - "axes": { - "shape.wrapped": [ - "Uri", - "enum", - "flags-enum", - "NodaTime.Instant" - ], - "text.entry": [ - "ISpanParsable" - ] - } - } - ], - "exclude": [ - { - "reason": "A closed set wraps no type, so it has no wrapped type's Parse to take for its parser.", - "pairs": [ - [ - "parse-shape=abstract-string-ctor", - "shape.kind=closed-set" - ], - [ - "parse-shape=implicit-result", - "shape.kind=closed-set" - ], - [ - "parse-shape=ordinary", - "shape.kind=closed-set" - ], - [ - "parse-shape=ref-text", - "shape.kind=closed-set" - ], - [ - "process.converter-lookup=none", - "shape.kind=closed-set" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=none", - "shape.kind=closed-set" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=closed-set", - "shape.site=in-generic-type" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-in-class" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-in-struct" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-two-levels" - ], - [ - "shape.kind=closed-set", - "shape.site=top-level" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ], - [ - "shape.kind=closed-set", - "text.culture=ar-SA" - ], - [ - "shape.kind=closed-set", - "text.culture=de-DE" - ], - [ - "shape.kind=closed-set", - "text.culture=globalization-invariant" - ], - [ - "shape.kind=closed-set", - "text.culture=invariant" - ], - [ - "shape.kind=closed-set", - "text.culture=tr-TR" - ], - [ - "shape.kind=closed-set", - "text.entry=IParsable" - ], - [ - "shape.kind=closed-set", - "text.entry=ISpanParsable" - ], - [ - "shape.kind=closed-set", - "text.entry=Parse" - ], - [ - "shape.kind=closed-set", - "text.entry=ToString-format" - ], - [ - "shape.kind=closed-set", - "text.entry=TryFormat-short-span" - ], - [ - "shape.kind=closed-set", - "text.entry=TryFrom" - ], - [ - "shape.kind=closed-set", - "text.entry=TryParse" - ], - [ - "shape.kind=closed-set", - "text.entry=type-converter" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=equality-operator (CMTK1011)" - ] - ] - }, - { - "reason": "A value object nested in a generic type fails the build with CMTK1005 (Generators README, Build errors), so it has no generated equality or parsing to check.", - "pairs": [ - [ - "parse-shape=abstract-string-ctor", - "shape.site=in-generic-type" - ], - [ - "parse-shape=implicit-result", - "shape.site=in-generic-type" - ], - [ - "parse-shape=ordinary", - "shape.site=in-generic-type" - ], - [ - "parse-shape=ref-text", - "shape.site=in-generic-type" - ], - [ - "process.converter-lookup=none", - "shape.site=in-generic-type" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-1", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-2", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-3", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=none", - "shape.site=in-generic-type" - ], - [ - "shape.kind=record-struct", - "shape.site=in-generic-type" - ], - [ - "shape.kind=sealed-record-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-struct", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=in-generic-type" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTime" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Guid" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeSpan" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Uri" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=bool" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=byte" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=char" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=decimal" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=double" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=flags-enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=int" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=long" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=own-record-class" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=string" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=ulong" - ], - [ - "shape.site=in-generic-type", - "text.culture=ar-SA" - ], - [ - "shape.site=in-generic-type", - "text.culture=de-DE" - ], - [ - "shape.site=in-generic-type", - "text.culture=globalization-invariant" - ], - [ - "shape.site=in-generic-type", - "text.culture=invariant" - ], - [ - "shape.site=in-generic-type", - "text.culture=tr-TR" - ], - [ - "shape.site=in-generic-type", - "text.entry=IParsable" - ], - [ - "shape.site=in-generic-type", - "text.entry=ISpanParsable" - ], - [ - "shape.site=in-generic-type", - "text.entry=Parse" - ], - [ - "shape.site=in-generic-type", - "text.entry=ToString-format" - ], - [ - "shape.site=in-generic-type", - "text.entry=TryFormat-short-span" - ], - [ - "shape.site=in-generic-type", - "text.entry=TryFrom" - ], - [ - "shape.site=in-generic-type", - "text.entry=TryParse" - ], - [ - "shape.site=in-generic-type", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=in-generic-type" - ] - ] - }, - { - "reason": "A record struct has no base type, so it cannot derive from a base record or inherit a base's explicit IEquatable.", - "pairs": [ - [ - "shape.inheritance=base-depth-1", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=validated-struct" - ] - ] - }, - { - "reason": "The parse shape is the wrapped type itself, a class of its own; a value object over one of the catalogue's wrapped types is the ordinary shape.", - "pairs": [ - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=DateOnly" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=DateTime" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=DateTimeOffset" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=Guid" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=NodaTime.Instant" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=TimeOnly" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=TimeSpan" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=Uri" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=bool" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=byte" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=char" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=decimal" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=double" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=enum" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=flags-enum" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=int" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=long" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=own-record-class" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=string" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.wrapped=ulong" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=DateOnly" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=DateTime" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=DateTimeOffset" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=Guid" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=NodaTime.Instant" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=TimeOnly" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=TimeSpan" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=Uri" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=bool" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=byte" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=char" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=decimal" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=double" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=enum" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=flags-enum" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=int" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=long" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=own-record-class" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=string" - ], - [ - "parse-shape=implicit-result", - "shape.wrapped=ulong" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=DateOnly" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=DateTime" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=DateTimeOffset" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=Guid" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=NodaTime.Instant" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=TimeOnly" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=TimeSpan" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=Uri" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=bool" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=byte" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=char" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=decimal" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=double" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=enum" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=flags-enum" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=int" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=long" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=own-record-class" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=string" - ], - [ - "parse-shape=ref-text", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "A declaration of a refused kind fails the build (CMTK1005, CMTK1006), so it is generated into nothing; .claude/probe-axes.md: shape.refused-kind is taken only where the refusal is the claim.", - "pairs": [ - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "text.entry=type-converter" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "parse-shape=abstract-string-ctor", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "parse-shape=implicit-result", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "parse-shape=ordinary", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "parse-shape=ref-text", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "process.converter-lookup=none", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.culture=ar-SA" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.culture=de-DE" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.culture=globalization-invariant" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.culture=invariant" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.culture=tr-TR" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=IParsable" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=ISpanParsable" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=Parse" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=ToString-format" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=TryFormat-short-span" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=TryFrom" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=TryParse" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "text.entry=type-converter" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "text.entry=type-converter" - ] - ] - }, - { - "reason": "A `Uri`, an enum and a NodaTime type have neither (Generators README, What is generated: no ISpanParsable or IUtf8SpanParsable where the underlying type has none, not-promised no-span-parsing-without-the-underlying-types), so parsing-where-it-can-be-parsed is inapplicable there and no check that reads shape.wrapped can credit them.", - "pairs": [ - [ - "shape.wrapped=NodaTime.Instant", - "text.entry=ISpanParsable" - ], - [ - "shape.wrapped=Uri", - "text.entry=ISpanParsable" - ], - [ - "shape.wrapped=enum", - "text.entry=ISpanParsable" - ], - [ - "shape.wrapped=flags-enum", - "text.entry=ISpanParsable" - ] - ] - } - ] - }, - { - "id": "C12-described-converter", - "source": "docs/json.md#describing-what-a-converter-writes", - "surfaces": [ - "http" - ], - "axes": { - "source": [ - "IDescribedJsonConverter", - "JsonSchemaDescriptions", - "decimal-converter" - ], - "http.host": [ - "minimal", - "mvc", - "both", - "route-group", - "controller-without-ApiController-visible" - ] - }, - "obligations": [ - { - "id": "own-converter-describes-itself", - "sentence": "**Your own converter** implements `IDescribedJsonConverter` and describes itself, as both decimal converters do.", - "axes": { - "source": [ - "IDescribedJsonConverter", - "decimal-converter" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "source": [ - "IDescribedJsonConverter", - "JsonSchemaDescriptions" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "options-describe", - "sentence": "**A converter someone else wrote**, or the serializer's own enum converter, is described in the options by `JsonSchemaDescriptions`.", - "axes": { - "source": [ - "JsonSchemaDescriptions" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "source": [ - "IDescribedJsonConverter", - "JsonSchemaDescriptions" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "every-position", - "sentence": "With CodoMetis.TypeKit.AspNetCore, `AddTypeKit()` writes the same descriptions into the OpenAPI document, in every position, a list's elements and a dictionary's values included.", - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "source": [ - "IDescribedJsonConverter", - "JsonSchemaDescriptions" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "parameter-keeps-aspnet", - "sentence": "So it keeps the description ASP.NET gives the type without your converters", - "axes": { - "source": [ - "decimal-converter" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "source": [ - "IDescribedJsonConverter", - "JsonSchemaDescriptions" - ] - }, - "cite": "record:0099" - } - ] - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C13-patch-required", - "source": "docs/merge-patches.md#required-constructor-parameters", - "surfaces": [ - "http" - ], - "axes": { - "declared": [ - "init", - "ctor-parameter" - ], - "http.host": [ - "minimal", - "mvc", - "both", - "route-group", - "controller-without-ApiController-visible" - ] - }, - "obligations": [ - { - "id": "doc-required-where-rejected", - "sentence": "So the document never calls a field optional when the endpoint rejects a request without it.", - "axes": { - "declared": [ - "ctor-parameter" - ], - "json.RespectRequiredConstructorParameters": [ - "true" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "doc-optional-where-absent", - "sentence": "`AddTypeKit()` leaves a patch out of `required` where the serializer lets it be absent, and keeps it there where the options reject a body without it.", - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "absent-is-default", - "sentence": "a property the request leaves out keeps its default, which is `Absent`." - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C14-declared-problem", - "source": "CHANGELOG.md#2.1.0-fixed", - "surfaces": [ - "http" - ], - "axes": { - "declared": [ - "ProducesProblem", - "ProducesResponseType", - "none" - ], - "http.host": [ - "minimal", - "mvc", - "both", - "route-group", - "controller-without-ApiController-visible" - ] - }, - "obligations": [ - { - "id": "declared-describes-fault", - "sentence": "OpenAPI: a problem response an endpoint declares itself (`ProducesProblem`, `[ProducesResponseType]`) describes `fault`, and names its problem types.", - "axes": { - "declared": [ - "ProducesProblem", - "ProducesResponseType" - ] - } - }, - { - "id": "declared-names-types", - "sentence": "OpenAPI: a problem response an endpoint declares itself (`ProducesProblem`, `[ProducesResponseType]`) describes `fault`, and names its problem types.", - "axes": { - "declared": [ - "ProducesProblem", - "ProducesResponseType" - ] - } - }, - { - "id": "ascending-with-declared", - "sentence": "OpenAPI: an operation with fault responses lists every status ascending, the ones its endpoint declares itself among them", - "axes": { - "declared": [ - "ProducesProblem", - "ProducesResponseType" - ] - } - }, - { - "id": "application-json-declared", - "sentence": "OpenAPI: a problem response an endpoint declares as `application/json`, such as `Produces(404)` or `[ProducesResponseType(404)]`, describes `fault` and `rule` and names its problem types, and keeps its media type", - "axes": { - "declared": [ - "ProducesResponseType" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C15-mvc-fault-status-doc", - "source": "CHANGELOG.md#2.1.0-fixed", - "surfaces": [ - "http" - ], - "axes": { - "setter": [ - "MapControllers().WithFaultStatus", - "action-attribute", - "controller-attribute" - ], - "kind": [ - "status-only", - "WithoutFaults" - ], - "http.host": [ - "minimal", - "mvc", - "both", - "route-group", - "controller-without-ApiController-visible" - ] - }, - "obligations": [ - { - "id": "builder-reaches-doc", - "sentence": "OpenAPI: a `WithFaultStatus` on the builder `MapControllers()` returns reaches the document, as it reached the response." - }, - { - "id": "without-faults-leaves-doc", - "sentence": "**`WithoutFaults`** and `[WithoutFaults]`: a route leaves out of its document the members it never answers with, and reporting one throws" - }, - { - "id": "status-per-scope", - "sentence": "`WithFaultStatus`/`[FaultStatus]` per endpoint, group or controller" - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C16-options-agreement", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", - "surfaces": [ - "http" - ], - "axes": { - "difference": [ - "converter-only-http", - "naming-policy", - "number-handling" - ], - "http.host": [ - "minimal", - "mvc", - "both", - "route-group", - "controller-without-ApiController-visible" - ] - }, - "obligations": [ - { - "id": "differing-refused", - "sentence": "Otherwise building the document fails rather than describe JSON the controllers don't write.", - "axes": { - "difference": [ - "converter-only-http" - ], - "http.host": [ - "mvc", - "both", - "controller-without-ApiController-visible" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "error-names-type-and-descriptions", - "sentence": "The error names the type and what each set of options says of it, also for a type held only inside a patch.", - "axes": { - "difference": [ - "converter-only-http" - ], - "http.host": [ - "mvc", - "both", - "controller-without-ApiController-visible" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "patch-held-refused", - "sentence": "The error names the type and what each set of options says of it, also for a type held only inside a patch.", - "axes": { - "difference": [ - "converter-only-http" - ], - "http.host": [ - "mvc", - "both", - "controller-without-ApiController-visible" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "same-converters-build", - "sentence": "A host with controllers registers the same converters and descriptions on both, in the same order.", - "axes": { - "difference": [ - "naming-policy", - "number-handling" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "minimal-host-not-compared", - "sentence": "A host with controllers registers the same converters and descriptions on both, in the same order.", - "axes": { - "difference": [ - "converter-only-http" - ], - "http.host": [ - "minimal", - "route-group" - ] - }, - "inapplicable": [ - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - } - ] - } - ], - "not-promised": [ - { - "id": "only-converters-and-descriptions-compared", - "source": "design/adr/0071-converters-say-what-they-write.md#0071-json-converters-say-what-they-write-decimals-get-one-spelling", - "sentence": "Where the host has a controller, building the document fails when the two option sets do not carry the same described converters and description sets, by type, naming each that differs; a host of minimal APIs alone is never compared.", - "axes": { - "difference": [ - "naming-policy", - "number-handling" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "C17-cmtk0014", - "source": "src/CodoMetis.TypeKit.Analyzers/README.md#cmtk0014", - "surfaces": [ - "analyzers" - ], - "axes": { - "value": [ - "(FaultKind)9", - "default", - "valid" - ], - "site": [ - "attribute-argument", - "cast", - "default-literal" - ] - }, - "obligations": [ - { - "id": "a-cast-naming-no-member", - "sentence": "A `FaultKind` or a `ConstructorAccessibility` that names no member: a cast", - "axes": { - "value": [ - "(FaultKind)9" - ] - } - }, - { - "id": "default-and-zero", - "sentence": "`default(FaultKind)`, a zero", - "axes": { - "value": [ - "default" - ] - } - }, - { - "id": "constructor-accessibility-too", - "sentence": "[RequireParameterlessConstructor((ConstructorAccessibility)7)] // CMTK0014: no member, read as public", - "axes": { - "site": [ - "attribute-argument" - ], - "value": [ - "(FaultKind)9" - ] - } - }, - { - "id": "reported-where-written", - "sentence": "Both are reported where the value is written:", - "axes": { - "value": [ - "(FaultKind)9", - "default" - ] - } - }, - { - "id": "a-member-is-silent", - "sentence": "public static readonly ImportFault Duplicate = new(FaultKind.Conflict);", - "axes": { - "value": [ - "valid" - ] - } - } - ], - "exclude": [ - { - "reason": "A default literal is the value 0, so it cannot spell (FaultKind)9 or a member: FaultKind's members start at 1.", - "pairs": [ - [ - "site=default-literal", - "value=(FaultKind)9" - ], - [ - "site=default-literal", - "value=valid" - ] - ] - }, - { - "reason": "A record struct has no base type, so it cannot derive from a base record or inherit a base's explicit IEquatable.", - "pairs": [ - [ - "shape.inheritance=base-depth-1", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=validated-struct" - ] - ] - }, - { - "reason": "A closed set wraps no type, so a closed set over a wrapped type cannot be declared.", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "Declared as .claude/probe-axes.md says (as C22's harness, BuildRefusals.cs, declares it), a closed set named as one of its generated members leaves their writing to the generators, so in this compilation without them it does not implement IClosedSet's members: the compiler's own CS0535, and no row can hold it beside the analyzer's places.", - "pairs": [ - [ - "shape.containing=class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=field", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=root", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=attribute-argument" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=cast" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=default-literal" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "value=(FaultKind)9" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "value=default" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "value=valid" - ] - ] - }, - { - "reason": "A value object with a hand-written operator == and != between two of the type is C#'s own CS0111 without the generators (Generators README, Build errors: CMTK1011, 'which C# also reports as CS0111'), since a record already declares both, so this compilation without the generators cannot hold it.", - "pairs": [ - [ - "shape.containing=class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=field", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=root", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=attribute-argument" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=cast" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=default-literal" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "value=(FaultKind)9" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "value=default" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "value=valid" - ] - ] - } - ] - }, - { - "id": "C18-cmtk1023", - "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", - "surfaces": [ - "generation" - ], - "axes": { - "base-member": [ - "Equals", - "GetHashCode", - "IEquatable" - ] - }, - "obligations": [ - { - "id": "names-each-base-member", - "sentence": "So a hand-written `Equals(TBase)`, `GetHashCode()` or `IEquatable.Equals` on a base record isn't used, and a warning names it (CMTK1023)." - }, - { - "id": "a-warning-and-generated", - "sentence": "A warning, and the value object is generated." - }, - { - "id": "generated-equality-never-calls-the-base", - "sentence": "Over a base record, the generated `Equals(TSelf)` and `GetHashCode()` compare the underlying value alone, and never call the base's." - } - ], - "exclude": [ - { - "reason": "A record struct or a closed set has no base record: C# gives a struct no base type, and a closed set is a sealed class that is never a record (CMTK1020). CMTK1023 is about a base record's equality.", - "pairs": [ - [ - "base-member=Equals", - "shape.kind=closed-set" - ], - [ - "base-member=Equals", - "shape.kind=record-struct" - ], - [ - "base-member=Equals", - "shape.kind=validated-struct" - ], - [ - "base-member=GetHashCode", - "shape.kind=closed-set" - ], - [ - "base-member=GetHashCode", - "shape.kind=record-struct" - ], - [ - "base-member=GetHashCode", - "shape.kind=validated-struct" - ], - [ - "base-member=IEquatable", - "shape.kind=closed-set" - ], - [ - "base-member=IEquatable", - "shape.kind=record-struct" - ], - [ - "base-member=IEquatable", - "shape.kind=validated-struct" - ], - [ - "shape.containing=class", - "shape.kind=closed-set" - ], - [ - "shape.containing=class", - "shape.kind=record-struct" - ], - [ - "shape.containing=class", - "shape.kind=validated-struct" - ], - [ - "shape.containing=generic-type", - "shape.kind=closed-set" - ], - [ - "shape.containing=generic-type", - "shape.kind=record-struct" - ], - [ - "shape.containing=generic-type", - "shape.kind=validated-struct" - ], - [ - "shape.containing=record-class", - "shape.kind=closed-set" - ], - [ - "shape.containing=record-class", - "shape.kind=record-struct" - ], - [ - "shape.containing=record-class", - "shape.kind=validated-struct" - ], - [ - "shape.containing=record-struct", - "shape.kind=closed-set" - ], - [ - "shape.containing=record-struct", - "shape.kind=record-struct" - ], - [ - "shape.containing=record-struct", - "shape.kind=validated-struct" - ], - [ - "shape.containing=struct", - "shape.kind=closed-set" - ], - [ - "shape.containing=struct", - "shape.kind=record-struct" - ], - [ - "shape.containing=struct", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=none", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=none", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=none", - "shape.kind=validated-struct" - ], - [ - "shape.kind=closed-set", - "shape.position=array-element" - ], - [ - "shape.kind=closed-set", - "shape.position=dictionary-key" - ], - [ - "shape.kind=closed-set", - "shape.position=dictionary-value" - ], - [ - "shape.kind=closed-set", - "shape.position=field" - ], - [ - "shape.kind=closed-set", - "shape.position=immutable-array-element" - ], - [ - "shape.kind=closed-set", - "shape.position=in-patch" - ], - [ - "shape.kind=closed-set", - "shape.position=list-element" - ], - [ - "shape.kind=closed-set", - "shape.position=nullable-property" - ], - [ - "shape.kind=closed-set", - "shape.position=polymorphic-derived-property" - ], - [ - "shape.kind=closed-set", - "shape.position=property" - ], - [ - "shape.kind=closed-set", - "shape.position=read-only-list-element" - ], - [ - "shape.kind=closed-set", - "shape.position=record-ctor-parameter" - ], - [ - "shape.kind=closed-set", - "shape.position=required-init" - ], - [ - "shape.kind=closed-set", - "shape.position=root" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=closed-set", - "shape.site=in-generic-type" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-in-class" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-in-struct" - ], - [ - "shape.kind=closed-set", - "shape.site=nested-two-levels" - ], - [ - "shape.kind=closed-set", - "shape.site=top-level" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ], - [ - "shape.kind=record-struct", - "shape.position=array-element" - ], - [ - "shape.kind=record-struct", - "shape.position=dictionary-key" - ], - [ - "shape.kind=record-struct", - "shape.position=dictionary-value" - ], - [ - "shape.kind=record-struct", - "shape.position=field" - ], - [ - "shape.kind=record-struct", - "shape.position=immutable-array-element" - ], - [ - "shape.kind=record-struct", - "shape.position=in-patch" - ], - [ - "shape.kind=record-struct", - "shape.position=list-element" - ], - [ - "shape.kind=record-struct", - "shape.position=nullable-property" - ], - [ - "shape.kind=record-struct", - "shape.position=polymorphic-derived-property" - ], - [ - "shape.kind=record-struct", - "shape.position=property" - ], - [ - "shape.kind=record-struct", - "shape.position=read-only-list-element" - ], - [ - "shape.kind=record-struct", - "shape.position=record-ctor-parameter" - ], - [ - "shape.kind=record-struct", - "shape.position=required-init" - ], - [ - "shape.kind=record-struct", - "shape.position=root" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=record-struct", - "shape.site=in-generic-type" - ], - [ - "shape.kind=record-struct", - "shape.site=nested-in-class" - ], - [ - "shape.kind=record-struct", - "shape.site=nested-in-struct" - ], - [ - "shape.kind=record-struct", - "shape.site=nested-two-levels" - ], - [ - "shape.kind=record-struct", - "shape.site=top-level" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=Guid" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=Uri" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=bool" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=byte" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=char" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=decimal" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=double" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=enum" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=int" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=long" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=string" - ], - [ - "shape.kind=record-struct", - "shape.wrapped=ulong" - ], - [ - "shape.kind=validated-struct", - "shape.position=array-element" - ], - [ - "shape.kind=validated-struct", - "shape.position=dictionary-key" - ], - [ - "shape.kind=validated-struct", - "shape.position=dictionary-value" - ], - [ - "shape.kind=validated-struct", - "shape.position=field" - ], - [ - "shape.kind=validated-struct", - "shape.position=immutable-array-element" - ], - [ - "shape.kind=validated-struct", - "shape.position=in-patch" - ], - [ - "shape.kind=validated-struct", - "shape.position=list-element" - ], - [ - "shape.kind=validated-struct", - "shape.position=nullable-property" - ], - [ - "shape.kind=validated-struct", - "shape.position=polymorphic-derived-property" - ], - [ - "shape.kind=validated-struct", - "shape.position=property" - ], - [ - "shape.kind=validated-struct", - "shape.position=read-only-list-element" - ], - [ - "shape.kind=validated-struct", - "shape.position=record-ctor-parameter" - ], - [ - "shape.kind=validated-struct", - "shape.position=required-init" - ], - [ - "shape.kind=validated-struct", - "shape.position=root" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=validated-struct", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-struct", - "shape.site=nested-in-class" - ], - [ - "shape.kind=validated-struct", - "shape.site=nested-in-struct" - ], - [ - "shape.kind=validated-struct", - "shape.site=nested-two-levels" - ], - [ - "shape.kind=validated-struct", - "shape.site=top-level" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=Guid" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=Uri" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=bool" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=byte" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=char" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=decimal" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=double" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=enum" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=int" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=long" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=string" - ], - [ - "shape.kind=validated-struct", - "shape.wrapped=ulong" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ] - ] - }, - { - "reason": "A value object nested in a generic type fails the build with CMTK1005 (Generators README, Build errors), so it has no generated equality or parsing to check.", - "pairs": [ - [ - "base-member=Equals", - "shape.site=in-generic-type" - ], - [ - "base-member=GetHashCode", - "shape.site=in-generic-type" - ], - [ - "base-member=IEquatable", - "shape.site=in-generic-type" - ], - [ - "shape.containing=class", - "shape.site=in-generic-type" - ], - [ - "shape.containing=generic-type", - "shape.site=in-generic-type" - ], - [ - "shape.containing=record-class", - "shape.site=in-generic-type" - ], - [ - "shape.containing=record-struct", - "shape.site=in-generic-type" - ], - [ - "shape.containing=struct", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-1", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-2", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-3", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=none", - "shape.site=in-generic-type" - ], - [ - "shape.kind=sealed-record-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-class", - "shape.site=in-generic-type" - ], - [ - "shape.position=array-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=dictionary-key", - "shape.site=in-generic-type" - ], - [ - "shape.position=dictionary-value", - "shape.site=in-generic-type" - ], - [ - "shape.position=field", - "shape.site=in-generic-type" - ], - [ - "shape.position=immutable-array-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=in-patch", - "shape.site=in-generic-type" - ], - [ - "shape.position=list-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=nullable-property", - "shape.site=in-generic-type" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.site=in-generic-type" - ], - [ - "shape.position=property", - "shape.site=in-generic-type" - ], - [ - "shape.position=read-only-list-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=record-ctor-parameter", - "shape.site=in-generic-type" - ], - [ - "shape.position=required-init", - "shape.site=in-generic-type" - ], - [ - "shape.position=root", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=in-generic-type" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTime" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Guid" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeSpan" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Uri" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=bool" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=byte" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=char" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=decimal" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=double" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=flags-enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=int" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=long" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=own-record-class" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=string" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=in-generic-type" - ] - ] - }, - { - "reason": "Without a base record there is no base to declare Equals, GetHashCode or IEquatable on.", - "pairs": [ - [ - "base-member=Equals", - "shape.inheritance=none" - ], - [ - "base-member=GetHashCode", - "shape.inheritance=none" - ], - [ - "base-member=IEquatable", - "shape.inheritance=none" - ], - [ - "shape.containing=class", - "shape.inheritance=none" - ], - [ - "shape.containing=generic-type", - "shape.inheritance=none" - ], - [ - "shape.containing=record-class", - "shape.inheritance=none" - ], - [ - "shape.containing=record-struct", - "shape.inheritance=none" - ], - [ - "shape.containing=struct", - "shape.inheritance=none" - ], - [ - "shape.inheritance=none", - "shape.kind=sealed-record-class" - ], - [ - "shape.inheritance=none", - "shape.kind=validated-class" - ], - [ - "shape.inheritance=none", - "shape.position=array-element" - ], - [ - "shape.inheritance=none", - "shape.position=dictionary-key" - ], - [ - "shape.inheritance=none", - "shape.position=dictionary-value" - ], - [ - "shape.inheritance=none", - "shape.position=field" - ], - [ - "shape.inheritance=none", - "shape.position=immutable-array-element" - ], - [ - "shape.inheritance=none", - "shape.position=in-patch" - ], - [ - "shape.inheritance=none", - "shape.position=list-element" - ], - [ - "shape.inheritance=none", - "shape.position=nullable-property" - ], - [ - "shape.inheritance=none", - "shape.position=polymorphic-derived-property" - ], - [ - "shape.inheritance=none", - "shape.position=property" - ], - [ - "shape.inheritance=none", - "shape.position=read-only-list-element" - ], - [ - "shape.inheritance=none", - "shape.position=record-ctor-parameter" - ], - [ - "shape.inheritance=none", - "shape.position=required-init" - ], - [ - "shape.inheritance=none", - "shape.position=root" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=none", - "shape.site=nested-in-class" - ], - [ - "shape.inheritance=none", - "shape.site=nested-in-struct" - ], - [ - "shape.inheritance=none", - "shape.site=nested-two-levels" - ], - [ - "shape.inheritance=none", - "shape.site=top-level" - ], - [ - "shape.inheritance=none", - "shape.wrapped=DateOnly" - ], - [ - "shape.inheritance=none", - "shape.wrapped=DateTime" - ], - [ - "shape.inheritance=none", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.inheritance=none", - "shape.wrapped=Guid" - ], - [ - "shape.inheritance=none", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.inheritance=none", - "shape.wrapped=TimeOnly" - ], - [ - "shape.inheritance=none", - "shape.wrapped=TimeSpan" - ], - [ - "shape.inheritance=none", - "shape.wrapped=Uri" - ], - [ - "shape.inheritance=none", - "shape.wrapped=bool" - ], - [ - "shape.inheritance=none", - "shape.wrapped=byte" - ], - [ - "shape.inheritance=none", - "shape.wrapped=char" - ], - [ - "shape.inheritance=none", - "shape.wrapped=decimal" - ], - [ - "shape.inheritance=none", - "shape.wrapped=double" - ], - [ - "shape.inheritance=none", - "shape.wrapped=enum" - ], - [ - "shape.inheritance=none", - "shape.wrapped=flags-enum" - ], - [ - "shape.inheritance=none", - "shape.wrapped=int" - ], - [ - "shape.inheritance=none", - "shape.wrapped=long" - ], - [ - "shape.inheritance=none", - "shape.wrapped=own-record-class" - ], - [ - "shape.inheritance=none", - "shape.wrapped=string" - ], - [ - "shape.inheritance=none", - "shape.wrapped=ulong" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=equality-operator (CMTK1011)" - ] - ] - }, - { - "reason": "CMTK1023 is 'A warning, and the value object is generated' (Generators README, Build errors); a refused declaration is not generated. A declaration of a refused kind fails the build (CMTK1005, CMTK1006), so it is generated into nothing; .claude/probe-axes.md: shape.refused-kind is taken only where the refusal is the claim.", - "pairs": [ - [ - "base-member=Equals", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "base-member=Equals", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "base-member=Equals", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "base-member=Equals", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.containing=class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.containing=class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.containing=class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.containing=class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=field", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=field", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=field", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=field", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=property", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=property", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=property", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=property", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.position=root", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.position=root", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.position=root", - "shape.refused-kind=over-value-object (CMTK1005)" - ], - [ - "shape.position=root", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.wrapped=ulong" - ], - [ - "base-member=Equals", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "base-member=Equals", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "base-member=Equals", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "base-member=Equals", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "base-member=Equals", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "base-member=GetHashCode", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "base-member=IEquatable", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.containing=class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.containing=class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.containing=class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=field", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=field", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=field", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=field", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=field", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=property", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=property", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=property", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=root", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.position=root", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.position=root", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.position=root", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=root", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=ulong" - ] - ] - } - ] - }, - { - "id": "C19-cmtk0010-kinds", - "source": "docs/type-constraints.md#the-kind-of-a-type-argument", - "surfaces": [ - "analyzers" - ], - "axes": { - "attribute": [ - "Interface", - "Unsealed", - "ValueObject", - "ClosedSet", - "FlagsEnum", - "ReadOnlyStruct", - "Distinct", - "Unrelated" - ], - "site": [ - "call", - "foreach", - "await", - "Deconstruct", - "collection-expression" - ] - }, - "obligations": [ - { - "id": "interface-and-unsealed", - "sentence": "**`[Interface]` and `[Unsealed]`** are what a proxy needs: a contract to implement, or a class to derive from.", - "axes": { - "attribute": [ - "Interface", - "Unsealed" - ] - } - }, - { - "id": "value-object-and-closed-set", - "sentence": "**`[ValueObject]` and `[ClosedSet]`** accept their marker interface with any type argument.", - "axes": { - "attribute": [ - "ValueObject", - "ClosedSet" - ] - } - }, - { - "id": "flags-enum-and-read-only-struct", - "sentence": "**`[FlagsEnum]` and `[ReadOnlyStruct]`** narrow what `where T : Enum` and `where T : struct` admit.", - "axes": { - "attribute": [ - "FlagsEnum", - "ReadOnlyStruct" - ] - } - }, - { - "id": "a-pair-once-at-the-later-argument", - "sentence": "A breach is reported once, at the later argument.", - "axes": { - "attribute": [ - "Distinct", - "Unrelated" - ] - } - }, - { - "id": "methods-the-compiler-calls", - "sentence": "**Methods the compiler calls for you:** a `foreach`'s `GetEnumerator`, an `await`'s `GetAwaiter`, a `Deconstruct` (in `var (a, b) = x`, a `foreach`, or a pattern like `x is (1, 2)`), and a collection expression's `Add` or builder method.", - "axes": { - "site": [ - "foreach", - "await", - "Deconstruct", - "collection-expression" - ] - } - } - ], - "exclude": [ - { - "reason": "A record struct has no base type, so it cannot derive from a base record or inherit a base's explicit IEquatable.", - "pairs": [ - [ - "shape.inheritance=base-depth-1", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=validated-struct" - ] - ] - }, - { - "reason": "A closed set wraps no type, so a closed set over a wrapped type cannot be declared.", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "Declared as .claude/probe-axes.md says (as C22's harness, BuildRefusals.cs, declares it), a closed set named as one of its generated members leaves their writing to the generators, so in this compilation without them it does not implement IClosedSet's members: the compiler's own CS0535, and no row can hold it beside the analyzer's places.", - "pairs": [ - [ - "attribute=ClosedSet", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "attribute=Distinct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "attribute=FlagsEnum", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "attribute=Interface", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "attribute=ReadOnlyStruct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "attribute=Unrelated", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "attribute=Unsealed", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "attribute=ValueObject", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=field", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=property", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.position=root", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=Deconstruct" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=await" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=call" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=collection-expression" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "site=foreach" - ] - ] - }, - { - "reason": "A value object with a hand-written operator == and != between two of the type is C#'s own CS0111 without the generators (Generators README, Build errors: CMTK1011, 'which C# also reports as CS0111'), since a record already declares both, so this compilation without the generators cannot hold it.", - "pairs": [ - [ - "attribute=ClosedSet", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "attribute=Distinct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "attribute=FlagsEnum", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "attribute=Interface", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "attribute=ReadOnlyStruct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "attribute=Unrelated", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "attribute=Unsealed", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "attribute=ValueObject", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=generic-type", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.containing=struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.inheritance=none", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=array-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=dictionary-key", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=dictionary-value", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=field", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=immutable-array-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=in-patch", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=list-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=nullable-property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=property", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=read-only-list-element", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=record-ctor-parameter", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=required-init", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.position=root", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-in-struct" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=nested-two-levels" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=top-level" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=Deconstruct" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=await" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=call" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=collection-expression" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "site=foreach" - ] - ] - } - ] - }, - { - "id": "C20-testing-shouldhold", - "source": "src/CodoMetis.TypeKit.Testing/README.md", - "surfaces": [ - "option-result" - ], - "axes": { - "arity": [ - "2", - "3" - ], - "set": [ - "first", - "last" - ], - "wrong-reason": [ - "no", - "yes" - ] - }, - "obligations": [ - { - "id": "fails-unless-it-holds-that-reason", - "sentence": "`ShouldHold(reason)` fails unless the fault holds that reason, whichever of its types the reason belongs to." - }, - { - "id": "whichever-of-its-types", - "sentence": "whichever of its types the reason belongs to", - "axes": { - "set": [ - "first", - "last" - ], - "arity": [ - "2", - "3" - ] - } - }, - { - "id": "hands-back-detail-and-rule", - "sentence": "It hands back the `Fault` with its detail and rule intact.", - "axes": { - "wrong-reason": [ - "no" - ] - } - }, - { - "id": "uninitialized-fails", - "sentence": "**An uninitialized result fails every assertion.**", - "axes": { - "or.operation": [ - "default-instance" - ] - } - }, - { - "id": "a-failure-says-the-outcome", - "sentence": "**A failure says what the outcome was**", - "axes": { - "wrong-reason": [ - "yes" - ] - } - } - ], - "exclude": [ - { - "reason": "Result.Catch turns an exception into a Result, never an Option (src/CodoMetis.TypeKit/ResultExtensions.cs), so no row can catch into an Option.", - "pairs": [ - [ - "or.operation=Result.Catch", - "or.shape=Option" - ] - ] - }, - { - "reason": "Result has no Select or SelectMany, so no query expression produces one, and FirstOrNone returns an Option, never a Result (src/CodoMetis.TypeKit/ResultExtensions.cs and OptionExtensions.cs).", - "pairs": [ - [ - "or.operation=FirstOrNone", - "or.shape=Result-E" - ], - [ - "or.operation=query-syntax", - "or.shape=Result-E" - ] - ] - } - ] - }, - { - "id": "C21-openapi-schema-parity", - "source": "README.md#what-you-get", - "surfaces": [ - "http", - "json" - ], - "axes": { - "http.host": [ - "minimal", - "mvc", - "both", - "route-group", - "controller-without-ApiController-visible" - ] - }, - "obligations": [ - { - "id": "the-underlying-types-schema", - "sentence": "The JSON, the database column and the OpenAPI schema are the same as the underlying type's.", - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - }, - "cite": "not-promised:own-converter-for-the-value-object" - }, - { - "axes": { - "json.Converters": [ - "own-for-wrapped" - ] - }, - "cite": "not-promised:own-converter-for-the-wrapped-type" - }, - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.NumberHandling": [ - "WriteAsString" - ], - "shape.wrapped": [ - "char" - ] - }, - "cite": "fact:KB-042" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "shape.wrapped": [ - "own-record-class" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "nullable-admits-null-as-the-wrapped-does", - "sentence": "Its OpenAPI schema is the one ASP.NET Core publishes for the underlying type.", - "axes": { - "http.nullability": [ - "nullable" - ] - }, - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - }, - "cite": "not-promised:own-converter-for-the-value-object" - }, - { - "axes": { - "json.Converters": [ - "own-for-wrapped" - ] - }, - "cite": "not-promised:own-converter-for-the-wrapped-type" - }, - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.NumberHandling": [ - "WriteAsString" - ], - "shape.wrapped": [ - "char" - ] - }, - "cite": "fact:KB-042" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "shape.wrapped": [ - "own-record-class" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "container-elements", - "sentence": "Its OpenAPI schema is the one ASP.NET Core publishes for the underlying type.", - "axes": { - "shape.position": [ - "list-element", - "array-element", - "immutable-array-element", - "read-only-list-element", - "dictionary-value" - ] - }, - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - }, - "cite": "not-promised:own-converter-for-the-value-object" - }, - { - "axes": { - "json.Converters": [ - "own-for-wrapped" - ] - }, - "cite": "not-promised:own-converter-for-the-wrapped-type" - }, - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.NumberHandling": [ - "WriteAsString" - ], - "shape.wrapped": [ - "char" - ] - }, - "cite": "fact:KB-042" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "shape.wrapped": [ - "own-record-class" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "dictionary-key", - "sentence": "Its OpenAPI schema is the one ASP.NET Core publishes for the underlying type.", - "axes": { - "shape.position": [ - "dictionary-key" - ] - }, - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - }, - "cite": "not-promised:own-converter-for-the-value-object" - }, - { - "axes": { - "json.Converters": [ - "own-for-wrapped" - ] - }, - "cite": "not-promised:own-converter-for-the-wrapped-type" - }, - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.NumberHandling": [ - "WriteAsString" - ], - "shape.wrapped": [ - "char" - ] - }, - "cite": "fact:KB-042" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "shape.wrapped": [ - "own-record-class" - ] - }, - "cite": "record:0099" - } - ] - }, - { - "id": "every-document-version", - "sentence": "Its OpenAPI schema is the one ASP.NET Core publishes for the underlying type.", - "axes": { - "openapi.OpenApiVersion": [ - "OpenApi2_0", - "OpenApi3_0" - ] - }, - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - }, - "cite": "not-promised:own-converter-for-the-value-object" - }, - { - "axes": { - "json.Converters": [ - "own-for-wrapped" - ] - }, - "cite": "not-promised:own-converter-for-the-wrapped-type" - }, - { - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ] - }, - "cite": "record:0099" - }, - { - "axes": { - "json.NumberHandling": [ - "WriteAsString" - ], - "shape.wrapped": [ - "char" - ] - }, - "cite": "fact:KB-042" - }, - { - "axes": { - "json.MaxDepth": [ - "3" - ], - "shape.wrapped": [ - "own-record-class" - ] - }, - "cite": "record:0099" - } - ] - } - ], - "not-promised": [ - { - "id": "a-closed-set-is-a-string-enum", - "source": "README.md#what-you-get", - "sentence": "One line per member gives you parsing by name, JSON as the name, an OpenAPI string enum and a text column.", - "axes": { - "shape.kind": [ - "closed-set" - ] - } - }, - { - "id": "own-converter-for-the-value-object", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", - "sentence": "That converter then writes it instead of the generated one, so it's described as that converter says (see \"A described type\" below).", - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - } - }, - { - "id": "own-converter-for-the-wrapped-type", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", - "sentence": "ASP.NET can't see through a JSON converter you write yourself. It writes the type as `{}`, or with only the `format` its CLR type maps to. It also drops a list's `items` and a dictionary's `additionalProperties` of that type.", - "axes": { - "json.Converters": [ - "own-for-wrapped" - ] - } - }, - { - "id": "a-class-value-object-admits-null", - "source": "design/adr/0036-nullable-value-object-admits-null-openapi.md#0036-a-nullable-value-object-admits-null-in-the-openapi-document-wherever-it-appears", - "sentence": "A nullable value object admits null in the OpenAPI document wherever it appears", - "axes": { - "shape.kind": [ - "sealed-record-class", - "validated-class" - ] - } - }, - { - "id": "a-nullable-value-object-admits-null", - "source": "design/adr/0036-nullable-value-object-admits-null-openapi.md#0036-a-nullable-value-object-admits-null-in-the-openapi-document-wherever-it-appears", - "sentence": "A nullable value object admits null in the OpenAPI document wherever it appears", - "axes": { - "http.nullability": [ - "nullable" - ] - } - }, - { - "id": "openapi-2-nullable-component-loses-its-type", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", - "sentence": "So under 2.0 a nullable value-object property loses its type, where the underlying type's nullable property keeps it.", - "axes": { - "openapi.OpenApiVersion": [ - "OpenApi2_0" - ], - "openapi.CreateSchemaReferenceId": [ - "default", - "own-prefixing" - ] - } - }, - { - "id": "enum-component-is-its-first-use", - "source": "design/knowledge/KB-027-an-enum-component-is-its-first-use.md#kb-027-the-component-aspnet-makes-for-an-enum-written-by-name-is-the-schema-of-the-first-use-it-meets", - "sentence": "So whether a parameter of the wrapped `E?` admits null in a document with components depends on the endpoints and their order, not on the parameter.", - "axes": { - "json.Converters": [ - "JsonStringEnumConverter" - ], - "http.nullability": [ - "nullable" - ], - "shape.wrapped": [ - "enum" - ] - } - }, - { - "id": "openapi-2-nullable-body-keeps-only-its-null", - "source": "design/knowledge/KB-001-openapi-2-nullable-body-drops-pattern.md#kb-001-under-openapi-20-aspnet-writes-a-nullable-request-body-as-allof-x-nullable-true-and-drops-its-pattern", - "sentence": "A minimal API writes it the same, and a nullable enum body, which has no `format`, keeps nothing but the null:", - "axes": { - "openapi.OpenApiVersion": [ - "OpenApi2_0" - ], - "http.binding": [ - "body" - ], - "http.nullability": [ - "nullable" - ], - "shape.kind": [ - "sealed-record-class", - "validated-class" - ] - } - }, - { - "id": "enum-component-lists-the-null-of-its-first-use", - "source": "design/knowledge/KB-027-an-enum-component-is-its-first-use.md#kb-027-the-component-aspnet-makes-for-an-enum-written-by-name-is-the-schema-of-the-first-use-it-meets", - "sentence": "What the component says is whatever schema ASP.NET built for the first use it met: met first as a nullable parameter, the component lists null among its values, for its non-nullable uses too; met first as itself, it does not, for its nullable uses too.", - "axes": { - "json.Converters": [ - "JsonStringEnumConverter" - ], - "shape.wrapped": [ - "enum" - ] - } - }, - { - "id": "openapi-2-nullable-element-loses-its-type", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", - "sentence": "A nullable element of a list or a dictionary loses its type the same way, since it's the component or null too.", - "axes": { - "openapi.OpenApiVersion": [ - "OpenApi2_0" - ], - "openapi.CreateSchemaReferenceId": [ - "default", - "own-prefixing" - ], - "shape.position": [ - "list-element", - "array-element", - "immutable-array-element", - "read-only-list-element", - "dictionary-value" - ] - } - }, - { - "id": "a-librarys-converter-for-the-wrapped-type", - "source": "src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says", - "sentence": "The same goes for a converter a library registers for its own type, such as NodaTime's for `Instant`.", - "axes": { - "shape.wrapped": [ - "NodaTime.Instant" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - }, - { - "reason": "Not promised (a-closed-set-is-a-string-enum): a closed set wraps nothing and is described as the string enum of its names, so it has no underlying type's schema to equal.", - "pairs": [ - [ - "http.binding=route", - "shape.kind=closed-set" - ], - [ - "http.binding=query", - "shape.kind=closed-set" - ], - [ - "http.binding=header", - "shape.kind=closed-set" - ], - [ - "http.binding=body", - "shape.kind=closed-set" - ], - [ - "http.binding=form", - "shape.kind=closed-set" - ], - [ - "http.binding=as-parameters", - "shape.kind=closed-set" - ], - [ - "http.host=minimal", - "shape.kind=closed-set" - ], - [ - "http.host=mvc", - "shape.kind=closed-set" - ], - [ - "http.host=both", - "shape.kind=closed-set" - ], - [ - "http.host=route-group", - "shape.kind=closed-set" - ], - [ - "http.host=controller-without-ApiController-visible", - "shape.kind=closed-set" - ], - [ - "http.nullability=as-declared", - "shape.kind=closed-set" - ], - [ - "http.nullability=nullable", - "shape.kind=closed-set" - ], - [ - "http.response=result-to-http-result", - "shape.kind=closed-set" - ], - [ - "http.response=composed-fault", - "shape.kind=closed-set" - ], - [ - "http.response=two-faults-same-name", - "shape.kind=closed-set" - ], - [ - "http.response=group-with-fault-status", - "shape.kind=closed-set" - ], - [ - "http.response=endpoint-without-faults", - "shape.kind=closed-set" - ], - [ - "http.response=declared-produces-problem", - "shape.kind=closed-set" - ], - [ - "input.json=valid", - "shape.kind=closed-set" - ], - [ - "input.json=refused-by-create", - "shape.kind=closed-set" - ], - [ - "input.json=wrong-token", - "shape.kind=closed-set" - ], - [ - "input.json=null", - "shape.kind=closed-set" - ], - [ - "input.json=empty-string", - "shape.kind=closed-set" - ], - [ - "input.json=at-type-limit", - "shape.kind=closed-set" - ], - [ - "input.json=quoted-number", - "shape.kind=closed-set" - ], - [ - "input.json=escape-sequence", - "shape.kind=closed-set" - ], - [ - "input.json=duplicate-property", - "shape.kind=closed-set" - ], - [ - "input.json=metadata-property", - "shape.kind=closed-set" - ], - [ - "json.AllowDuplicateProperties=default", - "shape.kind=closed-set" - ], - [ - "json.AllowDuplicateProperties=false", - "shape.kind=closed-set" - ], - [ - "json.AllowOutOfOrderMetadataProperties=default", - "shape.kind=closed-set" - ], - [ - "json.AllowOutOfOrderMetadataProperties=true", - "shape.kind=closed-set" - ], - [ - "json.AllowTrailingCommas=default", - "shape.kind=closed-set" - ], - [ - "json.AllowTrailingCommas=true", - "shape.kind=closed-set" - ], - [ - "json.Converters=default", - "shape.kind=closed-set" - ], - [ - "json.Converters=JsonStringEnumConverter", - "shape.kind=closed-set" - ], - [ - "json.Converters=StoredJsonConverterFactory", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=default", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenWritingDefault", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenWritingNull", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenWriting", - "shape.kind=closed-set" - ], - [ - "json.DefaultIgnoreCondition=WhenReading", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=default", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=CamelCase", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=SnakeCaseLower", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=KebabCaseUpper", - "shape.kind=closed-set" - ], - [ - "json.DictionaryKeyPolicy=own-upper-case", - "shape.kind=closed-set" - ], - [ - "json.Encoder=default", - "shape.kind=closed-set" - ], - [ - "json.Encoder=UnsafeRelaxedJsonEscaping", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyFields=default", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyFields=true", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyProperties=default", - "shape.kind=closed-set" - ], - [ - "json.IgnoreReadOnlyProperties=true", - "shape.kind=closed-set" - ], - [ - "json.IncludeFields=default", - "shape.kind=closed-set" - ], - [ - "json.IncludeFields=true", - "shape.kind=closed-set" - ], - [ - "json.MaxDepth=default", - "shape.kind=closed-set" - ], - [ - "json.MaxDepth=3", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=default", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=AllowReadingFromString", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=WriteAsString", - "shape.kind=closed-set" - ], - [ - "json.NumberHandling=AllowNamedFloatingPointLiterals", - "shape.kind=closed-set" - ], - [ - "json.PreferredObjectCreationHandling=default", - "shape.kind=closed-set" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "shape.kind=closed-set" - ], - [ - "json.PropertyNameCaseInsensitive=default", - "shape.kind=closed-set" - ], - [ - "json.PropertyNameCaseInsensitive=true", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=default", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=CamelCase", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=SnakeCaseLower", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=SnakeCaseUpper", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=KebabCaseLower", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=KebabCaseUpper", - "shape.kind=closed-set" - ], - [ - "json.PropertyNamingPolicy=own-upper-case", - "shape.kind=closed-set" - ], - [ - "json.ReadCommentHandling=default", - "shape.kind=closed-set" - ], - [ - "json.ReadCommentHandling=Skip", - "shape.kind=closed-set" - ], - [ - "json.ReferenceHandler=default", - "shape.kind=closed-set" - ], - [ - "json.ReferenceHandler=Preserve", - "shape.kind=closed-set" - ], - [ - "json.ReferenceHandler=IgnoreCycles", - "shape.kind=closed-set" - ], - [ - "json.RespectNullableAnnotations=default", - "shape.kind=closed-set" - ], - [ - "json.RespectNullableAnnotations=true", - "shape.kind=closed-set" - ], - [ - "json.RespectRequiredConstructorParameters=default", - "shape.kind=closed-set" - ], - [ - "json.RespectRequiredConstructorParameters=true", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolver=default", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolver=source-generated-context", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolver=default-with-renaming-modifier", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolverChain=default", - "shape.kind=closed-set" - ], - [ - "json.TypeInfoResolverChain=context-then-default", - "shape.kind=closed-set" - ], - [ - "json.UnknownTypeHandling=default", - "shape.kind=closed-set" - ], - [ - "json.UnknownTypeHandling=JsonNode", - "shape.kind=closed-set" - ], - [ - "json.UnmappedMemberHandling=default", - "shape.kind=closed-set" - ], - [ - "json.UnmappedMemberHandling=Disallow", - "shape.kind=closed-set" - ], - [ - "mvc.AllowEmptyInputInBodyModelBinding=default", - "shape.kind=closed-set" - ], - [ - "mvc.AllowEmptyInputInBodyModelBinding=true", - "shape.kind=closed-set" - ], - [ - "mvc.Conventions=default", - "shape.kind=closed-set" - ], - [ - "mvc.Conventions=own-route-prefix", - "shape.kind=closed-set" - ], - [ - "mvc.InputFormatters=default", - "shape.kind=closed-set" - ], - [ - "mvc.InputFormatters=xml-added", - "shape.kind=closed-set" - ], - [ - "mvc.MaxModelBindingCollectionSize=default", - "shape.kind=closed-set" - ], - [ - "mvc.MaxModelBindingCollectionSize=2", - "shape.kind=closed-set" - ], - [ - "mvc.MaxModelValidationErrors=default", - "shape.kind=closed-set" - ], - [ - "mvc.MaxModelValidationErrors=1", - "shape.kind=closed-set" - ], - [ - "mvc.MaxValidationDepth=default", - "shape.kind=closed-set" - ], - [ - "mvc.MaxValidationDepth=1", - "shape.kind=closed-set" - ], - [ - "mvc.ModelBinderProviders=default", - "shape.kind=closed-set" - ], - [ - "mvc.ModelBinderProviders=own-provider-first", - "shape.kind=closed-set" - ], - [ - "mvc.OutputFormatters=default", - "shape.kind=closed-set" - ], - [ - "mvc.OutputFormatters=xml-added", - "shape.kind=closed-set" - ], - [ - "mvc.RespectBrowserAcceptHeader=default", - "shape.kind=closed-set" - ], - [ - "mvc.RespectBrowserAcceptHeader=true", - "shape.kind=closed-set" - ], - [ - "mvc.ReturnHttpNotAcceptable=default", - "shape.kind=closed-set" - ], - [ - "mvc.ReturnHttpNotAcceptable=true", - "shape.kind=closed-set" - ], - [ - "mvc.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes=default", - "shape.kind=closed-set" - ], - [ - "mvc.SuppressImplicitRequiredAttributeForNonNullableReferenceTypes=true", - "shape.kind=closed-set" - ], - [ - "mvc.ValidateComplexTypesIfChildValidationFails=default", - "shape.kind=closed-set" - ], - [ - "mvc.ValidateComplexTypesIfChildValidationFails=true", - "shape.kind=closed-set" - ], - [ - "openapi.CreateSchemaReferenceId=default", - "shape.kind=closed-set" - ], - [ - "openapi.CreateSchemaReferenceId=own-prefixing", - "shape.kind=closed-set" - ], - [ - "openapi.CreateSchemaReferenceId=null-inlines-all", - "shape.kind=closed-set" - ], - [ - "openapi.OpenApiVersion=default", - "shape.kind=closed-set" - ], - [ - "openapi.OpenApiVersion=OpenApi2_0", - "shape.kind=closed-set" - ], - [ - "openapi.OpenApiVersion=OpenApi3_0", - "shape.kind=closed-set" - ], - [ - "openapi.transformers=default", - "shape.kind=closed-set" - ], - [ - "openapi.transformers=own-schema-transformer", - "shape.kind=closed-set" - ], - [ - "openapi.transformers=own-operation-transformer", - "shape.kind=closed-set" - ], - [ - "openapi.transformers=own-document-transformer", - "shape.kind=closed-set" - ], - [ - "process.converter-lookup=none", - "shape.kind=closed-set" - ], - [ - "process.converter-lookup=wrapped-type-converter-looked-up-first", - "shape.kind=closed-set" - ], - [ - "shape.containing=class", - "shape.kind=closed-set" - ], - [ - "shape.containing=record-class", - "shape.kind=closed-set" - ], - [ - "shape.containing=struct", - "shape.kind=closed-set" - ], - [ - "shape.containing=record-struct", - "shape.kind=closed-set" - ], - [ - "shape.containing=generic-type", - "shape.kind=closed-set" - ], - [ - "shape.kind=closed-set", - "shape.position=root" - ], - [ - "shape.kind=closed-set", - "shape.position=property" - ], - [ - "shape.kind=closed-set", - "shape.position=nullable-property" - ], - [ - "shape.kind=closed-set", - "shape.position=field" - ], - [ - "shape.kind=closed-set", - "shape.position=list-element" - ], - [ - "shape.kind=closed-set", - "shape.position=array-element" - ], - [ - "shape.kind=closed-set", - "shape.position=immutable-array-element" - ], - [ - "shape.kind=closed-set", - "shape.position=read-only-list-element" - ], - [ - "shape.kind=closed-set", - "shape.position=dictionary-key" - ], - [ - "shape.kind=closed-set", - "shape.position=dictionary-value" - ], - [ - "shape.kind=closed-set", - "shape.position=record-ctor-parameter" - ], - [ - "shape.kind=closed-set", - "shape.position=required-init" - ], - [ - "shape.kind=closed-set", - "shape.position=in-patch" - ], - [ - "shape.kind=closed-set", - "shape.position=polymorphic-derived-property" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ] - ] - } - ] - }, - { - "id": "E01-column-parity", - "source": "docs/ef-core.md#the-model", - "surfaces": [ - "ef" - ], - "axes": { - "ef.mapping": [ - "property", - "nullable", - "key", - "owned", - "complex" - ], - "ef.facet": [ - "none", - "HasMaxLength", - "HavePrecision", - "HasColumnType" - ], - "shape.wrapped": [ - "string", - "Guid", - "int", - "long", - "byte", - "ulong", - "decimal", - "double", - "bool", - "char", - "DateTime", - "DateTimeOffset", - "DateOnly", - "TimeOnly", - "TimeSpan", - "enum", - "flags-enum", - "Uri" - ], - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ] - }, - "narrow": { - "ef.query": "Every property maps as a column of the underlying type, nullable where the property is." - }, - "obligations": [ - { - "id": "column-type", - "sentence": "Every property maps as a column of the underlying type, nullable where the property is.", - "axes": { - "ef.mapping": [ - "property", - "key", - "owned", - "complex" - ] - } - }, - { - "id": "nullable", - "sentence": "nullable where the property is", - "axes": { - "ef.mapping": [ - "nullable" - ] - } - }, - { - "id": "facet-kept", - "sentence": "A facet you configure on it is kept: a precision, a length, a column type.", - "axes": { - "ef.facet": [ - "HasMaxLength", - "HavePrecision", - "HasColumnType" - ] - } - } - ], - "not-promised": [ - { - "id": "char-key-not-generated", - "source": "docs/ef-core.md#keys", - "sentence": "A key over `char` is always yours to assign", - "axes": { - "ef.mapping": [ - "key" - ], - "shape.wrapped": [ - "char" - ] - } - }, - { - "id": "guid-key-not-generated", - "source": "docs/ef-core.md#keys", - "sentence": "A key over a `Guid` is yours to assign", - "axes": { - "ef.mapping": [ - "key" - ], - "shape.wrapped": [ - "Guid" - ] - } - } - ] - }, - { - "id": "E02-value-translates-to-column", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "surfaces": [ - "ef" - ], - "axes": { - "ef.query": [ - "where-equals", - "where-value-compare", - "order-by", - "contains", - "select", - "group-by" - ], - "ef.accessor": [ - "Value", - "GetValue", - "ValueOrNull" - ], - "shape.wrapped": [ - "string", - "Guid", - "int", - "long", - "byte", - "ulong", - "decimal", - "double", - "bool", - "char", - "DateTime", - "DateTimeOffset", - "DateOnly", - "TimeOnly", - "TimeSpan", - "enum", - "flags-enum", - "Uri" - ], - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ] - }, - "narrow": { - "ef.mapping": "`.Value`, `GetValue()` and `ValueOrNull()` translate to the column itself." - }, - "obligations": [ - { - "id": "value", - "sentence": "`.Value`, `GetValue()` and `ValueOrNull()` translate to the column itself.", - "axes": { - "ef.accessor": [ - "Value" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.wrapped": [ - "DateTimeOffset", - "TimeSpan", - "ulong" - ], - "ef.query": [ - "where-value-compare", - "order-by" - ] - }, - "cite": "fact:KB-039" - } - ] - }, - { - "id": "get-value", - "sentence": "db.Orders.Where(o => o.Total.GetValue() > 100m);", - "axes": { - "ef.accessor": [ - "GetValue" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.wrapped": [ - "DateTimeOffset", - "TimeSpan", - "ulong" - ], - "ef.query": [ - "where-value-compare", - "order-by" - ] - }, - "cite": "fact:KB-039" - } - ] - }, - { - "id": "value-or-null", - "sentence": "Use `ValueOrNull()` for an optional value object, where `.Value` would first unwrap the `Nullable`.", - "axes": { - "ef.accessor": [ - "ValueOrNull" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.wrapped": [ - "DateTimeOffset", - "TimeSpan", - "ulong" - ], - "ef.query": [ - "where-value-compare", - "order-by" - ] - }, - "cite": "fact:KB-039" - } - ] - }, - { - "id": "column-not-cast", - "sentence": "A table's column is re-typed, not cast, so an index on it still serves the query.", - "inapplicable": [ - { - "axes": { - "shape.wrapped": [ - "DateTimeOffset", - "TimeSpan", - "ulong" - ], - "ef.query": [ - "where-value-compare", - "order-by" - ] - }, - "cite": "fact:KB-039" - } - ] - } - ], - "not-promised": [ - { - "id": "null-passes-no-comparison", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "sentence": "A null passes no `<`, `<=`, `>` or `>=` in the database.", - "axes": { - "ef.accessor": [ - "ValueOrNull" - ], - "ef.query": [ - "where-value-compare" - ] - } - }, - { - "id": "collation-orders", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "sentence": "Ordering in SQL follows the database's collation", - "axes": { - "ef.query": [ - "order-by" - ], - "shape.wrapped": [ - "string" - ] - } - } - ], - "exclude": [ - { - "reason": "C# defines no `>=` for a `string`, a `bool` or a `Uri`, so no query compares their `.Value` with it; E02ValueTranslatesProbe writes where-equals in its place there, so no row holds the pair.", - "pairs": [ - [ - "ef.query=where-value-compare", - "shape.wrapped=string" - ], - [ - "ef.query=where-value-compare", - "shape.wrapped=bool" - ], - [ - "ef.query=where-value-compare", - "shape.wrapped=Uri" - ] - ] - } - ] - }, - { - "id": "E03-column-round-trip", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#what-maps", - "surfaces": [ - "ef" - ], - "axes": { - "ef.mapping": [ - "property", - "nullable", - "key", - "owned", - "complex" - ], - "shape.wrapped": [ - "string", - "Guid", - "int", - "long", - "byte", - "ulong", - "decimal", - "double", - "bool", - "char", - "DateTime", - "DateTimeOffset", - "DateOnly", - "TimeOnly", - "TimeSpan", - "enum", - "flags-enum", - "Uri" - ], - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ] - }, - "narrow": { - "ef.query": "Keys, foreign keys, nullable properties, primitive collections and query parameters all map as plain columns of the underlying type." - }, - "obligations": [ - { - "id": "stored-as-underlying", - "sentence": "Every value object is stored in a plain column of its underlying type", - "axes": { - "ef.mapping": [ - "property", - "owned", - "complex" - ] - } - }, - { - "id": "keys", - "sentence": "Keys, foreign keys, nullable properties, primitive collections and query parameters all map as plain columns of the underlying type.", - "axes": { - "ef.mapping": [ - "key" - ] - } - }, - { - "id": "nullable-properties", - "sentence": "Keys, foreign keys, nullable properties, primitive collections and query parameters all map as plain columns of the underlying type.", - "axes": { - "ef.mapping": [ - "nullable" - ] - } - } - ] - }, - { - "id": "E04-integer-key-generated", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "surfaces": [ - "ef" - ], - "axes": { - "shape.wrapped": [ - "int", - "long", - "byte", - "ulong", - "char", - "Guid", - "string" - ], - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "ef.key-config": [ - "default", - "ValueGeneratedOnAdd", - "ValueGeneratedNever" - ] - }, - "narrow": { - "ef.query": "A single-column key over an integer is generated on add, like a key of that integer type.", - "ef.mapping": "A single-column key over an integer is generated on add, like a key of that integer type.", - "shape.site": "A single-column key over an integer is generated on add, like a key of that integer type." - }, - "obligations": [ - { - "id": "generated-on-add", - "sentence": "A single-column key over an integer is generated on add, like a key of that integer type.", - "axes": { - "shape.wrapped": [ - "int", - "long", - "ulong" - ] - } - }, - { - "id": "byte-not-generated", - "sentence": "That's EF's own list: `int`, `long`, `short`, `sbyte`, `uint`, `ulong` and `ushort`, and not `byte`.", - "axes": { - "shape.wrapped": [ - "byte" - ] - } - }, - { - "id": "char-key-yours", - "sentence": "A key over `char` is yours to assign.", - "axes": { - "shape.wrapped": [ - "char" - ], - "ef.key-config": [ - "default" - ] - } - }, - { - "id": "guid-key-yours", - "sentence": "A key over a `Guid` is yours to assign", - "axes": { - "shape.wrapped": [ - "Guid" - ], - "ef.key-config": [ - "default" - ] - } - }, - { - "id": "not-on-efs-list", - "sentence": "That's EF's own list: `int`, `long`, `short`, `sbyte`, `uint`, `ulong` and `ushort`, and not `byte`.", - "axes": { - "shape.wrapped": [ - "string" - ], - "ef.key-config": [ - "default" - ] - } - }, - { - "id": "own-configuration-wins", - "sentence": "A configuration you write yourself always wins over these defaults.", - "axes": { - "shape.wrapped": [ - "char", - "Guid", - "string" - ], - "ef.key-config": [ - "ValueGeneratedOnAdd", - "ValueGeneratedNever" - ] - } - } - ], - "not-promised": [ - { - "id": "meaningful-char-key-unchecked", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "sentence": "TypeKit stops that invented value, but it doesn't check that you set a meaningful one.", - "axes": { - "shape.wrapped": [ - "char" - ], - "ef.key-config": [ - "default" - ] - } - }, - { - "id": "ulong-key-on-a-decimal-column", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "sentence": "A default key over `ulong` throws at `Add` where the column is a `decimal`", - "axes": { - "shape.wrapped": [ - "ulong" - ], - "provider": [ - "SQL Server", - "PostgreSQL" - ] - } - } - ] - }, - { - "id": "E05-own-compareto-rejected", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "surfaces": [ - "ef" - ], - "axes": { - "ef.op": [ - "lt", - "gt", - "le", - "ge", - "CompareTo", - "OrderBy", - "OrderByDescending", - "ThenBy", - "Order", - "Min", - "Max", - "equals", - "HasQueryFilter", - "named-query-filter", - "value-order" - ], - "ef.vo": [ - "int-struct", - "string-struct", - "decimal-struct", - "DateTime-struct", - "int-class", - "string-class" - ] - }, - "narrow": { - "ef.query": "Your own `CompareTo(TSelf)` can't run in the database.", - "ef.mapping": "Your own `CompareTo(TSelf)` can't run in the database.", - "shape.kind": "Your own `CompareTo(TSelf)` can't run in the database.", - "shape.wrapped": "Your own `CompareTo(TSelf)` can't run in the database.", - "shape.site": "Your own `CompareTo(TSelf)` can't run in the database." - }, - "obligations": [ - { - "id": "comparison", - "sentence": "a comparison (`<`, `>`, `<=`, `>=`, `CompareTo`);", - "axes": { - "ef.op": [ - "lt", - "gt", - "le", - "ge", - "CompareTo" - ] - } - }, - { - "id": "ordering", - "sentence": "an ordering (`OrderBy`, `ThenBy`, their descending forms, `Order`);", - "axes": { - "ef.op": [ - "OrderBy", - "OrderByDescending", - "ThenBy", - "Order" - ] - } - }, - { - "id": "min-max", - "sentence": "`Min` or `Max`.", - "axes": { - "ef.op": [ - "Min", - "Max" - ] - } - }, - { - "id": "equality-allowed", - "sentence": "Equality isn't ordering, and stays allowed.", - "axes": { - "ef.op": [ - "equals" - ] - } - }, - { - "id": "value-order-allowed", - "sentence": "Compare or order by `.Value` where the underlying value's order is what you mean", - "axes": { - "ef.op": [ - "value-order" - ] - } - }, - { - "id": "query-filter", - "sentence": "A query filter (`HasQueryFilter`, named or not) that does the same fails when the model is built, naming the entity type, since it applies to every query of that type.", - "axes": { - "ef.op": [ - "HasQueryFilter", - "named-query-filter" - ] - } - } - ], - "not-promised": [ - { - "id": "collation-orders", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "sentence": "Ordering in SQL follows the database's collation", - "axes": { - "ef.op": [ - "value-order" - ], - "ef.vo": [ - "string-struct", - "string-class" - ] - } - } - ] - }, - { - "id": "E06-own-converter-no-value", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "surfaces": [ - "ef" - ], - "axes": { - "ef.converter": [ - "HasConversion-lambda", - "converter-type", - "ConfigureConventions" - ], - "ef.op": [ - "value-access", - "vo-equality" - ], - "shape.wrapped": [ - "string", - "int", - "Guid", - "decimal" - ] - }, - "narrow": { - "ef.query": "A property your application converts itself", - "ef.mapping": "A property your application converts itself", - "shape.kind": "A property your application converts itself", - "shape.site": "A property your application converts itself" - }, - "obligations": [ - { - "id": "value-not-translated", - "sentence": "So a filter, a sort, a grouping or an aggregate that reads `.Value` there fails with EF's \"could not be translated\", rather than comparing against what the column really holds.", - "axes": { - "ef.op": [ - "value-access" - ] - } - }, - { - "id": "value-object-comparison", - "sentence": "Comparing the value object itself (`o.Code == code`) goes through that converter and still works.", - "axes": { - "ef.op": [ - "vo-equality" - ] - } - } - ] - }, - { - "id": "E07-closed-set-stored-as-name", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#closed-sets", - "surfaces": [ - "ef" - ], - "axes": { - "ef.site": [ - "table", - "primitive-collection", - "json-column" - ], - "ef.event": [ - "round-trip", - "unknown-name-on-read", - "non-member-on-write" - ] - }, - "narrow": { - "ef.query": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself.", - "ef.mapping": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself.", - "shape.kind": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself.", - "shape.wrapped": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself.", - "shape.site": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself." - }, - "obligations": [ - { - "id": "stored-as-name", - "sentence": "A closed set (`IClosedSet`) is stored as a text column of the member's name, and read back as the member itself.", - "axes": { - "ef.event": [ - "round-trip" - ] - } - }, - { - "id": "in-collections-and-json", - "sentence": "That holds in a table, in a primitive collection of names (`text[]` on PostgreSQL, a JSON array elsewhere) and in a JSON column alike, with nothing registered per set.", - "axes": { - "ef.site": [ - "primitive-collection", - "json-column" - ], - "ef.event": [ - "round-trip" - ] - } - }, - { - "id": "unknown-name-throws", - "sentence": "A name the set no longer has throws when it's read, rather than reading as null.", - "axes": { - "ef.event": [ - "unknown-name-on-read" - ] - } - }, - { - "id": "non-member-throws", - "sentence": "An instance that isn't a member throws when it's written.", - "axes": { - "ef.event": [ - "non-member-on-write" - ] - } - } - ] - }, - { - "id": "E08-option-result-not-columns", - "source": "docs/ef-core.md#option-and-result", - "surfaces": [ - "ef" - ], - "axes": { - "ef.type": [ - "Option-int", - "Option-VO", - "Result-int-E", - "Result-VO-E", - "Result-E" - ], - "ef.position": [ - "property", - "key", - "collection", - "owned-property" - ], - "shape.wrapped": [ - "string", - "int", - "Guid" - ] - }, - "narrow": { - "ef.query": "EF can't map an `Option` or a `Result` property, and building the model fails with an error that says so.", - "ef.mapping": "EF can't map an `Option` or a `Result` property, and building the model fails with an error that says so.", - "shape.kind": "EF can't map an `Option` or a `Result` property, and building the model fails with an error that says so.", - "shape.site": "EF can't map an `Option` or a `Result` property, and building the model fails with an error that says so." - }, - "obligations": [ - { - "id": "model-fails", - "sentence": "EF can't map an `Option` or a `Result` property" - }, - { - "id": "error-says-so", - "sentence": "building the model fails with an error that says so" - } - ] - }, - { - "id": "E09-or-none-in-database", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#an-option-for-nothing-found", - "surfaces": [ - "ef" - ], - "axes": { - "ef.operator": [ - "FirstOrNoneAsync", - "FirstOrNone", - "SingleOrNoneAsync", - "SingleOrNone", - "ElementAtOrNone", - "FirstOrError" - ], - "ef.data": [ - "empty", - "one-match", - "many-match" - ], - "ef.condition": [ - "lambda", - "expression-variable" - ], - "shape.wrapped": [ - "int", - "Guid", - "string" - ] - }, - "narrow": { - "ef.query": "These ask the database for one row and let it run the condition:", - "ef.mapping": "These ask the database for one row and let it run the condition:", - "shape.kind": "These ask the database for one row and let it run the condition:", - "shape.site": "These ask the database for one row and let it run the condition:" - }, - "obligations": [ - { - "id": "first-reads-one-row", - "sentence": "read one row: `WHERE ... LIMIT @p`.", - "axes": { - "ef.operator": [ - "FirstOrNoneAsync", - "FirstOrNone", - "FirstOrError" - ] - } - }, - { - "id": "single-asks-for-two", - "sentence": "ask for two rows, and throw for more than one.", - "axes": { - "ef.operator": [ - "SingleOrNoneAsync", - "SingleOrNone" - ] - } - }, - { - "id": "element-at-skips", - "sentence": "skips to its row: `OFFSET @p`.", - "axes": { - "ef.operator": [ - "ElementAtOrNone" - ] - } - } - ], - "not-promised": [ - { - "id": "func-condition-runs-in-memory", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#an-option-for-nothing-found", - "sentence": "Then every row is read and the condition runs in memory, without a warning.", - "axes": { - "ef.condition": [ - "func-variable", - "method-group" - ] - } - }, - { - "id": "last-on-a-query-does-not-compile", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#an-option-for-nothing-found", - "sentence": "`LastOrNone` and `LastOrError` on a query don't compile.", - "axes": { - "ef.operator": [ - "LastOrNone", - "LastOrError" - ] - } - }, - { - "id": "not-precompiled-under-native-aot", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#an-option-for-nothing-found", - "sentence": "Under Native AOT these throw \"Query wasn't precompiled\"", - "axes": { - "process": [ - "native-aot" - ] - } - } - ] - }, - { - "id": "E10-read-back-skips-validation", - "source": "docs/ef-core.md#reading-back-without-validation", - "surfaces": [ - "ef" - ], - "axes": { - "ef.mapping": [ - "property", - "key", - "nullable", - "owned", - "complex" - ], - "shape.wrapped": [ - "string", - "int", - "Guid", - "decimal", - "DateTime", - "Uri" - ], - "shape.kind": [ - "validated-struct", - "validated-class" - ] - }, - "narrow": { - "ef.query": "EF reads a column back without validation, through the value object's `Materialize` rather than `Create`.", - "shape.site": "EF reads a column back without validation, through the value object's `Materialize` rather than `Create`." - }, - "obligations": [ - { - "id": "reads-without-create", - "sentence": "EF reads a column back without validation, through the value object's `Materialize` rather than `Create`." - }, - { - "id": "revalidate-finds", - "sentence": "`Revalidate()` finds the rows that today's rules reject, as above." - } - ] - }, - { - "id": "E11-usetypekit-order", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#setup", - "surfaces": [ - "ef" - ], - "axes": { - "ef.order": [ - "before-provider", - "after-provider" - ], - "ef.mapping": [ - "property", - "key", - "integer-key", - "nullable", - "owned", - "complex", - "closed-set" - ] - }, - "narrow": { - "ef.query": "it doesn't matter whether you call it before or after `UseNpgsql` or `UseSqlite`", - "shape.kind": "it doesn't matter whether you call it before or after `UseNpgsql` or `UseSqlite`", - "shape.site": "it doesn't matter whether you call it before or after `UseNpgsql` or `UseSqlite`", - "shape.wrapped": "it doesn't matter whether you call it before or after `UseNpgsql` or `UseSqlite`" - }, - "obligations": [ - { - "id": "order-does-not-matter", - "sentence": "it doesn't matter whether you call it before or after `UseNpgsql` or `UseSqlite`" - } - ], - "not-promised": [ - { - "id": "in-memory-provider-maps-nothing", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#setup", - "sentence": "EF's in-memory provider has no relational type mapping, so nothing is mapped there.", - "axes": { - "ef.provider": [ - "InMemory" - ] - } - } - ] - }, - { - "id": "E12-convention-by-vo-type", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#model-wide-conventions-follow-the-propertys-type", - "surfaces": [ - "ef" - ], - "axes": { - "ef.convention": [ - "HaveMaxLength", - "HavePrecision", - "HaveColumnType" - ], - "ef.convention-type": [ - "value-object", - "underlying" - ], - "shape.wrapped": [ - "string", - "decimal", - "Uri", - "int" - ] - }, - "narrow": { - "ef.query": "Configure the value object instead:", - "ef.mapping": "Configure the value object instead:", - "shape.site": "Configure the value object instead:", - "shape.kind": "Configure the value object instead:" - }, - "obligations": [ - { - "id": "value-object-type-reaches", - "sentence": "Configure the value object instead: `configurationBuilder.Properties().HavePrecision(18, 2)`.", - "axes": { - "ef.convention-type": [ - "value-object" - ] - } - }, - { - "id": "underlying-type-does-not-reach", - "sentence": "So `configurationBuilder.Properties().HavePrecision(18, 2)` reaches `decimal` properties, and not an `Amount` that wraps one.", - "axes": { - "ef.convention-type": [ - "underlying" - ] - } - } - ], - "not-promised": [ - { - "id": "underlying-type-convention", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#model-wide-conventions-follow-the-propertys-type", - "sentence": "EF applies `ConfigureConventions` by CLR type.", - "axes": { - "ef.convention-type": [ - "underlying" - ] - } - } - ] - }, - { - "id": "E13-primitive-collections", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "surfaces": [ - "ef" - ], - "axes": { - "ef.query": [ - "contains", - "any-value-equals" - ], - "shape.wrapped": [ - "string", - "int", - "Guid", - "long", - "decimal" - ] - }, - "narrow": { - "ef.mapping": "db.Orders.Where(o => o.Tags.Contains(tag));", - "shape.site": "db.Orders.Where(o => o.Tags.Contains(tag));", - "shape.kind": "db.Orders.Where(o => o.Tags.Contains(tag));" - }, - "obligations": [ - { - "id": "contains", - "sentence": "db.Orders.Where(o => o.Tags.Contains(tag));", - "axes": { - "ef.query": [ - "contains" - ] - } - }, - { - "id": "element-value", - "sentence": "db.Orders.Where(o => o.Tags.Any(t => t.Value == \"x\"));", - "axes": { - "ef.query": [ - "any-value-equals" - ] - } - } - ], - "not-promised": [ - { - "id": "element-converted-may-cast", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#queries", - "sentence": "These are converted instead, which works everywhere and may add a cast:", - "axes": { - "ef.query": [ - "any-value-equals" - ] - } - } - ] - }, - { - "id": "E14-inheritance-key-generation", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "surfaces": [ - "ef" - ], - "axes": { - "ef.inheritance": [ - "TPH", - "TPT", - "TPC" - ], - "shape.wrapped": [ - "int", - "long", - "Guid" - ] - }, - "narrow": { - "ef.query": "The root of an inheritance hierarchy follows the same rules under TPH and TPT.", - "ef.mapping": "The root of an inheritance hierarchy follows the same rules under TPH and TPT.", - "shape.site": "The root of an inheritance hierarchy follows the same rules under TPH and TPT.", - "shape.kind": "The root of an inheritance hierarchy follows the same rules under TPH and TPT." - }, - "obligations": [ - { - "id": "tph-tpt-root", - "sentence": "The root of an inheritance hierarchy follows the same rules under TPH and TPT.", - "axes": { - "ef.inheritance": [ - "TPH", - "TPT" - ] - } - }, - { - "id": "tpc-assign-on-sqlite", - "sentence": "SQLite has no such sequence, so there you assign it.", - "axes": { - "ef.inheritance": [ - "TPC" - ] - } - } - ], - "not-promised": [ - { - "id": "guid-key-not-generated", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "sentence": "A key over a `Guid` is yours to assign", - "axes": { - "shape.wrapped": [ - "Guid" - ] - } - } - ] - }, - { - "id": "E15-composite-key", - "source": "docs/ef-core.md#keys", - "surfaces": [ - "ef" - ], - "axes": { - "shape.wrapped": [ - "int", - "Guid", - "string", - "long" - ], - "ef.key-position": [ - "first", - "second" - ] - }, - "narrow": { - "ef.query": "A composite key of value objects is `HasKey(x => new { x.Order, x.Line })`, as for primitives.", - "ef.mapping": "A composite key of value objects is `HasKey(x => new { x.Order, x.Line })`, as for primitives.", - "shape.site": "A composite key of value objects is `HasKey(x => new { x.Order, x.Line })`, as for primitives.", - "shape.kind": "A composite key of value objects is `HasKey(x => new { x.Order, x.Line })`, as for primitives." - }, - "obligations": [ - { - "id": "as-for-primitives", - "sentence": "A composite key of value objects is `HasKey(x => new { x.Order, x.Line })`, as for primitives." - } - ] - }, - { - "id": "E16-own-configuration-wins", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "surfaces": [ - "ef" - ], - "axes": { - "ef.config": [ - "ValueGeneratedNever", - "ValueGeneratedOnAdd", - "HasConversion", - "HasColumnType", - "IsRequired-false" - ], - "shape.wrapped": [ - "int", - "long", - "char", - "Guid", - "string" - ] - }, - "narrow": { - "ef.query": "A configuration you write yourself always wins over these defaults.", - "ef.mapping": "A configuration you write yourself always wins over these defaults.", - "shape.site": "A configuration you write yourself always wins over these defaults.", - "shape.kind": "A configuration you write yourself always wins over these defaults." - }, - "obligations": [ - { - "id": "never", - "sentence": "A configuration you write yourself always wins over these defaults.", - "axes": { - "ef.config": [ - "ValueGeneratedNever" - ] - } - }, - { - "id": "on-add", - "sentence": "A configuration you write yourself always wins over these defaults.", - "axes": { - "ef.config": [ - "ValueGeneratedOnAdd" - ] - } - }, - { - "id": "column-type", - "sentence": "A facet you configure on the property, such as a maximum length or a column type, is kept.", - "axes": { - "ef.config": [ - "HasColumnType" - ] - } - }, - { - "id": "conversion", - "sentence": "A configuration you write yourself always wins over these defaults.", - "axes": { - "ef.config": [ - "HasConversion" - ] - } - }, - { - "id": "optional", - "sentence": "A configuration you write yourself always wins over these defaults.", - "axes": { - "ef.config": [ - "IsRequired-false" - ] - } - } - ], - "not-promised": [ - { - "id": "char-key-not-generated", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "sentence": "A key over `char` is yours to assign.", - "axes": { - "ef.config": [ - "HasColumnType", - "HasConversion" - ], - "shape.wrapped": [ - "char" - ] - } - }, - { - "id": "guid-key-not-generated", - "source": "src/CodoMetis.TypeKit.EntityFrameworkCore/README.md#keys", - "sentence": "A key over a `Guid` is yours to assign", - "axes": { - "ef.config": [ - "HasColumnType", - "HasConversion" - ], - "shape.wrapped": [ - "Guid" - ] - } - } - ] - }, - { - "id": "L01-json-bytes", - "source": "README.md#what-typekit-guarantees", - "surfaces": [ - "json" - ], - "obligations": [ - { - "id": "the-underlying-types-json", - "sentence": "Its JSON is the underlying type's under your options", - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "byte" - ], - "shape.position": [ - "array-element" - ] - }, - "cite": "not-promised:a-byte-array-is-base64" - } - ] - }, - { - "id": "as-a-dictionary-key-too", - "sentence": "Its JSON is the underlying type's under your options", - "axes": { - "shape.position": [ - "dictionary-key" - ] - }, - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-value-object" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says" - }, - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "shape.position": [ - "dictionary-key" - ], - "json.DictionaryKeyPolicy": [ - "CamelCase", - "SnakeCaseLower", - "KebabCaseUpper", - "own-upper-case" - ] - }, - "cite": "not-promised:a-closed-set-key-is-its-name" - } - ] - }, - { - "id": "read-as-the-underlying-type-reads", - "sentence": "**Swapping a primitive for a value object keeps the JSON, the column and the schema.**", - "inapplicable": [ - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "byte" - ], - "shape.position": [ - "array-element" - ] - }, - "cite": "not-promised:a-byte-array-is-base64" - }, - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "shape.position": [ - "dictionary-key" - ], - "json.DictionaryKeyPolicy": [ - "CamelCase", - "SnakeCaseLower", - "KebabCaseUpper", - "own-upper-case" - ] - }, - "cite": "not-promised:a-closed-set-key-is-its-name" - } - ] - } - ], - "not-promised": [ - { - "id": "a-datetime-is-written-as-utc", - "source": "README.md#what-typekit-guarantees", - "sentence": "Its JSON is the underlying type's under your options, with two exceptions: a `DateTime` is written as UTC", - "axes": { - "shape.wrapped": [ - "DateTime" - ] - } - }, - { - "id": "number-handling-on-the-containing-type", - "source": "README.md#what-typekit-guarantees", - "sentence": "and no converter sees number handling set by an attribute on the containing type" - }, - { - "id": "a-struct-refuses-a-json-null", - "source": "README.md#what-typekit-guarantees", - "sentence": "What changes is what the value object rejects, a JSON `null` for a struct value object included.", - "axes": { - "input.json": [ - "null" - ], - "shape.kind": [ - "record-struct", - "validated-struct" - ] - } - }, - { - "id": "a-validated-value-object-refuses-what-create-refuses", - "source": "README.md#what-typekit-guarantees", - "sentence": "What changes is what the value object rejects", - "axes": { - "input.json": [ - "refused-by-create" - ], - "shape.kind": [ - "validated-struct", - "validated-class" - ] - } - }, - { - "id": "an-enum-under-a-source-generated-context", - "source": "README.md#what-typekit-guarantees", - "sentence": "And under a source-generated context, a value object over an enum needs its enum listed", - "axes": { - "json.TypeInfoResolver": [ - "source-generated-context" - ], - "shape.wrapped": [ - "enum", - "flags-enum" - ] - } - }, - { - "id": "preserved-references-over-a-walked-class", - "source": "src/CodoMetis.TypeKit.Generators/README.md#json", - "sentence": "**Preserved references** (`ReferenceHandler.Preserve`) throw a `NotSupportedException` where the serializer could write `$id` or `$ref` inside the underlying value.", - "axes": { - "json.ReferenceHandler": [ - "Preserve" - ], - "shape.wrapped": [ - "own-record-class" - ] - } - }, - { - "id": "a-closed-set-key-is-its-name", - "source": "docs/closed-sets.md#send-it-as-json", - "sentence": "No naming policy renames it, because a renamed name wouldn't read back.", - "axes": { - "shape.kind": [ - "closed-set" - ], - "shape.position": [ - "dictionary-key" - ] - } - }, - { - "id": "a-byte-array-is-base64", - "source": "docs/json.md#the-wrapped-types-json", - "sentence": "An array of value objects over `byte` is a JSON array of numbers.", - "axes": { - "shape.wrapped": [ - "byte" - ], - "shape.position": [ - "array-element" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "L02-no-quote", - "source": "README.md#what-typekit-guarantees", - "surfaces": [ - "json", - "text", - "logging" - ], - "obligations": [ - { - "id": "parsing-never-repeats", - "sentence": "**Rejections never repeat the input.**", - "axes": { - "text.entry": [ - "Parse", - "TryParse", - "IParsable", - "ISpanParsable", - "type-converter" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "Uri", - "enum", - "flags-enum", - "NodaTime.Instant" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - }, - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#closed-sets" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "own-record-class" - ], - "text.entry": [ - "Parse", - "TryParse", - "IParsable", - "ISpanParsable", - "type-converter" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - }, - { - "axes": { - "text.entry": [ - "TryFrom", - "ToString-format", - "TryFormat-short-span" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#when-text-is-rejected" - } - ] - }, - { - "id": "json-never-repeats", - "sentence": "A JSON or parsing rejection names the value object and the rule, or the underlying type it could not read.", - "inapplicable": [ - { - "axes": { - "json.Converters": [ - "own-for-wrapped", - "own-for-value-object" - ] - }, - "cite": "not-promised:the-applications-own-converter" - } - ] - }, - { - "id": "no-inner-that-quotes", - "sentence": "It carries no inner exception that quotes the text.", - "axes": { - "text.entry": [ - "Parse", - "TryParse", - "IParsable", - "ISpanParsable", - "type-converter" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "Uri", - "enum", - "flags-enum", - "NodaTime.Instant" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - }, - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#closed-sets" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "own-record-class" - ], - "text.entry": [ - "Parse", - "TryParse", - "IParsable", - "ISpanParsable", - "type-converter" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - }, - { - "axes": { - "text.entry": [ - "TryFrom", - "ToString-format", - "TryFormat-short-span" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#when-text-is-rejected" - } - ] - } - ], - "not-promised": [ - { - "id": "a-rejected-dictionary-key-in-the-path", - "source": "README.md#what-typekit-guarantees", - "sentence": "One exception comes from System.Text.Json: a rejected dictionary key is in the `JsonException`'s `Path`, and so in MVC's 400", - "axes": { - "shape.position": [ - "dictionary-key" - ] - } - }, - { - "id": "the-applications-own-converter", - "source": "README.md#what-typekit-guarantees", - "sentence": "So a secret doesn't end up in a message or a log through this package, as long as your fault type doesn't carry it.", - "axes": { - "json.Converters": [ - "own-for-wrapped", - "own-for-value-object" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - }, - { - "reason": "System.Text.Json writes no dictionary key of a record class but through a converter of the application's (NotSupportedException for the wrapped type alike, L01's rows), where the JSON half is not promised (not-promised the-applications-own-converter), and a value object over a record class has no parsing (Generators README#what-is-generated), so no check of the claim can read the pair.", - "pairs": [ - [ - "shape.position=dictionary-key", - "shape.wrapped=own-record-class" - ] - ] - } - ] - }, - { - "id": "L03-one-rule", - "source": "README.md#what-typekit-guarantees", - "surfaces": [ - "text", - "json" - ], - "narrow": { - "input.json": "The generated `TryFrom`, `FromKnownGood`, JSON converter, parsing and type converter all run it, and each reports the fault you return from `Create`" - }, - "obligations": [ - { - "id": "tryfrom-runs-create", - "sentence": "The generated `TryFrom`, `FromKnownGood`, JSON converter, parsing and type converter all run it", - "axes": { - "text.entry": [ - "TryFrom" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "text.entry": [ - "TryFrom" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#closed-sets" - } - ] - }, - { - "id": "parsing-runs-create", - "sentence": "The generated `TryFrom`, `FromKnownGood`, JSON converter, parsing and type converter all run it", - "axes": { - "text.entry": [ - "Parse", - "TryParse", - "IParsable", - "ISpanParsable", - "type-converter" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "Uri", - "enum", - "flags-enum", - "NodaTime.Instant" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - }, - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#closed-sets" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "own-record-class" - ], - "text.entry": [ - "Parse", - "TryParse", - "IParsable", - "ISpanParsable", - "type-converter" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - } - ] - }, - { - "id": "json-runs-create", - "sentence": "The generated `TryFrom`, `FromKnownGood`, JSON converter, parsing and type converter all run it" - }, - { - "id": "refusals-name-the-fault", - "sentence": "the `JsonException`, the `FormatException` and the `FromKnownGood` exception all name your `EmailFault.NoAt`" - } - ], - "not-promised": [ - { - "id": "stored-json-skips-validation", - "source": "README.md#what-typekit-guarantees", - "sentence": "`StoredJsonConverterFactory` reads JSON the application stored itself.", - "axes": { - "json.Converters": [ - "StoredJsonConverterFactory" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "L04-round-trip", - "source": "src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting", - "surfaces": [ - "text", - "json" - ], - "obligations": [ - { - "id": "invariant-whatever-the-culture", - "sentence": "A null `IFormatProvider` means the invariant culture in every generated `Parse`, `TryParse`, `ToString` and `TryFormat`, so the text doesn't depend on the culture the process runs in." - }, - { - "id": "the-type-converter-writes-the-same", - "sentence": "`ToString()` is the underlying type's own default text under the invariant culture, and the type converter and `$\"{x}\"` write the same.", - "axes": { - "text.entry": [ - "ToString-format", - "TryFormat-short-span", - "type-converter" - ] - }, - "inapplicable": [ - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "text.entry": [ - "ToString-format", - "TryFormat-short-span" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#closed-sets" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "own-record-class" - ], - "text.entry": [ - "type-converter" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - } - ] - }, - { - "id": "text-reads-back", - "sentence": "That text reads back as the same value wherever the underlying type's own does", - "inapplicable": [ - { - "axes": { - "shape.kind": [ - "closed-set" - ], - "text.entry": [ - "TryFrom", - "ISpanParsable", - "ToString-format", - "TryFormat-short-span" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#closed-sets" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "own-record-class" - ], - "text.entry": [ - "Parse", - "TryParse", - "IParsable", - "ISpanParsable", - "type-converter", - "ToString-format", - "TryFormat-short-span" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "Uri", - "enum", - "flags-enum", - "NodaTime.Instant" - ], - "text.entry": [ - "ISpanParsable" - ] - }, - "cite": "docs:src/CodoMetis.TypeKit.Generators/README.md#what-is-generated" - }, - { - "axes": { - "shape.kind": [ - "record-struct", - "sealed-record-class", - "validated-struct", - "validated-class" - ], - "shape.wrapped": [ - "TimeOnly", - "DateTime", - "DateTimeOffset" - ], - "text.entry": [ - "type-converter" - ] - }, - "cite": "not-promised:dates-and-times-lose-detail-as-text" - } - ] - }, - { - "id": "json-refusal-is-a-json-exception", - "sentence": "Input it can't read throws a `JsonException` naming the value object and the underlying type" - } - ], - "not-promised": [ - { - "id": "dates-and-times-lose-detail-as-text", - "source": "src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting", - "sentence": "**`TimeOnly`, `DateTime` and `DateTimeOffset` lose detail as text.**", - "axes": { - "shape.wrapped": [ - "TimeOnly", - "DateTime", - "DateTimeOffset" - ] - } - } - ], - "exclude": [ - { - "reason": "System.Text.Json refuses PreferredObjectCreationHandling.Populate together with a ReferenceHandler on the options object; JsonRow substitutes the default, so the pair never applies.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ] - ] - } - ] - }, - { - "id": "L05-equality", - "source": "src/CodoMetis.TypeKit.Generators/README.md#equality", - "surfaces": [ - "generation" - ], - "narrow": { - "shape.refused-kind": "A declaration that can't be generated fails the build, so no type is left half-generated." - }, - "obligations": [ - { - "id": "equality-is-the-underlying-values", - "sentence": "Equality is the underlying value's, like the ordering, the JSON and the column." - }, - { - "id": "ordering-follows-the-underlying-type", - "sentence": "`CompareTo` and the operators follow the underlying type's ordering, ordinal for a string." - }, - { - "id": "through-every-base", - "sentence": "The value object implements each base's `IEquatable` again (`IReimplementedEquatable`), so a collection of the base compares it the way the value object does.", - "axes": { - "shape.inheritance": [ - "base-depth-1", - "base-depth-2", - "base-depth-3", - "base-explicit-IEquatable" - ] - } - } - ], - "exclude": [ - { - "reason": "A struct cannot derive from a base type (C# structs inherit only from ValueType), so it has no base record.", - "pairs": [ - [ - "shape.inheritance=base-depth-1", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=validated-struct" - ] - ] - }, - { - "reason": "A closed set wraps nothing (Generators README#closed-sets: a sealed class whose instances are its members), so it has no shape.wrapped.", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "A value object or closed set nested in a generic type fails the build (CMTK1005, CMTK1018; Generators README#build-errors), so no such type exists to compare.", - "pairs": [ - [ - "shape.containing=class", - "shape.site=in-generic-type" - ], - [ - "shape.containing=generic-type", - "shape.site=in-generic-type" - ], - [ - "shape.containing=record-class", - "shape.site=in-generic-type" - ], - [ - "shape.containing=record-struct", - "shape.site=in-generic-type" - ], - [ - "shape.containing=struct", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-1", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-2", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-3", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=none", - "shape.site=in-generic-type" - ], - [ - "shape.kind=closed-set", - "shape.site=in-generic-type" - ], - [ - "shape.kind=record-struct", - "shape.site=in-generic-type" - ], - [ - "shape.kind=sealed-record-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-struct", - "shape.site=in-generic-type" - ], - [ - "shape.position=array-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=dictionary-key", - "shape.site=in-generic-type" - ], - [ - "shape.position=dictionary-value", - "shape.site=in-generic-type" - ], - [ - "shape.position=field", - "shape.site=in-generic-type" - ], - [ - "shape.position=immutable-array-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=in-patch", - "shape.site=in-generic-type" - ], - [ - "shape.position=list-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=nullable-property", - "shape.site=in-generic-type" - ], - [ - "shape.position=polymorphic-derived-property", - "shape.site=in-generic-type" - ], - [ - "shape.position=property", - "shape.site=in-generic-type" - ], - [ - "shape.position=read-only-list-element", - "shape.site=in-generic-type" - ], - [ - "shape.position=record-ctor-parameter", - "shape.site=in-generic-type" - ], - [ - "shape.position=required-init", - "shape.site=in-generic-type" - ], - [ - "shape.position=root", - "shape.site=in-generic-type" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTime" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Guid" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeSpan" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Uri" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=bool" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=byte" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=char" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=decimal" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=double" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=flags-enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=int" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=long" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=own-record-class" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=string" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "A value at the root is not held by a containing type, so shape.containing has nothing to apply to.", - "pairs": [ - [ - "shape.containing=class", - "shape.position=root" - ], - [ - "shape.containing=generic-type", - "shape.position=root" - ], - [ - "shape.containing=record-class", - "shape.position=root" - ], - [ - "shape.containing=record-struct", - "shape.position=root" - ], - [ - "shape.containing=struct", - "shape.position=root" - ] - ] - } - ] - }, - { - "id": "L06-off-the-wire", - "source": "README.md#what-typekit-guarantees", - "surfaces": [ - "json", - "option-result" - ], - "obligations": [ - { - "id": "never-written", - "sentence": "System.Text.Json throws `NotSupportedException` for either, in both directions, instead of writing `{}` that reads back as `None`." - }, - { - "id": "never-read", - "sentence": "System.Text.Json throws `NotSupportedException` for either, in both directions, instead of writing `{}` that reads back as `None`." - } - ], - "not-promised": [ - { - "id": "a-source-generated-context-refuses-a-nullable", - "source": "src/CodoMetis.TypeKit/README.md#not-wire-types", - "sentence": "**A source-generated context can't describe an `Option?` or `Result<...>?` property.**", - "axes": { - "json.TypeInfoResolver": [ - "source-generated-context" - ], - "shape.position": [ - "nullable-property" - ] - } - }, - { - "id": "a-missing-property-reaches-no-converter", - "source": "src/CodoMetis.TypeKit/README.md#not-wire-types", - "sentence": "**A missing property reaches no converter.**" - } - ], - "exclude": [ - { - "reason": "A closed set wraps nothing (Generators README#closed-sets: a sealed class whose instances are its members), so it has no shape.wrapped.", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "A value at the root is not held by a containing type, so shape.containing has nothing to apply to.", - "pairs": [ - [ - "shape.containing=class", - "shape.position=root" - ], - [ - "shape.containing=generic-type", - "shape.position=root" - ], - [ - "shape.containing=record-class", - "shape.position=root" - ], - [ - "shape.containing=record-struct", - "shape.position=root" - ], - [ - "shape.containing=struct", - "shape.position=root" - ] - ] - }, - { - "reason": "Result has no Select or SelectMany, so no query expression produces one, and FirstOrNone returns an Option, never a Result (src/CodoMetis.TypeKit/ResultExtensions.cs and OptionExtensions.cs).", - "pairs": [ - [ - "or.operation=FirstOrNone", - "or.shape=Result-E" - ], - [ - "or.operation=query-syntax", - "or.shape=Result-E" - ] - ] - }, - { - "reason": "System.Text.Json serializes a field only under IncludeFields (or [JsonInclude]), so without it the member is in no document and no converter runs in either direction, for the plain type alike.", - "pairs": [ - [ - "json.IncludeFields=default", - "shape.position=field" - ] - ] - }, - { - "reason": "System.Text.Json refuses JsonObjectCreationHandling.Populate together with a reference handler (InvalidOperationException when the options are used; harness/JsonRow.cs runs such a row without Populate), so no row can set both.", - "pairs": [ - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=IgnoreCycles" - ], - [ - "json.PreferredObjectCreationHandling=Populate", - "json.ReferenceHandler=Preserve" - ] - ] - } - ] - }, - { - "id": "C22-generator-refusals", - "source": "src/CodoMetis.TypeKit.Generators/README.md#build-errors", - "surfaces": [ - "generation", - "analyzers" - ], - "axes": { - "build-host": [ - "sdk-build" - ] - }, - "narrow": { - "shape.position": "A declaration that can't be generated fails the build, so no type is left half-generated.", - "shape.containing": "A declaration that can't be generated fails the build, so no type is left half-generated." - }, - "obligations": [ - { - "id": "no-type-half-generated", - "sentence": "A declaration that can't be generated fails the build, so no type is left half-generated." - }, - { - "id": "every-error-links-the-table", - "sentence": "Every error ends with a link to this table." - }, - { - "id": "unsealed-record-class", - "sentence": "A record class not declared `sealed`.", - "axes": { - "shape.refused-kind": [ - "unsealed-record-class (CMTK1006)" - ] - } - }, - { - "id": "over-value-object", - "sentence": "or a value object (itself included). Wrap what the other value object wraps instead.", - "axes": { - "shape.refused-kind": [ - "over-value-object (CMTK1005)" - ] - } - }, - { - "id": "generic-wrapped", - "sentence": "A generic underlying type, a tuple included, fails the build (CMTK1005).", - "axes": { - "shape.refused-kind": [ - "generic-wrapped (CMTK1005)" - ] - } - }, - { - "id": "abstract", - "sentence": "An abstract type with a value-object marker. It would get no member, and a value held as it, of a type derived from it, would have no `Value` and be written as `{}`.", - "axes": { - "shape.refused-kind": [ - "abstract (CMTK1005)" - ] - } - }, - { - "id": "over-object", - "sentence": "or wrapping `object`, `System.Enum`, an array, a pointer, a nullable type, a generic type (a tuple included) or a value object (itself included).", - "axes": { - "shape.refused-kind": [ - "over-object (CMTK1005)" - ] - } - }, - { - "id": "over-system-enum", - "sentence": "or wrapping `object`, `System.Enum`, an array, a pointer, a nullable type, a generic type (a tuple included) or a value object (itself included).", - "axes": { - "shape.refused-kind": [ - "over-System.Enum (CMTK1005)" - ] - } - }, - { - "id": "named-as-generated-member", - "sentence": "named `Value` or as another member the generators add (`From`, `TryFrom`, `FromKnownGood`, `Revalidate`, `Parse`, `TryParse`, `TryFormat`, `MinValue`, `MaxValue`, `CompareTo`)", - "axes": { - "shape.refused-kind": [ - "named-as-generated-member (CMTK1005)" - ] - } - }, - { - "id": "closed-set-named-as-generated-member", - "sentence": "a set itself named as one of them (a member can't have the name of its type)", - "axes": { - "shape.refused-kind": [ - "closed-set-named-as-generated-member (CMTK1021)" - ] - } - }, - { - "id": "equality-operator", - "sentence": "So a hand-written `Equals(TSelf)`, `GetHashCode()`, `IEquatable.Equals`, or `operator ==` or `!=` between two of the type, fails the build (CMTK1011).", - "axes": { - "shape.refused-kind": [ - "equality-operator (CMTK1011)" - ] - } - } - ], - "exclude": [ - { - "reason": "A declaration nested in a generic type is refused for that alone (Generators README, Build errors: CMTK1005, \"Generic or nested in a generic type\"; the axes file's refused list), so it cannot show which refusal of its own the generators report.", - "pairs": [ - [ - "build-host=sdk-build", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-1", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-2", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-depth-3", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=explicit-interface-member", - "shape.site=in-generic-type" - ], - [ - "shape.inheritance=none", - "shape.site=in-generic-type" - ], - [ - "shape.kind=closed-set", - "shape.site=in-generic-type" - ], - [ - "shape.kind=record-struct", - "shape.site=in-generic-type" - ], - [ - "shape.kind=sealed-record-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-class", - "shape.site=in-generic-type" - ], - [ - "shape.kind=validated-struct", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=abstract (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=equality-operator (CMTK1011)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=generic-wrapped (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=named-as-generated-member (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=over-value-object (CMTK1005)", - "shape.site=in-generic-type" - ], - [ - "shape.refused-kind=unsealed-record-class (CMTK1006)", - "shape.site=in-generic-type" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTime" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Guid" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeOnly" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=TimeSpan" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=Uri" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=bool" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=byte" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=char" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=decimal" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=double" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=flags-enum" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=int" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=long" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=own-record-class" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=string" - ], - [ - "shape.site=in-generic-type", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "C# seals every struct and refuses an abstract one, so a record struct can be neither an unsealed nor an abstract record class; a closed set is never a record (CMTK1020), and an unsealed or abstract one is CMTK1014, a refusal of its own.", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=record-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=abstract (CMTK1005)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=unsealed-record-class (CMTK1006)" - ] - ] - }, - { - "reason": "A closed set wraps no type and has no equality of the underlying value to write by hand (Generators README, Closed sets), so it cannot be declared over a type or with a value object's equality; its own name clash is closed-set-named-as-generated-member (CMTK1021).", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.refused-kind=equality-operator (CMTK1011)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=generic-wrapped (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=named-as-generated-member (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-System.Enum (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-object (CMTK1005)" - ], - [ - "shape.kind=closed-set", - "shape.refused-kind=over-value-object (CMTK1005)" - ] - ] - }, - { - "reason": "CMTK1021 is a closed set's refusal; a value object named as a generated member is named-as-generated-member (CMTK1005).", - "pairs": [ - [ - "shape.kind=record-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=sealed-record-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-class", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.kind=validated-struct", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ] - ] - }, - { - "reason": "The refused kind fixes what is wrapped: object, System.Enum; and a closed set wraps no type (Generators README, Closed sets), so shape.wrapped has nothing to apply to.", - "pairs": [ - [ - "shape.kind=closed-set", - "shape.wrapped=DateOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTime" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Guid" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeOnly" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=TimeSpan" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=Uri" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=bool" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=byte" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=char" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=decimal" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=double" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=flags-enum" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=int" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=long" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=own-record-class" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=string" - ], - [ - "shape.kind=closed-set", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-System.Enum (CMTK1005)", - "shape.wrapped=ulong" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTime" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=DateTimeOffset" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Guid" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=NodaTime.Instant" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeOnly" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=TimeSpan" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=Uri" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=bool" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=byte" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=char" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=decimal" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=double" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=flags-enum" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=int" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=long" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=own-record-class" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=string" - ], - [ - "shape.refused-kind=over-object (CMTK1005)", - "shape.wrapped=ulong" - ] - ] - }, - { - "reason": "A record struct has no base type, and a closed set is a sealed class that is never a record (CMTK1020), so neither derives from the base records of shape.inheritance; an explicit member of another interface they implement themselves.", - "pairs": [ - [ - "shape.inheritance=base-depth-1", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-1", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-2", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-depth-3", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=closed-set" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=record-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.kind=validated-struct" - ], - [ - "shape.inheritance=base-explicit-IEquatable", - "shape.refused-kind=closed-set-named-as-generated-member (CMTK1021)" - ] - ] - } - ] - } -] diff --git a/tools/probe/corpus/dismissed.jsonl b/tools/probe/corpus/dismissed.jsonl deleted file mode 100644 index 4327b389..00000000 --- a/tools/probe/corpus/dismissed.jsonl +++ /dev/null @@ -1 +0,0 @@ -{"id": "G1", "reason": "Not a TypeKit bug: the MVC [FromBody] T v of a class is nullable, so ASP.NET makes the body optional, and its OpenAPI 2.0 writer turns any nullable body into allOf:[{x-nullable:true}] without pattern; a plain int? without TypeKit gets the same bytes. The harness compared a nullable class value object with a non-nullable int (fix-f2-g1-openapi, measured at 1ee02fc).", "date": "2026-10-15"} diff --git a/tools/probe/corpus/harness/BaseProbe.cs b/tools/probe/corpus/harness/BaseProbe.cs deleted file mode 100644 index 13b5e370..00000000 --- a/tools/probe/corpus/harness/BaseProbe.cs +++ /dev/null @@ -1,11 +0,0 @@ -// Calibration of the shared HTTP host (not a claim): runs the baseline's wire half over its own rows of claim C07 to find harness mistakes -// before the claims use it. The schema half is claim C21's (C21OpenapiSchemaParityProbe). Each comparison is a check of its own (HttpBaseline.Run), -// of the implicit obligation claim: C07 promises nothing of the wire, and its seed names no obligation. -using Probes.Shapes; -namespace Probes; -public sealed class BaseProbe -{ - public static TheoryData Data() => Rows.Data("BaseProbe"); - [Theory, MemberData(nameof(Data))] - public Task Row(string probe, int idx) => Rec.Run("BASE", idx, async r => await HttpBaseline.Run(r, Registry.Of(r), schema: false), probe); -} diff --git a/tools/probe/corpus/harness/BuildConsumer.cs b/tools/probe/corpus/harness/BuildConsumer.cs deleted file mode 100644 index 15dddbc5..00000000 --- a/tools/probe/corpus/harness/BuildConsumer.cs +++ /dev/null @@ -1,125 +0,0 @@ -// A throwaway consumer of the generators, built once per probe run with every row's declarations in a file of its own, for what -// only a build can show: the generators' diagnostics (CMTK1xxx). It builds as test/CodoMetis.TypeKit.Generators.Tests' BuildOutcomeTests -// does: under artifacts/, against the projects this run already built (--no-dependencies), outside the repository's build settings. -using System.Diagnostics; -using System.Reflection; -using System.Reflection.Metadata; -using System.Reflection.PortableExecutable; -using System.Text.RegularExpressions; -namespace Probes.Build; - -public sealed record BuildDiagnostic(string File, string Severity, string Id, string Message); - -public sealed record ConsumerOutcome(int ExitCode, string Output, IReadOnlyList Diagnostics, IReadOnlyDictionary> Methods) -{ - /// The diagnostics reported in one of the consumer's files. - public IEnumerable In(string file) => Diagnostics.Where(d => Path.GetFileName(d.File) == file); -} - -public static partial class Consumer -{ - static readonly Dictionary>> Builds = new(); - - /// Builds the consumer of once per in this process. - public static Task Build(string key, Func> files) - { - lock (Builds) - { - if (!Builds.TryGetValue(key, out var build)) Builds[key] = build = new(() => Run(files())); - return build.Value; - } - } - - public static string RepositoryRoot() - { - for (var d = new DirectoryInfo(AppContext.BaseDirectory); d is not null; d = d.Parent) - if (File.Exists(Path.Combine(d.FullName, "CodoMetis.TypeKit.slnx"))) return d.FullName; - throw new InvalidOperationException($"no CodoMetis.TypeKit.slnx above {AppContext.BaseDirectory}"); - } - - static string Configuration => typeof(Consumer).Assembly.GetCustomAttribute()?.Configuration - ?? throw new InvalidOperationException("the probe assembly carries no AssemblyConfigurationAttribute"); - - static async Task Run(IReadOnlyDictionary files) - { - var root = RepositoryRoot(); - var dir = Path.Combine(root, "artifacts", $"probe-consumer-{Guid.NewGuid():N}"); - Directory.CreateDirectory(dir); - try - { - var src = Path.Combine(root, "src"); - await File.WriteAllTextAsync(Path.Combine(dir, "Consumer.csproj"), $""" - - - net10.0 - enable - disable - ProbeConsumer - - - - {typeof(NodaTime.Instant).Assembly.Location} - - - """); - await File.WriteAllTextAsync(Path.Combine(dir, "Directory.Build.props"), ""); - await File.WriteAllTextAsync(Path.Combine(dir, "Directory.Build.targets"), ""); - await File.WriteAllTextAsync(Path.Combine(dir, "Directory.Packages.props"), - "false"); - foreach (var (name, text) in files) await File.WriteAllTextAsync(Path.Combine(dir, name), text); - - var dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") is { Length: > 0 } host ? host : "dotnet"; - var info = new ProcessStartInfo(dotnet, ["build", dir, "-c", Configuration, "--no-dependencies", "-p:RestoreRecursive=false", "-nodeReuse:false", "-clp:NoSummary", "-v:q", "-nologo"]) - { - RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = dir, - }; - // The run's code coverage instruments this process through its environment; the build is not the probe's code. - foreach (var k in info.Environment.Keys.ToList()) - if (k.StartsWith("CORECLR_", StringComparison.OrdinalIgnoreCase) || k.StartsWith("COR_", StringComparison.OrdinalIgnoreCase) - || k.Contains("CODECOVERAGE", StringComparison.OrdinalIgnoreCase) || k.StartsWith("MicrosoftInstrumentationEngine", StringComparison.OrdinalIgnoreCase) - || k.Equals("DOTNET_STARTUP_HOOKS", StringComparison.OrdinalIgnoreCase)) - info.Environment.Remove(k); - info.Environment["DOTNET_CLI_UI_LANGUAGE"] = "en"; - using var process = Process.Start(info)!; - var stdout = process.StandardOutput.ReadToEndAsync(); - var stderr = process.StandardError.ReadToEndAsync(); - using (var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(10))) await process.WaitForExitAsync(timeout.Token); - var output = await stdout + await stderr; - var diagnostics = Line().Matches(output) - .Select(m => new BuildDiagnostic(m.Groups["file"].Value.Trim(), m.Groups["sev"].Value, m.Groups["id"].Value, m.Groups["msg"].Value.Trim())) - .Distinct().ToList(); - var dll = Path.Combine(dir, "bin", Configuration, "net10.0", "ProbeConsumer.dll"); - return new ConsumerOutcome(process.ExitCode, output, diagnostics, File.Exists(dll) ? Methods(dll) : new Dictionary>()); - } - finally - { - try { Directory.Delete(dir, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - } - - /// The methods each type of the built assembly declares, by the type's metadata name (nesting with '+'), read without loading it. - static Dictionary> Methods(string dll) - { - using var stream = File.OpenRead(dll); - using var pe = new PEReader(stream); - var md = pe.GetMetadataReader(); - string Name(TypeDefinition t) - { - var name = md.GetString(t.Name); - var outer = t.GetDeclaringType(); - if (!outer.IsNil) return Name(md.GetTypeDefinition(outer)) + "+" + name; - var ns = md.GetString(t.Namespace); - return ns.Length == 0 ? name : ns + "." + name; - } - var result = new Dictionary>(); - foreach (var h in md.TypeDefinitions) - { - var t = md.GetTypeDefinition(h); - result[Name(t)] = [.. t.GetMethods().Select(m => md.GetString(md.GetMethodDefinition(m).Name))]; - } - return result; - } - - [GeneratedRegex(@"^(?[^\r\n(]+)\((?\d+),(?\d+)\): (?warning|error) (?[A-Z]+\d+): (?.*?)(?: \[[^\]\r\n]*\])?\r?$", RegexOptions.Multiline)] - private static partial Regex Line(); -} diff --git a/tools/probe/corpus/harness/BuildDecls.cs b/tools/probe/corpus/harness/BuildDecls.cs deleted file mode 100644 index 7efdb659..00000000 --- a/tools/probe/corpus/harness/BuildDecls.cs +++ /dev/null @@ -1,91 +0,0 @@ -// The hand half of C09Shapes.g.cs and C11Shapes.g.cs (generated from the rows, one value object per row): the tables' rows, and the -// wrapped types of C11's parse shapes. -namespace Probes.Build; - -/// One C09 row as declared: the effective settings, the value object, its bases from the direct one up, and its factory. -public sealed record C09Case(int Index, string Key, string Kind, string Site, string Inheritance, Type Vo, Type[] Bases, Func Make, (string Axis, string Value)[] Substituted); - -/// -/// One C11 row as declared: the effective settings, the value object and the type it wraps, and for a parse shape its control: a value -/// object of the same kind, site and inheritance over the nearest type that does parse. -/// -public sealed record C11Case(int Index, string Key, string Kind, string Site, string Inheritance, Type Vo, Type Wrapped, Type? Control, Type? ControlWrapped, (string Axis, string Value)[] Substituted); - -public static class Decls -{ - /// - /// The row's settings as the generator keyed them; a table generated from other rows is a harness error, not a pass. Read from - /// the cached copy of the row, so the run's own row marks no axis read. - /// - public static string Key(string probe, int index) => - string.Join(";", Probes.Shapes.Rows.Get(probe, index).All.OrderBy(kv => kv.Key, StringComparer.Ordinal).Select(kv => $"{kv.Key}={kv.Value}")); -} - -// C11's parse shapes, each the whole wrapped type (CHANGELOG 2.1.0, Fixed): "A wrapped type's `Parse` whose result can't be assigned -// to it, or that takes its text by `ref`, or an abstract class's string constructor". - -/// implicit-result: a static Parse whose result, an int, cannot be assigned to the type. -public sealed class C11ParsesToInt -{ - public static int Parse(string text) => text.Length; - public override string ToString() => "parses-to-int"; -} - -/// ref-text: a static Parse that takes its text by ref. -public sealed class C11ParsesByRef -{ - public static C11ParsesByRef Parse(ref string text) { text = ""; return new(); } - public override string ToString() => "parses-by-ref"; -} - -/// ref-text: a string constructor that takes its text by ref. -public sealed class C11ConstructedByRef -{ - public C11ConstructedByRef(ref string text) => text = ""; - private C11ConstructedByRef() { } - public static C11ConstructedByRef Sample() => new(); - public override string ToString() => "constructed-by-ref"; -} - -/// abstract-string-ctor: an abstract class with a public string constructor, which only a derived class can call. -public abstract class C11AbstractText -{ - public C11AbstractText(string text) => Text = text; - public string Text { get; } - public override string ToString() => Text; -} - -public sealed class C11ConcreteText(string text) : C11AbstractText(text); - -// The controls, each the nearest type that can be parsed (Generators README, "What you can wrap": "Parsing where it can be parsed"): -// a Parse whose result converts to the type by a user-defined implicit operator, so it can be assigned; a Parse taking its text by in, -// which a call passes as it is; a sealed class's public string constructor. - -/// Control of implicit-result: Parse gives a type that converts to this one implicitly. -public sealed class C11ParsedShape -{ - public string Text { get; init; } = ""; - public static C11ParsedCircle Parse(string text) => new() { Text = text }; - public override string ToString() => Text; -} - -public sealed class C11ParsedCircle -{ - public string Text { get; init; } = ""; - public static implicit operator C11ParsedShape(C11ParsedCircle circle) => new() { Text = circle.Text }; -} - -/// Control of ref-text: Parse takes its text by in. -public sealed class C11ParsesFromIn -{ - public string Text { get; private init; } = ""; - public static C11ParsesFromIn Parse(in string text) => new() { Text = text }; - public override string ToString() => Text; -} - -/// Control of abstract-string-ctor: a sealed class with a public string constructor. -public sealed class C11ConstructedConcrete(string text) -{ - public string Text { get; } = text; - public override string ToString() => Text; -} diff --git a/tools/probe/corpus/harness/BuildRefusals.cs b/tools/probe/corpus/harness/BuildRefusals.cs deleted file mode 100644 index be3d79da..00000000 --- a/tools/probe/corpus/harness/BuildRefusals.cs +++ /dev/null @@ -1,280 +0,0 @@ -// The declarations the generators refuse (shape.refused-kind), written as C# per row, and the consumer build that shows what the -// generators did with them (C22). The build is BuildConsumer.cs's (a throwaway consumer under artifacts/, against the projects this run -// already built, outside the repository's build settings) with Metalama's transformed files kept, since a refused declaration fails the -// build and leaves no assembly to read: the transformed source is where a member the generators added to a refused type would show. -using System.Diagnostics; -using System.Reflection; -using System.Text.RegularExpressions; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.CSharp.Syntax; -namespace Probes.Build; - -/// A consumer build: its exit code, its output, its diagnostics, and Metalama's transformed source of each file. -public sealed record RefusalOutcome(int ExitCode, string Output, IReadOnlyList Diagnostics, IReadOnlyDictionary Transformed) -{ - /// The diagnostics reported in one of the consumer's files. - public IEnumerable In(string file) => Diagnostics.Where(d => Path.GetFileName(d.File) == file); -} - -/// -/// One refusal of the Generators README's "Build errors" table, as a row declares it: the id the table gives it, the obligation of C22 -/// that states it, the consumer it is built in, and the compiler errors that may stand beside it in its own file. -/// -/// -/// A compiler error in a consumer hid every LAMA0611 of the other files (e4b751c), so the declarations whose refusal comes with a -/// compiler error of its own are built apart: a refused closed set leaves its interface's members unimplemented (CS0535, as -/// BuildOutcomeTests allows it), and a hand-written equality operator of a record is also C#'s CS0111 (the README says so). -/// -public sealed record Refusal(string Kind, string Id, string Obligation, string Group, IReadOnlyList Companions); - -/// One row's declarations: the refused type, as the diagnostics name it, and a control the generators must generate. -public sealed record RefusedDeclaration(int Index, Refusal Refusal, string Name, string Display, string Control, string File, string Source); - -public static partial class Refusals -{ - /// The refusals C22 declares, by their shape.refused-kind value; any other value is the harness's mistake, never a pass. - public static readonly IReadOnlyDictionary Table = new Refusal[] - { - new("unsealed-record-class (CMTK1006)", "CMTK1006", "unsealed-record-class", "value-objects", []), - new("over-value-object (CMTK1005)", "CMTK1005", "over-value-object", "value-objects", []), - new("generic-wrapped (CMTK1005)", "CMTK1005", "generic-wrapped", "value-objects", []), - // The label keeps the id it had when C09's and C11's rows were generated; an abstract value object is CMTK1022 since 1ef30be. - new("abstract (CMTK1005)", "CMTK1022", "abstract", "value-objects", []), - new("over-object (CMTK1005)", "CMTK1005", "over-object", "value-objects", []), - new("over-System.Enum (CMTK1005)", "CMTK1005", "over-system-enum", "value-objects", []), - new("named-as-generated-member (CMTK1005)", "CMTK1005", "named-as-generated-member", "value-objects", []), - new("closed-set-named-as-generated-member (CMTK1021)", "CMTK1021", "closed-set-named-as-generated-member", "closed-sets", ["CS0535"]), - new("equality-operator (CMTK1011)", "CMTK1011", "equality-operator", "operators", ["CS0111"]), - }.ToDictionary(r => r.Kind); - - /// - /// The value-object names CMTK1005 refuses (the README's list), and the closed-set names CMTK1021 refuses; a row takes one by its - /// index. C17's and C19's probes name their named-as-generated-member declaration from the same list. - /// - public static readonly string[] ValueObjectNames = ["Value", "From", "TryFrom", "FromKnownGood", "Revalidate", "Parse", "TryParse", "TryFormat", "MinValue", "MaxValue", "CompareTo"]; - static readonly string[] ClosedSetNames = ["Name", "QualifiedName", "All", "Match", "Parse", "TryParse"]; - - /// The link every generator error ends with (Generators README, Build errors: "Every error ends with a link to this table"). - public const string TableLink = "https://github.com/CaffeinatedCoder/CodoMetis.TypeKit/blob/main/src/CodoMetis.TypeKit.Generators/README.md#build-errors"; - - static readonly string[] Structs = ["record-struct", "validated-struct"]; - - /// - /// The row's declarations. Reads shape.refused-kind, shape.kind, shape.site, shape.inheritance and build-host, and shape.wrapped where - /// the refused kind wraps it. A refused kind the table does not hold, a combination the claim excludes, and a refusal the README's - /// Build errors table no longer lists are harness errors: the row would show nothing about a refusal the README states. - /// - public static RefusedDeclaration Declare(Probes.Shapes.Row r) - { - if (r["build-host"] != "sdk-build") throw new InvalidOperationException("build-host " + r["build-host"] + " has no build here"); - var kindValue = r["shape.refused-kind"]; - if (!Table.TryGetValue(kindValue, out var refusal)) throw new InvalidOperationException($"shape.refused-kind '{kindValue}' is no refusal this harness declares"); - if (!ReadmeLists(refusal.Id)) throw new InvalidOperationException($"the Generators README's Build errors table has no row for {refusal.Id}"); - var kind = r["shape.kind"]; var site = r["shape.site"]; var inheritance = r["shape.inheritance"]; - var n = r.Index; var id = n.ToString(); - var closedSet = kind == "closed-set"; - if (closedSet != (refusal.Group == "closed-sets")) throw new InvalidOperationException($"{kindValue} cannot be declared as a {kind} (excluded in claims.json)"); - if (Structs.Contains(kind) && refusal.Obligation is "unsealed-record-class" or "abstract") throw new InvalidOperationException($"a {kind} cannot be {refusal.Obligation} (excluded in claims.json)"); - if ((Structs.Contains(kind) || closedSet) && inheritance is not ("none" or "explicit-interface-member")) throw new InvalidOperationException($"a {kind} has no base record for {inheritance} (excluded in claims.json)"); - if (site == "in-generic-type") throw new InvalidOperationException("in-generic-type is a refusal of its own (excluded in claims.json)"); - - var name = refusal.Obligation switch - { - "named-as-generated-member" => ValueObjectNames[n % ValueObjectNames.Length], - "closed-set-named-as-generated-member" => ClosedSetNames[n % ClosedSetNames.Length], - _ => $"V{id}", - }; - var control = closedSet ? $"S{id}" : $"K{id}"; - var body = new System.Text.StringBuilder(); - body.AppendLine($"public enum VF{id} {{ No }}"); - var explicitMember = inheritance == "explicit-interface-member"; - if (explicitMember) body.AppendLine($"public interface IMark{id} {{ int Mark(); }}"); - - // The bases, from the topmost down: plain abstract records, the direct one with an explicit IEquatable of its own or an explicit - // member of another interface where shape.inheritance says so. A struct or a closed set implements that interface itself. - var depth = inheritance switch { "base-depth-2" => 2, "base-depth-3" => 3, "none" => 0, _ => 1 }; - if (closedSet || Structs.Contains(kind)) depth = 0; - for (var d = depth; d >= 1; d--) - { - var b = $"B{id}_{d}"; - var parents = new List(); - if (d < depth) parents.Add($"B{id}_{d + 1}"); - var members = ""; - if (d == 1 && inheritance == "base-explicit-IEquatable") { parents.Add($"IEquatable<{b}>"); members = $"bool IEquatable<{b}>.Equals({b}? other) => ReferenceEquals(this, other);"; } - if (d == 1 && explicitMember) { parents.Add($"IMark{id}"); members = $"int IMark{id}.Mark() => 0;"; } - body.AppendLine($"public abstract record {b}{(parents.Count > 0 ? " : " + string.Join(", ", parents) : "")} {{ {members} }}"); - } - var baseList = depth > 0 ? $"B{id}_1, " : ""; - var ownMark = explicitMember && depth == 0; - var markList = ownMark ? $", IMark{id}" : ""; - var markMember = ownMark ? $" int IMark{id}.Mark() => 0;" : ""; - - if (closedSet) - { - body.AppendLine($"public sealed partial class {name} : IClosedSet<{name}>{markList} {{ public static readonly {name} A{id} = new();{markMember} }}"); - body.AppendLine($"public sealed partial class {control} : IClosedSet<{control}>{markList} {{ public static readonly {control} A{id} = new();{markMember} }}"); - } - else - { - var w = refusal.Obligation is "over-object" or "over-system-enum" ? null : Src.Wrapped(r["shape.wrapped"], id); - var wrapped = refusal.Obligation switch - { - "over-value-object" => $"I{id}", - "generic-wrapped" => $"List<{w}>", - "over-object" => "object", - "over-system-enum" => "System.Enum", - _ => w!, - }; - if (refusal.Obligation == "over-value-object") body.AppendLine($"public readonly partial record struct I{id} : IValue<{w}>;"); - var modifier = refusal.Obligation switch { "unsealed-record-class" => "", "abstract" => "abstract ", _ => "sealed " }; - var operators = refusal.Obligation == "equality-operator"; - body.AppendLine(ValueObject(kind, name, modifier, baseList, wrapped, id, markList, markMember, operators)); - body.AppendLine(ValueObject(kind, control, "sealed ", baseList, w ?? "int", id, markList, markMember, operators: false)); - } - - var source = Src.Usings + $"namespace C22R{id};\n" + Src.WrappedDeclarations(id) + Src.AtSite(site, id, body.ToString()) + "\n"; - return new(n, refusal, name, Nesting(site, id) + name, control, $"R{id}.cs", source); - } - - static string ValueObject(string kind, string name, string modifier, string baseList, string wrapped, string id, string markList, string markMember, bool operators) - { - var validated = kind.StartsWith("validated", StringComparison.Ordinal); - var marker = validated ? $"IValidatedValue<{name}, {wrapped}, VF{id}>" : $"IValue<{wrapped}>"; - var head = Structs.Contains(kind) - ? $"public readonly partial record struct {name} : {marker}{markList}" - : $"public {modifier}partial record {name} : {baseList}{marker}{markList}"; - var members = new List(); - if (validated) members.Add($"public static Result<{name}, VF{id}> Create({wrapped} value) => Result.Error(VF{id}.No);"); - if (operators) - { - var t = Structs.Contains(kind) ? name : name + "?"; - members.Add($"public static bool operator ==({t} left, {t} right) => true;"); - members.Add($"public static bool operator !=({t} left, {t} right) => false;"); - } - if (markMember.Length > 0) members.Add(markMember.Trim()); - return members.Count == 0 ? head + ";" : $"{head}\n{{\n {string.Join("\n ", members)}\n}}"; - } - - /// shape.site's nesting as a diagnostic names a type: its containing types, dotted. - static string Nesting(string site, string id) => site switch - { - "top-level" => "", "nested-in-class" or "nested-in-struct" => $"O{id}.", "nested-two-levels" => $"O{id}.P{id}.", - _ => throw new ArgumentException("shape.site " + site), - }; - - static readonly Lazy Readme = new(() => File.ReadAllText(Path.Combine(Consumer.RepositoryRoot(), "src", "CodoMetis.TypeKit.Generators", "README.md"))); - - /// Whether the README's Build errors table has a row for . - public static bool ReadmeLists(string id) => - Readme.Value.IndexOf("\n## Build errors", StringComparison.Ordinal) is var at and >= 0 - && Readme.Value.IndexOf($"\n| {id} |", at, StringComparison.Ordinal) >= 0; - - /// Whether the README still has the heading the table link points at. - public static bool ReadmeHasBuildErrorsHeading() => Readme.Value.Split('\n').Any(l => l.TrimEnd('\r') == "## Build errors"); - - /// - /// How the generators left a type in Metalama's transformed source: null where its declaration is as it was written (no base - /// type and no member added, and no {Name}Extensions companion), else what they added. Throws where the type is not there. - /// - public static string? Added(RefusalOutcome build, RefusedDeclaration d, string name) - { - if (!build.Transformed.TryGetValue(d.File, out var transformed)) throw new InvalidOperationException($"Metalama wrote no transformed {d.File}"); - var written = One(CSharpSyntaxTree.ParseText(d.Source).GetRoot(), name, "the row's source"); - var woven = One(CSharpSyntaxTree.ParseText(transformed).GetRoot(), name, "the transformed " + d.File); - var added = new List(); - var bw = written.BaseList?.Types.Count ?? 0; var bt = woven.BaseList?.Types.Count ?? 0; - if (bt != bw) added.Add($"{bt - bw} base type(s)"); - if (woven.Members.Count != written.Members.Count) added.Add($"{woven.Members.Count - written.Members.Count} member(s)"); - var companion = name + "Extensions"; - foreach (var (file, text) in build.Transformed) - if (text.Contains($"namespace C22R{d.Index}", StringComparison.Ordinal) && text.Contains(companion, StringComparison.Ordinal) - && CSharpSyntaxTree.ParseText(text).GetRoot().DescendantNodes().OfType().Any(c => c.Identifier.Text == companion)) - added.Add($"the companion {companion} ({file})"); - return added.Count == 0 ? null : string.Join(", ", added); - } - - static TypeDeclarationSyntax One(Microsoft.CodeAnalysis.SyntaxNode root, string name, string where) - { - var found = root.DescendantNodes().OfType().Where(t => t.Identifier.Text == name).ToList(); - return found.Count == 1 ? found[0] : throw new InvalidOperationException($"{found.Count} declarations of {name} in {where}"); - } - - static readonly Dictionary>> Builds = new(); - - /// Builds the consumer of once per in this process. - public static Task Build(string key, Func> files) - { - lock (Builds) - { - if (!Builds.TryGetValue(key, out var build)) Builds[key] = build = new(() => Run(files())); - return build.Value; - } - } - - static string Configuration => typeof(Refusals).Assembly.GetCustomAttribute()?.Configuration - ?? throw new InvalidOperationException("the probe assembly carries no AssemblyConfigurationAttribute"); - - static async Task Run(IReadOnlyDictionary files) - { - var root = Consumer.RepositoryRoot(); - var dir = Path.Combine(root, "artifacts", $"probe-refusals-{Guid.NewGuid():N}"); - Directory.CreateDirectory(dir); - try - { - var src = Path.Combine(root, "src"); - await File.WriteAllTextAsync(Path.Combine(dir, "Consumer.csproj"), $""" - - - net10.0 - enable - disable - ProbeRefusals - true - - - - {typeof(NodaTime.Instant).Assembly.Location} - - - """); - await File.WriteAllTextAsync(Path.Combine(dir, "Directory.Build.props"), ""); - await File.WriteAllTextAsync(Path.Combine(dir, "Directory.Build.targets"), ""); - await File.WriteAllTextAsync(Path.Combine(dir, "Directory.Packages.props"), - "false"); - foreach (var (name, text) in files) await File.WriteAllTextAsync(Path.Combine(dir, name), text); - - var dotnet = Environment.GetEnvironmentVariable("DOTNET_HOST_PATH") is { Length: > 0 } host ? host : "dotnet"; - var info = new ProcessStartInfo(dotnet, ["build", dir, "-c", Configuration, "--no-dependencies", "-p:RestoreRecursive=false", "-nodeReuse:false", "-clp:NoSummary", "-v:q", "-nologo"]) - { - RedirectStandardOutput = true, RedirectStandardError = true, WorkingDirectory = dir, - }; - // The run's code coverage instruments this process through its environment; the build is not the probe's code. - foreach (var k in info.Environment.Keys.ToList()) - if (k.StartsWith("CORECLR_", StringComparison.OrdinalIgnoreCase) || k.StartsWith("COR_", StringComparison.OrdinalIgnoreCase) - || k.Contains("CODECOVERAGE", StringComparison.OrdinalIgnoreCase) || k.StartsWith("MicrosoftInstrumentationEngine", StringComparison.OrdinalIgnoreCase) - || k.Equals("DOTNET_STARTUP_HOOKS", StringComparison.OrdinalIgnoreCase)) - info.Environment.Remove(k); - info.Environment["DOTNET_CLI_UI_LANGUAGE"] = "en"; - using var process = Process.Start(info)!; - var stdout = process.StandardOutput.ReadToEndAsync(); - var stderr = process.StandardError.ReadToEndAsync(); - using (var timeout = new CancellationTokenSource(TimeSpan.FromMinutes(10))) await process.WaitForExitAsync(timeout.Token); - var output = await stdout + await stderr; - var diagnostics = Line().Matches(output) - .Select(m => new BuildDiagnostic(m.Groups["file"].Value.Trim(), m.Groups["sev"].Value, m.Groups["id"].Value, m.Groups["msg"].Value.Trim())) - .Distinct().ToList(); - var woven = Path.Combine(dir, "obj", Configuration, "net10.0", "metalama"); - var transformed = Directory.Exists(woven) - ? Directory.GetFiles(woven, "*.cs").ToDictionary(f => Path.GetFileName(f), f => File.ReadAllText(f)) - : new Dictionary(); - return new RefusalOutcome(process.ExitCode, output, diagnostics, transformed); - } - finally - { - try { Directory.Delete(dir, recursive: true); } catch (IOException) { } catch (UnauthorizedAccessException) { } - } - } - - [GeneratedRegex(@"^(?[^\r\n(]+)\((?\d+),(?\d+)\): (?warning|error) (?[A-Z]+\d+): (?.*?)(?: \[[^\]\r\n]*\])?\r?$", RegexOptions.Multiline)] - private static partial Regex Line(); -} diff --git a/tools/probe/corpus/harness/BuildRoslyn.cs b/tools/probe/corpus/harness/BuildRoslyn.cs deleted file mode 100644 index 2f3584d1..00000000 --- a/tools/probe/corpus/harness/BuildRoslyn.cs +++ /dev/null @@ -1,167 +0,0 @@ -// The analyzer probes' compiler (C17 CMTK0014, C19 CMTK0010): a CSharpCompilation over the real CodoMetis.TypeKit and the -// running framework, with the CMTK analyzer that reports the claim's id attached, as test/CodoMetis.TypeKit.Analyzers.Tests -// runs it. Sources carry markup {|#label:...|}; a marked span is where the probe expects (or forbids) a report. -using System.Collections.Immutable; -using System.Reflection; -using Microsoft.CodeAnalysis; -using Microsoft.CodeAnalysis.CSharp; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Text; -namespace Probes.Build; - -/// One marked span of a source file, by its label. -public sealed record Mark(string Label, string File, TextSpan Span, string Text); - -/// A source file whose markup was taken out, with the spans it marked. -public sealed record Marked(string File, string Text, IReadOnlyList Marks) -{ - /// Strips {|#label:…|} (not nested) and records each span in the stripped text. - public static Marked Parse(string file, string source) - { - var sb = new System.Text.StringBuilder(); var marks = new List(); var i = 0; - while (i < source.Length) - { - var open = source.IndexOf("{|#", i, StringComparison.Ordinal); - if (open < 0) { sb.Append(source, i, source.Length - i); break; } - sb.Append(source, i, open - i); - var colon = source.IndexOf(':', open); - var label = source[(open + 3)..colon]; - var close = source.IndexOf("|}", colon, StringComparison.Ordinal); - if (close < 0) throw new InvalidOperationException($"unclosed markup {label} in {file}"); - var inner = source[(colon + 1)..close]; - if (inner.Contains("{|", StringComparison.Ordinal)) throw new InvalidOperationException($"nested markup in {label} of {file}"); - marks.Add(new Mark(label, file, new TextSpan(sb.Length, inner.Length), inner)); - sb.Append(inner); - i = close + 2; - } - return new Marked(file, sb.ToString(), marks); - } -} - -/// What one compilation reported: compiler errors, the analyzer's diagnostics, analyzer failures. -public sealed record Analysis(IReadOnlyList Errors, IReadOnlyList Reported, IReadOnlyList AnalyzerFailures, IReadOnlyList Files) -{ - public IEnumerable Marks => Files.SelectMany(f => f.Marks); - public static string Where(Diagnostic d) => $"{d.Location.SourceTree?.FilePath}:{d.Location.SourceSpan} '{d.Location.SourceTree?.GetText().ToString(d.Location.SourceSpan)}'"; - public string ErrorText() => string.Join("; ", Errors.Take(5).Select(e => $"{e.Id} {Where(e)}: {e.GetMessage()}")); -} - -public static class Rz -{ - static readonly Lazy> Refs = new(() => - { - // The running shared framework (implementation assemblies, as the probe's own process binds), plus the real base package and NodaTime. - var framework = Path.GetDirectoryName(typeof(object).Assembly.Location)!; - var tpa = ((string)AppContext.GetData("TRUSTED_PLATFORM_ASSEMBLIES")!).Split(Path.PathSeparator) - .Where(p => Path.GetDirectoryName(p) == framework && Path.GetFileName(p) is var n && (n.StartsWith("System.") || n.StartsWith("Microsoft.") || n is "mscorlib.dll" or "netstandard.dll")); - return [.. tpa.Concat([typeof(CodoMetis.TypeKit.Option<>).Assembly.Location, typeof(NodaTime.Instant).Assembly.Location]) - .Distinct().Select(p => (MetadataReference)MetadataReference.CreateFromFile(p))]; - }); - - /// The CMTK analyzers that report , found by the id they declare, never by type name. - public static ImmutableArray AnalyzersFor(string id) => - [ - .. typeof(CodoMetis.TypeKit.Analyzers.TypeArgumentConstraintAnalyzer).Assembly.GetTypes() - .Where(t => !t.IsAbstract && typeof(DiagnosticAnalyzer).IsAssignableFrom(t) && t.GetCustomAttribute() is not null) - .Select(t => (DiagnosticAnalyzer)Activator.CreateInstance(t)!) - .Where(a => a.SupportedDiagnostics.Any(d => d.Id == id)), - ]; - - public static async Task Analyze(string id, IEnumerable<(string File, string Source)> sources) - { - var files = sources.Select(s => Marked.Parse(s.File, s.Source)).ToList(); - var parse = new CSharpParseOptions(LanguageVersion.Latest); - var trees = files.Select(f => CSharpSyntaxTree.ParseText(f.Text, parse, path: f.File)).ToList(); - var compilation = CSharpCompilation.Create("ProbeBuild" + Guid.NewGuid().ToString("N"), trees, Refs.Value, - new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary, nullableContextOptions: NullableContextOptions.Enable)); - var analyzers = AnalyzersFor(id); - if (analyzers.Length == 0) throw new InvalidOperationException($"no CMTK analyzer declares {id}"); - var failures = new List(); - var options = new CompilationWithAnalyzersOptions(new AnalyzerOptions([]), (ex, analyzer, diagnostic) => { lock (failures) failures.Add(diagnostic); }, - concurrentAnalysis: false, logAnalyzerExecutionTime: false); - var with = compilation.WithAnalyzers(analyzers, options); - var analyzerDiagnostics = await with.GetAnalyzerDiagnosticsAsync(CancellationToken.None); - var errors = compilation.GetDiagnostics().Where(d => d.Severity == DiagnosticSeverity.Error).ToList(); - failures.AddRange(analyzerDiagnostics.Where(d => d.Id == "AD0001")); - return new Analysis(errors, [.. analyzerDiagnostics.Where(d => d.Id == id)], failures, files); - } - - /// - /// Compares the reports with the marks: every mark in reported exactly once at its span, nothing - /// reported anywhere else. Null when they agree, else what differs. - /// - public static string? Agree(Analysis a, IReadOnlySet expected, Func? message = null) => - Agree(a, expected, _ => true, outside: true, message); - - /// - /// The same over the marks selects, one check's share of a compilation: each of them in - /// reported exactly once at its span, the others silent, and with , nothing - /// reported outside every mark. A report at a mark the check does not judge is another check's. - /// - public static string? Agree(Analysis a, IReadOnlySet expected, Func judged, bool outside, Func? message = null) - { - var marks = a.Marks.ToList(); - foreach (var label in expected) if (!marks.Any(m => m.Label == label)) throw new InvalidOperationException($"no mark {label}"); - var problems = new List(); - foreach (var m in marks.Where(judged)) - { - var at = At(a, m); - if (expected.Contains(m.Label)) - { - if (at.Count != 1) problems.Add($"{m.Label} '{m.Text}': reported {at.Count} times, expected once"); - else if (message?.Invoke(at[0], m) is { } bad) problems.Add($"{m.Label} '{m.Text}': {bad}"); - } - else if (at.Count != 0) problems.Add($"{m.Label} '{m.Text}': reported, expected silent ({at[0].GetMessage()})"); - } - if (outside) problems.AddRange(Outside(a).Select(d => $"reported outside every mark at {Analysis.Where(d)}: {d.GetMessage()}")); - return problems.Count == 0 ? null : string.Join("; ", problems); - } - - /// The reports exactly at a mark's span. - public static List At(Analysis a, Mark m) => - [.. a.Reported.Where(d => d.Location.SourceTree?.FilePath == m.File && d.Location.SourceSpan == m.Span)]; - - /// The reports whose span holds a mark's: at it, or at the syntax around it. - public static List Around(Analysis a, Mark m) => - [.. a.Reported.Where(d => d.Location.SourceTree?.FilePath == m.File && d.Location.SourceSpan.Contains(m.Span))]; - - /// The reports whose span holds no mark. - public static List Outside(Analysis a) => - [.. a.Reported.Where(d => !a.Marks.Any(m => d.Location.SourceTree?.FilePath == m.File && d.Location.SourceSpan.Contains(m.Span)))]; - - /// - /// Whether a value is reported, wherever around it: each mark names that is in - /// has exactly one report at or around it, the others none. Where the report sits is 's question, so a - /// report moved to the syntax around its value still counts here. Null when they agree, else what differs. - /// - public static string? Reported(Analysis a, IEnumerable judged, IReadOnlySet expected, Func? message = null) - { - var problems = new List(); - foreach (var label in judged) - { - var m = a.Marks.SingleOrDefault(x => x.Label == label) ?? throw new InvalidOperationException($"no mark {label}"); - var around = Around(a, m); - if (expected.Contains(label)) - { - if (around.Count != 1) problems.Add($"{m.Label} '{m.Text}': reported {around.Count} times, expected once"); - else if (message?.Invoke(around[0], m) is { } bad) problems.Add($"{m.Label} '{m.Text}': {bad}"); - } - else if (around.Count != 0) problems.Add($"{m.Label} '{m.Text}': reported, expected silent ({around[0].GetMessage()})"); - } - return problems.Count == 0 ? null : string.Join("; ", problems); - } - - /// - /// Where the reports sit: every report at or around a mark in is at the mark's own span, the value as - /// written, and none is outside every mark. Null when so, else what differs. - /// - public static string? WhereWritten(Analysis a, IReadOnlySet expected) - { - var problems = new List(); - foreach (var m in a.Marks.Where(m => expected.Contains(m.Label))) - foreach (var d in Around(a, m).Where(d => d.Location.SourceSpan != m.Span)) - problems.Add($"{m.Label} '{m.Text}': reported at {Analysis.Where(d)}, not at the value"); - problems.AddRange(Outside(a).Select(d => $"reported outside every mark at {Analysis.Where(d)}: {d.GetMessage()}")); - return problems.Count == 0 ? null : string.Join("; ", problems); - } -} diff --git a/tools/probe/corpus/harness/BuildSource.cs b/tools/probe/corpus/harness/BuildSource.cs deleted file mode 100644 index bc37edfc..00000000 --- a/tools/probe/corpus/harness/BuildSource.cs +++ /dev/null @@ -1,58 +0,0 @@ -// Source text for the build family's probes (C17, C18, C19): a row's shape.* axes written as C#, for a compilation the probe -// makes itself (an analyzer run, or the consumer build of C18). Each piece names its types after a suffix, so the pieces of -// several rows can sit in one compilation. -namespace Probes.Build; - -public static class Src -{ - public const string Usings = - """ - #nullable enable - using System; - using System.Collections.Generic; - using System.Collections.Immutable; - using System.Threading.Tasks; - using CodoMetis.TypeKit; - using CodoMetis.TypeKit.ClosedSets; - using CodoMetis.TypeKit.ValueObjects; - - """; - - /// shape.site: declared at the top level, nested in a class or a struct, two levels down, or in a generic type. - public static string AtSite(string site, string id, string inner) => site switch - { - "top-level" => inner, - "nested-in-class" => $"public static partial class O{id}\n{{\n{inner}\n}}", - "nested-in-struct" => $"public partial struct O{id}\n{{\n{inner}\n}}", - "nested-two-levels" => $"public static partial class O{id}\n{{\npublic sealed partial class P{id}\n{{\n{inner}\n}}\n}}", - "in-generic-type" => $"public static partial class O{id}\n{{\n{inner}\n}}", - _ => throw new ArgumentException("shape.site " + site), - }; - - /// shape.wrapped as a type name; enum, flags-enum and own-record-class are declared by . - public static string Wrapped(string wrapped, string id) => wrapped switch - { - "string" or "int" or "long" or "byte" or "ulong" or "decimal" or "double" or "bool" or "char" => wrapped, - "Guid" or "DateTime" or "DateTimeOffset" or "DateOnly" or "TimeOnly" or "TimeSpan" or "Uri" => "System." + wrapped, - "enum" => $"WEnum{id}", "flags-enum" => $"WFlags{id}", "NodaTime.Instant" => "global::NodaTime.Instant", "own-record-class" => $"WRec{id}", - _ => throw new ArgumentException("shape.wrapped " + wrapped), - }; - - public static string WrappedDeclarations(string id) => - $"public enum WEnum{id} {{ A, B }}\n[Flags] public enum WFlags{id} {{ None = 0, P = 1, Q = 2 }}\npublic sealed record WRec{id} {{ public int N {{ get; init; }} }}\n"; - - public static bool IsStructKind(string kind) => kind is "record-struct" or "validated-struct"; - - /// The type head of shape.containing, and the type as written from inside it (generic-type is closed over int outside). - public static string ContainerHead(string containing, string name, string primary = "") => containing switch - { - "class" => $"public sealed partial class {name}{primary}", - "record-class" => $"public sealed partial record {name}{primary}", - "struct" => $"public partial struct {name}{primary}", - "record-struct" => $"public partial record struct {name}{primary}", - "generic-type" => $"public sealed partial class {name}{primary}", - _ => throw new ArgumentException("shape.containing " + containing), - }; - - public static string ContainerSelf(string containing, string name) => containing == "generic-type" ? $"{name}" : name; -} diff --git a/tools/probe/corpus/harness/C09Shapes.g.cs b/tools/probe/corpus/harness/C09Shapes.g.cs deleted file mode 100644 index 2a365946..00000000 --- a/tools/probe/corpus/harness/C09Shapes.g.cs +++ /dev/null @@ -1,1031 +0,0 @@ -// by tools/probe/generators/decls.cs from rows/C09BaseRecordEqualityProbe.json.gz: one value object per row. Do not edit; regenerate with the rows. -#nullable enable -// CMTK1023: the bases' own equality is the point (C09) or beside it (C11); CS8851: a base's Equals without GetHashCode, on purpose. -#pragma warning disable CMTK1023, CS8851, CS1591 -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -namespace Probes.Build.C09; - -// row 0 -public static partial class O0 -{ -public sealed partial class P0 -{ -public abstract record B0_1 : System.IEquatable { bool System.IEquatable.Equals(B0_1? other) => ReferenceEquals(this, other); } -public sealed partial record V0 : B0_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PRec value) => new V0(value); -} -} -} - -// row 1 -public static partial class O1 -{ -public sealed partial class P1 -{ -public abstract record B1_3 { public virtual bool Equals(B1_3? other) => ReferenceEquals(this, other); } -public abstract record B1_2 : B1_3 { } -public abstract record B1_1 : B1_2 { } -public sealed partial record V1 : B1_1, IValue; -} -} - -// row 2 -public partial struct O2 -{ -public abstract record B2_1 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public sealed partial record V2 : B2_1, IValidatedValue -{ - public static Result Create(long value) => new V2(value); -} -} - -// row 3 -public abstract record B3_1 { } -public sealed partial record V3 : B3_1, IValue; - -// row 4 -public interface IMark4 { int Mark(); } -public abstract record B4_1 : IMark4 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); int IMark4.Mark() => 1; } -public sealed partial record V4 : B4_1, IValidatedValue -{ - public static Result Create(string value) => new V4(value); -} - -// row 5 -public static partial class O5 -{ -public interface IMark5 { int Mark(); } -public abstract record B5_1 : System.IEquatable, IMark5 { bool System.IEquatable.Equals(B5_1? other) => ReferenceEquals(this, other); int IMark5.Mark() => 1; } -public sealed partial record V5 : B5_1, IValue; -} - -// row 6 -public static partial class O6 -{ -public abstract record B6_2 { } -public abstract record B6_1 : B6_2 { } -public sealed partial record V6 : B6_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PFlags value) => new V6(value); -} -} - -// row 7 -public partial struct O7 -{ -public abstract record B7_2 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B7_1 : B7_2 { } -public sealed partial record V7 : B7_1, IValue; -} - -// row 8 -public partial struct O8 -{ -public abstract record B8_1 : System.IEquatable { bool System.IEquatable.Equals(B8_1? other) => ReferenceEquals(this, other); } -public sealed partial record V8 : B8_1, IValue; -} - -// row 9 -public static partial class O9 -{ -public sealed partial class P9 -{ -public interface IMark9 { int Mark(); } -public abstract record B9_1 : IMark9 { int IMark9.Mark() => 1; } -public sealed partial record V9 : B9_1, IValue; -} -} - -// row 10 -public abstract record B10_2 { public virtual bool Equals(B10_2? other) => ReferenceEquals(this, other); } -public abstract record B10_1 : B10_2 { } -public sealed partial record V10 : B10_1, IValidatedValue -{ - public static Result Create(global::System.DateOnly value) => new V10(value); -} - -// row 11 -public static partial class O11 -{ -public abstract record B11_1 { public virtual bool Equals(B11_1? other) => ReferenceEquals(this, other); } -public sealed partial record V11 : B11_1, IValidatedValue -{ - public static Result Create(global::System.Guid value) => new V11(value); -} -} - -// row 12 -public partial struct O12 -{ -public abstract record B12_2 : System.IEquatable { bool System.IEquatable.Equals(B12_2? other) => ReferenceEquals(this, other); } -public abstract record B12_1 : B12_2 { } -public sealed partial record V12 : B12_1, IValidatedValue -{ - public static Result Create(ulong value) => new V12(value); -} -} - -// row 13 -public partial struct O13 -{ -public abstract record B13_3 { public virtual bool Equals(B13_3? other) => ReferenceEquals(this, other); } -public abstract record B13_2 : B13_3 { } -public abstract record B13_1 : B13_2 { } -public sealed partial record V13 : B13_1, IValidatedValue -{ - public static Result Create(decimal value) => new V13(value); -} -} - -// row 14 -public static partial class O14 -{ -public abstract record B14_3 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B14_2 : B14_3 { } -public abstract record B14_1 : B14_2 { } -public sealed partial record V14 : B14_1, IValue; -} - -// row 15 -public partial struct O15 -{ -public interface IMark15 { int Mark(); } -public abstract record B15_1 : IMark15 { public virtual bool Equals(B15_1? other) => ReferenceEquals(this, other); int IMark15.Mark() => 1; } -public sealed partial record V15 : B15_1, IValidatedValue -{ - public static Result Create(global::NodaTime.Instant value) => new V15(value); -} -} - -// row 16 -public abstract record B16_3 : System.IEquatable { bool System.IEquatable.Equals(B16_3? other) => ReferenceEquals(this, other); } -public abstract record B16_2 : B16_3 { } -public abstract record B16_1 : B16_2 { } -public sealed partial record V16 : B16_1, IValue; - -// row 17 -public static partial class O17 -{ -public abstract record B17_1 : System.IEquatable { public virtual bool Equals(B17_1? other) => ReferenceEquals(this, other); bool System.IEquatable.Equals(B17_1? other) => ReferenceEquals(this, other); } -public sealed partial record V17 : B17_1, IValue; -} - -// row 18 -public static partial class O18 -{ -public sealed partial class P18 -{ -public abstract record B18_1 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public sealed partial record V18 : B18_1, IValue; -} -} - -// row 19 -public abstract record B19_1 : System.IEquatable { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); bool System.IEquatable.Equals(B19_1? other) => ReferenceEquals(this, other); } -public sealed partial record V19 : B19_1, IValidatedValue -{ - public static Result Create(char value) => new V19(value); -} - -// row 20 -public abstract record B20_3 { } -public abstract record B20_2 : B20_3 { } -public abstract record B20_1 : B20_2 { } -public sealed partial record V20 : B20_1, IValidatedValue -{ - public static Result Create(global::System.DateTime value) => new V20(value); -} - -// row 21 -public static partial class O21 -{ -public abstract record B21_2 : System.IEquatable { bool System.IEquatable.Equals(B21_2? other) => ReferenceEquals(this, other); } -public abstract record B21_1 : B21_2 { } -public sealed partial record V21 : B21_1, IValue; -} - -// row 22 -public static partial class O22 -{ -public sealed partial class P22 -{ -public abstract record B22_2 { } -public abstract record B22_1 : B22_2 { } -public sealed partial record V22 : B22_1, IValue; -} -} - -// row 23 -public abstract record B23_1 : System.IEquatable { bool System.IEquatable.Equals(B23_1? other) => ReferenceEquals(this, other); } -public sealed partial record V23 : B23_1, IValidatedValue -{ - public static Result Create(global::System.TimeOnly value) => new V23(value); -} - -// row 24 -public static partial class O24 -{ -public sealed partial class P24 -{ -public abstract record B24_1 { } -public sealed partial record V24 : B24_1, IValidatedValue -{ - public static Result Create(global::System.Uri value) => new V24(value); -} -} -} - -// row 25 -public static partial class O25 -{ -public abstract record B25_1 { } -public sealed partial record V25 : B25_1, IValidatedValue -{ - public static Result Create(bool value) => new V25(value); -} -} - -// row 26 -public static partial class O26 -{ -public sealed partial class P26 -{ -public abstract record B26_3 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B26_2 : B26_3 { } -public abstract record B26_1 : B26_2 { } -public sealed partial record V26 : B26_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PEnum value) => new V26(value); -} -} -} - -// row 27 -public abstract record B27_2 { } -public abstract record B27_1 : B27_2 { } -public sealed partial record V27 : B27_1, IValue; - -// row 28 -public partial struct O28 -{ -public abstract record B28_1 { } -public sealed partial record V28 : B28_1, IValue; -} - -// row 29 -public partial struct O29 -{ -public interface IMark29 { int Mark(); } -public abstract record B29_1 : System.IEquatable, IMark29 { bool System.IEquatable.Equals(B29_1? other) => ReferenceEquals(this, other); int IMark29.Mark() => 1; } -public sealed partial record V29 : B29_1, IValue; -} - -// row 30 -public abstract record B30_2 : System.IEquatable { bool System.IEquatable.Equals(B30_2? other) => ReferenceEquals(this, other); } -public abstract record B30_1 : B30_2 { } -public sealed partial record V30 : B30_1, IValue; - -// row 31 -public static partial class O31 -{ -public sealed partial class P31 -{ -public abstract record B31_3 { } -public abstract record B31_2 : B31_3 { } -public abstract record B31_1 : B31_2 { } -public sealed partial record V31 : B31_1, IValue; -} -} - -// row 32 -public partial struct O32 -{ -public abstract record B32_1 : System.IEquatable { bool System.IEquatable.Equals(B32_1? other) => ReferenceEquals(this, other); } -public sealed partial record V32 : B32_1, IValidatedValue -{ - public static Result Create(double value) => new V32(value); -} -} - -// row 33 -public static partial class O33 -{ -public sealed partial class P33 -{ -public abstract record B33_1 : System.IEquatable { public virtual bool Equals(B33_1? other) => ReferenceEquals(this, other); bool System.IEquatable.Equals(B33_1? other) => ReferenceEquals(this, other); } -public sealed partial record V33 : B33_1, IValue; -} -} - -// row 34 -public partial struct O34 -{ -public abstract record B34_1 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public sealed partial record V34 : B34_1, IValidatedValue -{ - public static Result Create(global::System.DateTimeOffset value) => new V34(value); -} -} - -// row 35 -public abstract record B35_3 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B35_2 : B35_3 { } -public abstract record B35_1 : B35_2 { } -public sealed partial record V35 : B35_1, IValue; - -// row 36 -public static partial class O36 -{ -public abstract record B36_3 { } -public abstract record B36_2 : B36_3 { } -public abstract record B36_1 : B36_2 { } -public sealed partial record V36 : B36_1, IValue; -} - -// row 37 -public static partial class O37 -{ -public abstract record B37_1 : System.IEquatable { bool System.IEquatable.Equals(B37_1? other) => ReferenceEquals(this, other); } -public sealed partial record V37 : B37_1, IValue; -} - -// row 38 -public partial struct O38 -{ -public abstract record B38_2 : System.IEquatable { bool System.IEquatable.Equals(B38_2? other) => ReferenceEquals(this, other); } -public abstract record B38_1 : B38_2 { } -public sealed partial record V38 : B38_1, IValue; -} - -// row 39 -public abstract record B39_2 { public virtual bool Equals(B39_2? other) => ReferenceEquals(this, other); } -public abstract record B39_1 : B39_2 { } -public sealed partial record V39 : B39_1, IValidatedValue -{ - public static Result Create(byte value) => new V39(value); -} - -// row 40 -public partial struct O40 -{ -public abstract record B40_3 { public virtual bool Equals(B40_3? other) => ReferenceEquals(this, other); } -public abstract record B40_2 : B40_3 { } -public abstract record B40_1 : B40_2 { } -public sealed partial record V40 : B40_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PFlags value) => new V40(value); -} -} - -// row 41 -public abstract record B41_3 { } -public abstract record B41_2 : B41_3 { } -public abstract record B41_1 : B41_2 { } -public sealed partial record V41 : B41_1, IValidatedValue -{ - public static Result Create(global::System.TimeSpan value) => new V41(value); -} - -// row 42 -public partial struct O42 -{ -public interface IMark42 { int Mark(); } -public abstract record B42_1 : IMark42 { public virtual bool Equals(B42_1? other) => ReferenceEquals(this, other); int IMark42.Mark() => 1; } -public sealed partial record V42 : B42_1, IValue; -} - -// row 43 -public partial struct O43 -{ -public abstract record B43_1 { public virtual bool Equals(B43_1? other) => ReferenceEquals(this, other); } -public sealed partial record V43 : B43_1, IValue; -} - -// row 44 -public partial struct O44 -{ -public abstract record B44_1 : System.IEquatable { public virtual bool Equals(B44_1? other) => ReferenceEquals(this, other); bool System.IEquatable.Equals(B44_1? other) => ReferenceEquals(this, other); } -public sealed partial record V44 : B44_1, IValue; -} - -// row 45 -public static partial class O45 -{ -public abstract record B45_1 { } -public sealed partial record V45 : B45_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PEnum value) => new V45(value); -} -} - -// row 46 -public static partial class O46 -{ -public interface IMark46 { int Mark(); } -public abstract record B46_1 : System.IEquatable, IMark46 { bool System.IEquatable.Equals(B46_1? other) => ReferenceEquals(this, other); int IMark46.Mark() => 1; } -public sealed partial record V46 : B46_1, IValidatedValue -{ - public static Result Create(long value) => new V46(value); -} -} - -// row 47 -public abstract record B47_3 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B47_2 : B47_3 { } -public abstract record B47_1 : B47_2 { } -public sealed partial record V47 : B47_1, IValue; - -// row 48 -public partial struct O48 -{ -public abstract record B48_3 { } -public abstract record B48_2 : B48_3 { } -public abstract record B48_1 : B48_2 { } -public sealed partial record V48 : B48_1, IValue; -} - -// row 49 -public interface IMark49 { int Mark(); } -public abstract record B49_1 : IMark49 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); int IMark49.Mark() => 1; } -public sealed partial record V49 : B49_1, IValue; - -// row 50 -public static partial class O50 -{ -public sealed partial class P50 -{ -public abstract record B50_3 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B50_2 : B50_3 { } -public abstract record B50_1 : B50_2 { } -public sealed partial record V50 : B50_1, IValue; -} -} - -// row 51 -public abstract record B51_1 : System.IEquatable { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); bool System.IEquatable.Equals(B51_1? other) => ReferenceEquals(this, other); } -public sealed partial record V51 : B51_1, IValidatedValue -{ - public static Result Create(bool value) => new V51(value); -} - -// row 52 -public abstract record B52_2 { } -public abstract record B52_1 : B52_2 { } -public sealed partial record V52 : B52_1, IValue; - -// row 53 -public partial struct O53 -{ -public interface IMark53 { int Mark(); } -public abstract record B53_1 : IMark53 { int IMark53.Mark() => 1; } -public sealed partial record V53 : B53_1, IValue; -} - -// row 54 -public partial struct O54 -{ -public abstract record B54_1 : System.IEquatable { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); bool System.IEquatable.Equals(B54_1? other) => ReferenceEquals(this, other); } -public sealed partial record V54 : B54_1, IValidatedValue -{ - public static Result Create(global::System.DateTime value) => new V54(value); -} -} - -// row 55 -public partial struct O55 -{ -public abstract record B55_3 : System.IEquatable { bool System.IEquatable.Equals(B55_3? other) => ReferenceEquals(this, other); } -public abstract record B55_2 : B55_3 { } -public abstract record B55_1 : B55_2 { } -public sealed partial record V55 : B55_1, IValue; -} - -// row 56 -public interface IMark56 { int Mark(); } -public abstract record B56_1 : IMark56 { int IMark56.Mark() => 1; } -public sealed partial record V56 : B56_1, IValidatedValue -{ - public static Result Create(decimal value) => new V56(value); -} - -// row 57 -public static partial class O57 -{ -public abstract record B57_1 { public virtual bool Equals(B57_1? other) => ReferenceEquals(this, other); } -public sealed partial record V57 : B57_1, IValidatedValue -{ - public static Result Create(double value) => new V57(value); -} -} - -// row 58 -public interface IMark58 { int Mark(); } -public abstract record B58_1 : IMark58 { public virtual bool Equals(B58_1? other) => ReferenceEquals(this, other); int IMark58.Mark() => 1; } -public sealed partial record V58 : B58_1, IValidatedValue -{ - public static Result Create(global::System.TimeOnly value) => new V58(value); -} - -// row 59 -public partial struct O59 -{ -public abstract record B59_1 : System.IEquatable { bool System.IEquatable.Equals(B59_1? other) => ReferenceEquals(this, other); } -public sealed partial record V59 : B59_1, IValue; -} - -// row 60 -public abstract record B60_1 : System.IEquatable { public virtual bool Equals(B60_1? other) => ReferenceEquals(this, other); bool System.IEquatable.Equals(B60_1? other) => ReferenceEquals(this, other); } -public sealed partial record V60 : B60_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PEnum value) => new V60(value); -} - -// row 61 -public abstract record B61_1 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public sealed partial record V61 : B61_1, IValue; - -// row 62 -public static partial class O62 -{ -public abstract record B62_2 { public virtual bool Equals(B62_2? other) => ReferenceEquals(this, other); } -public abstract record B62_1 : B62_2 { } -public sealed partial record V62 : B62_1, IValue; -} - -// row 63 -public static partial class O63 -{ -public sealed partial class P63 -{ -public abstract record B63_1 : System.IEquatable { public virtual bool Equals(B63_1? other) => ReferenceEquals(this, other); bool System.IEquatable.Equals(B63_1? other) => ReferenceEquals(this, other); } -public sealed partial record V63 : B63_1, IValidatedValue -{ - public static Result Create(long value) => new V63(value); -} -} -} - -// row 64 -public static partial class O64 -{ -public sealed partial class P64 -{ -public abstract record B64_1 : System.IEquatable { bool System.IEquatable.Equals(B64_1? other) => ReferenceEquals(this, other); } -public sealed partial record V64 : B64_1, IValidatedValue -{ - public static Result Create(int value) => new V64(value); -} -} -} - -// row 65 -public static partial class O65 -{ -public abstract record B65_1 : System.IEquatable { bool System.IEquatable.Equals(B65_1? other) => ReferenceEquals(this, other); } -public sealed partial record V65 : B65_1, IValue; -} - -// row 66 -public static partial class O66 -{ -public sealed partial class P66 -{ -public abstract record B66_1 : System.IEquatable { bool System.IEquatable.Equals(B66_1? other) => ReferenceEquals(this, other); } -public sealed partial record V66 : B66_1, IValidatedValue -{ - public static Result Create(global::System.TimeSpan value) => new V66(value); -} -} -} - -// row 67 -public static partial class O67 -{ -public sealed partial class P67 -{ -public abstract record B67_1 { public virtual bool Equals(B67_1? other) => ReferenceEquals(this, other); } -public sealed partial record V67 : B67_1, IValue; -} -} - -// row 68 -public static partial class O68 -{ -public abstract record B68_1 { public virtual bool Equals(B68_1? other) => ReferenceEquals(this, other); } -public sealed partial record V68 : B68_1, IValidatedValue -{ - public static Result Create(ulong value) => new V68(value); -} -} - -// row 69 -public static partial class O69 -{ -public sealed partial class P69 -{ -public abstract record B69_2 { public virtual bool Equals(B69_2? other) => ReferenceEquals(this, other); } -public abstract record B69_1 : B69_2 { } -public sealed partial record V69 : B69_1, IValue; -} -} - -// row 70 -public abstract record B70_2 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B70_1 : B70_2 { } -public sealed partial record V70 : B70_1, IValidatedValue -{ - public static Result Create(global::System.Uri value) => new V70(value); -} - -// row 71 -public interface IMark71 { int Mark(); } -public abstract record B71_1 : System.IEquatable, IMark71 { bool System.IEquatable.Equals(B71_1? other) => ReferenceEquals(this, other); int IMark71.Mark() => 1; } -public sealed partial record V71 : B71_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PFlags value) => new V71(value); -} - -// row 72 -public static partial class O72 -{ -public abstract record B72_1 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public sealed partial record V72 : B72_1, IValue; -} - -// row 73 -public static partial class O73 -{ -public sealed partial class P73 -{ -public abstract record B73_1 : System.IEquatable { bool System.IEquatable.Equals(B73_1? other) => ReferenceEquals(this, other); } -public sealed partial record V73 : B73_1, IValue; -} -} - -// row 74 -public static partial class O74 -{ -public sealed partial class P74 -{ -public abstract record B74_2 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B74_1 : B74_2 { } -public sealed partial record V74 : B74_1, IValidatedValue -{ - public static Result Create(double value) => new V74(value); -} -} -} - -// row 75 -public static partial class O75 -{ -public abstract record B75_1 : System.IEquatable { bool System.IEquatable.Equals(B75_1? other) => ReferenceEquals(this, other); } -public sealed partial record V75 : B75_1, IValue; -} - -// row 76 -public static partial class O76 -{ -public interface IMark76 { int Mark(); } -public abstract record B76_1 : System.IEquatable, IMark76 { bool System.IEquatable.Equals(B76_1? other) => ReferenceEquals(this, other); int IMark76.Mark() => 1; } -public sealed partial record V76 : B76_1, IValidatedValue -{ - public static Result Create(global::System.DateTimeOffset value) => new V76(value); -} -} - -// row 77 -public static partial class O77 -{ -public sealed partial class P77 -{ -public abstract record B77_2 : System.IEquatable { bool System.IEquatable.Equals(B77_2? other) => ReferenceEquals(this, other); } -public abstract record B77_1 : B77_2 { } -public sealed partial record V77 : B77_1, IValidatedValue -{ - public static Result Create(char value) => new V77(value); -} -} -} - -// row 78 -public static partial class O78 -{ -public sealed partial class P78 -{ -public abstract record B78_1 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public sealed partial record V78 : B78_1, IValue; -} -} - -// row 79 -public static partial class O79 -{ -public abstract record B79_2 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B79_1 : B79_2 { } -public sealed partial record V79 : B79_1, IValue; -} - -// row 80 -public static partial class O80 -{ -public sealed partial class P80 -{ -public abstract record B80_3 { public virtual bool Equals(B80_3? other) => ReferenceEquals(this, other); } -public abstract record B80_2 : B80_3 { } -public abstract record B80_1 : B80_2 { } -public sealed partial record V80 : B80_1, IValidatedValue -{ - public static Result Create(global::System.TimeOnly value) => new V80(value); -} -} -} - -// row 81 -public static partial class O81 -{ -public interface IMark81 { int Mark(); } -public abstract record B81_1 : System.IEquatable, IMark81 { bool System.IEquatable.Equals(B81_1? other) => ReferenceEquals(this, other); int IMark81.Mark() => 1; } -public sealed partial record V81 : B81_1, IValue; -} - -// row 82 -public static partial class O82 -{ -public interface IMark82 { int Mark(); } -public abstract record B82_1 : IMark82 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); int IMark82.Mark() => 1; } -public sealed partial record V82 : B82_1, IValue; -} - -// row 83 -public abstract record B83_1 : System.IEquatable { bool System.IEquatable.Equals(B83_1? other) => ReferenceEquals(this, other); } -public sealed partial record V83 : B83_1, IValue; - -// row 84 -public abstract record B84_1 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public sealed partial record V84 : B84_1, IValidatedValue -{ - public static Result Create(char value) => new V84(value); -} - -// row 85 -public static partial class O85 -{ -public interface IMark85 { int Mark(); } -public abstract record B85_1 : System.IEquatable, IMark85 { bool System.IEquatable.Equals(B85_1? other) => ReferenceEquals(this, other); int IMark85.Mark() => 1; } -public sealed partial record V85 : B85_1, IValue; -} - -// row 86 -public partial struct O86 -{ -public abstract record B86_2 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); } -public abstract record B86_1 : B86_2 { } -public sealed partial record V86 : B86_1, IValue; -} - -// row 87 -public partial struct O87 -{ -public abstract record B87_1 : System.IEquatable { bool System.IEquatable.Equals(B87_1? other) => ReferenceEquals(this, other); } -public sealed partial record V87 : B87_1, IValidatedValue -{ - public static Result Create(global::System.DateTimeOffset value) => new V87(value); -} -} - -// row 88 -public static partial class O88 -{ -public abstract record B88_2 : System.IEquatable { bool System.IEquatable.Equals(B88_2? other) => ReferenceEquals(this, other); } -public abstract record B88_1 : B88_2 { } -public sealed partial record V88 : B88_1, IValidatedValue -{ - public static Result Create(global::System.Guid value) => new V88(value); -} -} - -// row 89 -public static partial class O89 -{ -public abstract record B89_1 : System.IEquatable { bool System.IEquatable.Equals(B89_1? other) => ReferenceEquals(this, other); } -public sealed partial record V89 : B89_1, IValue; -} - -// row 90 -public abstract record B90_3 : System.IEquatable { bool System.IEquatable.Equals(B90_3? other) => ReferenceEquals(this, other); } -public abstract record B90_2 : B90_3 { } -public abstract record B90_1 : B90_2 { } -public sealed partial record V90 : B90_1, IValidatedValue -{ - public static Result Create(long value) => new V90(value); -} - -// row 91 -public static partial class O91 -{ -public sealed partial class P91 -{ -public abstract record B91_2 : System.IEquatable { bool System.IEquatable.Equals(B91_2? other) => ReferenceEquals(this, other); } -public abstract record B91_1 : B91_2 { } -public sealed partial record V91 : B91_1, IValue; -} -} - -// row 92 -public abstract record B92_3 : System.IEquatable { bool System.IEquatable.Equals(B92_3? other) => ReferenceEquals(this, other); } -public abstract record B92_2 : B92_3 { } -public abstract record B92_1 : B92_2 { } -public sealed partial record V92 : B92_1, IValidatedValue -{ - public static Result Create(bool value) => new V92(value); -} - -// row 93 -public abstract record B93_1 : System.IEquatable { bool System.IEquatable.Equals(B93_1? other) => ReferenceEquals(this, other); } -public sealed partial record V93 : B93_1, IValidatedValue -{ - public static Result Create(global::NodaTime.Instant value) => new V93(value); -} - -// row 94 -public static partial class O94 -{ -public sealed partial class P94 -{ -public interface IMark94 { int Mark(); } -public abstract record B94_1 : System.IEquatable, IMark94 { bool System.IEquatable.Equals(B94_1? other) => ReferenceEquals(this, other); int IMark94.Mark() => 1; } -public sealed partial record V94 : B94_1, IValue; -} -} - -// row 95 -public static partial class O95 -{ -public abstract record B95_1 : System.IEquatable { bool System.IEquatable.Equals(B95_1? other) => ReferenceEquals(this, other); } -public sealed partial record V95 : B95_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PFlags value) => new V95(value); -} -} - -// row 96 -public partial struct O96 -{ -public interface IMark96 { int Mark(); } -public abstract record B96_1 : IMark96 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); int IMark96.Mark() => 1; } -public sealed partial record V96 : B96_1, IValue; -} - -// row 97 -public static partial class O97 -{ -public sealed partial class P97 -{ -public abstract record B97_3 { } -public abstract record B97_2 : B97_3 { } -public abstract record B97_1 : B97_2 { } -public sealed partial record V97 : B97_1, IValue; -} -} - -// row 98 -public static partial class O98 -{ -public interface IMark98 { int Mark(); } -public abstract record B98_1 : System.IEquatable, IMark98 { bool System.IEquatable.Equals(B98_1? other) => ReferenceEquals(this, other); int IMark98.Mark() => 1; } -public sealed partial record V98 : B98_1, IValue; -} - -// row 99 -public static partial class O99 -{ -public interface IMark99 { int Mark(); } -public abstract record B99_1 : IMark99 { public override int GetHashCode() => System.Runtime.CompilerServices.RuntimeHelpers.GetHashCode(this); int IMark99.Mark() => 1; } -public sealed partial record V99 : B99_1, IValidatedValue -{ - public static Result Create(double value) => new V99(value); -} -} - -// row 100 -public static partial class O100 -{ -public abstract record B100_3 : System.IEquatable { bool System.IEquatable.Equals(B100_3? other) => ReferenceEquals(this, other); } -public abstract record B100_2 : B100_3 { } -public abstract record B100_1 : B100_2 { } -public sealed partial record V100 : B100_1, IValidatedValue -{ - public static Result Create(global::System.Uri value) => new V100(value); -} -} - -public static partial class C09Table -{ - public static readonly C09Case[] Cases = - [ - new(0, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=own-record-class", "validated-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O0.P0.V0), [typeof(O0.P0.B0_1)], x => O0.P0.V0.FromKnownGood((global::Probes.Shapes.PRec)x), []), - new(1, "base-equality=override-Equals;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateTimeOffset", "sealed-record-class", "nested-two-levels", "base-depth-3", typeof(O1.P1.V1), [typeof(O1.P1.B1_1), typeof(O1.P1.B1_2), typeof(O1.P1.B1_3)], x => O1.P1.V1.From((global::System.DateTimeOffset)x), []), - new(2, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=long", "validated-class", "nested-in-struct", "base-depth-1", typeof(O2.V2), [typeof(O2.B2_1)], x => O2.V2.FromKnownGood((long)x), []), - new(3, "base-equality=none;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=DateOnly", "sealed-record-class", "top-level", "base-depth-1", typeof(V3), [typeof(B3_1)], x => V3.From((global::System.DateOnly)x), []), - new(4, "base-equality=override-GetHashCode;collection=Distinct;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=string", "validated-class", "top-level", "explicit-interface-member", typeof(V4), [typeof(B4_1)], x => V4.FromKnownGood((string)x), []), - new(5, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=Uri", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O5.V5), [typeof(O5.B5_1)], x => O5.V5.From((global::System.Uri)x), []), - new(6, "base-equality=none;collection=HashSet;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=flags-enum", "validated-class", "nested-in-class", "base-depth-2", typeof(O6.V6), [typeof(O6.B6_1), typeof(O6.B6_2)], x => O6.V6.FromKnownGood((global::Probes.Shapes.PFlags)x), []), - new(7, "base-equality=override-GetHashCode;collection=HashSet;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=TimeSpan", "sealed-record-class", "nested-in-struct", "base-depth-2", typeof(O7.V7), [typeof(O7.B7_1), typeof(O7.B7_2)], x => O7.V7.From((global::System.TimeSpan)x), []), - new(8, "base-equality=none;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=TimeOnly", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O8.V8), [typeof(O8.B8_1)], x => O8.V8.From((global::System.TimeOnly)x), []), - new(9, "base-equality=none;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=byte", "sealed-record-class", "nested-two-levels", "explicit-interface-member", typeof(O9.P9.V9), [typeof(O9.P9.B9_1)], x => O9.P9.V9.From((byte)x), []), - new(10, "base-equality=override-Equals;collection=Dictionary-key;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=DateOnly", "validated-class", "top-level", "base-depth-2", typeof(V10), [typeof(B10_1), typeof(B10_2)], x => V10.FromKnownGood((global::System.DateOnly)x), []), - new(11, "base-equality=override-Equals;collection=Distinct;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=Guid", "validated-class", "nested-in-class", "base-depth-1", typeof(O11.V11), [typeof(O11.B11_1)], x => O11.V11.FromKnownGood((global::System.Guid)x), []), - new(12, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=ulong", "validated-class", "nested-in-struct", "base-depth-2", typeof(O12.V12), [typeof(O12.B12_1), typeof(O12.B12_2)], x => O12.V12.FromKnownGood((ulong)x), []), - new(13, "base-equality=override-Equals;collection=HashSet;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=decimal", "validated-class", "nested-in-struct", "base-depth-3", typeof(O13.V13), [typeof(O13.B13_1), typeof(O13.B13_2), typeof(O13.B13_3)], x => O13.V13.FromKnownGood((decimal)x), []), - new(14, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=closed-set;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=int", "sealed-record-class", "nested-in-class", "base-depth-3", typeof(O14.V14), [typeof(O14.B14_1), typeof(O14.B14_2), typeof(O14.B14_3)], x => O14.V14.From((int)x), [("shape.kind", "sealed-record-class")]), - new(15, "base-equality=override-Equals;collection=HashSet;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=NodaTime.Instant", "validated-class", "nested-in-struct", "explicit-interface-member", typeof(O15.V15), [typeof(O15.B15_1)], x => O15.V15.FromKnownGood((global::NodaTime.Instant)x), []), - new(16, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=double", "sealed-record-class", "top-level", "base-depth-3", typeof(V16), [typeof(B16_1), typeof(B16_2), typeof(B16_3)], x => V16.From((double)x), []), - new(17, "base-equality=override-Equals;collection=Dictionary-key;shape.inheritance=base-explicit-IEquatable;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeSpan", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O17.V17), [typeof(O17.B17_1)], x => O17.V17.From((global::System.TimeSpan)x), [("shape.kind", "sealed-record-class")]), - new(18, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=flags-enum", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O18.P18.V18), [typeof(O18.P18.B18_1)], x => O18.P18.V18.From((global::Probes.Shapes.PFlags)x), []), - new(19, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=char", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V19), [typeof(B19_1)], x => V19.FromKnownGood((char)x), []), - new(20, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=DateTime", "validated-class", "top-level", "base-depth-3", typeof(V20), [typeof(B20_1), typeof(B20_2), typeof(B20_3)], x => V20.FromKnownGood((global::System.DateTime)x), []), - new(21, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateTime", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O21.V21), [typeof(O21.B21_1), typeof(O21.B21_2)], x => O21.V21.From((global::System.DateTime)x), []), - new(22, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=NodaTime.Instant", "sealed-record-class", "nested-two-levels", "base-depth-2", typeof(O22.P22.V22), [typeof(O22.P22.B22_1), typeof(O22.P22.B22_2)], x => O22.P22.V22.From((global::NodaTime.Instant)x), []), - new(23, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=TimeOnly", "validated-class", "top-level", "base-depth-1", typeof(V23), [typeof(B23_1)], x => V23.FromKnownGood((global::System.TimeOnly)x), []), - new(24, "base-equality=none;collection=Distinct;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Uri", "validated-class", "nested-two-levels", "base-depth-1", typeof(O24.P24.V24), [typeof(O24.P24.B24_1)], x => O24.P24.V24.FromKnownGood((global::System.Uri)x), []), - new(25, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=bool", "validated-class", "nested-in-class", "base-depth-1", typeof(O25.V25), [typeof(O25.B25_1)], x => O25.V25.FromKnownGood((bool)x), []), - new(26, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=enum", "validated-class", "nested-two-levels", "base-depth-3", typeof(O26.P26.V26), [typeof(O26.P26.B26_1), typeof(O26.P26.B26_2), typeof(O26.P26.B26_3)], x => O26.P26.V26.FromKnownGood((global::Probes.Shapes.PEnum)x), []), - new(27, "base-equality=none;collection=Distinct;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=long", "sealed-record-class", "top-level", "base-depth-2", typeof(V27), [typeof(B27_1), typeof(B27_2)], x => V27.From((long)x), []), - new(28, "base-equality=none;collection=Distinct;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=own-record-class", "sealed-record-class", "nested-in-struct", "base-depth-1", typeof(O28.V28), [typeof(O28.B28_1)], x => O28.V28.From((global::Probes.Shapes.PRec)x), []), - new(29, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=bool", "sealed-record-class", "nested-in-struct", "explicit-interface-member", typeof(O29.V29), [typeof(O29.B29_1)], x => O29.V29.From((bool)x), []), - new(30, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=int", "sealed-record-class", "top-level", "base-depth-2", typeof(V30), [typeof(B30_1), typeof(B30_2)], x => V30.From((int)x), []), - new(31, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=ulong", "sealed-record-class", "nested-two-levels", "base-depth-3", typeof(O31.P31.V31), [typeof(O31.P31.B31_1), typeof(O31.P31.B31_2), typeof(O31.P31.B31_3)], x => O31.P31.V31.From((ulong)x), []), - new(32, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=double", "validated-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O32.V32), [typeof(O32.B32_1)], x => O32.V32.FromKnownGood((double)x), []), - new(33, "base-equality=override-Equals;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=string", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O33.P33.V33), [typeof(O33.P33.B33_1)], x => O33.P33.V33.From((string)x), []), - new(34, "base-equality=override-GetHashCode;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=DateTimeOffset", "validated-class", "nested-in-struct", "base-depth-1", typeof(O34.V34), [typeof(O34.B34_1)], x => O34.V34.FromKnownGood((global::System.DateTimeOffset)x), []), - new(35, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=Guid", "sealed-record-class", "top-level", "base-depth-3", typeof(V35), [typeof(B35_1), typeof(B35_2), typeof(B35_3)], x => V35.From((global::System.Guid)x), []), - new(36, "base-equality=none;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=char", "sealed-record-class", "nested-in-class", "base-depth-3", typeof(O36.V36), [typeof(O36.B36_1), typeof(O36.B36_2), typeof(O36.B36_3)], x => O36.V36.From((char)x), []), - new(37, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=decimal", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O37.V37), [typeof(O37.B37_1)], x => O37.V37.From((decimal)x), []), - new(38, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=enum", "sealed-record-class", "nested-in-struct", "base-depth-2", typeof(O38.V38), [typeof(O38.B38_1), typeof(O38.B38_2)], x => O38.V38.From((global::Probes.Shapes.PEnum)x), []), - new(39, "base-equality=override-Equals;collection=HashSet;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=byte", "validated-class", "top-level", "base-depth-2", typeof(V39), [typeof(B39_1), typeof(B39_2)], x => V39.FromKnownGood((byte)x), []), - new(40, "base-equality=override-Equals;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=flags-enum", "validated-class", "nested-in-struct", "base-depth-3", typeof(O40.V40), [typeof(O40.B40_1), typeof(O40.B40_2), typeof(O40.B40_3)], x => O40.V40.FromKnownGood((global::Probes.Shapes.PFlags)x), []), - new(41, "base-equality=none;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=TimeSpan", "validated-class", "top-level", "base-depth-3", typeof(V41), [typeof(B41_1), typeof(B41_2), typeof(B41_3)], x => V41.FromKnownGood((global::System.TimeSpan)x), []), - new(42, "base-equality=override-Equals;collection=HashSet;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=char", "sealed-record-class", "nested-in-struct", "explicit-interface-member", typeof(O42.V42), [typeof(O42.B42_1)], x => O42.V42.From((char)x), []), - new(43, "base-equality=override-Equals;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=int", "sealed-record-class", "nested-in-struct", "base-depth-1", typeof(O43.V43), [typeof(O43.B43_1)], x => O43.V43.From((int)x), []), - new(44, "base-equality=override-Equals;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Uri", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O44.V44), [typeof(O44.B44_1)], x => O44.V44.From((global::System.Uri)x), []), - new(45, "base-equality=none;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=enum", "validated-class", "nested-in-class", "base-depth-1", typeof(O45.V45), [typeof(O45.B45_1)], x => O45.V45.FromKnownGood((global::Probes.Shapes.PEnum)x), []), - new(46, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=long", "validated-class", "nested-in-class", "explicit-interface-member", typeof(O46.V46), [typeof(O46.B46_1)], x => O46.V46.FromKnownGood((long)x), []), - new(47, "base-equality=override-GetHashCode;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=NodaTime.Instant", "sealed-record-class", "top-level", "base-depth-3", typeof(V47), [typeof(B47_1), typeof(B47_2), typeof(B47_3)], x => V47.From((global::NodaTime.Instant)x), []), - new(48, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=string", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O48.V48), [typeof(O48.B48_1), typeof(O48.B48_2), typeof(O48.B48_3)], x => O48.V48.From((string)x), []), - new(49, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=own-record-class", "sealed-record-class", "top-level", "explicit-interface-member", typeof(V49), [typeof(B49_1)], x => V49.From((global::Probes.Shapes.PRec)x), [("shape.kind", "sealed-record-class")]), - new(50, "base-equality=override-GetHashCode;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateOnly", "sealed-record-class", "nested-two-levels", "base-depth-3", typeof(O50.P50.V50), [typeof(O50.P50.B50_1), typeof(O50.P50.B50_2), typeof(O50.P50.B50_3)], x => O50.P50.V50.From((global::System.DateOnly)x), []), - new(51, "base-equality=override-GetHashCode;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=bool", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V51), [typeof(B51_1)], x => V51.FromKnownGood((bool)x), []), - new(52, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=DateTimeOffset", "sealed-record-class", "top-level", "base-depth-2", typeof(V52), [typeof(B52_1), typeof(B52_2)], x => V52.From((global::System.DateTimeOffset)x), []), - new(53, "base-equality=none;collection=HashSet;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Guid", "sealed-record-class", "nested-in-struct", "explicit-interface-member", typeof(O53.V53), [typeof(O53.B53_1)], x => O53.V53.From((global::System.Guid)x), []), - new(54, "base-equality=override-GetHashCode;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=DateTime", "validated-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O54.V54), [typeof(O54.B54_1)], x => O54.V54.FromKnownGood((global::System.DateTime)x), []), - new(55, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=byte", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O55.V55), [typeof(O55.B55_1), typeof(O55.B55_2), typeof(O55.B55_3)], x => O55.V55.From((byte)x), []), - new(56, "base-equality=none;collection=Distinct;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=decimal", "validated-class", "top-level", "explicit-interface-member", typeof(V56), [typeof(B56_1)], x => V56.FromKnownGood((decimal)x), [("shape.kind", "validated-class")]), - new(57, "base-equality=override-Equals;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=double", "validated-class", "nested-in-class", "base-depth-1", typeof(O57.V57), [typeof(O57.B57_1)], x => O57.V57.FromKnownGood((double)x), []), - new(58, "base-equality=override-Equals;collection=HashSet;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=TimeOnly", "validated-class", "top-level", "explicit-interface-member", typeof(V58), [typeof(B58_1)], x => V58.FromKnownGood((global::System.TimeOnly)x), [("shape.kind", "validated-class")]), - new(59, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=closed-set;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateOnly", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O59.V59), [typeof(O59.B59_1)], x => O59.V59.From((global::System.DateOnly)x), [("shape.kind", "sealed-record-class")]), - new(60, "base-equality=override-Equals;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=enum", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V60), [typeof(B60_1)], x => V60.FromKnownGood((global::Probes.Shapes.PEnum)x), []), - new(61, "base-equality=override-GetHashCode;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=ulong", "sealed-record-class", "top-level", "base-depth-1", typeof(V61), [typeof(B61_1)], x => V61.From((ulong)x), []), - new(62, "base-equality=override-Equals;collection=Distinct;shape.inheritance=base-depth-2;shape.kind=closed-set;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=own-record-class", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O62.V62), [typeof(O62.B62_1), typeof(O62.B62_2)], x => O62.V62.From((global::Probes.Shapes.PRec)x), [("shape.kind", "sealed-record-class")]), - new(63, "base-equality=override-Equals;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=long", "validated-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O63.P63.V63), [typeof(O63.P63.B63_1)], x => O63.P63.V63.FromKnownGood((long)x), []), - new(64, "base-equality=none;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=int", "validated-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O64.P64.V64), [typeof(O64.P64.B64_1)], x => O64.P64.V64.FromKnownGood((int)x), []), - new(65, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=string", "sealed-record-class", "nested-in-class", "base-depth-1", typeof(O65.V65), [typeof(O65.B65_1)], x => O65.V65.From((string)x), []), - new(66, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=TimeSpan", "validated-class", "nested-two-levels", "base-depth-1", typeof(O66.P66.V66), [typeof(O66.P66.B66_1)], x => O66.P66.V66.FromKnownGood((global::System.TimeSpan)x), []), - new(67, "base-equality=override-Equals;collection=Distinct;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateTime", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O67.P67.V67), [typeof(O67.P67.B67_1)], x => O67.P67.V67.From((global::System.DateTime)x), []), - new(68, "base-equality=override-Equals;collection=Dictionary-key;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=ulong", "validated-class", "nested-in-class", "base-depth-1", typeof(O68.V68), [typeof(O68.B68_1)], x => O68.V68.FromKnownGood((ulong)x), [("shape.kind", "validated-class")]), - new(69, "base-equality=override-Equals;collection=Dictionary-key;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=bool", "sealed-record-class", "nested-two-levels", "base-depth-2", typeof(O69.P69.V69), [typeof(O69.P69.B69_1), typeof(O69.P69.B69_2)], x => O69.P69.V69.From((bool)x), []), - new(70, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=Uri", "validated-class", "top-level", "base-depth-2", typeof(V70), [typeof(B70_1), typeof(B70_2)], x => V70.FromKnownGood((global::System.Uri)x), []), - new(71, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=flags-enum", "validated-class", "top-level", "explicit-interface-member", typeof(V71), [typeof(B71_1)], x => V71.FromKnownGood((global::Probes.Shapes.PFlags)x), []), - new(72, "base-equality=override-GetHashCode;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=byte", "sealed-record-class", "nested-in-class", "base-depth-1", typeof(O72.V72), [typeof(O72.B72_1)], x => O72.V72.From((byte)x), []), - new(73, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Guid", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O73.P73.V73), [typeof(O73.P73.B73_1)], x => O73.P73.V73.From((global::System.Guid)x), []), - new(74, "base-equality=override-GetHashCode;collection=HashSet;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=double", "validated-class", "nested-two-levels", "base-depth-2", typeof(O74.P74.V74), [typeof(O74.P74.B74_1), typeof(O74.P74.B74_2)], x => O74.P74.V74.FromKnownGood((double)x), []), - new(75, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=NodaTime.Instant", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O75.V75), [typeof(O75.B75_1)], x => O75.V75.From((global::NodaTime.Instant)x), []), - new(76, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateTimeOffset", "validated-class", "nested-in-class", "explicit-interface-member", typeof(O76.V76), [typeof(O76.B76_1)], x => O76.V76.FromKnownGood((global::System.DateTimeOffset)x), []), - new(77, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=char", "validated-class", "nested-two-levels", "base-depth-2", typeof(O77.P77.V77), [typeof(O77.P77.B77_1), typeof(O77.P77.B77_2)], x => O77.P77.V77.FromKnownGood((char)x), []), - new(78, "base-equality=override-GetHashCode;collection=HashSet;shape.inheritance=base-depth-1;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=decimal", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O78.P78.V78), [typeof(O78.P78.B78_1)], x => O78.P78.V78.From((decimal)x), [("shape.kind", "sealed-record-class")]), - new(79, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeOnly", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O79.V79), [typeof(O79.B79_1), typeof(O79.B79_2)], x => O79.V79.From((global::System.TimeOnly)x), []), - new(80, "base-equality=override-Equals;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=TimeOnly", "validated-class", "nested-two-levels", "base-depth-3", typeof(O80.P80.V80), [typeof(O80.P80.B80_1), typeof(O80.P80.B80_2), typeof(O80.P80.B80_3)], x => O80.P80.V80.FromKnownGood((global::System.TimeOnly)x), []), - new(81, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=ulong", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O81.V81), [typeof(O81.B81_1)], x => O81.V81.From((ulong)x), []), - new(82, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateOnly", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O82.V82), [typeof(O82.B82_1)], x => O82.V82.From((global::System.DateOnly)x), []), - new(83, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=ulong", "sealed-record-class", "top-level", "base-explicit-IEquatable", typeof(V83), [typeof(B83_1)], x => V83.From((ulong)x), []), - new(84, "base-equality=override-GetHashCode;collection=Distinct;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=char", "validated-class", "top-level", "base-depth-1", typeof(V84), [typeof(B84_1)], x => V84.FromKnownGood((char)x), []), - new(85, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeSpan", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O85.V85), [typeof(O85.B85_1)], x => O85.V85.From((global::System.TimeSpan)x), []), - new(86, "base-equality=override-GetHashCode;collection=Distinct;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=decimal", "sealed-record-class", "nested-in-struct", "base-depth-2", typeof(O86.V86), [typeof(O86.B86_1), typeof(O86.B86_2)], x => O86.V86.From((decimal)x), []), - new(87, "base-equality=none;collection=HashSet;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTimeOffset", "validated-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O87.V87), [typeof(O87.B87_1)], x => O87.V87.FromKnownGood((global::System.DateTimeOffset)x), []), - new(88, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=Guid", "validated-class", "nested-in-class", "base-depth-2", typeof(O88.V88), [typeof(O88.B88_1), typeof(O88.B88_2)], x => O88.V88.FromKnownGood((global::System.Guid)x), []), - new(89, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=in-generic-type;shape.wrapped=byte", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O89.V89), [typeof(O89.B89_1)], x => O89.V89.From((byte)x), [("shape.site", "nested-in-class")]), - new(90, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=long", "validated-class", "top-level", "base-depth-3", typeof(V90), [typeof(B90_1), typeof(B90_2), typeof(B90_3)], x => V90.FromKnownGood((long)x), []), - new(91, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=string", "sealed-record-class", "nested-two-levels", "base-depth-2", typeof(O91.P91.V91), [typeof(O91.P91.B91_1), typeof(O91.P91.B91_2)], x => O91.P91.V91.From((string)x), []), - new(92, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=base-depth-3;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=bool", "validated-class", "top-level", "base-depth-3", typeof(V92), [typeof(B92_1), typeof(B92_2), typeof(B92_3)], x => V92.FromKnownGood((bool)x), [("shape.kind", "validated-class")]), - new(93, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=NodaTime.Instant", "validated-class", "top-level", "base-depth-1", typeof(V93), [typeof(B93_1)], x => V93.FromKnownGood((global::NodaTime.Instant)x), []), - new(94, "base-equality=explicit-IEquatable;collection=Distinct;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=int", "sealed-record-class", "nested-two-levels", "explicit-interface-member", typeof(O94.P94.V94), [typeof(O94.P94.B94_1)], x => O94.P94.V94.From((int)x), []), - new(95, "base-equality=none;collection=Distinct;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=flags-enum", "validated-class", "nested-in-class", "base-explicit-IEquatable", typeof(O95.V95), [typeof(O95.B95_1)], x => O95.V95.FromKnownGood((global::Probes.Shapes.PFlags)x), []), - new(96, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=enum", "sealed-record-class", "nested-in-struct", "explicit-interface-member", typeof(O96.V96), [typeof(O96.B96_1)], x => O96.V96.From((global::Probes.Shapes.PEnum)x), []), - new(97, "base-equality=none;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=own-record-class", "sealed-record-class", "nested-two-levels", "base-depth-3", typeof(O97.P97.V97), [typeof(O97.P97.B97_1), typeof(O97.P97.B97_2), typeof(O97.P97.B97_3)], x => O97.P97.V97.From((global::Probes.Shapes.PRec)x), []), - new(98, "base-equality=explicit-IEquatable;collection=HashSet;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=DateTime", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O98.V98), [typeof(O98.B98_1)], x => O98.V98.From((global::System.DateTime)x), []), - new(99, "base-equality=override-GetHashCode;collection=Dictionary-key;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=in-generic-type;shape.wrapped=double", "validated-class", "nested-in-class", "explicit-interface-member", typeof(O99.V99), [typeof(O99.B99_1)], x => O99.V99.FromKnownGood((double)x), [("shape.site", "nested-in-class")]), - new(100, "base-equality=explicit-IEquatable;collection=Dictionary-key;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=in-generic-type;shape.wrapped=Uri", "validated-class", "nested-in-class", "base-depth-3", typeof(O100.V100), [typeof(O100.B100_1), typeof(O100.B100_2), typeof(O100.B100_3)], x => O100.V100.FromKnownGood((global::System.Uri)x), [("shape.site", "nested-in-class")]), - ]; -} diff --git a/tools/probe/corpus/harness/C11Shapes.g.cs b/tools/probe/corpus/harness/C11Shapes.g.cs deleted file mode 100644 index 992834fa..00000000 --- a/tools/probe/corpus/harness/C11Shapes.g.cs +++ /dev/null @@ -1,2596 +0,0 @@ -// by tools/probe/generators/decls.cs from rows/C11ParseShapesBuildProbe.json.gz: one value object per row. Do not edit; regenerate with the rows. -#nullable enable -// CMTK1023: the bases' own equality is the point (C09) or beside it (C11); CS8851: a base's Equals without GetHashCode, on purpose. -#pragma warning disable CMTK1023, CS8851, CS1591 -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -namespace Probes.Build.C11; - -// row 0 -public static partial class O0 -{ -public interface IMark0 { int Mark(); } -public sealed partial record V0 : IValue, IMark0 { int IMark0.Mark() => 1; } -} - -// row 1 -public sealed partial record V1 : IValidatedValue -{ - public static Result Create(global::System.TimeSpan value) => new V1(value); -} - -// row 2 -public partial struct O2 -{ -public abstract record B2_2 { } -public abstract record B2_1 : B2_2 { } -public sealed partial record V2 : B2_1, IValue { } -public abstract record BC2_2 { } -public abstract record BC2_1 : BC2_2 { } -public sealed partial record VC2 : BC2_1, IValue { } -} - -// row 3 -public static partial class O3 -{ -public sealed partial class P3 -{ -public readonly partial record struct V3 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V3(value); -} -public readonly partial record struct VC3 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC3(value); -} -} -} - -// row 4 -public interface IMark4 { int Mark(); } -public readonly partial record struct V4 : IValue, IMark4 { int IMark4.Mark() => 1; } -public readonly partial record struct VC4 : IValue, IMark4 { int IMark4.Mark() => 1; } - -// row 5 -public partial struct O5 -{ -public abstract record B5_1 { } -public sealed partial record V5 : B5_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V5(value); -} -public abstract record BC5_1 { } -public sealed partial record VC5 : BC5_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC5(value); -} -} - -// row 6 -public static partial class O6 -{ -public readonly partial record struct V6 : IValue { } -public readonly partial record struct VC6 : IValue { } -} - -// row 7 -public static partial class O7 -{ -public sealed partial class P7 -{ -public abstract record B7_1 { } -public sealed partial record V7 : B7_1, IValue { } -public abstract record BC7_1 { } -public sealed partial record VC7 : BC7_1, IValue { } -} -} - -// row 8 -public static partial class O8 -{ -public sealed partial class P8 -{ -public abstract record B8_1 : System.IEquatable { bool System.IEquatable.Equals(B8_1? other) => ReferenceEquals(this, other); } -public sealed partial record V8 : B8_1, IValue { } -} -} - -// row 9 -public partial struct O9 -{ -public abstract record B9_3 { } -public abstract record B9_2 : B9_3 { } -public abstract record B9_1 : B9_2 { } -public sealed partial record V9 : B9_1, IValidatedValue -{ - public static Result Create(global::NodaTime.Instant value) => new V9(value); -} -} - -// row 10 -public abstract record B10_1 : System.IEquatable { bool System.IEquatable.Equals(B10_1? other) => ReferenceEquals(this, other); } -public sealed partial record V10 : B10_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V10(value); -} -public abstract record BC10_1 : System.IEquatable { bool System.IEquatable.Equals(BC10_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC10 : BC10_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC10(value); -} - -// row 11 -public static partial class O11 -{ -public interface IMark11 { int Mark(); } -public readonly partial record struct V11 : IValidatedValue, IMark11 -{ - public static Result Create(global::Probes.Build.C11ConstructedByRef value) => new V11(value); int IMark11.Mark() => 1; -} -public readonly partial record struct VC11 : IValidatedValue, IMark11 -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC11(value); int IMark11.Mark() => 1; -} -} - -// row 12 -public abstract record B12_2 { } -public abstract record B12_1 : B12_2 { } -public sealed partial record V12 : B12_1, IValue { } - -// row 13 -public static partial class O13 -{ -public abstract record B13_3 { } -public abstract record B13_2 : B13_3 { } -public abstract record B13_1 : B13_2 { } -public sealed partial record V13 : B13_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V13(value); -} -public abstract record BC13_3 { } -public abstract record BC13_2 : BC13_3 { } -public abstract record BC13_1 : BC13_2 { } -public sealed partial record VC13 : BC13_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC13(value); -} -} - -// row 14 -public partial struct O14 -{ -public readonly partial record struct V14 : IValidatedValue -{ - public static Result Create(global::System.DateOnly value) => new V14(value); -} -} - -// row 15 -public partial struct O15 -{ -public readonly partial record struct V15 : IValue { } -public readonly partial record struct VC15 : IValue { } -} - -// row 16 -public static partial class O16 -{ -public sealed partial class P16 -{ -public interface IMark16 { int Mark(); } -public readonly partial record struct V16 : IValue, IMark16 { int IMark16.Mark() => 1; } -public readonly partial record struct VC16 : IValue, IMark16 { int IMark16.Mark() => 1; } -} -} - -// row 17 -public readonly partial record struct V17 : IValue { } - -// row 18 -public static partial class O18 -{ -public sealed partial class P18 -{ -public abstract record B18_2 { } -public abstract record B18_1 : B18_2 { } -public sealed partial record V18 : B18_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V18(value); -} -public abstract record BC18_2 { } -public abstract record BC18_1 : BC18_2 { } -public sealed partial record VC18 : BC18_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC18(value); -} -} -} - -// row 19 -public static partial class O19 -{ -public abstract record B19_1 : System.IEquatable { bool System.IEquatable.Equals(B19_1? other) => ReferenceEquals(this, other); } -public sealed partial record V19 : B19_1, IValue { } -public abstract record BC19_1 : System.IEquatable { bool System.IEquatable.Equals(BC19_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC19 : BC19_1, IValue { } -} - -// row 20 -public partial struct O20 -{ -public interface IMark20 { int Mark(); } -public readonly partial record struct V20 : IValidatedValue, IMark20 -{ - public static Result Create(global::Probes.Build.C11ParsesByRef value) => new V20(value); int IMark20.Mark() => 1; -} -public readonly partial record struct VC20 : IValidatedValue, IMark20 -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC20(value); int IMark20.Mark() => 1; -} -} - -// row 21 -public static partial class O21 -{ -public readonly partial record struct V21 : IValidatedValue -{ - public static Result Create(global::System.DateTimeOffset value) => new V21(value); -} -} - -// row 22 -public static partial class O22 -{ -public sealed partial class P22 -{ -public interface IMark22 { int Mark(); } -public readonly partial record struct V22 : IValue, IMark22 { int IMark22.Mark() => 1; } -public readonly partial record struct VC22 : IValue, IMark22 { int IMark22.Mark() => 1; } -} -} - -// row 23 -public static partial class O23 -{ -public sealed partial class P23 -{ -public abstract record B23_3 { } -public abstract record B23_2 : B23_3 { } -public abstract record B23_1 : B23_2 { } -public sealed partial record V23 : B23_1, IValue { } -} -} - -// row 24 -public static partial class O24 -{ -public readonly partial record struct V24 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V24(value); -} -public readonly partial record struct VC24 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC24(value); -} -} - -// row 25 -public interface IMark25 { int Mark(); } -public readonly partial record struct V25 : IValidatedValue, IMark25 -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V25(value); int IMark25.Mark() => 1; -} -public readonly partial record struct VC25 : IValidatedValue, IMark25 -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC25(value); int IMark25.Mark() => 1; -} - -// row 26 -public partial struct O26 -{ -public abstract record B26_1 : System.IEquatable { bool System.IEquatable.Equals(B26_1? other) => ReferenceEquals(this, other); } -public sealed partial record V26 : B26_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesByRef value) => new V26(value); -} -public abstract record BC26_1 : System.IEquatable { bool System.IEquatable.Equals(BC26_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC26 : BC26_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC26(value); -} -} - -// row 27 -public static partial class O27 -{ -public readonly partial record struct V27 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V27(value); -} -public readonly partial record struct VC27 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC27(value); -} -} - -// row 28 -public readonly partial record struct V28 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesByRef value) => new V28(value); -} -public readonly partial record struct VC28 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC28(value); -} - -// row 29 -public partial struct O29 -{ -public interface IMark29 { int Mark(); } -public sealed partial record V29 : IValidatedValue, IMark29 -{ - public static Result Create(global::Probes.Build.C11ConstructedByRef value) => new V29(value); int IMark29.Mark() => 1; -} -public sealed partial record VC29 : IValidatedValue, IMark29 -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC29(value); int IMark29.Mark() => 1; -} -} - -// row 30 -public abstract record B30_2 { } -public abstract record B30_1 : B30_2 { } -public sealed partial record V30 : B30_1, IValue { } -public abstract record BC30_2 { } -public abstract record BC30_1 : BC30_2 { } -public sealed partial record VC30 : BC30_1, IValue { } - -// row 31 -public static partial class O31 -{ -public readonly partial record struct V31 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V31(value); -} -public readonly partial record struct VC31 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC31(value); -} -} - -// row 32 -public partial struct O32 -{ -public sealed partial record V32 : IValue { } -public sealed partial record VC32 : IValue { } -} - -// row 33 -public static partial class O33 -{ -public abstract record B33_1 { } -public sealed partial record V33 : B33_1, IValidatedValue -{ - public static Result Create(long value) => new V33(value); -} -} - -// row 34 -public abstract record B34_3 { } -public abstract record B34_2 : B34_3 { } -public abstract record B34_1 : B34_2 { } -public sealed partial record V34 : B34_1, IValue { } -public abstract record BC34_3 { } -public abstract record BC34_2 : BC34_3 { } -public abstract record BC34_1 : BC34_2 { } -public sealed partial record VC34 : BC34_1, IValue { } - -// row 35 -public abstract record B35_2 { } -public abstract record B35_1 : B35_2 { } -public sealed partial record V35 : B35_1, IValue { } - -// row 36 -public partial struct O36 -{ -public abstract record B36_2 { } -public abstract record B36_1 : B36_2 { } -public sealed partial record V36 : B36_1, IValue { } -public abstract record BC36_2 { } -public abstract record BC36_1 : BC36_2 { } -public sealed partial record VC36 : BC36_1, IValue { } -} - -// row 37 -public static partial class O37 -{ -public sealed partial class P37 -{ -public sealed partial record V37 : IValue { } -} -} - -// row 38 -public partial struct O38 -{ -public abstract record B38_2 { } -public abstract record B38_1 : B38_2 { } -public sealed partial record V38 : B38_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesByRef value) => new V38(value); -} -public abstract record BC38_2 { } -public abstract record BC38_1 : BC38_2 { } -public sealed partial record VC38 : BC38_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC38(value); -} -} - -// row 39 -public partial struct O39 -{ -public abstract record B39_3 { } -public abstract record B39_2 : B39_3 { } -public abstract record B39_1 : B39_2 { } -public sealed partial record V39 : B39_1, IValue { } -} - -// row 40 -public partial struct O40 -{ -public readonly partial record struct V40 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V40(value); -} -public readonly partial record struct VC40 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC40(value); -} -} - -// row 41 -public partial struct O41 -{ -public abstract record B41_1 : System.IEquatable { bool System.IEquatable.Equals(B41_1? other) => ReferenceEquals(this, other); } -public sealed partial record V41 : B41_1, IValue { } -} - -// row 42 -public static partial class O42 -{ -public sealed partial class P42 -{ -public abstract record B42_1 : System.IEquatable { bool System.IEquatable.Equals(B42_1? other) => ReferenceEquals(this, other); } -public sealed partial record V42 : B42_1, IValue { } -} -} - -// row 43 -public partial struct O43 -{ -public readonly partial record struct V43 : IValue { } -public readonly partial record struct VC43 : IValue { } -} - -// row 44 -public static partial class O44 -{ -public sealed partial class P44 -{ -public readonly partial record struct V44 : IValue { } -public readonly partial record struct VC44 : IValue { } -} -} - -// row 45 -public readonly partial record struct V45 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedByRef value) => new V45(value); -} -public readonly partial record struct VC45 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC45(value); -} - -// row 46 -public static partial class O46 -{ -public readonly partial record struct V46 : IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PFlags value) => new V46(value); -} -} - -// row 47 -public static partial class O47 -{ -public readonly partial record struct V47 : IValidatedValue -{ - public static Result Create(int value) => new V47(value); -} -} - -// row 48 -public static partial class O48 -{ -public readonly partial record struct V48 : IValue { } -public readonly partial record struct VC48 : IValue { } -} - -// row 49 -public static partial class O49 -{ -public abstract record B49_1 : System.IEquatable { bool System.IEquatable.Equals(B49_1? other) => ReferenceEquals(this, other); } -public sealed partial record V49 : B49_1, IValidatedValue -{ - public static Result Create(bool value) => new V49(value); -} -} - -// row 50 -public abstract record B50_1 : System.IEquatable { bool System.IEquatable.Equals(B50_1? other) => ReferenceEquals(this, other); } -public sealed partial record V50 : B50_1, IValue { } -public abstract record BC50_1 : System.IEquatable { bool System.IEquatable.Equals(BC50_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC50 : BC50_1, IValue { } - -// row 51 -public static partial class O51 -{ -public abstract record B51_3 { } -public abstract record B51_2 : B51_3 { } -public abstract record B51_1 : B51_2 { } -public sealed partial record V51 : B51_1, IValidatedValue -{ - public static Result Create(global::System.DateOnly value) => new V51(value); -} -} - -// row 52 -public partial struct O52 -{ -public sealed partial record V52 : IValue { } -} - -// row 53 -public static partial class O53 -{ -public sealed partial class P53 -{ -public abstract record B53_3 { } -public abstract record B53_2 : B53_3 { } -public abstract record B53_1 : B53_2 { } -public sealed partial record V53 : B53_1, IValidatedValue -{ - public static Result Create(string value) => new V53(value); -} -} -} - -// row 54 -public static partial class O54 -{ -public sealed partial class P54 -{ -public abstract record B54_3 { } -public abstract record B54_2 : B54_3 { } -public abstract record B54_1 : B54_2 { } -public sealed partial record V54 : B54_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V54(value); -} -public abstract record BC54_3 { } -public abstract record BC54_2 : BC54_3 { } -public abstract record BC54_1 : BC54_2 { } -public sealed partial record VC54 : BC54_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC54(value); -} -} -} - -// row 55 -public static partial class O55 -{ -public sealed partial class P55 -{ -public abstract record B55_1 { } -public sealed partial record V55 : B55_1, IValue { } -} -} - -// row 56 -public static partial class O56 -{ -public sealed partial class P56 -{ -public abstract record B56_1 { } -public sealed partial record V56 : B56_1, IValidatedValue -{ - public static Result Create(decimal value) => new V56(value); -} -} -} - -// row 57 -public abstract record B57_1 { } -public sealed partial record V57 : B57_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PEnum value) => new V57(value); -} - -// row 58 -public readonly partial record struct V58 : IValue { } - -// row 59 -public static partial class O59 -{ -public sealed partial class P59 -{ -public abstract record B59_3 { } -public abstract record B59_2 : B59_3 { } -public abstract record B59_1 : B59_2 { } -public sealed partial record V59 : B59_1, IValidatedValue -{ - public static Result Create(double value) => new V59(value); -} -} -} - -// row 60 -public static partial class O60 -{ -public interface IMark60 { int Mark(); } -public sealed partial record V60 : IValue, IMark60 { int IMark60.Mark() => 1; } -} - -// row 61 -public partial struct O61 -{ -public abstract record B61_1 { } -public sealed partial record V61 : B61_1, IValue { } -public abstract record BC61_1 { } -public sealed partial record VC61 : BC61_1, IValue { } -} - -// row 62 -public static partial class O62 -{ -public sealed partial class P62 -{ -public readonly partial record struct V62 : IValidatedValue -{ - public static Result Create(global::System.DateTime value) => new V62(value); -} -} -} - -// row 63 -public interface IMark63 { int Mark(); } -public readonly partial record struct V63 : IValue, IMark63 { int IMark63.Mark() => 1; } - -// row 64 -public static partial class O64 -{ -public readonly partial record struct V64 : IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PEnum value) => new V64(value); -} -} - -// row 65 -public abstract record B65_2 { } -public abstract record B65_1 : B65_2 { } -public sealed partial record V65 : B65_1, IValidatedValue -{ - public static Result Create(global::System.Guid value) => new V65(value); -} - -// row 66 -public abstract record B66_1 : System.IEquatable { bool System.IEquatable.Equals(B66_1? other) => ReferenceEquals(this, other); } -public sealed partial record V66 : B66_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PRec value) => new V66(value); -} - -// row 67 -public static partial class O67 -{ -public sealed partial class P67 -{ -public readonly partial record struct V67 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V67(value); -} -public readonly partial record struct VC67 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC67(value); -} -} -} - -// row 68 -public static partial class O68 -{ -public sealed partial class P68 -{ -public readonly partial record struct V68 : IValue { } -} -} - -// row 69 -public partial struct O69 -{ -public interface IMark69 { int Mark(); } -public readonly partial record struct V69 : IValidatedValue, IMark69 -{ - public static Result Create(long value) => new V69(value); int IMark69.Mark() => 1; -} -} - -// row 70 -public static partial class O70 -{ -public sealed partial class P70 -{ -public interface IMark70 { int Mark(); } -public readonly partial record struct V70 : IValue, IMark70 { int IMark70.Mark() => 1; } -} -} - -// row 71 -public static partial class O71 -{ -public sealed partial class P71 -{ -public readonly partial record struct V71 : IValue { } -} -} - -// row 72 -public partial struct O72 -{ -public readonly partial record struct V72 : IValue { } -} - -// row 73 -public static partial class O73 -{ -public interface IMark73 { int Mark(); } -public readonly partial record struct V73 : IValidatedValue, IMark73 -{ - public static Result Create(global::Probes.Build.C11ConstructedByRef value) => new V73(value); int IMark73.Mark() => 1; -} -public readonly partial record struct VC73 : IValidatedValue, IMark73 -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC73(value); int IMark73.Mark() => 1; -} -} - -// row 74 -public static partial class O74 -{ -public readonly partial record struct V74 : IValue { } -} - -// row 75 -public partial struct O75 -{ -public abstract record B75_1 { } -public sealed partial record V75 : B75_1, IValidatedValue -{ - public static Result Create(int value) => new V75(value); -} -} - -// row 76 -public static partial class O76 -{ -public interface IMark76 { int Mark(); } -public sealed partial record V76 : IValue, IMark76 { int IMark76.Mark() => 1; } -} - -// row 77 -public readonly partial record struct V77 : IValue { } -public readonly partial record struct VC77 : IValue { } - -// row 78 -public static partial class O78 -{ -public sealed partial class P78 -{ -public readonly partial record struct V78 : IValue { } -public readonly partial record struct VC78 : IValue { } -} -} - -// row 79 -public abstract record B79_1 : System.IEquatable { bool System.IEquatable.Equals(B79_1? other) => ReferenceEquals(this, other); } -public sealed partial record V79 : B79_1, IValue { } -public abstract record BC79_1 : System.IEquatable { bool System.IEquatable.Equals(BC79_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC79 : BC79_1, IValue { } - -// row 80 -public static partial class O80 -{ -public readonly partial record struct V80 : IValidatedValue -{ - public static Result Create(global::NodaTime.Instant value) => new V80(value); -} -} - -// row 81 -public static partial class O81 -{ -public abstract record B81_1 : System.IEquatable { bool System.IEquatable.Equals(B81_1? other) => ReferenceEquals(this, other); } -public sealed partial record V81 : B81_1, IValue { } -public abstract record BC81_1 : System.IEquatable { bool System.IEquatable.Equals(BC81_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC81 : BC81_1, IValue { } -} - -// row 82 -public static partial class O82 -{ -public sealed partial class P82 -{ -public interface IMark82 { int Mark(); } -public readonly partial record struct V82 : IValue, IMark82 { int IMark82.Mark() => 1; } -} -} - -// row 83 -public abstract record B83_2 { } -public abstract record B83_1 : B83_2 { } -public sealed partial record V83 : B83_1, IValidatedValue -{ - public static Result Create(string value) => new V83(value); -} - -// row 84 -public static partial class O84 -{ -public abstract record B84_2 { } -public abstract record B84_1 : B84_2 { } -public sealed partial record V84 : B84_1, IValidatedValue -{ - public static Result Create(long value) => new V84(value); -} -} - -// row 85 -public partial struct O85 -{ -public abstract record B85_1 : System.IEquatable { bool System.IEquatable.Equals(B85_1? other) => ReferenceEquals(this, other); } -public sealed partial record V85 : B85_1, IValue { } -} - -// row 86 -public static partial class O86 -{ -public sealed partial class P86 -{ -public abstract record B86_2 { } -public abstract record B86_1 : B86_2 { } -public sealed partial record V86 : B86_1, IValue { } -public abstract record BC86_2 { } -public abstract record BC86_1 : BC86_2 { } -public sealed partial record VC86 : BC86_1, IValue { } -} -} - -// row 87 -public static partial class O87 -{ -public sealed partial class P87 -{ -public sealed partial record V87 : IValue { } -} -} - -// row 88 -public sealed partial record V88 : IValue { } -public sealed partial record VC88 : IValue { } - -// row 89 -public abstract record B89_2 { } -public abstract record B89_1 : B89_2 { } -public sealed partial record V89 : B89_1, IValidatedValue -{ - public static Result Create(ulong value) => new V89(value); -} - -// row 90 -public readonly partial record struct V90 : IValue { } - -// row 91 -public static partial class O91 -{ -public sealed partial class P91 -{ -public abstract record B91_1 { } -public sealed partial record V91 : B91_1, IValue { } -public abstract record BC91_1 { } -public sealed partial record VC91 : BC91_1, IValue { } -} -} - -// row 92 -public static partial class O92 -{ -public abstract record B92_2 { } -public abstract record B92_1 : B92_2 { } -public sealed partial record V92 : B92_1, IValidatedValue -{ - public static Result Create(global::System.TimeSpan value) => new V92(value); -} -} - -// row 93 -public abstract record B93_3 { } -public abstract record B93_2 : B93_3 { } -public abstract record B93_1 : B93_2 { } -public sealed partial record V93 : B93_1, IValue { } - -// row 94 -public partial struct O94 -{ -public abstract record B94_2 { } -public abstract record B94_1 : B94_2 { } -public sealed partial record V94 : B94_1, IValue { } -} - -// row 95 -public abstract record B95_3 { } -public abstract record B95_2 : B95_3 { } -public abstract record B95_1 : B95_2 { } -public sealed partial record V95 : B95_1, IValidatedValue -{ - public static Result Create(char value) => new V95(value); -} - -// row 96 -public partial struct O96 -{ -public abstract record B96_3 { } -public abstract record B96_2 : B96_3 { } -public abstract record B96_1 : B96_2 { } -public sealed partial record V96 : B96_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PRec value) => new V96(value); -} -} - -// row 97 -public abstract record B97_1 { } -public sealed partial record V97 : B97_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PFlags value) => new V97(value); -} - -// row 98 -public abstract record B98_2 { } -public abstract record B98_1 : B98_2 { } -public sealed partial record V98 : B98_1, IValue { } - -// row 99 -public interface IMark99 { int Mark(); } -public readonly partial record struct V99 : IValidatedValue, IMark99 -{ - public static Result Create(string value) => new V99(value); int IMark99.Mark() => 1; -} - -// row 100 -public abstract record B100_1 { } -public sealed partial record V100 : B100_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V100(value); -} -public abstract record BC100_1 { } -public sealed partial record VC100 : BC100_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC100(value); -} - -// row 101 -public static partial class O101 -{ -public interface IMark101 { int Mark(); } -public readonly partial record struct V101 : IValidatedValue, IMark101 -{ - public static Result Create(global::System.DateOnly value) => new V101(value); int IMark101.Mark() => 1; -} -} - -// row 102 -public static partial class O102 -{ -public sealed partial class P102 -{ -public interface IMark102 { int Mark(); } -public sealed partial record V102 : IValue, IMark102 { int IMark102.Mark() => 1; } -} -} - -// row 103 -public readonly partial record struct V103 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V103(value); -} -public readonly partial record struct VC103 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC103(value); -} - -// row 104 -public static partial class O104 -{ -public sealed partial class P104 -{ -public readonly partial record struct V104 : IValue { } -} -} - -// row 105 -public interface IMark105 { int Mark(); } -public readonly partial record struct V105 : IValue, IMark105 { int IMark105.Mark() => 1; } - -// row 106 -public static partial class O106 -{ -public sealed partial class P106 -{ -public abstract record B106_1 : System.IEquatable { bool System.IEquatable.Equals(B106_1? other) => ReferenceEquals(this, other); } -public sealed partial record V106 : B106_1, IValue { } -public abstract record BC106_1 : System.IEquatable { bool System.IEquatable.Equals(BC106_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC106 : BC106_1, IValue { } -} -} - -// row 107 -public static partial class O107 -{ -public sealed partial class P107 -{ -public readonly partial record struct V107 : IValue { } -public readonly partial record struct VC107 : IValue { } -} -} - -// row 108 -public static partial class O108 -{ -public sealed partial class P108 -{ -public readonly partial record struct V108 : IValidatedValue -{ - public static Result Create(bool value) => new V108(value); -} -} -} - -// row 109 -public static partial class O109 -{ -public abstract record B109_2 { } -public abstract record B109_1 : B109_2 { } -public sealed partial record V109 : B109_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedByRef value) => new V109(value); -} -public abstract record BC109_2 { } -public abstract record BC109_1 : BC109_2 { } -public sealed partial record VC109 : BC109_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC109(value); -} -} - -// row 110 -public static partial class O110 -{ -public sealed partial class P110 -{ -public abstract record B110_1 : System.IEquatable { bool System.IEquatable.Equals(B110_1? other) => ReferenceEquals(this, other); } -public sealed partial record V110 : B110_1, IValue { } -} -} - -// row 111 -public static partial class O111 -{ -public readonly partial record struct V111 : IValue { } -public readonly partial record struct VC111 : IValue { } -} - -// row 112 -public partial struct O112 -{ -public interface IMark112 { int Mark(); } -public readonly partial record struct V112 : IValidatedValue, IMark112 -{ - public static Result Create(int value) => new V112(value); int IMark112.Mark() => 1; -} -} - -// row 113 -public partial struct O113 -{ -public abstract record B113_3 { } -public abstract record B113_2 : B113_3 { } -public abstract record B113_1 : B113_2 { } -public sealed partial record V113 : B113_1, IValue { } -} - -// row 114 -public partial struct O114 -{ -public abstract record B114_2 { } -public abstract record B114_1 : B114_2 { } -public sealed partial record V114 : B114_1, IValidatedValue -{ - public static Result Create(double value) => new V114(value); -} -} - -// row 115 -public static partial class O115 -{ -public readonly partial record struct V115 : IValidatedValue -{ - public static Result Create(global::System.DateTime value) => new V115(value); -} -} - -// row 116 -public static partial class O116 -{ -public abstract record B116_2 { } -public abstract record B116_1 : B116_2 { } -public sealed partial record V116 : B116_1, IValue { } -} - -// row 117 -public static partial class O117 -{ -public abstract record B117_1 { } -public sealed partial record V117 : B117_1, IValue { } -public abstract record BC117_1 { } -public sealed partial record VC117 : BC117_1, IValue { } -} - -// row 118 -public partial struct O118 -{ -public abstract record B118_1 : System.IEquatable { bool System.IEquatable.Equals(B118_1? other) => ReferenceEquals(this, other); } -public sealed partial record V118 : B118_1, IValue { } -} - -// row 119 -public static partial class O119 -{ -public sealed partial class P119 -{ -public abstract record B119_1 { } -public sealed partial record V119 : B119_1, IValue { } -} -} - -// row 120 -public partial struct O120 -{ -public abstract record B120_2 { } -public abstract record B120_1 : B120_2 { } -public sealed partial record V120 : B120_1, IValidatedValue -{ - public static Result Create(global::NodaTime.Instant value) => new V120(value); -} -} - -// row 121 -public abstract record B121_3 { } -public abstract record B121_2 : B121_3 { } -public abstract record B121_1 : B121_2 { } -public sealed partial record V121 : B121_1, IValue { } -public abstract record BC121_3 { } -public abstract record BC121_2 : BC121_3 { } -public abstract record BC121_1 : BC121_2 { } -public sealed partial record VC121 : BC121_1, IValue { } - -// row 122 -public static partial class O122 -{ -public readonly partial record struct V122 : IValue { } -} - -// row 123 -public partial struct O123 -{ -public interface IMark123 { int Mark(); } -public sealed partial record V123 : IValue, IMark123 { int IMark123.Mark() => 1; } -public sealed partial record VC123 : IValue, IMark123 { int IMark123.Mark() => 1; } -} - -// row 124 -public partial struct O124 -{ -public readonly partial record struct V124 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesByRef value) => new V124(value); -} -public readonly partial record struct VC124 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC124(value); -} -} - -// row 125 -public static partial class O125 -{ -public interface IMark125 { int Mark(); } -public readonly partial record struct V125 : IValidatedValue, IMark125 -{ - public static Result Create(global::System.Uri value) => new V125(value); int IMark125.Mark() => 1; -} -} - -// row 126 -public static partial class O126 -{ -public sealed partial class P126 -{ -public abstract record B126_1 : System.IEquatable { bool System.IEquatable.Equals(B126_1? other) => ReferenceEquals(this, other); } -public sealed partial record V126 : B126_1, IValidatedValue -{ - public static Result Create(decimal value) => new V126(value); -} -} -} - -// row 127 -public static partial class O127 -{ -public abstract record B127_1 : System.IEquatable { bool System.IEquatable.Equals(B127_1? other) => ReferenceEquals(this, other); } -public sealed partial record V127 : B127_1, IValue { } -} - -// row 128 -public static partial class O128 -{ -public abstract record B128_1 : System.IEquatable { bool System.IEquatable.Equals(B128_1? other) => ReferenceEquals(this, other); } -public sealed partial record V128 : B128_1, IValidatedValue -{ - public static Result Create(global::System.DateTime value) => new V128(value); -} -} - -// row 129 -public readonly partial record struct V129 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedByRef value) => new V129(value); -} -public readonly partial record struct VC129 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC129(value); -} - -// row 130 -public static partial class O130 -{ -public interface IMark130 { int Mark(); } -public readonly partial record struct V130 : IValue, IMark130 { int IMark130.Mark() => 1; } -} - -// row 131 -public partial struct O131 -{ -public interface IMark131 { int Mark(); } -public readonly partial record struct V131 : IValue, IMark131 { int IMark131.Mark() => 1; } -} - -// row 132 -public interface IMark132 { int Mark(); } -public readonly partial record struct V132 : IValidatedValue, IMark132 -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V132(value); int IMark132.Mark() => 1; -} -public readonly partial record struct VC132 : IValidatedValue, IMark132 -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC132(value); int IMark132.Mark() => 1; -} - -// row 133 -public static partial class O133 -{ -public abstract record B133_2 { } -public abstract record B133_1 : B133_2 { } -public sealed partial record V133 : B133_1, IValue { } -} - -// row 134 -public abstract record B134_2 { } -public abstract record B134_1 : B134_2 { } -public sealed partial record V134 : B134_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesByRef value) => new V134(value); -} -public abstract record BC134_2 { } -public abstract record BC134_1 : BC134_2 { } -public sealed partial record VC134 : BC134_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC134(value); -} - -// row 135 -public partial struct O135 -{ -public abstract record B135_3 { } -public abstract record B135_2 : B135_3 { } -public abstract record B135_1 : B135_2 { } -public sealed partial record V135 : B135_1, IValue { } -} - -// row 136 -public partial struct O136 -{ -public interface IMark136 { int Mark(); } -public readonly partial record struct V136 : IValue, IMark136 { int IMark136.Mark() => 1; } -} - -// row 137 -public static partial class O137 -{ -public abstract record B137_1 { } -public sealed partial record V137 : B137_1, IValue { } -public abstract record BC137_1 { } -public sealed partial record VC137 : BC137_1, IValue { } -} - -// row 138 -public static partial class O138 -{ -public readonly partial record struct V138 : IValue { } -public readonly partial record struct VC138 : IValue { } -} - -// row 139 -public static partial class O139 -{ -public sealed partial class P139 -{ -public abstract record B139_1 : System.IEquatable { bool System.IEquatable.Equals(B139_1? other) => ReferenceEquals(this, other); } -public sealed partial record V139 : B139_1, IValue { } -} -} - -// row 140 -public static partial class O140 -{ -public abstract record B140_2 { } -public abstract record B140_1 : B140_2 { } -public sealed partial record V140 : B140_1, IValue { } -} - -// row 141 -public static partial class O141 -{ -public interface IMark141 { int Mark(); } -public readonly partial record struct V141 : IValue, IMark141 { int IMark141.Mark() => 1; } -public readonly partial record struct VC141 : IValue, IMark141 { int IMark141.Mark() => 1; } -} - -// row 142 -public static partial class O142 -{ -public readonly partial record struct V142 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V142(value); -} -public readonly partial record struct VC142 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC142(value); -} -} - -// row 143 -public sealed partial record V143 : IValue { } -public sealed partial record VC143 : IValue { } - -// row 144 -public static partial class O144 -{ -public sealed partial class P144 -{ -public interface IMark144 { int Mark(); } -public readonly partial record struct V144 : IValue, IMark144 { int IMark144.Mark() => 1; } -public readonly partial record struct VC144 : IValue, IMark144 { int IMark144.Mark() => 1; } -} -} - -// row 145 -public static partial class O145 -{ -public sealed partial class P145 -{ -public readonly partial record struct V145 : IValidatedValue -{ - public static Result Create(ulong value) => new V145(value); -} -} -} - -// row 146 -public static partial class O146 -{ -public readonly partial record struct V146 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V146(value); -} -public readonly partial record struct VC146 : IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC146(value); -} -} - -// row 147 -public static partial class O147 -{ -public sealed partial class P147 -{ -public interface IMark147 { int Mark(); } -public readonly partial record struct V147 : IValidatedValue, IMark147 -{ - public static Result Create(global::Probes.Build.C11AbstractText value) => new V147(value); int IMark147.Mark() => 1; -} -public readonly partial record struct VC147 : IValidatedValue, IMark147 -{ - public static Result Create(global::Probes.Build.C11ConstructedConcrete value) => new VC147(value); int IMark147.Mark() => 1; -} -} -} - -// row 148 -public static partial class O148 -{ -public sealed partial record V148 : IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PEnum value) => new V148(value); -} -} - -// row 149 -public partial struct O149 -{ -public interface IMark149 { int Mark(); } -public sealed partial record V149 : IValue, IMark149 { int IMark149.Mark() => 1; } -public sealed partial record VC149 : IValue, IMark149 { int IMark149.Mark() => 1; } -} - -// row 150 -public static partial class O150 -{ -public sealed partial class P150 -{ -public abstract record B150_3 { } -public abstract record B150_2 : B150_3 { } -public abstract record B150_1 : B150_2 { } -public sealed partial record V150 : B150_1, IValidatedValue -{ - public static Result Create(decimal value) => new V150(value); -} -} -} - -// row 151 -public static partial class O151 -{ -public sealed partial record V151 : IValidatedValue -{ - public static Result Create(ulong value) => new V151(value); -} -} - -// row 152 -public static partial class O152 -{ -public interface IMark152 { int Mark(); } -public sealed partial record V152 : IValue, IMark152 { int IMark152.Mark() => 1; } -public sealed partial record VC152 : IValue, IMark152 { int IMark152.Mark() => 1; } -} - -// row 153 -public static partial class O153 -{ -public sealed partial record V153 : IValue { } -public sealed partial record VC153 : IValue { } -} - -// row 154 -public static partial class O154 -{ -public sealed partial class P154 -{ -public abstract record B154_1 : System.IEquatable { bool System.IEquatable.Equals(B154_1? other) => ReferenceEquals(this, other); } -public sealed partial record V154 : B154_1, IValue { } -public abstract record BC154_1 : System.IEquatable { bool System.IEquatable.Equals(BC154_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC154 : BC154_1, IValue { } -} -} - -// row 155 -public sealed partial record V155 : IValue { } -public sealed partial record VC155 : IValue { } - -// row 156 -public interface IMark156 { int Mark(); } -public readonly partial record struct V156 : IValidatedValue, IMark156 -{ - public static Result Create(global::System.DateTimeOffset value) => new V156(value); int IMark156.Mark() => 1; -} - -// row 157 -public static partial class O157 -{ -public sealed partial class P157 -{ -public abstract record B157_1 : System.IEquatable { bool System.IEquatable.Equals(B157_1? other) => ReferenceEquals(this, other); } -public sealed partial record V157 : B157_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesToInt value) => new V157(value); -} -public abstract record BC157_1 : System.IEquatable { bool System.IEquatable.Equals(BC157_1? other) => ReferenceEquals(this, other); } -public sealed partial record VC157 : BC157_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsedShape value) => new VC157(value); -} -} -} - -// row 158 -public static partial class O158 -{ -public sealed partial class P158 -{ -public abstract record B158_3 { } -public abstract record B158_2 : B158_3 { } -public abstract record B158_1 : B158_2 { } -public sealed partial record V158 : B158_1, IValidatedValue -{ - public static Result Create(string value) => new V158(value); -} -} -} - -// row 159 -public static partial class O159 -{ -public sealed partial class P159 -{ -public abstract record B159_2 { } -public abstract record B159_1 : B159_2 { } -public sealed partial record V159 : B159_1, IValue { } -} -} - -// row 160 -public partial struct O160 -{ -public interface IMark160 { int Mark(); } -public sealed partial record V160 : IValue, IMark160 { int IMark160.Mark() => 1; } -} - -// row 161 -public static partial class O161 -{ -public readonly partial record struct V161 : IValue { } -} - -// row 162 -public partial struct O162 -{ -public abstract record B162_3 { } -public abstract record B162_2 : B162_3 { } -public abstract record B162_1 : B162_2 { } -public sealed partial record V162 : B162_1, IValue { } -} - -// row 163 -public partial struct O163 -{ -public abstract record B163_1 : System.IEquatable { bool System.IEquatable.Equals(B163_1? other) => ReferenceEquals(this, other); } -public sealed partial record V163 : B163_1, IValue { } -} - -// row 164 -public partial struct O164 -{ -public interface IMark164 { int Mark(); } -public readonly partial record struct V164 : IValidatedValue, IMark164 -{ - public static Result Create(byte value) => new V164(value); int IMark164.Mark() => 1; -} -} - -// row 165 -public abstract record B165_1 : System.IEquatable { bool System.IEquatable.Equals(B165_1? other) => ReferenceEquals(this, other); } -public sealed partial record V165 : B165_1, IValue { } - -// row 166 -public static partial class O166 -{ -public readonly partial record struct V166 : IValue { } -} - -// row 167 -public static partial class O167 -{ -public readonly partial record struct V167 : IValue { } -} - -// row 168 -public static partial class O168 -{ -public readonly partial record struct V168 : IValue { } -} - -// row 169 -public partial struct O169 -{ -public interface IMark169 { int Mark(); } -public readonly partial record struct V169 : IValue, IMark169 { int IMark169.Mark() => 1; } -} - -// row 170 -public static partial class O170 -{ -public sealed partial class P170 -{ -public readonly partial record struct V170 : IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PRec value) => new V170(value); -} -} -} - -// row 171 -public static partial class O171 -{ -public sealed partial record V171 : IValidatedValue -{ - public static Result Create(byte value) => new V171(value); -} -} - -// row 172 -public static partial class O172 -{ -public sealed partial class P172 -{ -public abstract record B172_2 { } -public abstract record B172_1 : B172_2 { } -public sealed partial record V172 : B172_1, IValue { } -} -} - -// row 173 -public abstract record B173_3 { } -public abstract record B173_2 : B173_3 { } -public abstract record B173_1 : B173_2 { } -public sealed partial record V173 : B173_1, IValidatedValue -{ - public static Result Create(global::System.TimeOnly value) => new V173(value); -} - -// row 174 -public static partial class O174 -{ -public abstract record B174_3 { } -public abstract record B174_2 : B174_3 { } -public abstract record B174_1 : B174_2 { } -public sealed partial record V174 : B174_1, IValidatedValue -{ - public static Result Create(global::System.Uri value) => new V174(value); -} -} - -// row 175 -public partial struct O175 -{ -public readonly partial record struct V175 : IValidatedValue -{ - public static Result Create(double value) => new V175(value); -} -} - -// row 176 -public partial struct O176 -{ -public abstract record B176_1 : System.IEquatable { bool System.IEquatable.Equals(B176_1? other) => ReferenceEquals(this, other); } -public sealed partial record V176 : B176_1, IValue { } -} - -// row 177 -public static partial class O177 -{ -public abstract record B177_3 { } -public abstract record B177_2 : B177_3 { } -public abstract record B177_1 : B177_2 { } -public sealed partial record V177 : B177_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ConstructedByRef value) => new V177(value); -} -public abstract record BC177_3 { } -public abstract record BC177_2 : BC177_3 { } -public abstract record BC177_1 : BC177_2 { } -public sealed partial record VC177 : BC177_1, IValidatedValue -{ - public static Result Create(global::Probes.Build.C11ParsesFromIn value) => new VC177(value); -} -} - -// row 178 -public partial struct O178 -{ -public interface IMark178 { int Mark(); } -public readonly partial record struct V178 : IValidatedValue, IMark178 -{ - public static Result Create(global::System.Guid value) => new V178(value); int IMark178.Mark() => 1; -} -} - -// row 179 -public static partial class O179 -{ -public readonly partial record struct V179 : IValidatedValue -{ - public static Result Create(global::System.TimeOnly value) => new V179(value); -} -} - -// row 180 -public static partial class O180 -{ -public sealed partial class P180 -{ -public interface IMark180 { int Mark(); } -public readonly partial record struct V180 : IValue, IMark180 { int IMark180.Mark() => 1; } -} -} - -// row 181 -public interface IMark181 { int Mark(); } -public readonly partial record struct V181 : IValidatedValue, IMark181 -{ - public static Result Create(global::System.TimeSpan value) => new V181(value); int IMark181.Mark() => 1; -} - -// row 182 -public static partial class O182 -{ -public readonly partial record struct V182 : IValidatedValue -{ - public static Result Create(decimal value) => new V182(value); -} -} - -// row 183 -public partial struct O183 -{ -public abstract record B183_3 { } -public abstract record B183_2 : B183_3 { } -public abstract record B183_1 : B183_2 { } -public sealed partial record V183 : B183_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PFlags value) => new V183(value); -} -} - -// row 184 -public abstract record B184_1 { } -public sealed partial record V184 : B184_1, IValidatedValue -{ - public static Result Create(global::System.DateTime value) => new V184(value); -} - -// row 185 -public static partial class O185 -{ -public sealed partial class P185 -{ -public readonly partial record struct V185 : IValue { } -} -} - -// row 186 -public abstract record B186_3 { } -public abstract record B186_2 : B186_3 { } -public abstract record B186_1 : B186_2 { } -public sealed partial record V186 : B186_1, IValue { } - -// row 187 -public static partial class O187 -{ -public abstract record B187_1 { } -public sealed partial record V187 : B187_1, IValidatedValue -{ - public static Result Create(global::System.DateOnly value) => new V187(value); -} -} - -// row 188 -public static partial class O188 -{ -public sealed partial class P188 -{ -public interface IMark188 { int Mark(); } -public readonly partial record struct V188 : IValue, IMark188 { int IMark188.Mark() => 1; } -} -} - -// row 189 -public static partial class O189 -{ -public sealed partial class P189 -{ -public interface IMark189 { int Mark(); } -public sealed partial record V189 : IValidatedValue, IMark189 -{ - public static Result Create(global::NodaTime.Instant value) => new V189(value); int IMark189.Mark() => 1; -} -} -} - -// row 190 -public partial struct O190 -{ -public abstract record B190_3 { } -public abstract record B190_2 : B190_3 { } -public abstract record B190_1 : B190_2 { } -public sealed partial record V190 : B190_1, IValue { } -} - -// row 191 -public static partial class O191 -{ -public sealed partial class P191 -{ -public interface IMark191 { int Mark(); } -public sealed partial record V191 : IValue, IMark191 { int IMark191.Mark() => 1; } -} -} - -// row 192 -public abstract record B192_1 : System.IEquatable { bool System.IEquatable.Equals(B192_1? other) => ReferenceEquals(this, other); } -public sealed partial record V192 : B192_1, IValidatedValue -{ - public static Result Create(ulong value) => new V192(value); -} - -// row 193 -public abstract record B193_2 { } -public abstract record B193_1 : B193_2 { } -public sealed partial record V193 : B193_1, IValue { } - -// row 194 -public static partial class O194 -{ -public abstract record B194_2 { } -public abstract record B194_1 : B194_2 { } -public sealed partial record V194 : B194_1, IValidatedValue -{ - public static Result Create(byte value) => new V194(value); -} -} - -// row 195 -public interface IMark195 { int Mark(); } -public sealed partial record V195 : IValue, IMark195 { int IMark195.Mark() => 1; } - -// row 196 -public abstract record B196_1 : System.IEquatable { bool System.IEquatable.Equals(B196_1? other) => ReferenceEquals(this, other); } -public sealed partial record V196 : B196_1, IValidatedValue -{ - public static Result Create(double value) => new V196(value); -} - -// row 197 -public static partial class O197 -{ -public abstract record B197_2 { } -public abstract record B197_1 : B197_2 { } -public sealed partial record V197 : B197_1, IValue { } -} - -// row 198 -public partial struct O198 -{ -public readonly partial record struct V198 : IValidatedValue -{ - public static Result Create(byte value) => new V198(value); -} -} - -// row 199 -public static partial class O199 -{ -public sealed partial class P199 -{ -public interface IMark199 { int Mark(); } -public readonly partial record struct V199 : IValidatedValue, IMark199 -{ - public static Result Create(char value) => new V199(value); int IMark199.Mark() => 1; -} -} -} - -// row 200 -public partial struct O200 -{ -public abstract record B200_1 : System.IEquatable { bool System.IEquatable.Equals(B200_1? other) => ReferenceEquals(this, other); } -public sealed partial record V200 : B200_1, IValue { } -} - -// row 201 -public partial struct O201 -{ -public abstract record B201_3 { } -public abstract record B201_2 : B201_3 { } -public abstract record B201_1 : B201_2 { } -public sealed partial record V201 : B201_1, IValue { } -} - -// row 202 -public partial struct O202 -{ -public abstract record B202_2 { } -public abstract record B202_1 : B202_2 { } -public sealed partial record V202 : B202_1, IValue { } -} - -// row 203 -public static partial class O203 -{ -public sealed partial class P203 -{ -public abstract record B203_1 { } -public sealed partial record V203 : B203_1, IValue { } -} -} - -// row 204 -public partial struct O204 -{ -public abstract record B204_1 { } -public sealed partial record V204 : B204_1, IValidatedValue -{ - public static Result Create(global::System.TimeOnly value) => new V204(value); -} -} - -// row 205 -public partial struct O205 -{ -public abstract record B205_1 { } -public sealed partial record V205 : B205_1, IValue { } -} - -// row 206 -public static partial class O206 -{ -public sealed partial class P206 -{ -public abstract record B206_1 { } -public sealed partial record V206 : B206_1, IValidatedValue -{ - public static Result Create(global::System.Uri value) => new V206(value); -} -} -} - -// row 207 -public abstract record B207_1 { } -public sealed partial record V207 : B207_1, IValue { } - -// row 208 -public abstract record B208_2 { } -public abstract record B208_1 : B208_2 { } -public sealed partial record V208 : B208_1, IValidatedValue -{ - public static Result Create(long value) => new V208(value); -} - -// row 209 -public static partial class O209 -{ -public sealed partial class P209 -{ -public interface IMark209 { int Mark(); } -public sealed partial record V209 : IValidatedValue, IMark209 -{ - public static Result Create(ulong value) => new V209(value); int IMark209.Mark() => 1; -} -} -} - -// row 210 -public static partial class O210 -{ -public abstract record B210_1 { } -public sealed partial record V210 : B210_1, IValidatedValue -{ - public static Result Create(bool value) => new V210(value); -} -} - -// row 211 -public abstract record B211_1 : System.IEquatable { bool System.IEquatable.Equals(B211_1? other) => ReferenceEquals(this, other); } -public sealed partial record V211 : B211_1, IValidatedValue -{ - public static Result Create(global::System.TimeSpan value) => new V211(value); -} - -// row 212 -public static partial class O212 -{ -public abstract record B212_3 { } -public abstract record B212_2 : B212_3 { } -public abstract record B212_1 : B212_2 { } -public sealed partial record V212 : B212_1, IValidatedValue -{ - public static Result Create(int value) => new V212(value); -} -} - -// row 213 -public static partial class O213 -{ -public abstract record B213_1 : System.IEquatable { bool System.IEquatable.Equals(B213_1? other) => ReferenceEquals(this, other); } -public sealed partial record V213 : B213_1, IValue { } -} - -// row 214 -public abstract record B214_1 { } -public sealed partial record V214 : B214_1, IValidatedValue -{ - public static Result Create(decimal value) => new V214(value); -} - -// row 215 -public static partial class O215 -{ -public sealed partial class P215 -{ -public abstract record B215_1 : System.IEquatable { bool System.IEquatable.Equals(B215_1? other) => ReferenceEquals(this, other); } -public sealed partial record V215 : B215_1, IValidatedValue -{ - public static Result Create(byte value) => new V215(value); -} -} -} - -// row 216 -public static partial class O216 -{ -public sealed partial class P216 -{ -public abstract record B216_1 { } -public sealed partial record V216 : B216_1, IValidatedValue -{ - public static Result Create(global::Probes.Shapes.PRec value) => new V216(value); -} -} -} - -// row 217 -public partial struct O217 -{ -public readonly partial record struct V217 : IValidatedValue -{ - public static Result Create(int value) => new V217(value); -} -} - -// row 218 -public static partial class O218 -{ -public abstract record B218_1 : System.IEquatable { bool System.IEquatable.Equals(B218_1? other) => ReferenceEquals(this, other); } -public sealed partial record V218 : B218_1, IValue { } -} - -// row 219 -public static partial class O219 -{ -public sealed partial class P219 -{ -public abstract record B219_2 { } -public abstract record B219_1 : B219_2 { } -public sealed partial record V219 : B219_1, IValidatedValue -{ - public static Result Create(global::System.Guid value) => new V219(value); -} -} -} - -// row 220 -public static partial class O220 -{ -public sealed partial class P220 -{ -public abstract record B220_1 { } -public sealed partial record V220 : B220_1, IValue { } -} -} - -// row 221 -public partial struct O221 -{ -public sealed partial record V221 : IValue { } -} - -// row 222 -public partial struct O222 -{ -public abstract record B222_1 { } -public sealed partial record V222 : B222_1, IValue { } -} - -// row 223 -public sealed partial record V223 : IValue { } - -// row 224 -public partial struct O224 -{ -public abstract record B224_1 { } -public sealed partial record V224 : B224_1, IValidatedValue -{ - public static Result Create(global::NodaTime.Instant value) => new V224(value); -} -} - -// row 225 -public static partial class O225 -{ -public sealed partial class P225 -{ -public readonly partial record struct V225 : IValue { } -} -} - -// row 226 -public static partial class O226 -{ -public sealed partial class P226 -{ -public readonly partial record struct V226 : IValidatedValue -{ - public static Result Create(global::System.DateTimeOffset value) => new V226(value); -} -} -} - -// row 227 -public static partial class O227 -{ -public abstract record B227_3 { } -public abstract record B227_2 : B227_3 { } -public abstract record B227_1 : B227_2 { } -public sealed partial record V227 : B227_1, IValue { } -} - -// row 228 -public static partial class O228 -{ -public readonly partial record struct V228 : IValidatedValue -{ - public static Result Create(global::System.TimeSpan value) => new V228(value); -} -} - -// row 229 -public static partial class O229 -{ -public sealed partial class P229 -{ -public abstract record B229_1 : System.IEquatable { bool System.IEquatable.Equals(B229_1? other) => ReferenceEquals(this, other); } -public sealed partial record V229 : B229_1, IValidatedValue -{ - public static Result Create(global::System.DateTime value) => new V229(value); -} -} -} - -// row 230 -public partial struct O230 -{ -public abstract record B230_1 : System.IEquatable { bool System.IEquatable.Equals(B230_1? other) => ReferenceEquals(this, other); } -public sealed partial record V230 : B230_1, IValidatedValue -{ - public static Result Create(double value) => new V230(value); -} -} - -// row 231 -public static partial class O231 -{ -public interface IMark231 { int Mark(); } -public readonly partial record struct V231 : IValidatedValue, IMark231 -{ - public static Result Create(global::System.Uri value) => new V231(value); int IMark231.Mark() => 1; -} -} - -// row 232 -public partial struct O232 -{ -public interface IMark232 { int Mark(); } -public readonly partial record struct V232 : IValue, IMark232 { int IMark232.Mark() => 1; } -} - -// row 233 -public static partial class O233 -{ -public sealed partial class P233 -{ -public readonly partial record struct V233 : IValue { } -} -} - -// row 234 -public readonly partial record struct V234 : IValue { } - -// row 235 -public abstract record B235_3 { } -public abstract record B235_2 : B235_3 { } -public abstract record B235_1 : B235_2 { } -public sealed partial record V235 : B235_1, IValidatedValue -{ - public static Result Create(global::System.DateTimeOffset value) => new V235(value); -} - -// row 236 -public abstract record B236_3 { } -public abstract record B236_2 : B236_3 { } -public abstract record B236_1 : B236_2 { } -public sealed partial record V236 : B236_1, IValidatedValue -{ - public static Result Create(bool value) => new V236(value); -} - -// row 237 -public static partial class O237 -{ -public abstract record B237_2 { } -public abstract record B237_1 : B237_2 { } -public sealed partial record V237 : B237_1, IValue { } -} - -// row 238 -public static partial class O238 -{ -public sealed partial class P238 -{ -public abstract record B238_1 { } -public sealed partial record V238 : B238_1, IValue { } -} -} - -// row 239 -public abstract record B239_1 { } -public sealed partial record V239 : B239_1, IValue { } - -// row 240 -public abstract record B240_1 { } -public sealed partial record V240 : B240_1, IValue { } - -// row 241 -public static partial class O241 -{ -public sealed partial class P241 -{ -public interface IMark241 { int Mark(); } -public sealed partial record V241 : IValidatedValue, IMark241 -{ - public static Result Create(global::Probes.Shapes.PEnum value) => new V241(value); int IMark241.Mark() => 1; -} -} -} - -// row 242 -public partial struct O242 -{ -public abstract record B242_3 { } -public abstract record B242_2 : B242_3 { } -public abstract record B242_1 : B242_2 { } -public sealed partial record V242 : B242_1, IValue { } -} - -// row 243 -public readonly partial record struct V243 : IValue { } - -// row 244 -public static partial class O244 -{ -public sealed partial class P244 -{ -public readonly partial record struct V244 : IValidatedValue -{ - public static Result Create(global::NodaTime.Instant value) => new V244(value); -} -} -} - -// row 245 -public partial struct O245 -{ -public interface IMark245 { int Mark(); } -public readonly partial record struct V245 : IValue, IMark245 { int IMark245.Mark() => 1; } -} - -// row 246 -public static partial class O246 -{ -public sealed partial class P246 -{ -public abstract record B246_3 { } -public abstract record B246_2 : B246_3 { } -public abstract record B246_1 : B246_2 { } -public sealed partial record V246 : B246_1, IValidatedValue -{ - public static Result Create(global::System.Uri value) => new V246(value); -} -} -} - -public static partial class C11Table -{ - public static readonly C11Case[] Cases = - [ - new(0, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=double;text.culture=invariant;text.entry=TryFrom", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O0.V0), typeof(double), null, null, []), - new(1, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=TimeSpan;text.culture=de-DE;text.entry=ISpanParsable", "validated-class", "top-level", "none", typeof(V1), typeof(global::System.TimeSpan), null, null, []), - new(2, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=decimal;text.culture=tr-TR;text.entry=TryFormat-short-span", "sealed-record-class", "nested-in-struct", "base-depth-2", typeof(O2.V2), typeof(global::Probes.Build.C11ParsesToInt), typeof(O2.VC2), typeof(global::Probes.Build.C11ParsedShape), []), - new(3, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=TimeOnly;text.culture=tr-TR;text.entry=Parse", "validated-struct", "nested-two-levels", "none", typeof(O3.P3.V3), typeof(global::Probes.Build.C11AbstractText), typeof(O3.P3.VC3), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(4, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=decimal;text.culture=ar-SA;text.entry=ToString-format", "record-struct", "top-level", "explicit-interface-member", typeof(V4), typeof(global::Probes.Build.C11ParsesByRef), typeof(VC4), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(5, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=char;text.culture=globalization-invariant;text.entry=IParsable", "validated-class", "nested-in-struct", "base-depth-1", typeof(O5.V5), typeof(global::Probes.Build.C11AbstractText), typeof(O5.VC5), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(6, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=string;text.culture=globalization-invariant;text.entry=TryParse", "record-struct", "nested-in-class", "none", typeof(O6.V6), typeof(global::Probes.Build.C11ParsesToInt), typeof(O6.VC6), typeof(global::Probes.Build.C11ParsedShape), []), - new(7, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=NodaTime.Instant;text.culture=invariant;text.entry=type-converter", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O7.P7.V7), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O7.P7.VC7), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(8, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=long;text.culture=ar-SA;text.entry=IParsable", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O8.P8.V8), typeof(long), null, null, []), - new(9, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=NodaTime.Instant;text.culture=de-DE;text.entry=TryParse", "validated-class", "nested-in-struct", "base-depth-3", typeof(O9.V9), typeof(global::NodaTime.Instant), null, null, []), - new(10, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=ulong;text.culture=invariant;text.entry=TryFormat-short-span", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V10), typeof(global::Probes.Build.C11ParsesToInt), typeof(VC10), typeof(global::Probes.Build.C11ParsedShape), []), - new(11, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=Guid;text.culture=de-DE;text.entry=Parse", "validated-struct", "nested-in-class", "explicit-interface-member", typeof(O11.V11), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O11.VC11), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(12, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=bool;text.culture=globalization-invariant;text.entry=type-converter", "sealed-record-class", "top-level", "base-depth-2", typeof(V12), typeof(bool), null, null, []), - new(13, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateTime;text.culture=tr-TR;text.entry=ToString-format", "validated-class", "nested-in-class", "base-depth-3", typeof(O13.V13), typeof(global::Probes.Build.C11AbstractText), typeof(O13.VC13), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(14, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateOnly;text.culture=ar-SA;text.entry=ISpanParsable", "validated-struct", "nested-in-struct", "none", typeof(O14.V14), typeof(global::System.DateOnly), null, null, [("shape.inheritance", "none")]), - new(15, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=int;text.culture=invariant;text.entry=TryFrom", "record-struct", "nested-in-struct", "none", typeof(O15.V15), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O15.VC15), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(16, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=enum;text.culture=globalization-invariant;text.entry=TryFormat-short-span", "record-struct", "nested-two-levels", "explicit-interface-member", typeof(O16.P16.V16), typeof(global::Probes.Build.C11AbstractText), typeof(O16.P16.VC16), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(17, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=Uri;text.culture=tr-TR;text.entry=Parse", "record-struct", "top-level", "none", typeof(V17), typeof(global::System.Uri), null, null, [("shape.inheritance", "none")]), - new(18, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=TimeOnly;text.culture=ar-SA;text.entry=TryFrom", "validated-class", "nested-two-levels", "base-depth-2", typeof(O18.P18.V18), typeof(global::Probes.Build.C11ParsesToInt), typeof(O18.P18.VC18), typeof(global::Probes.Build.C11ParsedShape), []), - new(19, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=string;text.culture=de-DE;text.entry=ISpanParsable", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O19.V19), typeof(global::Probes.Build.C11AbstractText), typeof(O19.VC19), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(20, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=TimeSpan;text.culture=tr-TR;text.entry=IParsable", "validated-struct", "nested-in-struct", "explicit-interface-member", typeof(O20.V20), typeof(global::Probes.Build.C11ParsesByRef), typeof(O20.VC20), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(21, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=DateTimeOffset;text.culture=ar-SA;text.entry=TryFormat-short-span", "validated-struct", "nested-in-class", "none", typeof(O21.V21), typeof(global::System.DateTimeOffset), null, null, []), - new(22, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=ulong;text.culture=de-DE;text.entry=ISpanParsable", "record-struct", "nested-two-levels", "explicit-interface-member", typeof(O22.P22.V22), typeof(global::Probes.Build.C11ParsesToInt), typeof(O22.P22.VC22), typeof(global::Probes.Build.C11ParsedShape), []), - new(23, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=byte;text.culture=invariant;text.entry=ToString-format", "sealed-record-class", "nested-two-levels", "base-depth-3", typeof(O23.P23.V23), typeof(byte), null, null, []), - new(24, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=validated-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=own-record-class;text.culture=de-DE;text.entry=ToString-format", "validated-struct", "nested-in-class", "none", typeof(O24.V24), typeof(global::Probes.Build.C11ParsesToInt), typeof(O24.VC24), typeof(global::Probes.Build.C11ParsedShape), [("shape.inheritance", "none")]), - new(25, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=char;text.culture=ar-SA;text.entry=TryParse", "validated-struct", "top-level", "explicit-interface-member", typeof(V25), typeof(global::Probes.Build.C11AbstractText), typeof(VC25), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(26, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=byte;text.culture=globalization-invariant;text.entry=Parse", "validated-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O26.V26), typeof(global::Probes.Build.C11ParsesByRef), typeof(O26.VC26), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(27, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=ulong;text.culture=ar-SA;text.entry=type-converter", "validated-struct", "nested-in-class", "none", typeof(O27.V27), typeof(global::Probes.Build.C11ParsesToInt), typeof(O27.VC27), typeof(global::Probes.Build.C11ParsedShape), [("shape.inheritance", "none")]), - new(28, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=TimeOnly;text.culture=globalization-invariant;text.entry=TryFrom", "validated-struct", "top-level", "none", typeof(V28), typeof(global::Probes.Build.C11ParsesByRef), typeof(VC28), typeof(global::Probes.Build.C11ParsesFromIn), [("shape.inheritance", "none")]), - new(29, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=flags-enum;text.culture=de-DE;text.entry=type-converter", "validated-class", "nested-in-struct", "explicit-interface-member", typeof(O29.V29), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O29.VC29), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(30, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=DateOnly;text.culture=invariant;text.entry=TryParse", "sealed-record-class", "top-level", "base-depth-2", typeof(V30), typeof(global::Probes.Build.C11ParsesByRef), typeof(VC30), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(31, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=decimal;text.culture=invariant;text.entry=IParsable", "validated-struct", "nested-in-class", "none", typeof(O31.V31), typeof(global::Probes.Build.C11ParsesToInt), typeof(O31.VC31), typeof(global::Probes.Build.C11ParsedShape), []), - new(32, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Guid;text.culture=globalization-invariant;text.entry=ToString-format", "sealed-record-class", "nested-in-struct", "none", typeof(O32.V32), typeof(global::Probes.Build.C11AbstractText), typeof(O32.VC32), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(33, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=long;text.culture=de-DE;text.entry=TryFrom", "validated-class", "nested-in-class", "base-depth-1", typeof(O33.V33), typeof(long), null, null, []), - new(34, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=flags-enum;text.culture=globalization-invariant;text.entry=Parse", "sealed-record-class", "top-level", "base-depth-3", typeof(V34), typeof(global::Probes.Build.C11ParsesToInt), typeof(VC34), typeof(global::Probes.Build.C11ParsedShape), []), - new(35, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=int;text.culture=globalization-invariant;text.entry=IParsable", "sealed-record-class", "top-level", "base-depth-2", typeof(V35), typeof(int), null, null, []), - new(36, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTime;text.culture=invariant;text.entry=Parse", "sealed-record-class", "nested-in-struct", "base-depth-2", typeof(O36.V36), typeof(global::Probes.Build.C11AbstractText), typeof(O36.VC36), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(37, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=char;text.culture=tr-TR;text.entry=type-converter", "sealed-record-class", "nested-two-levels", "none", typeof(O37.P37.V37), typeof(char), null, null, []), - new(38, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=DateTimeOffset;text.culture=globalization-invariant;text.entry=ISpanParsable", "validated-class", "nested-in-struct", "base-depth-2", typeof(O38.V38), typeof(global::Probes.Build.C11ParsesByRef), typeof(O38.VC38), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(39, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=enum;text.culture=ar-SA;text.entry=Parse", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O39.V39), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(40, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Uri;text.culture=invariant;text.entry=ISpanParsable", "validated-struct", "nested-in-struct", "none", typeof(O40.V40), typeof(global::Probes.Build.C11ParsesToInt), typeof(O40.VC40), typeof(global::Probes.Build.C11ParsedShape), [("shape.inheritance", "none")]), - new(41, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=own-record-class;text.culture=tr-TR;text.entry=ISpanParsable", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O41.V41), typeof(global::Probes.Shapes.PRec), null, null, []), - new(42, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Guid;text.culture=tr-TR;text.entry=TryParse", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O42.P42.V42), typeof(global::System.Guid), null, null, []), - new(43, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=bool;text.culture=de-DE;text.entry=IParsable", "record-struct", "nested-in-struct", "none", typeof(O43.V43), typeof(global::Probes.Build.C11AbstractText), typeof(O43.VC43), typeof(global::Probes.Build.C11ConstructedConcrete), [("shape.inheritance", "none")]), - new(44, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=TimeOnly;text.culture=de-DE;text.entry=TryFormat-short-span", "record-struct", "nested-two-levels", "none", typeof(O44.P44.V44), typeof(global::Probes.Build.C11ParsesByRef), typeof(O44.P44.VC44), typeof(global::Probes.Build.C11ParsesFromIn), [("shape.inheritance", "none")]), - new(45, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=double;text.culture=ar-SA;text.entry=TryFormat-short-span", "validated-struct", "top-level", "none", typeof(V45), typeof(global::Probes.Build.C11ConstructedByRef), typeof(VC45), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(46, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=flags-enum;text.culture=invariant;text.entry=ToString-format", "validated-struct", "nested-in-class", "none", typeof(O46.V46), typeof(global::Probes.Shapes.PFlags), null, null, [("shape.inheritance", "none")]), - new(47, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=int;text.culture=de-DE;text.entry=type-converter", "validated-struct", "nested-in-class", "none", typeof(O47.V47), typeof(int), null, null, [("shape.inheritance", "none")]), - new(48, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=in-generic-type;shape.wrapped=Guid;text.culture=invariant;text.entry=type-converter", "record-struct", "nested-in-class", "none", typeof(O48.V48), typeof(global::Probes.Build.C11AbstractText), typeof(O48.VC48), typeof(global::Probes.Build.C11ConstructedConcrete), [("shape.site", "nested-in-class"), ("shape.inheritance", "none")]), - new(49, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=bool;text.culture=tr-TR;text.entry=TryFrom", "validated-class", "nested-in-class", "base-explicit-IEquatable", typeof(O49.V49), typeof(bool), null, null, []), - new(50, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=DateTimeOffset;text.culture=invariant;text.entry=type-converter", "sealed-record-class", "top-level", "base-explicit-IEquatable", typeof(V50), typeof(global::Probes.Build.C11ParsesByRef), typeof(VC50), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(51, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateOnly;text.culture=globalization-invariant;text.entry=IParsable", "validated-class", "nested-in-class", "base-depth-3", typeof(O51.V51), typeof(global::System.DateOnly), null, null, []), - new(52, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=ulong;text.culture=tr-TR;text.entry=TryFrom", "sealed-record-class", "nested-in-struct", "none", typeof(O52.V52), typeof(ulong), null, null, []), - new(53, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=string;text.culture=invariant;text.entry=TryFormat-short-span", "validated-class", "nested-two-levels", "base-depth-3", typeof(O53.P53.V53), typeof(string), null, null, []), - new(54, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Uri;text.culture=ar-SA;text.entry=TryFrom", "validated-class", "nested-two-levels", "base-depth-3", typeof(O54.P54.V54), typeof(global::Probes.Build.C11AbstractText), typeof(O54.P54.VC54), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(55, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=TimeSpan;text.culture=ar-SA;text.entry=TryParse", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O55.P55.V55), typeof(global::System.TimeSpan), null, null, []), - new(56, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=decimal;text.culture=globalization-invariant;text.entry=TryParse", "validated-class", "nested-two-levels", "base-depth-1", typeof(O56.P56.V56), typeof(decimal), null, null, []), - new(57, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=enum;text.culture=de-DE;text.entry=ToString-format", "validated-class", "top-level", "base-depth-1", typeof(V57), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(58, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=byte;text.culture=tr-TR;text.entry=TryParse", "record-struct", "top-level", "none", typeof(V58), typeof(byte), null, null, []), - new(59, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=double;text.culture=tr-TR;text.entry=TryParse", "validated-class", "nested-two-levels", "base-depth-3", typeof(O59.P59.V59), typeof(double), null, null, []), - new(60, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeOnly;text.culture=invariant;text.entry=ToString-format", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O60.V60), typeof(global::System.TimeOnly), null, null, []), - new(61, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=string;text.culture=tr-TR;text.entry=Parse", "sealed-record-class", "nested-in-struct", "base-depth-1", typeof(O61.V61), typeof(global::Probes.Build.C11ParsesToInt), typeof(O61.VC61), typeof(global::Probes.Build.C11ParsedShape), []), - new(62, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateTime;text.culture=ar-SA;text.entry=ISpanParsable", "validated-struct", "nested-two-levels", "none", typeof(O62.P62.V62), typeof(global::System.DateTime), null, null, []), - new(63, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=NodaTime.Instant;text.culture=tr-TR;text.entry=TryFrom", "record-struct", "top-level", "explicit-interface-member", typeof(V63), typeof(global::NodaTime.Instant), null, null, []), - new(64, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=enum;text.culture=tr-TR;text.entry=type-converter", "validated-struct", "nested-in-class", "none", typeof(O64.V64), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(65, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=Guid;text.culture=ar-SA;text.entry=TryFormat-short-span", "validated-class", "top-level", "base-depth-2", typeof(V65), typeof(global::System.Guid), null, null, []), - new(66, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=own-record-class;text.culture=ar-SA;text.entry=IParsable", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V66), typeof(global::Probes.Shapes.PRec), null, null, []), - new(67, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=bool;text.culture=ar-SA;text.entry=ISpanParsable", "validated-struct", "nested-two-levels", "none", typeof(O67.P67.V67), typeof(global::Probes.Build.C11AbstractText), typeof(O67.P67.VC67), typeof(global::Probes.Build.C11ConstructedConcrete), [("shape.inheritance", "none")]), - new(68, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=DateOnly;text.culture=tr-TR;text.entry=ToString-format", "record-struct", "nested-two-levels", "none", typeof(O68.P68.V68), typeof(global::System.DateOnly), null, null, [("shape.inheritance", "none")]), - new(69, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=long;text.culture=invariant;text.entry=TryParse", "validated-struct", "nested-in-struct", "explicit-interface-member", typeof(O69.V69), typeof(long), null, null, []), - new(70, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateTimeOffset;text.culture=tr-TR;text.entry=Parse", "record-struct", "nested-two-levels", "explicit-interface-member", typeof(O70.P70.V70), typeof(global::System.DateTimeOffset), null, null, []), - new(71, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=flags-enum;text.culture=ar-SA;text.entry=TryFormat-short-span", "record-struct", "nested-two-levels", "none", typeof(O71.P71.V71), typeof(global::Probes.Shapes.PFlags), null, null, []), - new(72, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=double;text.culture=globalization-invariant;text.entry=ToString-format", "record-struct", "nested-in-struct", "none", typeof(O72.V72), typeof(double), null, null, [("shape.inheritance", "none")]), - new(73, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=byte;text.culture=de-DE;text.entry=ISpanParsable", "validated-struct", "nested-in-class", "explicit-interface-member", typeof(O73.V73), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O73.VC73), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(74, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=char;text.culture=de-DE;text.entry=TryFormat-short-span", "record-struct", "nested-in-class", "none", typeof(O74.V74), typeof(char), null, null, []), - new(75, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=int;text.culture=ar-SA;text.entry=TryFormat-short-span", "validated-class", "nested-in-struct", "base-depth-1", typeof(O75.V75), typeof(int), null, null, []), - new(76, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=Uri;text.culture=de-DE;text.entry=TryParse", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O76.V76), typeof(global::System.Uri), null, null, []), - new(77, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=long;text.culture=globalization-invariant;text.entry=ToString-format", "record-struct", "top-level", "none", typeof(V77), typeof(global::Probes.Build.C11AbstractText), typeof(VC77), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(78, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=own-record-class;text.culture=invariant;text.entry=TryParse", "record-struct", "nested-two-levels", "none", typeof(O78.P78.V78), typeof(global::Probes.Build.C11ParsesToInt), typeof(O78.P78.VC78), typeof(global::Probes.Build.C11ParsedShape), []), - new(79, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=DateTime;text.culture=globalization-invariant;text.entry=TryFrom", "sealed-record-class", "top-level", "base-explicit-IEquatable", typeof(V79), typeof(global::Probes.Build.C11ParsesToInt), typeof(VC79), typeof(global::Probes.Build.C11ParsedShape), []), - new(80, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=NodaTime.Instant;text.culture=globalization-invariant;text.entry=IParsable", "validated-struct", "nested-in-class", "none", typeof(O80.V80), typeof(global::NodaTime.Instant), null, null, [("shape.inheritance", "none")]), - new(81, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=closed-set;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeSpan;text.culture=ar-SA;text.entry=Parse", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O81.V81), typeof(global::Probes.Build.C11AbstractText), typeof(O81.VC81), typeof(global::Probes.Build.C11ConstructedConcrete), [("shape.kind", "sealed-record-class")]), - new(82, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateTime;text.culture=de-DE;text.entry=IParsable", "record-struct", "nested-two-levels", "explicit-interface-member", typeof(O82.P82.V82), typeof(global::System.DateTime), null, null, []), - new(83, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=string;text.culture=ar-SA;text.entry=ToString-format", "validated-class", "top-level", "base-depth-2", typeof(V83), typeof(string), null, null, []), - new(84, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=long;text.culture=tr-TR;text.entry=ISpanParsable", "validated-class", "nested-in-class", "base-depth-2", typeof(O84.V84), typeof(long), null, null, []), - new(85, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=TimeOnly;text.culture=invariant;text.entry=type-converter", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O85.V85), typeof(global::System.TimeOnly), null, null, []), - new(86, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=byte;text.culture=ar-SA;text.entry=TryFrom", "sealed-record-class", "nested-two-levels", "base-depth-2", typeof(O86.P86.V86), typeof(global::Probes.Build.C11ParsesToInt), typeof(O86.P86.VC86), typeof(global::Probes.Build.C11ParsedShape), []), - new(87, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=int;text.culture=tr-TR;text.entry=ToString-format", "sealed-record-class", "nested-two-levels", "none", typeof(O87.P87.V87), typeof(int), null, null, []), - new(88, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=none;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=DateOnly;text.culture=de-DE;text.entry=type-converter", "sealed-record-class", "top-level", "none", typeof(V88), typeof(global::Probes.Build.C11AbstractText), typeof(VC88), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(89, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=ulong;text.culture=globalization-invariant;text.entry=TryParse", "validated-class", "top-level", "base-depth-2", typeof(V89), typeof(ulong), null, null, []), - new(90, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=NodaTime.Instant;text.culture=ar-SA;text.entry=TryFormat-short-span", "record-struct", "top-level", "none", typeof(V90), typeof(global::NodaTime.Instant), null, null, []), - new(91, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=closed-set;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateTimeOffset;text.culture=de-DE;text.entry=ToString-format", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O91.P91.V91), typeof(global::Probes.Build.C11ParsesToInt), typeof(O91.P91.VC91), typeof(global::Probes.Build.C11ParsedShape), [("shape.kind", "sealed-record-class")]), - new(92, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=in-generic-type;shape.wrapped=TimeSpan;text.culture=invariant;text.entry=type-converter", "validated-class", "nested-in-class", "base-depth-2", typeof(O92.V92), typeof(global::System.TimeSpan), null, null, [("shape.site", "nested-in-class")]), - new(93, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=decimal;text.culture=de-DE;text.entry=ISpanParsable", "sealed-record-class", "top-level", "base-depth-3", typeof(V93), typeof(decimal), null, null, []), - new(94, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Uri;text.culture=globalization-invariant;text.entry=IParsable", "sealed-record-class", "nested-in-struct", "base-depth-2", typeof(O94.V94), typeof(global::System.Uri), null, null, []), - new(95, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=char;text.culture=invariant;text.entry=TryFrom", "validated-class", "top-level", "base-depth-3", typeof(V95), typeof(char), null, null, []), - new(96, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=own-record-class;text.culture=globalization-invariant;text.entry=type-converter", "validated-class", "nested-in-struct", "base-depth-3", typeof(O96.V96), typeof(global::Probes.Shapes.PRec), null, null, []), - new(97, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=flags-enum;text.culture=tr-TR;text.entry=TryFrom", "validated-class", "top-level", "base-depth-1", typeof(V97), typeof(global::Probes.Shapes.PFlags), null, null, []), - new(98, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=enum;text.culture=invariant;text.entry=TryParse", "sealed-record-class", "top-level", "base-depth-2", typeof(V98), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(99, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=string;text.culture=invariant;text.entry=TryFrom", "validated-struct", "top-level", "explicit-interface-member", typeof(V99), typeof(string), null, null, []), - new(100, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=double;text.culture=de-DE;text.entry=ISpanParsable", "validated-class", "top-level", "base-depth-1", typeof(V100), typeof(global::Probes.Build.C11ParsesToInt), typeof(VC100), typeof(global::Probes.Build.C11ParsedShape), []), - new(101, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=in-generic-type;shape.wrapped=DateOnly;text.culture=tr-TR;text.entry=Parse", "validated-struct", "nested-in-class", "explicit-interface-member", typeof(O101.V101), typeof(global::System.DateOnly), null, null, [("shape.site", "nested-in-class")]), - new(102, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=bool;text.culture=invariant;text.entry=TryParse", "sealed-record-class", "nested-two-levels", "explicit-interface-member", typeof(O102.P102.V102), typeof(bool), null, null, []), - new(103, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=bool;text.culture=ar-SA;text.entry=ToString-format", "validated-struct", "top-level", "none", typeof(V103), typeof(global::Probes.Build.C11ParsesToInt), typeof(VC103), typeof(global::Probes.Build.C11ParsedShape), [("shape.inheritance", "none")]), - new(104, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=TimeSpan;text.culture=globalization-invariant;text.entry=TryFormat-short-span", "record-struct", "nested-two-levels", "none", typeof(O104.P104.V104), typeof(global::System.TimeSpan), null, null, []), - new(105, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=own-record-class;text.culture=de-DE;text.entry=TryFormat-short-span", "record-struct", "top-level", "explicit-interface-member", typeof(V105), typeof(global::Probes.Shapes.PRec), null, null, []), - new(106, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=enum;text.culture=globalization-invariant;text.entry=ISpanParsable", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O106.P106.V106), typeof(global::Probes.Build.C11ParsesToInt), typeof(O106.P106.VC106), typeof(global::Probes.Build.C11ParsedShape), []), - new(107, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Uri;text.culture=tr-TR;text.entry=ToString-format", "record-struct", "nested-two-levels", "none", typeof(O107.P107.V107), typeof(global::Probes.Build.C11ParsesToInt), typeof(O107.P107.VC107), typeof(global::Probes.Build.C11ParsedShape), []), - new(108, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=bool;text.culture=tr-TR;text.entry=TryFormat-short-span", "validated-struct", "nested-two-levels", "none", typeof(O108.P108.V108), typeof(bool), null, null, []), - new(109, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=flags-enum;text.culture=ar-SA;text.entry=ISpanParsable", "validated-class", "nested-in-class", "base-depth-2", typeof(O109.V109), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O109.VC109), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(110, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Uri;text.culture=ar-SA;text.entry=type-converter", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O110.P110.V110), typeof(global::System.Uri), null, null, []), - new(111, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=int;text.culture=de-DE;text.entry=ISpanParsable", "record-struct", "nested-in-class", "none", typeof(O111.V111), typeof(global::Probes.Build.C11ParsesToInt), typeof(O111.VC111), typeof(global::Probes.Build.C11ParsedShape), [("shape.inheritance", "none")]), - new(112, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=int;text.culture=ar-SA;text.entry=TryParse", "validated-struct", "nested-in-struct", "explicit-interface-member", typeof(O112.V112), typeof(int), null, null, []), - new(113, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=ulong;text.culture=globalization-invariant;text.entry=Parse", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O113.V113), typeof(ulong), null, null, []), - new(114, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=double;text.culture=de-DE;text.entry=IParsable", "validated-class", "nested-in-struct", "base-depth-2", typeof(O114.V114), typeof(double), null, null, []), - new(115, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateTime;text.culture=tr-TR;text.entry=TryParse", "validated-struct", "nested-in-class", "none", typeof(O115.V115), typeof(global::System.DateTime), null, null, [("shape.inheritance", "none")]), - new(116, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=closed-set;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=in-generic-type;shape.wrapped=char;text.culture=globalization-invariant;text.entry=ISpanParsable", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O116.V116), typeof(char), null, null, [("shape.kind", "sealed-record-class"), ("shape.site", "nested-in-class")]), - new(117, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=byte;text.culture=tr-TR;text.entry=TryFormat-short-span", "sealed-record-class", "nested-in-class", "base-depth-1", typeof(O117.V117), typeof(global::Probes.Build.C11ParsesToInt), typeof(O117.VC117), typeof(global::Probes.Build.C11ParsedShape), []), - new(118, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=char;text.culture=ar-SA;text.entry=Parse", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O118.V118), typeof(char), null, null, []), - new(119, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Guid;text.culture=tr-TR;text.entry=IParsable", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O119.P119.V119), typeof(global::System.Guid), null, null, []), - new(120, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=NodaTime.Instant;text.culture=invariant;text.entry=ToString-format", "validated-class", "nested-in-struct", "base-depth-2", typeof(O120.V120), typeof(global::NodaTime.Instant), null, null, []), - new(121, "parse-shape=abstract-string-ctor;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=closed-set;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=TimeSpan;text.culture=invariant;text.entry=TryFrom", "sealed-record-class", "top-level", "base-depth-3", typeof(V121), typeof(global::Probes.Build.C11AbstractText), typeof(VC121), typeof(global::Probes.Build.C11ConstructedConcrete), [("shape.kind", "sealed-record-class")]), - new(122, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=in-generic-type;shape.wrapped=long;text.culture=globalization-invariant;text.entry=type-converter", "record-struct", "nested-in-class", "none", typeof(O122.V122), typeof(long), null, null, [("shape.site", "nested-in-class"), ("shape.inheritance", "none")]), - new(123, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=closed-set;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateOnly;text.culture=de-DE;text.entry=TryFrom", "sealed-record-class", "nested-in-struct", "explicit-interface-member", typeof(O123.V123), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O123.VC123), typeof(global::Probes.Build.C11ParsesFromIn), [("shape.kind", "sealed-record-class")]), - new(124, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTimeOffset;text.culture=tr-TR;text.entry=TryFrom", "validated-struct", "nested-in-struct", "none", typeof(O124.V124), typeof(global::Probes.Build.C11ParsesByRef), typeof(O124.VC124), typeof(global::Probes.Build.C11ParsesFromIn), [("shape.inheritance", "none")]), - new(125, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=Uri;text.culture=tr-TR;text.entry=TryFormat-short-span", "validated-struct", "nested-in-class", "explicit-interface-member", typeof(O125.V125), typeof(global::System.Uri), null, null, []), - new(126, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=decimal;text.culture=de-DE;text.entry=Parse", "validated-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O126.P126.V126), typeof(decimal), null, null, []), - new(127, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=closed-set;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=NodaTime.Instant;text.culture=ar-SA;text.entry=Parse", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O127.V127), typeof(global::NodaTime.Instant), null, null, [("shape.kind", "sealed-record-class")]), - new(128, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=DateTime;text.culture=de-DE;text.entry=type-converter", "validated-class", "nested-in-class", "base-explicit-IEquatable", typeof(O128.V128), typeof(global::System.DateTime), null, null, []), - new(129, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=own-record-class;text.culture=ar-SA;text.entry=TryFrom", "validated-struct", "top-level", "none", typeof(V129), typeof(global::Probes.Build.C11ConstructedByRef), typeof(VC129), typeof(global::Probes.Build.C11ParsesFromIn), [("shape.inheritance", "none")]), - new(130, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=char;text.culture=globalization-invariant;text.entry=ToString-format", "record-struct", "nested-in-class", "explicit-interface-member", typeof(O130.V130), typeof(char), null, null, []), - new(131, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=long;text.culture=globalization-invariant;text.entry=TryFormat-short-span", "record-struct", "nested-in-struct", "explicit-interface-member", typeof(O131.V131), typeof(long), null, null, []), - new(132, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=double;text.culture=invariant;text.entry=Parse", "validated-struct", "top-level", "explicit-interface-member", typeof(V132), typeof(global::Probes.Build.C11AbstractText), typeof(VC132), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(133, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=own-record-class;text.culture=de-DE;text.entry=Parse", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O133.V133), typeof(global::Probes.Shapes.PRec), null, null, []), - new(134, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=NodaTime.Instant;text.culture=invariant;text.entry=ISpanParsable", "validated-class", "top-level", "base-depth-2", typeof(V134), typeof(global::Probes.Build.C11ParsesByRef), typeof(VC134), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(135, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=closed-set;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=TimeSpan;text.culture=globalization-invariant;text.entry=ToString-format", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O135.V135), typeof(global::System.TimeSpan), null, null, [("shape.kind", "sealed-record-class")]), - new(136, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=TimeOnly;text.culture=de-DE;text.entry=TryParse", "record-struct", "nested-in-struct", "explicit-interface-member", typeof(O136.V136), typeof(global::System.TimeOnly), null, null, []), - new(137, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=byte;text.culture=ar-SA;text.entry=IParsable", "sealed-record-class", "nested-in-class", "base-depth-1", typeof(O137.V137), typeof(global::Probes.Build.C11ParsesToInt), typeof(O137.VC137), typeof(global::Probes.Build.C11ParsedShape), []), - new(138, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeOnly;text.culture=invariant;text.entry=ISpanParsable", "record-struct", "nested-in-class", "none", typeof(O138.V138), typeof(global::Probes.Build.C11AbstractText), typeof(O138.VC138), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(139, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=closed-set;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=flags-enum;text.culture=de-DE;text.entry=TryParse", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O139.P139.V139), typeof(global::Probes.Shapes.PFlags), null, null, [("shape.kind", "sealed-record-class")]), - new(140, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=bool;text.culture=globalization-invariant;text.entry=Parse", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O140.V140), typeof(bool), null, null, []), - new(141, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=in-generic-type;shape.wrapped=DateTimeOffset;text.culture=de-DE;text.entry=IParsable", "record-struct", "nested-in-class", "explicit-interface-member", typeof(O141.V141), typeof(global::Probes.Build.C11AbstractText), typeof(O141.VC141), typeof(global::Probes.Build.C11ConstructedConcrete), [("shape.site", "nested-in-class")]), - new(142, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=in-generic-type;shape.wrapped=TimeOnly;text.culture=invariant;text.entry=IParsable", "validated-struct", "nested-in-class", "none", typeof(O142.V142), typeof(global::Probes.Build.C11ParsesToInt), typeof(O142.VC142), typeof(global::Probes.Build.C11ParsedShape), [("shape.site", "nested-in-class")]), - new(143, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=none;shape.kind=closed-set;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=byte;text.culture=tr-TR;text.entry=type-converter", "sealed-record-class", "top-level", "none", typeof(V143), typeof(global::Probes.Build.C11ConstructedByRef), typeof(VC143), typeof(global::Probes.Build.C11ParsesFromIn), [("shape.kind", "sealed-record-class")]), - new(144, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=enum;text.culture=de-DE;text.entry=TryFrom", "record-struct", "nested-two-levels", "explicit-interface-member", typeof(O144.P144.V144), typeof(global::Probes.Build.C11AbstractText), typeof(O144.P144.VC144), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(145, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=ulong;text.culture=de-DE;text.entry=ToString-format", "validated-struct", "nested-two-levels", "none", typeof(O145.P145.V145), typeof(ulong), null, null, []), - new(146, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=int;text.culture=de-DE;text.entry=Parse", "validated-struct", "nested-in-class", "none", typeof(O146.V146), typeof(global::Probes.Build.C11ParsesToInt), typeof(O146.VC146), typeof(global::Probes.Build.C11ParsedShape), []), - new(147, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=Guid;text.culture=ar-SA;text.entry=ISpanParsable", "validated-struct", "nested-two-levels", "explicit-interface-member", typeof(O147.P147.V147), typeof(global::Probes.Build.C11AbstractText), typeof(O147.P147.VC147), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(148, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=in-generic-type;shape.wrapped=enum;text.culture=globalization-invariant;text.entry=IParsable", "validated-class", "nested-in-class", "none", typeof(O148.V148), typeof(global::Probes.Shapes.PEnum), null, null, [("shape.site", "nested-in-class")]), - new(149, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTime;text.culture=invariant;text.entry=TryFormat-short-span", "sealed-record-class", "nested-in-struct", "explicit-interface-member", typeof(O149.V149), typeof(global::Probes.Build.C11ParsesToInt), typeof(O149.VC149), typeof(global::Probes.Build.C11ParsedShape), []), - new(150, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=decimal;text.culture=de-DE;text.entry=TryFrom", "validated-class", "nested-two-levels", "base-depth-3", typeof(O150.P150.V150), typeof(decimal), null, null, []), - new(151, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=ulong;text.culture=invariant;text.entry=IParsable", "validated-class", "nested-in-class", "none", typeof(O151.V151), typeof(ulong), null, null, []), - new(152, "parse-shape=abstract-string-ctor;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=double;text.culture=ar-SA;text.entry=type-converter", "sealed-record-class", "nested-in-class", "explicit-interface-member", typeof(O152.V152), typeof(global::Probes.Build.C11AbstractText), typeof(O152.VC152), typeof(global::Probes.Build.C11ConstructedConcrete), []), - new(153, "parse-shape=implicit-result;process.converter-lookup=none;shape.inheritance=none;shape.kind=closed-set;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=in-generic-type;shape.wrapped=Guid;text.culture=ar-SA;text.entry=TryFrom", "sealed-record-class", "nested-in-class", "none", typeof(O153.V153), typeof(global::Probes.Build.C11ParsesToInt), typeof(O153.VC153), typeof(global::Probes.Build.C11ParsedShape), [("shape.kind", "sealed-record-class"), ("shape.site", "nested-in-class")]), - new(154, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=long;text.culture=invariant;text.entry=Parse", "sealed-record-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O154.P154.V154), typeof(global::Probes.Build.C11ParsesToInt), typeof(O154.P154.VC154), typeof(global::Probes.Build.C11ParsedShape), []), - new(155, "parse-shape=ref-text;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=closed-set;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=string;text.culture=globalization-invariant;text.entry=type-converter", "sealed-record-class", "top-level", "none", typeof(V155), typeof(global::Probes.Build.C11ConstructedByRef), typeof(VC155), typeof(global::Probes.Build.C11ParsesFromIn), [("shape.kind", "sealed-record-class")]), - new(156, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=DateTimeOffset;text.culture=de-DE;text.entry=TryParse", "validated-struct", "top-level", "explicit-interface-member", typeof(V156), typeof(global::System.DateTimeOffset), null, null, []), - new(157, "parse-shape=implicit-result;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateOnly;text.culture=globalization-invariant;text.entry=TryFormat-short-span", "validated-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O157.P157.V157), typeof(global::Probes.Build.C11ParsesToInt), typeof(O157.P157.VC157), typeof(global::Probes.Build.C11ParsedShape), []), - new(158, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=string;text.culture=invariant;text.entry=IParsable", "validated-class", "nested-two-levels", "base-depth-3", typeof(O158.P158.V158), typeof(string), null, null, []), - new(159, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=flags-enum;text.culture=globalization-invariant;text.entry=IParsable", "sealed-record-class", "nested-two-levels", "base-depth-2", typeof(O159.P159.V159), typeof(global::Probes.Shapes.PFlags), null, null, []), - new(160, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=decimal;text.culture=invariant;text.entry=type-converter", "sealed-record-class", "nested-in-struct", "explicit-interface-member", typeof(O160.V160), typeof(decimal), null, null, []), - new(161, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=Guid;text.culture=invariant;text.entry=type-converter", "record-struct", "nested-in-class", "none", typeof(O161.V161), typeof(global::System.Guid), null, null, []), - new(162, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTime;text.culture=invariant;text.entry=TryFormat-short-span", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O162.V162), typeof(global::System.DateTime), null, null, []), - new(163, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTimeOffset;text.culture=invariant;text.entry=ToString-format", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O163.V163), typeof(global::System.DateTimeOffset), null, null, []), - new(164, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=byte;text.culture=ar-SA;text.entry=IParsable", "validated-struct", "nested-in-struct", "explicit-interface-member", typeof(O164.V164), typeof(byte), null, null, []), - new(165, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=DateOnly;text.culture=de-DE;text.entry=ToString-format", "sealed-record-class", "top-level", "base-explicit-IEquatable", typeof(V165), typeof(global::System.DateOnly), null, null, []), - new(166, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=decimal;text.culture=ar-SA;text.entry=ToString-format", "record-struct", "nested-in-class", "none", typeof(O166.V166), typeof(decimal), null, null, []), - new(167, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=string;text.culture=de-DE;text.entry=Parse", "record-struct", "nested-in-class", "none", typeof(O167.V167), typeof(string), null, null, []), - new(168, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=ulong;text.culture=ar-SA;text.entry=TryFormat-short-span", "record-struct", "nested-in-class", "none", typeof(O168.V168), typeof(ulong), null, null, [("shape.inheritance", "none")]), - new(169, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=bool;text.culture=de-DE;text.entry=ISpanParsable", "record-struct", "nested-in-struct", "explicit-interface-member", typeof(O169.V169), typeof(bool), null, null, []), - new(170, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=own-record-class;text.culture=invariant;text.entry=ToString-format", "validated-struct", "nested-two-levels", "none", typeof(O170.P170.V170), typeof(global::Probes.Shapes.PRec), null, null, []), - new(171, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=byte;text.culture=globalization-invariant;text.entry=ISpanParsable", "validated-class", "nested-in-class", "none", typeof(O171.V171), typeof(byte), null, null, []), - new(172, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=TimeOnly;text.culture=ar-SA;text.entry=Parse", "sealed-record-class", "nested-two-levels", "base-depth-2", typeof(O172.P172.V172), typeof(global::System.TimeOnly), null, null, []), - new(173, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=TimeOnly;text.culture=tr-TR;text.entry=TryFormat-short-span", "validated-class", "top-level", "base-depth-3", typeof(V173), typeof(global::System.TimeOnly), null, null, []), - new(174, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=Uri;text.culture=invariant;text.entry=ISpanParsable", "validated-class", "nested-in-class", "base-depth-3", typeof(O174.V174), typeof(global::System.Uri), null, null, []), - new(175, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=double;text.culture=ar-SA;text.entry=ISpanParsable", "validated-struct", "nested-in-struct", "none", typeof(O175.V175), typeof(double), null, null, [("shape.inheritance", "none")]), - new(176, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=string;text.culture=tr-TR;text.entry=ToString-format", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O176.V176), typeof(string), null, null, []), - new(177, "parse-shape=ref-text;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateTimeOffset;text.culture=globalization-invariant;text.entry=TryFrom", "validated-class", "nested-in-class", "base-depth-3", typeof(O177.V177), typeof(global::Probes.Build.C11ConstructedByRef), typeof(O177.VC177), typeof(global::Probes.Build.C11ParsesFromIn), []), - new(178, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=Guid;text.culture=tr-TR;text.entry=ToString-format", "validated-struct", "nested-in-struct", "explicit-interface-member", typeof(O178.V178), typeof(global::System.Guid), null, null, []), - new(179, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeOnly;text.culture=globalization-invariant;text.entry=TryFrom", "validated-struct", "nested-in-class", "none", typeof(O179.V179), typeof(global::System.TimeOnly), null, null, []), - new(180, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=enum;text.culture=globalization-invariant;text.entry=ISpanParsable", "record-struct", "nested-two-levels", "explicit-interface-member", typeof(O180.P180.V180), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(181, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=TimeSpan;text.culture=tr-TR;text.entry=IParsable", "validated-struct", "top-level", "explicit-interface-member", typeof(V181), typeof(global::System.TimeSpan), null, null, []), - new(182, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=decimal;text.culture=tr-TR;text.entry=TryFormat-short-span", "validated-struct", "nested-in-class", "none", typeof(O182.V182), typeof(decimal), null, null, [("shape.inheritance", "none")]), - new(183, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=flags-enum;text.culture=invariant;text.entry=ISpanParsable", "validated-class", "nested-in-struct", "base-depth-3", typeof(O183.V183), typeof(global::Probes.Shapes.PFlags), null, null, []), - new(184, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=DateTime;text.culture=globalization-invariant;text.entry=type-converter", "validated-class", "top-level", "base-depth-1", typeof(V184), typeof(global::System.DateTime), null, null, []), - new(185, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=bool;text.culture=ar-SA;text.entry=IParsable", "record-struct", "nested-two-levels", "none", typeof(O185.P185.V185), typeof(bool), null, null, [("shape.inheritance", "none")]), - new(186, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=long;text.culture=globalization-invariant;text.entry=ToString-format", "sealed-record-class", "top-level", "base-depth-3", typeof(V186), typeof(long), null, null, []), - new(187, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=in-generic-type;shape.wrapped=DateOnly;text.culture=invariant;text.entry=TryFrom", "validated-class", "nested-in-class", "base-depth-1", typeof(O187.V187), typeof(global::System.DateOnly), null, null, [("shape.site", "nested-in-class")]), - new(188, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=int;text.culture=invariant;text.entry=Parse", "record-struct", "nested-two-levels", "explicit-interface-member", typeof(O188.P188.V188), typeof(int), null, null, []), - new(189, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=NodaTime.Instant;text.culture=invariant;text.entry=ISpanParsable", "validated-class", "nested-two-levels", "explicit-interface-member", typeof(O189.P189.V189), typeof(global::NodaTime.Instant), null, null, []), - new(190, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Guid;text.culture=de-DE;text.entry=TryFrom", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O190.V190), typeof(global::System.Guid), null, null, []), - new(191, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=string;text.culture=globalization-invariant;text.entry=TryParse", "sealed-record-class", "nested-two-levels", "explicit-interface-member", typeof(O191.P191.V191), typeof(string), null, null, []), - new(192, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=ulong;text.culture=globalization-invariant;text.entry=type-converter", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V192), typeof(ulong), null, null, []), - new(193, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=DateOnly;text.culture=globalization-invariant;text.entry=TryParse", "sealed-record-class", "top-level", "base-depth-2", typeof(V193), typeof(global::System.DateOnly), null, null, []), - new(194, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=byte;text.culture=de-DE;text.entry=TryFrom", "validated-class", "nested-in-class", "base-depth-2", typeof(O194.V194), typeof(byte), null, null, []), - new(195, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=flags-enum;text.culture=globalization-invariant;text.entry=Parse", "sealed-record-class", "top-level", "explicit-interface-member", typeof(V195), typeof(global::Probes.Shapes.PFlags), null, null, []), - new(196, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=double;text.culture=ar-SA;text.entry=type-converter", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V196), typeof(double), null, null, []), - new(197, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=DateTime;text.culture=invariant;text.entry=TryFrom", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O197.V197), typeof(global::System.DateTime), null, null, []), - new(198, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=byte;text.culture=invariant;text.entry=type-converter", "validated-struct", "nested-in-struct", "none", typeof(O198.V198), typeof(byte), null, null, [("shape.inheritance", "none")]), - new(199, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=char;text.culture=invariant;text.entry=IParsable", "validated-struct", "nested-two-levels", "explicit-interface-member", typeof(O199.P199.V199), typeof(char), null, null, []), - new(200, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=enum;text.culture=ar-SA;text.entry=TryFormat-short-span", "sealed-record-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O200.V200), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(201, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Guid;text.culture=globalization-invariant;text.entry=ISpanParsable", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O201.V201), typeof(global::System.Guid), null, null, []), - new(202, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTimeOffset;text.culture=de-DE;text.entry=IParsable", "sealed-record-class", "nested-in-struct", "base-depth-2", typeof(O202.V202), typeof(global::System.DateTimeOffset), null, null, []), - new(203, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=char;text.culture=invariant;text.entry=TryParse", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O203.P203.V203), typeof(char), null, null, []), - new(204, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=TimeOnly;text.culture=invariant;text.entry=ISpanParsable", "validated-class", "nested-in-struct", "base-depth-1", typeof(O204.V204), typeof(global::System.TimeOnly), null, null, []), - new(205, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=string;text.culture=tr-TR;text.entry=ISpanParsable", "sealed-record-class", "nested-in-struct", "base-depth-1", typeof(O205.V205), typeof(string), null, null, []), - new(206, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Uri;text.culture=tr-TR;text.entry=TryFrom", "validated-class", "nested-two-levels", "base-depth-1", typeof(O206.P206.V206), typeof(global::System.Uri), null, null, []), - new(207, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=DateTimeOffset;text.culture=ar-SA;text.entry=ISpanParsable", "sealed-record-class", "top-level", "base-depth-1", typeof(V207), typeof(global::System.DateTimeOffset), null, null, []), - new(208, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=long;text.culture=ar-SA;text.entry=Parse", "validated-class", "top-level", "base-depth-2", typeof(V208), typeof(long), null, null, []), - new(209, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=ulong;text.culture=de-DE;text.entry=ISpanParsable", "validated-class", "nested-two-levels", "explicit-interface-member", typeof(O209.P209.V209), typeof(ulong), null, null, []), - new(210, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=bool;text.culture=invariant;text.entry=ToString-format", "validated-class", "nested-in-class", "base-depth-1", typeof(O210.V210), typeof(bool), null, null, []), - new(211, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=top-level;shape.wrapped=TimeSpan;text.culture=tr-TR;text.entry=Parse", "validated-class", "top-level", "base-explicit-IEquatable", typeof(V211), typeof(global::System.TimeSpan), null, null, []), - new(212, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=int;text.culture=globalization-invariant;text.entry=TryFrom", "validated-class", "nested-in-class", "base-depth-3", typeof(O212.V212), typeof(int), null, null, []), - new(213, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=in-generic-type;shape.wrapped=int;text.culture=invariant;text.entry=ToString-format", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O213.V213), typeof(int), null, null, [("shape.site", "nested-in-class")]), - new(214, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=top-level;shape.wrapped=decimal;text.culture=tr-TR;text.entry=IParsable", "validated-class", "top-level", "base-depth-1", typeof(V214), typeof(decimal), null, null, []), - new(215, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=byte;text.culture=de-DE;text.entry=TryFormat-short-span", "validated-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O215.P215.V215), typeof(byte), null, null, []), - new(216, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=own-record-class;text.culture=ar-SA;text.entry=TryParse", "validated-class", "nested-two-levels", "base-depth-1", typeof(O216.P216.V216), typeof(global::Probes.Shapes.PRec), null, null, []), - new(217, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=int;text.culture=invariant;text.entry=ISpanParsable", "validated-struct", "nested-in-struct", "none", typeof(O217.V217), typeof(int), null, null, []), - new(218, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=in-generic-type;shape.wrapped=string;text.culture=globalization-invariant;text.entry=type-converter", "sealed-record-class", "nested-in-class", "base-explicit-IEquatable", typeof(O218.V218), typeof(string), null, null, [("shape.site", "nested-in-class")]), - new(219, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=Guid;text.culture=invariant;text.entry=Parse", "validated-class", "nested-two-levels", "base-depth-2", typeof(O219.P219.V219), typeof(global::System.Guid), null, null, []), - new(220, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-two-levels;shape.wrapped=flags-enum;text.culture=globalization-invariant;text.entry=type-converter", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O220.P220.V220), typeof(global::Probes.Shapes.PFlags), null, null, []), - new(221, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=sealed-record-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=byte;text.culture=invariant;text.entry=Parse", "sealed-record-class", "nested-in-struct", "none", typeof(O221.V221), typeof(byte), null, null, []), - new(222, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=DateTime;text.culture=tr-TR;text.entry=Parse", "sealed-record-class", "nested-in-struct", "base-depth-1", typeof(O222.V222), typeof(global::System.DateTime), null, null, []), - new(223, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=none;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=own-record-class;text.culture=tr-TR;text.entry=TryFrom", "sealed-record-class", "top-level", "none", typeof(V223), typeof(global::Probes.Shapes.PRec), null, null, []), - new(224, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-struct;shape.wrapped=NodaTime.Instant;text.culture=tr-TR;text.entry=type-converter", "validated-class", "nested-in-struct", "base-depth-1", typeof(O224.V224), typeof(global::NodaTime.Instant), null, null, []), - new(225, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-2;shape.kind=record-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateOnly;text.culture=de-DE;text.entry=type-converter", "record-struct", "nested-two-levels", "none", typeof(O225.P225.V225), typeof(global::System.DateOnly), null, null, [("shape.inheritance", "none")]), - new(226, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-two-levels;shape.wrapped=DateTimeOffset;text.culture=invariant;text.entry=type-converter", "validated-struct", "nested-two-levels", "none", typeof(O226.P226.V226), typeof(global::System.DateTimeOffset), null, null, [("shape.inheritance", "none")]), - new(227, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeOnly;text.culture=ar-SA;text.entry=IParsable", "sealed-record-class", "nested-in-class", "base-depth-3", typeof(O227.V227), typeof(global::System.TimeOnly), null, null, []), - new(228, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-struct;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-class;shape.wrapped=TimeSpan;text.culture=globalization-invariant;text.entry=TryFrom", "validated-struct", "nested-in-class", "none", typeof(O228.V228), typeof(global::System.TimeSpan), null, null, [("shape.inheritance", "none")]), - new(229, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=DateTime;text.culture=tr-TR;text.entry=ToString-format", "validated-class", "nested-two-levels", "base-explicit-IEquatable", typeof(O229.P229.V229), typeof(global::System.DateTime), null, null, []), - new(230, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-explicit-IEquatable;shape.kind=validated-class;shape.refused-kind=generic-wrapped (CMTK1005);shape.site=nested-in-struct;shape.wrapped=double;text.culture=globalization-invariant;text.entry=TryFormat-short-span", "validated-class", "nested-in-struct", "base-explicit-IEquatable", typeof(O230.V230), typeof(double), null, null, []), - new(231, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-struct;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-class;shape.wrapped=Uri;text.culture=ar-SA;text.entry=ToString-format", "validated-struct", "nested-in-class", "explicit-interface-member", typeof(O231.V231), typeof(global::System.Uri), null, null, []), - new(232, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=over-value-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=double;text.culture=ar-SA;text.entry=Parse", "record-struct", "nested-in-struct", "explicit-interface-member", typeof(O232.V232), typeof(double), null, null, []), - new(233, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-two-levels;shape.wrapped=DateOnly;text.culture=tr-TR;text.entry=TryFormat-short-span", "record-struct", "nested-two-levels", "none", typeof(O233.P233.V233), typeof(global::System.DateOnly), null, null, [("shape.inheritance", "none")]), - new(234, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=record-struct;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=enum;text.culture=invariant;text.entry=TryFrom", "record-struct", "top-level", "none", typeof(V234), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(235, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=top-level;shape.wrapped=DateTimeOffset;text.culture=globalization-invariant;text.entry=TryFrom", "validated-class", "top-level", "base-depth-3", typeof(V235), typeof(global::System.DateTimeOffset), null, null, []), - new(236, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=bool;text.culture=de-DE;text.entry=Parse", "validated-class", "top-level", "base-depth-3", typeof(V236), typeof(bool), null, null, []), - new(237, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-2;shape.kind=sealed-record-class;shape.refused-kind=unsealed-record-class (CMTK1006);shape.site=nested-in-class;shape.wrapped=decimal;text.culture=tr-TR;text.entry=TryFormat-short-span", "sealed-record-class", "nested-in-class", "base-depth-2", typeof(O237.V237), typeof(decimal), null, null, []), - new(238, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=ulong;text.culture=de-DE;text.entry=TryFormat-short-span", "sealed-record-class", "nested-two-levels", "base-depth-1", typeof(O238.P238.V238), typeof(ulong), null, null, []), - new(239, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=double;text.culture=de-DE;text.entry=IParsable", "sealed-record-class", "top-level", "base-depth-1", typeof(V239), typeof(double), null, null, []), - new(240, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-1;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=top-level;shape.wrapped=byte;text.culture=ar-SA;text.entry=TryFrom", "sealed-record-class", "top-level", "base-depth-1", typeof(V240), typeof(byte), null, null, []), - new(241, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=validated-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-two-levels;shape.wrapped=enum;text.culture=de-DE;text.entry=IParsable", "validated-class", "nested-two-levels", "explicit-interface-member", typeof(O241.P241.V241), typeof(global::Probes.Shapes.PEnum), null, null, []), - new(242, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=sealed-record-class;shape.refused-kind=abstract (CMTK1005);shape.site=nested-in-struct;shape.wrapped=flags-enum;text.culture=ar-SA;text.entry=TryFrom", "sealed-record-class", "nested-in-struct", "base-depth-3", typeof(O242.V242), typeof(global::Probes.Shapes.PFlags), null, null, []), - new(243, "parse-shape=ordinary;process.converter-lookup=wrapped-type-converter-looked-up-first;shape.inheritance=base-depth-3;shape.kind=record-struct;shape.refused-kind=closed-set-named-as-generated-member (CMTK1021);shape.site=top-level;shape.wrapped=Uri;text.culture=ar-SA;text.entry=IParsable", "record-struct", "top-level", "none", typeof(V243), typeof(global::System.Uri), null, null, [("shape.inheritance", "none")]), - new(244, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=none;shape.kind=validated-struct;shape.refused-kind=equality-operator (CMTK1011);shape.site=nested-two-levels;shape.wrapped=NodaTime.Instant;text.culture=ar-SA;text.entry=TryFormat-short-span", "validated-struct", "nested-two-levels", "none", typeof(O244.P244.V244), typeof(global::NodaTime.Instant), null, null, []), - new(245, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=explicit-interface-member;shape.kind=record-struct;shape.refused-kind=over-object (CMTK1005);shape.site=nested-in-struct;shape.wrapped=Uri;text.culture=invariant;text.entry=TryFormat-short-span", "record-struct", "nested-in-struct", "explicit-interface-member", typeof(O245.V245), typeof(global::System.Uri), null, null, []), - new(246, "parse-shape=ordinary;process.converter-lookup=none;shape.inheritance=base-depth-3;shape.kind=validated-class;shape.refused-kind=over-System.Enum (CMTK1005);shape.site=nested-two-levels;shape.wrapped=Uri;text.culture=ar-SA;text.entry=TryParse", "validated-class", "nested-two-levels", "base-depth-3", typeof(O246.P246.V246), typeof(global::System.Uri), null, null, []), - ]; -} diff --git a/tools/probe/corpus/harness/Cmp.cs b/tools/probe/corpus/harness/Cmp.cs deleted file mode 100644 index a435030b..00000000 --- a/tools/probe/corpus/harness/Cmp.cs +++ /dev/null @@ -1,128 +0,0 @@ -// Composed-fault errors for C03/C05 (harness). e: 1 first set, 2 last set, 3 last set with detail, 4/5/6 first set with a rule (enum / closed set / nested enum). -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ClosedSets; -using CodoMetis.TypeKit.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Routing; -namespace Probes; - -public sealed partial class KFault : IFaultSet -{ - public static readonly KFault Clash = new(FaultKind.Conflict, null); - public static readonly KFault Lost = new(FaultKind.NotFound, null); - private KFault(FaultKind kind, string? description) { Kind = kind; Description = description; } - public FaultKind Kind { get; } - public string? Description { get; } -} - -public static class Cmp -{ - public const string Detail = "It is at version 3, not 2."; - static Fault Rule(int e) => e switch - { - 4 => Fault.Of(HFault.Clash).WithRule(PRule.A), - 5 => Fault.Of(HFault.Clash).WithRule(RuleSet.X), - 6 => Fault.Of(HFault.Clash).WithRule(Holder.NestedRule.N), - _ => throw new ArgumentOutOfRangeException(nameof(e)), - }; - public static Result> Two(int e) => e switch - { - 1 => Fault.Of(HFault.Missing), - 2 => Fault.Of(GFault.Raced), - 3 => Fault.Of(Fault.Of(GFault.Old, Detail)), - >= 4 and <= 6 => Fault.Of(Rule(e)), - _ => Result>.Success(), - }; - public static Result> TwoK(int e) => e switch - { - 1 => Fault.Of(HFault.Missing), - 2 => Fault.Of(KFault.Clash), - 3 => Fault.Of(Fault.Of(KFault.Lost, Detail)), - >= 4 and <= 6 => Fault.Of(Rule(e)), - _ => Result>.Success(), - }; - public static Result> Three(int e) => e switch - { - 1 => Fault.Of(HFault.Missing), - 2 => Fault.Of(SFault.First), - 3 => Fault.Of(Fault.Of(SFault.Second, Detail)), - >= 4 and <= 6 => Fault.Of(Rule(e)), - _ => Result>.Success(), - }; - public static Result> ThreeK(int e) => e switch - { - 1 => Fault.Of(HFault.Missing), - 2 => Fault.Of(SFault.First), - 3 => Fault.Of(Fault.Of(SFault.Second, Detail)), - >= 4 and <= 6 => Fault.Of(Rule(e)), - _ => Result>.Success(), - }; - - // ---- expectations - /// (fault type name, member name, status) of the error e answers, by arity and variant; variant group moves HFault.Clash to 422. - public static (string Type, string Member, int Status) Expect(int arity, string variant, int e) - { - var k = variant == "dup"; - int clash = variant == "group" ? 422 : 409; - return e switch - { - 1 => ("HFault", "Missing", 404), - 2 => arity == 2 ? (k ? ("KFault", "Clash", 409) : ("GFault", "Raced", 409)) : ("SFault", "First", 409), - 3 => arity == 2 ? (k ? ("KFault", "Lost", 404) : ("GFault", "Old", 412)) : ("SFault", "Second", 410), - _ => ("HFault", "Clash", clash), - }; - } - /// The statuses >= 400 the operation lists: those of every member of every answered type, minus the left-out ones, plus the declared 429. - public static SortedSet DocStatuses(int arity, string variant) - { - var s = new SortedSet(); - var h = new (string, int)[] { ("Missing", 404), ("Clash", variant == "group" ? 422 : 409), ("Bad", 422), ("Denied", 403), ("Down", 503), ("Stale", 412), ("Garbled", 400) }; - foreach (var (m, st) in h) if (!(variant == "wo" && m == "Bad")) s.Add(st); - if (variant == "dup") { s.Add(409); s.Add(404); } else { s.Add(404); s.Add(409); s.Add(412); } - if (arity == 3) { s.Add(409); s.Add(410); } - if (variant == "declared") s.Add(429); - return s; - } - - static readonly Dictionary Kinds = new(StringComparer.Ordinal) - { - ["HFault.Missing"] = "NotFound", ["HFault.Clash"] = "Conflict", ["HFault.Bad"] = "Invalid", ["HFault.Denied"] = "Forbidden", - ["HFault.Down"] = "Unavailable", ["HFault.Stale"] = "PreconditionFailed", ["HFault.Garbled"] = "Malformed", - ["GFault.Gone"] = "NotFound", ["GFault.Raced"] = "Conflict", ["GFault.Old"] = "PreconditionFailed", - ["KFault.Clash"] = "Conflict", ["KFault.Lost"] = "NotFound", - }; - /// The kind of a fault-set member, or null for a member of the enum SFault, which has none. - public static string? KindOf(string type, string member) => Kinds.GetValueOrDefault(type + "." + member); - /// The problem types the operation's response for names: every member answered with it, of every set. - public static HashSet TypesAt(int arity, string variant, int status) - { - var members = new List<(string Type, string Member, int Status)> - { - ("HFault", "Missing", 404), ("HFault", "Clash", variant == "group" ? 422 : 409), ("HFault", "Denied", 403), ("HFault", "Down", 503), - ("HFault", "Stale", 412), ("HFault", "Garbled", 400), - }; - if (variant != "wo") members.Add(("HFault", "Bad", 422)); - if (variant == "dup") members.AddRange([("KFault", "Clash", 409), ("KFault", "Lost", 404)]); - else members.AddRange([("GFault", "Gone", 404), ("GFault", "Raced", 409), ("GFault", "Old", 412)]); - // arity 3 adds SFault, an enum mapped to 409 and 410 with no type - return [.. members.Where(m => m.Status == status).Select(m => ComposedClaims.TypeOf(m.Type, KindOf(m.Type, m.Member)!))]; - } - - public static RouteHandlerBuilder Decorate(RouteHandlerBuilder rb, string variant) => variant switch - { - "wo" => rb.WithoutFaults(HFault.Bad), - "group" => rb.WithFaultStatus(HFault.Clash, 422), - "declared" => rb.ProducesProblem(429), - _ => rb, - }; - public static void MapMinimal(IEndpointRouteBuilder b) - { - foreach (var v in new[] { "plain", "wo", "group", "declared", "dup" }) - { - var k = v == "dup"; - Decorate(k ? b.MapGet($"/m/2/{v}", (int e) => TwoK(e).ToHttpResult()) : b.MapGet($"/m/2/{v}", (int e) => Two(e).ToHttpResult()), v); - Decorate(k ? b.MapGet($"/m/3/{v}", (int e) => ThreeK(e).ToHttpResult()) : b.MapGet($"/m/3/{v}", (int e) => Three(e).ToHttpResult()), v); - } - } -} diff --git a/tools/probe/corpus/harness/ComposedProbes.cs b/tools/probe/corpus/harness/ComposedProbes.cs deleted file mode 100644 index 56532242..00000000 --- a/tools/probe/corpus/harness/ComposedProbes.cs +++ /dev/null @@ -1,228 +0,0 @@ -// Claims C03-composed-faults (AspNetCore README#faults-of-several-types) and C05-with-rule (README#the-rule-that-refused). -// C03: a Fault is "answered as its own set maps it" (status, `fault`, problem type, detail, rule intact); "its operation lists the -// statuses of both sets, in ascending order, with the problem types of both where they share a status"; "the host doesn't start while either -// set is unmapped". The host maps every fault set with a problem type of its set and kind (TypeOf), so two sets sharing a status name two types. -// C05: WithRule(member) is written as `rule`, "qualified with its type's name", a nested type after its nesting chain "whatever QualifiedFaultNames -// says"; "The status, the mapping and the document stay the error's"; a value no member names "throws where it's given"; the document describes `rule`. -// Oracle: claim (README text) + differential wire/document. Expected: see Cmp.Expect / Cmp.DocStatuses. Each obligation is a check of its -// own (ComposedChecks.C03, C05), every check on one host. -using System.Text.Json.Nodes; -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Probes.Faults; -using Probes.Shapes; -namespace Probes; - -public static class ComposedClaims -{ - /// The application's own string converter (json.Converters=own-for-wrapped) wraps every string it writes: take it off again. - static string? Un(Row r, string? s) => r.Is("json.Converters", "own-for-wrapped") && s is not null && s.StartsWith("own:") ? s[4..].Trim('"') : s; - static string Cut(string? s) => s is null ? "(null)" : s.Length > 200 ? s[..200] + "..." : s; - public static string Variant(Row r) => r["http.response"] switch - { - "endpoint-without-faults" => "wo", "group-with-fault-status" => "group", "declared-produces-problem" => "declared", "two-faults-same-name" => "dup", _ => "plain", - }; - static HostCfg Cfg(Row r) => new() - { - FaultControllers = true, Controllers = Hx.Plain(r) ? [typeof(RulePlainController)] : [typeof(RuleApiController)], - Minimal = Cmp.MapMinimal, Qualified = r.Is("qualified", "QualifiedFaultNames"), - Mapping = "own-per-set-type", - Services = s => s.AddFaultResponses(f => FaultResponse.For(f.Kind).WithType(new Uri(TypeOf(f.GetType().Name, f.Kind.ToString())))), - }; - - /// The problem type the host maps a fault-set member to: its set's and its kind's, so two sets sharing a status name two types. - public static string TypeOf(string set, string kind) => $"https://p.example/{set}/{kind}"; - - /// The problem's rule, an aspect of its own beside 's. - public const string Rule = "rule"; - - /// - /// The wire and the document of one endpoint family under the row, one check (Row.Check) per obligation names an - /// aspect for ('s and ), against ; every check runs on the one host, with - /// the answers fetched once. e: the error code of Cmp. - /// - public static async Task WireAndDoc(Row r, Entry en, int arity, int e, string rule, Func of, Baseline against) - { - var variant = Variant(r); - var obligations = Aspect.Answer.Append(Rule).Concat(Aspect.Doc).SelectMany(of).Distinct().ToList(); - WebApplication? app = null; Exception? down = null; - try { app = await Hx.Start(r, en, Cfg(r)); } - catch (Exception ex) when (ex is not Sides.InputsNotEquivalent) { down = ex; } - await using var _ = app; - string? failed = null; - if (down is not null) - { - foreach (var o in obligations) { var f = r.Check(o, against, _ => "host did not start: " + down.GetType().Name + ": " + Cut(down.Message)); failed ??= f; } - return failed; - } - var qualified = r.Is("qualified", "QualifiedFaultNames"); - var sides = new List(); - if (Hx.HasMinimal(r)) sides.Add(Hx.M(r, $"/m/{arity}/{variant}")); - if (Hx.HasMvc(r)) sides.Add(Hx.C(r, $"/c/{arity}/{variant}")); - JsonNode? doc = null; Exception? docFailed = null; - try { doc = await Hx.Doc(app!); } - catch (Exception ex) { docFailed = ex; } - var wires = new Dictionary(); - foreach (var path in sides) wires[path] = await Hx.Get(app!, $"{path}?e={e}"); - var (type, member, status) = Cmp.Expect(arity, variant, e); - - foreach (var o in obligations) - { - var f = await r.Check(o, against, async c => - { - bool want(string aspect) => of(aspect).Contains(o); - if (Aspect.Answer.Any(want) || want(Rule)) - foreach (var path in sides) - { - var w = wires[path]; - if (w.Thrown is not null) return $"{path} e={e}: request threw {w.Thrown}"; - if (want(Aspect.Status) && (int)w.Status != status) return $"{path} e={e}: status {(int)w.Status}, expected {status}: {Cut(w.Body)}"; - JsonNode? body; try { body = JsonNode.Parse(w.Body); } catch { return $"{path} e={e}: body is not JSON: {Cut(w.Body)}"; } - var fault = Un(r, body?["fault"]?.GetValue()); - var wantFault = qualified ? $"{type}.{member}" : member; - if (want(Aspect.Fault) && fault != wantFault) return $"{path} e={e}: fault {fault}, expected {wantFault}"; - if (want(Aspect.Detail) && e == 3 && Un(r, body?["detail"]?.GetValue()) != Cmp.Detail) return $"{path} e={e}: detail {body?["detail"]}, expected the occurrence's"; - var gotRule = Un(r, body?["rule"]?.GetValue()); - if (want(Rule) && gotRule != (rule == "" ? null : rule)) return $"{path} e={e}: rule {gotRule ?? "(none)"}, expected {(rule == "" ? "(none)" : rule)}"; - // an enum (SFault) is mapped to a status alone, and ASP.NET writes its own default type there - if (want(Aspect.Type) && Cmp.KindOf(type, member) is { } kind && Un(r, body?["type"]?.GetValue()) is var gotType && gotType != TypeOf(type, kind)) - return $"{path} e={e}: type {gotType ?? "(none)"}, expected {TypeOf(type, kind)}"; - if (want(Aspect.Status) && int.Parse(body?["status"]?.ToString() is { } st ? Un(r, st)!.Trim('"') : "0") != status) return $"{path} e={e}: problem status field {body?["status"]}"; - } - if (!Aspect.Doc.Any(want)) return null; - // the document: one ASP.NET alone refuses the same way has nothing to check (noted there) - if (docFailed is not null) - return await Hx.DocRefusedByAspNetAlone(r, en, Cfg(r), docFailed, (Aspect.Answer.Any(want) || want(Rule)) && sides.Count > 0) is { } alone - ? alone + " @ " + string.Join(" < ", (docFailed.StackTrace ?? "").Split('\n').Take(4).Select(x => x.Trim().Split('(')[0])) - : null; - foreach (var path in sides) - { - var ops = doc!["paths"]?[path]?["get"]; - if (ops is null && !Hx.InDocument(r) && path.Contains("/c/")) { c.Note(NoteKeys.DocControllerNotInApiExplorer); continue; } - if (ops is null) return $"document has no GET {path}"; - var keys = ops["responses"]!.AsObject().Select(kv => kv.Key).Where(k => int.TryParse(k, out var n) && n >= 400).Select(int.Parse).ToList(); - var listed = Cmp.DocStatuses(arity, variant); - // A status the endpoint declares itself (the 429) takes its place among the sets' statuses (fixed in 8d1ebd8). - if ((want(Aspect.DocListed) || want(Aspect.DocOrder)) && !keys.SequenceEqual(listed)) return $"{path}: document lists {string.Join(",", keys)}, expected {string.Join(",", listed)} (ascending)"; - foreach (var k in keys) - { - if (variant == "declared" && k == 429) continue; // a declared problem is ASP.NET's, not an answered fault - var txt = Hx.Deep(doc!, ops["responses"]![k.ToString()])?.ToJsonString() ?? ""; - if (want(Aspect.DocDescribes) && !txt.Contains("\"fault\"")) return $"{path} {k}: response schema does not describe fault: {Cut(txt)}"; - if (want(Aspect.DocDescribes) && !txt.Contains("\"rule\"")) return $"{path} {k}: response schema does not describe rule: {Cut(txt)}"; - var desc = ops["responses"]![k.ToString()]?["description"]?.GetValue() ?? ""; - const string Lead = "Problem types: "; - var at = desc.IndexOf(Lead, StringComparison.Ordinal); - var types = at < 0 ? [] : desc[(at + Lead.Length)..].Split(", ").ToHashSet(); - var wantTypes = Cmp.TypesAt(arity, variant, k); - if (want(Aspect.DocTypes) && !types.SetEquals(wantTypes)) - return $"{path} {k}: description \"{desc}\" names problem types {{{string.Join(", ", types)}}}, expected {{{string.Join(", ", wantTypes)}}}"; - } - } - return null; - }); - failed ??= f; - } - return failed; - } - - /// "The host doesn't start while either set is unmapped." - public static async Task UnmappedDoesNotStart(Row r, Entry en, int arity, int e) - { - var cfg = Cfg(r); cfg.MapBlanketMapping = false; - var unmapped = e == 1 ? "HFault" : arity == 2 ? "GFault" : "GFault"; - var variant = Variant(r); - if (variant == "dup") unmapped = e == 1 ? "HFault" : "KFault"; - cfg.Services = s => - { - if (unmapped != "HFault") s.AddFaultResponses(); - if (unmapped != "GFault" && variant != "dup") s.AddFaultResponses(); - if (unmapped != "KFault" && variant == "dup") s.AddFaultResponses(); - if (arity == 3) s.AddFaultResponses(f => f switch { SFault.First => 409, _ => 410 }); - }; - WebApplication? app = null; - try { app = await Hx.Start(r, en, cfg); } - catch (Exception ex) when (ex is not Sides.InputsNotEquivalent) - { - return ex.ToString().Contains(unmapped) ? null : $"host refused to start but not naming {unmapped}: {ex.GetType().Name}: {Cut(ex.Message)}"; - } - await using var _ = app; - return $"host started with {unmapped} unmapped (arity {arity}, variant {variant}); answered only at request time?"; - } -} - -public static class ComposedChecks -{ - static void Process(Row r, Entry en) { if (r.Is("process.converter-lookup", "wrapped-type-converter-looked-up-first")) _ = System.ComponentModel.TypeDescriptor.GetConverter(Make.Held(en.Plain)); } - /// - /// C03, a check per obligation: each-error-own-set (status, fault, problem type, and the rule where the error names none), - /// detail-and-rule-intact (error with-detail or with-rule: the detail and the rule), statuses-ascending (the statuses each operation - /// lists, in order, and their problem schemas), shared-status-types (the problem types each status's description names), and - /// unmapped-set-stops-start (a host with one set unmapped, against the refusal) in every row. - /// - public static async Task C03(Row r) - { - var en = Registry.Of(r); Process(r, en); - var arity = int.Parse(r["arity"]); - var e = r["error"] switch { "first-set" => 1, "last-set" => 2, "with-detail" => 3, _ => 4 }; - var intact = r["error"] is "with-detail" or "with-rule"; - string[] Of(string aspect) => aspect switch - { - Aspect.Detail or ComposedClaims.Rule when intact => ["detail-and-rule-intact"], - Aspect.DocListed or Aspect.DocOrder or Aspect.DocDescribes => ["statuses-ascending"], - Aspect.DocTypes => ["shared-status-types"], - _ => ["each-error-own-set"], - }; - var answered = await ComposedClaims.WireAndDoc(r, en, arity, e, e == 4 ? "PRule.A" : "", Of, - Baseline.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#faults-of-several-types")); - var unmapped = await r.Check("unmapped-set-stops-start", Baseline.Refusal, _ => ComposedClaims.UnmappedDoesNotStart(r, en, arity, e)); - return answered ?? unmapped; - } - /// - /// C05, a check per obligation: unnamed-value-throws (ruletype flags-combination or undefined-cast, against the refusal); - /// rule-qualified (enum, closed-set) or nested-chain (nested-enum), and no-namespace, for the rule on the wire; stays-the-errors for - /// the status, fault, problem type and the document, and in the rows of an unnamed value for the error answered without a rule. - /// - public static async Task C05(Row r) - { - var en = Registry.Of(r); Process(r, en); - var ruletype = r["ruletype"]; - // a value no member names throws where it is given (never a rule on the wire) - var thrown = ruletype switch - { - "flags-combination" => r.Check("unnamed-value-throws", Baseline.Refusal, c => - { - try { _ = Fault.Of(HFault.Clash).WithRule((FRule)3); return "WithRule(flags combination) did not throw"; } catch (ArgumentException) { return null; } - }), - "undefined-cast" => r.Check("unnamed-value-throws", Baseline.Refusal, c => - { - try { _ = Fault.Of(HFault.Clash).WithRule((PRule)9); return "WithRule(undefined cast) did not throw"; } catch (ArgumentException) { return null; } - }), - _ => null, - }; - var (e, rule) = ruletype switch { "enum" => (4, "PRule.A"), "closed-set" => (5, "RuleSet.X"), "nested-enum" => (6, "Holder.NestedRule.N"), _ => (1, "") }; - var named = ruletype switch { "enum" or "closed-set" => "rule-qualified", "nested-enum" => "nested-chain", _ => null }; - string[] Of(string aspect) => aspect == ComposedClaims.Rule && named is not null ? [named, "no-namespace"] : ["stays-the-errors"]; - var answered = await ComposedClaims.WireAndDoc(r, en, 3, e, rule, Of, Baseline.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#the-rule-that-refused")); - return thrown ?? answered; - } -} -public static class ComposedShrinkInit -{ - [System.Runtime.CompilerServices.ModuleInitializer] - public static void Init() { ShrinkChecks.All["C03"] = ComposedChecks.C03; ShrinkChecks.All["C05"] = ComposedChecks.C05; } -} -public sealed class C03ComposedProbe -{ - public static TheoryData Data() => Rows.Data("C03ComposedProbe"); - [Theory, MemberData(nameof(Data))] - public Task Row(string probe, int idx) => Rec.Run("C03", idx, ComposedChecks.C03, probe); -} -public sealed class C05RuleProbe -{ - public static TheoryData Data() => Rows.Data("C05RuleProbe"); - [Theory, MemberData(nameof(Data))] - public Task Row(string probe, int idx) => Rec.Run("C05", idx, ComposedChecks.C05, probe); -} diff --git a/tools/probe/corpus/harness/Containers.g.cs b/tools/probe/corpus/harness/Containers.g.cs deleted file mode 100644 index d79627ed..00000000 --- a/tools/probe/corpus/harness/Containers.g.cs +++ /dev/null @@ -1,25 +0,0 @@ -// by tools/probe/generators/containers.cs -#nullable disable -#pragma warning disable CS8618, CS1591, CS0649 -using System.Text.Json.Serialization; -namespace Probes.Shapes; -public class Cn_class_P { public M P { get; set; } } -public class Cn_class_F { public M F; } -public class Cn_class_C { public M P { get; } [JsonConstructor] public Cn_class_C(M p) { P = p; } } -public class Cn_class_R { public required M P { get; init; } } -public record class Cn_record_class_P { public M P { get; set; } } -public record class Cn_record_class_F { public M F; } -public record class Cn_record_class_C(M P); -public record class Cn_record_class_R { public required M P { get; init; } } -public struct Cn_struct_P { public M P { get; set; } } -public struct Cn_struct_F { public M F; } -public struct Cn_struct_C { public M P { get; } [JsonConstructor] public Cn_struct_C(M p) { P = p; } } -public struct Cn_struct_R { public required M P { get; init; } } -public record struct Cn_record_struct_P { public M P { get; set; } } -public record struct Cn_record_struct_F { public M F; } -public record struct Cn_record_struct_C { public M P { get; } [JsonConstructor] public Cn_record_struct_C(M p) { P = p; } } -public record struct Cn_record_struct_R { public required M P { get; init; } } -public class Cn_generic_type_P { public M P { get; set; } } -public class Cn_generic_type_F { public M F; } -public class Cn_generic_type_C { public M P { get; } [JsonConstructor] public Cn_generic_type_C(M p) { P = p; } } -public class Cn_generic_type_R { public required M P { get; init; } } diff --git a/tools/probe/corpus/harness/DecimalSpelling.cs b/tools/probe/corpus/harness/DecimalSpelling.cs deleted file mode 100644 index 11a7c617..00000000 --- a/tools/probe/corpus/harness/DecimalSpelling.cs +++ /dev/null @@ -1,230 +0,0 @@ -// Claim C01-decimal-spelling (docs/json.md#decimals-in-one-spelling): DecimalNumberJsonConverter writes the smallest scale as a -// number, DecimalStringJsonConverter as a string; "Register either one once, and it covers every decimal: nullable ones, dictionary -// keys, and every value object over a decimal, since its JSON is the underlying type's." "Both read a number, or a string holding one, -// and keep the scale they read." -// Oracle: claim (the minimal spelling, computed here independently of the product) plus differential (the value object's document is the -// plain decimal's under the same options), with the options' own leaving-out shown by a baseline without the converter in the same row. -using System.Globalization; -using System.Text.Json; -using System.Text.Json.Serialization; -using CodoMetis.TypeKit; -using Probes.Shapes; -namespace Probes.Json; - -public static class DecimalSpelling -{ - static readonly CultureInfo Inv = CultureInfo.InvariantCulture; - const char Bs = (char)92; - - /// The input axis as decimal text: max28digits is 7.8 at scale 28, the largest scale a decimal has. - public static string Text(string input) => input == "max28digits" ? "7.8000000000000000000000000000" : input; - - /// The minimal spelling, worked out without the product: invariant digits, trailing fractional zeros dropped, a zero written 0. - public static string Canonical(decimal d) - { - if (d == 0m) return "0"; - var s = d.ToString(Inv); - return s.Contains('.') ? s.TrimEnd('0').TrimEnd('.') : s; - } - - static bool IsDecimal(JTok t, decimal d) => t.Type switch - { - JsonTokenType.Number => decimal.TryParse(t.Raw, NumberStyles.Float, Inv, out var x) && x == d, - JsonTokenType.String or JsonTokenType.PropertyName => t.Text is { Length: > 0 } s && s.Trim() == s && decimal.TryParse(s, NumberStyles.Float, Inv, out var y) && y == d, - _ => false, - }; - - /// The decimals a read value holds, as the serializer writes them by default: with the scale they hold. - static List Held(object? v, Type declared, decimal d) - { - if (v is null) return []; - var json = JsonSerializer.Serialize(v, declared, Jx.Plain); - return Jx.Tokens(json).Where(t => IsDecimal(t, d)).Select(t => decimal.Parse(t.Type == JsonTokenType.Number ? t.Raw : t.Text!, NumberStyles.Float, Inv)).ToList(); - } - static bool Same(decimal a, decimal b) => decimal.GetBits(a).SequenceEqual(decimal.GetBits(b)); - static string Show(List l) => string.Join(",", l.Select(x => x.ToString(Inv))); - - /// What one form of the document read as: the decimal's and the value object's refusals and the decimals each holds. - sealed record Form(string Name, string Json, Exception? Ep, Exception? Ev, List Cp, List Cv, bool BothRefused); - - /// - /// C01's checks, one per obligation, over one write and one read of the row's shape: the converter's spelling of each value - /// (smallest-scale; number-is-a-number or string-is-a-string, by the row's converter) against the docs' minimal spelling worked out - /// here; every decimal of the document, a key's included, and the value object's document against the plain decimal's - /// (covers-every-decimal, the twin); each form of the input read (reads-a-number-or-a-string) with the scale it carries - /// (keeps-the-scale-it-reads). The options' own leaving-out is shown by a baseline without the converter in the same row: a - /// direction it leaves out has nothing to check. Every check runs; returns the first failure. - /// - public static string? Check(Row r) - { - var kind = r["shape.kind"]; - if (kind == "closed-set") { r.Outcome("harness-error", "C01 excludes closed-set: a closed set holds no decimal"); return null; } - var e = Registry.Get(kind, "decimal"); - var text = Text(r["input"]); - var d = decimal.Parse(text, NumberStyles.Float, Inv); - var canon = Canonical(d); - var number = r["converter"] == "number"; - JsonConverter conv = number ? new DecimalNumberJsonConverter() : new DecimalStringJsonConverter(); - var position = r["shape.position"]; - var containing = position == "root" ? "class" : r["shape.containing"]; - var (hp, hv) = Sides.Twins(position, containing, e, d, Make.Vo(e, d)); - var o = JCtx.Apply(r, e, hp.Declared, hv.Declared); - var ob = new JsonSerializerOptions(o); // the baseline: the same options, the serializer's own decimal - foreach (var c in ob.Converters.Where(c => c.CanConvert(typeof(decimal))).ToList()) ob.Converters.Remove(c); - o.Converters.Insert(0, conv); // registered first: an application converter for decimals (own-for-wrapped) comes after it - var ownVo = r.Is("json.Converters", "own-for-value-object"); - var docs = Baseline.Docs("docs/json.md#decimals-in-one-spelling"); - const string Smallest = "smallest-scale", Covers = "covers-every-decimal", Reads = "reads-a-number-or-a-string", Keeps = "keeps-the-scale-it-reads"; - var kindOf = number ? "number-is-a-number" : "string-is-a-string"; - - // Writing: one spelling, the smallest scale, as a number or a string; the value object's document is the decimal's. - var (jp, xp) = Jx.Ser(hp, o); var (jv, xv) = Jx.Ser(hv, o); var (jb, xb) = Jx.Ser(hp, ob); - if (xb is not null && xp is not null && (xv is not null || ownVo) && xp.GetType() == xb.GetType()) - { - // the options refuse the shape for the serializer's own decimal too: nothing is written or read to compare. Where the value - // object is refused alike, at the same stage with the same exception type, the decimal without TypeKit's converter is side (1) - // of the three-sided differential (Sides), and each check is the framework's refusal; otherwise none can tell. - var one = new Sides.Answer("write", null, xb); var three = new Sides.Answer("write", null, xv); - if (Sides.Alike(one, three)) - { - var framework = Sides.Measured(one, "the serializer's own decimal, without TypeKit's converter"); - foreach (var o2 in new[] { Smallest, kindOf, Covers, Reads, Keeps }) r.Check(o2, framework.Baseline, c => Sides.Refuses(c, framework, three)); - return null; - } - var why = "the options refuse the shape for the serializer's own decimal too: " + Jx.Ex(xb); - string? None(Check c) => c.Inconclusive(why); - foreach (var ob2 in new[] { Smallest, kindOf }) r.Check(ob2, docs, None); - r.Check(Covers, Baseline.Twin, None); r.Check(Reads, docs, None); r.Check(Keeps, docs, None); - return null; - } - var tp = xp is null ? Jx.Tokens(jp!).Where(t => IsDecimal(t, d)).ToList() : []; - var tb = xb is null ? Jx.Tokens(jb!).Where(t => IsDecimal(t, d)).ToList() : null; - var writeLeftOut = tb is { Count: 0 } && xp is null && tp.Count == 0; - string? WriteLeftOut(Check c) { c.Note(NoteKeys.JsonMemberLeftOutByOptions, "write: " + Jx.Cut(jb)); return c.Inconclusive("write: the options leave the member out, for the serializer's own decimal too: " + Jx.Cut(jb)); } - var want = number ? JsonTokenType.Number : JsonTokenType.String; - var f1 = r.Check(Smallest, docs, c => - { - if (xp is not null) return $"write: the decimal with {conv.GetType().Name} threw {Jx.Ex(xp)}; without it the serializer wrote {Jx.Cut(jb)}"; - if (tb is { Count: 0 } && tp.Count != 0) return $"write: the options leave the member out without the converter ({Jx.Cut(jb)}) but it wrote {Jx.Cut(jp)}"; - if (writeLeftOut) return WriteLeftOut(c); - if (tp.Count == 0) return $"write: no decimal in {Jx.Cut(jp)}; the serializer alone wrote {Jx.Cut(jb)}"; - foreach (var t in tp.Where(t => t.Type != JsonTokenType.PropertyName)) - if (t.Raw != canon) return $"write: {t.Type} {t.Raw}, expected the smallest scale {canon}, in {Jx.Cut(jp)}"; - c.Note(NoteKeys.Compared, $"write {text} -> {canon}"); - return null; - }); - var f2 = r.Check(kindOf, docs, c => - { - if (xp is not null) return c.Inconclusive("write: the decimal with the converter threw, judged by smallest-scale"); - if (writeLeftOut) return WriteLeftOut(c); - var values = tp.Where(t => t.Type != JsonTokenType.PropertyName).ToList(); - if (values.Count == 0) return c.Inconclusive($"write: no decimal value in {Jx.Cut(jp)}, only keys, which are names"); - foreach (var t in values) - if (t.Type != want) return $"write: {t.Type} {t.Raw}, expected the {want} {canon}, in {Jx.Cut(jp)}"; - c.Note(NoteKeys.Compared, $"write as a {want}"); - return null; - }); - - // Reading: a number, a string holding one, and an escaped string all read, the scale they carry kept, on both sides alike. - var full = Jx.Full(o); - var (jf, xf) = Jx.Ser(hp, full); - if (xf is not null) { r.Outcome("harness-error", "the document with every member written threw " + Jx.Ex(xf)); return f1 ?? f2; } - var at = Jx.Tokens(jf!).Where(t => IsDecimal(t, d)).ToList(); - string? readNothing = null; - var forms = new List
(); - var leftOut = false; - if (at.Count == 0) - { - // Nothing to read either: the options leave the member out of every document (a field without IncludeFields). - var (jfb, _) = Jx.Ser(hp, Jx.Full(ob)); - if (jfb is null || Jx.Tokens(jfb).Any(t => IsDecimal(t, d))) { r.Outcome("harness-error", "no decimal in the document with every member written: " + jf); return f1 ?? f2; } - readNothing = "read: the options leave the member out of every document, for the serializer's own decimal too: " + Jx.Cut(jf); - } - else - { - var key = at.Any(t => t.Type == JsonTokenType.PropertyName); - var texts = new List<(string Name, string Json)>(); - if (!key) texts.Add(("number", Jx.Rewrite(jf!, at, _ => text))); - texts.Add(("string", Jx.Rewrite(jf!, at, _ => "\"" + text + "\""))); - texts.Add(("escaped string", Jx.Rewrite(jf!, at, _ => "\"" + Bs + "u00" + ((int)text[0]).ToString("x2") + text[1..] + "\""))); - // The baseline reads the first form with the serializer's own decimal: it shows whether these options read the member at all. - var (vb, eb) = Jx.De(texts[0].Json, hp.Declared, ob); - leftOut = eb is null && Held(vb, hp.Declared, d).Count == 0; - foreach (var (name, json) in texts) - { - var (vp, ep) = Jx.De(json, hp.Declared, o); - var (vv, ev) = ownVo ? (null, null) : Jx.De(json, hv.Declared, o); - var both = ep is not null && eb is not null && (ownVo || ev is not null) && ep.GetType() == eb.GetType(); - var cp = ep is null ? Held(vp, hp.Declared, d) : []; var cv = ownVo ? cp : ev is null ? Held(vv, hv.Declared, d) : []; - forms.Add(new(name, json, ep, ev, cp, cv, both)); - } - if (leftOut && forms.All(f => f.Ep is null && f.Ev is null && f.Cp.Count == 0 && f.Cv.Count == 0)) - readNothing = "read: the options read no member, for the serializer's own decimal too"; - } - string? ReadLeftOut(Check c) { c.Note(NoteKeys.JsonMemberLeftOutByOptions, "read"); return c.Inconclusive(readNothing!); } - // The forms both sides refuse alike under these options (the serializer's own decimal too) have nothing to compare. - var compared = forms.Where(f => !f.BothRefused).ToList(); - string? AllRefused(Check c) - { - foreach (var f in forms.Where(f => f.BothRefused)) c.Log.AppendLine($"read {f.Name}: both refuse: {Jx.Ex(f.Ep!)}"); - return c.Inconclusive("read: every form is refused for the serializer's own decimal too: " + Jx.Ex(forms[0].Ep!)); - } - var f4 = r.Check(Reads, docs, c => - { - if (readNothing is not null) return ReadLeftOut(c); - foreach (var f in forms) - { - if (f.BothRefused) continue; - if (f.Ep is not null) return $"read {f.Name}: the decimal with {conv.GetType().Name} threw {Jx.Ex(f.Ep)}\n text={Jx.Cut(f.Json)}"; - if (leftOut) { if (f.Cp.Count != 0) return $"read {f.Name}: the options read no member for the serializer's own decimal, yet it read {Show(f.Cp)}"; continue; } - if (f.Cp.Count == 0) return $"read {f.Name}: the decimal read nothing\n text={Jx.Cut(f.Json)}"; - } - if (compared.Count == 0) return AllRefused(c); - c.Note(NoteKeys.Compared, "read " + string.Join(", ", compared.Select(f => f.Name))); - return null; - }); - var f5 = r.Check(Keeps, docs, c => - { - if (readNothing is not null) return ReadLeftOut(c); - if (compared.Count == 0) return AllRefused(c); - var kept = 0; - foreach (var f in compared) - { - if (f.Cp.Count == 0) continue; // read nothing, or refused: reads-a-number-or-a-string judges it - if (f.Cp.Any(x => !Same(x, d))) return $"read {f.Name}: read {Show(f.Cp)}, expected {d.ToString(Inv)} with its scale\n text={Jx.Cut(f.Json)}"; - kept++; - } - if (kept == 0) return c.Inconclusive("read: no form read a decimal to hold its scale to"); - c.Note(NoteKeys.Compared, $"read with the scale of {text}"); - return null; - }); - var f3 = r.Check(Covers, ownVo ? Baseline.RoundTrip : Baseline.Twin, c => - { - if (xv is not null && !ownVo) return $"write: the value object threw {Jx.Ex(xv)} where the decimal wrote {Jx.Cut(jp)}"; - if (xp is not null) return c.Inconclusive("write: the decimal with the converter threw, judged by smallest-scale"); - if (tb is not null && tb.Count != 0 && tp.Count != tb.Count) return $"write: {tp.Count} decimals in {Jx.Cut(jp)}, {tb.Count} in {Jx.Cut(jb)}"; - foreach (var t in tp.Where(t => t.Type == JsonTokenType.PropertyName)) - if (t.Raw != canon) return $"write: key {t.Raw}, expected {canon}, in {Jx.Cut(jp)}"; - if (ownVo) - { - // "Your own converter wins" (AspNetCore README#what-the-document-says): the application's converter writes the value object, - // so the decimal's document is none of its; its own document reads back under the same options - c.Note(NoteKeys.OwnVo); - var (_, xr) = Jx.De(jv!, hv.Declared, o); - if (xr is not null) return "own converter for the value object: its document does not read back: " + Jx.Ex(xr); - return null; - } - if (jv != jp) return $"write: the value object wrote {Jx.Cut(jv)}, the decimal {Jx.Cut(jp)}"; - foreach (var f in compared) - { - if (f.Ep is not null) continue; // the decimal refused it: reads-a-number-or-a-string judges it - if (f.Ev is not null) return $"read {f.Name}: the value object threw {Jx.Ex(f.Ev)} where the decimal read it\n text={Jx.Cut(f.Json)}"; - if (f.Cp.Count != f.Cv.Count || f.Cp.Zip(f.Cv).Any(z => !Same(z.First, z.Second))) return $"read {f.Name}: the value object read {Show(f.Cv)}, the decimal {Show(f.Cp)}\n text={Jx.Cut(f.Json)}"; - } - if (writeLeftOut && (readNothing is not null || compared.Count == 0)) return WriteLeftOut(c); - c.Note(NoteKeys.Compared, $"{(number ? "number" : "string")} {text} -> {canon}, the value object's document the decimal's"); - return null; - }); - return f1 ?? f2 ?? f3 ?? f4 ?? f5; - } -} diff --git a/tools/probe/corpus/harness/EfExtra.cs b/tools/probe/corpus/harness/EfExtra.cs deleted file mode 100644 index 26ef7def..00000000 --- a/tools/probe/corpus/harness/EfExtra.cs +++ /dev/null @@ -1,48 +0,0 @@ -// Entity shapes and helpers for the EF claims E07, E13, E14, E15 (reuse EfHarness.cs: EfDb, EfModel, Q, EfRegistry, KeyRegistry). -#nullable enable -using System.Linq.Expressions; -using System.Reflection; -using Microsoft.EntityFrameworkCore; -using Probes.Shapes; -namespace Probes.Ef; - -public class EntL where T : notnull { public int Id { get; set; } public List Items { get; set; } = []; } -public class EntComp where TA : notnull where TB : notnull { public TA A { get; set; } = default!; public TB B { get; set; } = default!; public string Tag { get; set; } = ""; } -public abstract class RootE where T : notnull { public T Id { get; set; } = default!; public string Name { get; set; } = ""; } -public class LeafA : RootE where T : notnull { public int X { get; set; } } -public class LeafB : RootE where T : notnull { public int Y { get; set; } } - -public static class EfX -{ - /// The i-th distinct plain value of a wrapped type. - public static object Sample(string wrapped, int i) => wrapped switch - { - "string" => "val-" + (char)('a' + i), - "int" => 10 + i, - "long" => 4_000_000_000L + i, - "Guid" => new Guid(i + 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11), - "decimal" => 1.5m + i, - _ => throw new ArgumentException(wrapped), - }; - public static EfEntry Top(string kind, string wrapped) => EfRegistry.All.FirstOrDefault(x => x.Kind == kind && x.Wrapped == wrapped && x.Site == "top-level") - ?? EfRegistry.All.First(x => (x.Kind is "record-struct" or "sealed-record-class") && x.Wrapped == wrapped && x.Site == "top-level"); - public static readonly string[] Kinds = ["record-struct", "sealed-record-class", "validated-class", "validated-struct"]; - public static object New(Type t) => Activator.CreateInstance(t)!; - public static void Set(object o, string prop, object? v) => o.GetType().GetProperty(prop)!.SetValue(o, v); - public static object? Get(object o, string prop) => o.GetType().GetProperty(prop)!.GetValue(o); - public static string Plainify(object? k) - { - if (k is not null && k.GetType().Namespace == "Probes.Ef") k = k.GetType().GetProperty("Value")!.GetValue(k); - return Convert.ToString(k, System.Globalization.CultureInfo.InvariantCulture) ?? "null"; - } - public static IEnumerable Enumerate(IQueryable q) { foreach (var o in q) yield return o!; } - public static async Task RawScalar(EfDb db, string sql) - { - await using var cmd = db.Connection.CreateCommand(); cmd.CommandText = sql; - return Convert.ToString(await cmd.ExecuteScalarAsync(), System.Globalization.CultureInfo.InvariantCulture); - } - public static async Task Raw(EfDb db, string sql) - { - await using var cmd = db.Connection.CreateCommand(); cmd.CommandText = sql; await cmd.ExecuteNonQueryAsync(); - } -} diff --git a/tools/probe/corpus/harness/EfExtra2.cs b/tools/probe/corpus/harness/EfExtra2.cs deleted file mode 100644 index 2cffd4cf..00000000 --- a/tools/probe/corpus/harness/EfExtra2.cs +++ /dev/null @@ -1,82 +0,0 @@ -// Shared by the EF claims E06, E11, E12, E16 (after EfHarness.cs / EfExtra.cs): a context with ConventionS and the order of UseTypeKit, -// and an application-owned converter ("C:" + the plain value as text) for any type. -#nullable enable -using System.Globalization; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Storage.ValueConversion; -using Probes.Shapes; -namespace Probes.Ef; - -public sealed class DynCtx2(DbContextOptions options, Action configure, Action? conventions) : DbContext(options) -{ - protected override void OnModelCreating(ModelBuilder modelBuilder) => configure(modelBuilder); - protected override void ConfigureConventions(ModelConfigurationBuilder configurationBuilder) => conventions?.Invoke(configurationBuilder); -} - -public sealed class EfDb2 : IAsyncDisposable -{ - public SqliteConnection Connection { get; } - public DynCtx2 Context { get; } - // order: "none" (no UseTypeKit), "before-provider" or "after-provider". - public EfDb2(Action configure, string order, Action? conventions = null) - { - Connection = new SqliteConnection("Data Source=:memory:"); - Connection.Open(); - var b = new DbContextOptionsBuilder(); - if (order == "before-provider") b.UseTypeKit(); - b.UseSqlite(Connection); - if (order == "after-provider") b.UseTypeKit(); - b.ReplaceService(); - Context = new DynCtx2(b.Options, configure, conventions); - } - public async ValueTask DisposeAsync() { await Context.DisposeAsync(); await Connection.DisposeAsync(); } -} - -/// An application-owned conversion of T to text: "C:" + the plain value. T is the value object or its wrapped type. -public static class OwnConv -{ - public static Func ToF = _ => throw new InvalidOperationException("OwnConv not set up"); - public static Func FromF = _ => throw new InvalidOperationException("OwnConv not set up"); - public static string To(T v) => ToF(v); - public static T From(string s) => FromF(s); - public static void Setup(string wrapped, Func make, Func plain) - { - ToF = v => "C:" + Fmt(plain(v!)); - FromF = s => (T)make(Parse(wrapped, s[2..])); - } - public static string Fmt(object p) => Convert.ToString(p, CultureInfo.InvariantCulture)!; - public static object Parse(string wrapped, string s) => wrapped switch - { - "string" => s, "int" => int.Parse(s, CultureInfo.InvariantCulture), "long" => long.Parse(s, CultureInfo.InvariantCulture), - "Guid" => Guid.Parse(s), "decimal" => decimal.Parse(s, CultureInfo.InvariantCulture), "char" => s[0], - _ => throw new ArgumentException(wrapped), - }; -} - -public sealed class OwnConverter() : ValueConverter(v => OwnConv.To(v), s => OwnConv.From(s)); - -public static class OwnConvs -{ - /// Sets OwnConv up for the value object (voSide) or for its wrapped type, returns the converter type. - public static Type Setup(EfEntry e, bool vo) - { - if (vo) - { - typeof(OwnConv<>).MakeGenericType(e.Vo).GetMethod("Setup")!.Invoke(null, [e.Wrapped, (Func)e.MakeVo, (Func)(v => v.GetType().GetProperty("Value")!.GetValue(v)!)]); - return typeof(OwnConverter<>).MakeGenericType(e.Vo); - } - typeof(OwnConv<>).MakeGenericType(e.Plain).GetMethod("Setup")!.Invoke(null, [e.Wrapped, (Func)(p => p), (Func)(p => p)]); - return typeof(OwnConverter<>).MakeGenericType(e.Plain); - } -} - -public static class EfX2 -{ - public static async Task RawScalar(SqliteConnection c, string sql) - { - await using var cmd = c.CreateCommand(); cmd.CommandText = sql; - return Convert.ToString(await cmd.ExecuteScalarAsync(), CultureInfo.InvariantCulture); - } -} diff --git a/tools/probe/corpus/harness/EfHarness.cs b/tools/probe/corpus/harness/EfHarness.cs deleted file mode 100644 index c6de75a1..00000000 --- a/tools/probe/corpus/harness/EfHarness.cs +++ /dev/null @@ -1,191 +0,0 @@ -// Shared EF row-applying code for the EF satellite claims (surface ef): the entity shapes, a DbContext built from a -// configuration delegate, the model descriptor and the query helpers. Written once; later EF claims reuse it. -#nullable enable -using System.Linq.Expressions; -using System.Reflection; -using Microsoft.Data.Sqlite; -using Microsoft.EntityFrameworkCore; -using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Metadata.Builders; -using Probes.Shapes; -namespace Probes.Ef; - -public sealed record EfEntry(string Kind, string Wrapped, string Site, Type Vo, Type Plain, Func Valid) -{ - /// The value object around a plain value, by its public seams: From, else FromKnownGood. - public object MakeVo(object plain) - { - var from = Vo.GetMethod("From", BindingFlags.Public | BindingFlags.Static, [Plain]); - if (from is not null) return from.Invoke(null, [plain])!; - var known = Vo.GetMethod("FromKnownGood", BindingFlags.Public | BindingFlags.Static, [Plain, typeof(string)]); - return known is null ? throw new InvalidOperationException($"{Vo} has neither From nor FromKnownGood") : known.Invoke(null, [plain, null])!; - } - - /// The generated companion extension GetValue / ValueOrNull of this value object (a static class in its namespace; the first parameter is the value object, or its Nullable for ValueOrNull). - public MethodInfo? Companion(string name) - { - var first = name == "ValueOrNull" && Vo.IsValueType ? typeof(Nullable<>).MakeGenericType(Vo) : Vo; - return Vo.Assembly.GetTypes().Where(t => t.IsAbstract && t.IsSealed && t.Namespace == Vo.Namespace) - .SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.Static)) - .FirstOrDefault(m => m.Name == name && m.GetParameters() is [var p] && p.ParameterType == first); - } -} - -public static partial class EfRegistry -{ - public static readonly List All = Build(); - static List Build() { var e = new List(); Fill(e); return e; } - static partial void Fill(List e); - public static EfEntry Of(Row r) { var k = r["shape.kind"]; var w = r["shape.wrapped"]; var s = r["shape.site"]; return All.First(x => x.Kind == k && x.Wrapped == w && x.Site == s); } -} - -public class EntP where T : notnull { public int Id { get; set; } public T P { get; set; } = default!; } -public class EntNC where T : class { public int Id { get; set; } public T? P { get; set; } } -public class EntK where T : notnull { public T Id { get; set; } = default!; public string Tag { get; set; } = ""; } -public class Own where T : notnull { public T P { get; set; } = default!; } -public class EntO where T : notnull { public int Id { get; set; } public Own O { get; set; } = new(); } -public class EntC where T : notnull { public int Id { get; set; } public Own C { get; set; } = new(); } -public sealed class Box { public T Value = default!; } - -public sealed class UniqueModelKey : Microsoft.EntityFrameworkCore.Infrastructure.IModelCacheKeyFactory -{ - public object Create(DbContext context, bool designTime) => new object(); -} - -public sealed class DynCtx(DbContextOptions options, Action configure) : DbContext(options) -{ - protected override void OnModelCreating(ModelBuilder modelBuilder) => configure(modelBuilder); -} - -public sealed class EfDb : IAsyncDisposable -{ - public SqliteConnection Connection { get; } - public DynCtx Context { get; } - public EfDb(Action configure, bool typeKit, Action? extra = null) - { - Connection = new SqliteConnection("Data Source=:memory:"); - Connection.Open(); - var b = new DbContextOptionsBuilder().UseSqlite(Connection).ReplaceService(); - if (typeKit) b.UseTypeKit(); - extra?.Invoke(b); - Context = new DynCtx(b.Options, configure); - } - public async ValueTask DisposeAsync() { await Context.DisposeAsync(); await Connection.DisposeAsync(); } -} - -public static class EfModel -{ - public static Type Entity(string mapping, Type t) => mapping switch - { - "property" => typeof(EntP<>).MakeGenericType(t), - "nullable" => t.IsValueType ? typeof(EntP<>).MakeGenericType(typeof(Nullable<>).MakeGenericType(t)) : typeof(EntNC<>).MakeGenericType(t), - "key" => typeof(EntK<>).MakeGenericType(t), - "owned" => typeof(EntO<>).MakeGenericType(t), - "complex" => typeof(EntC<>).MakeGenericType(t), - _ => throw new ArgumentException("mapping " + mapping), - }; - - public static readonly Dictionary ColumnTypes = new() - { - ["string"] = "VARCHAR(40)", ["Uri"] = "VARCHAR(40)", ["Guid"] = "UNIQUEIDENTIFIER", ["int"] = "BIGINT", ["long"] = "BIGINT", ["byte"] = "TINYINT", - ["ulong"] = "BIGINT", ["decimal"] = "NUMERIC(10,2)", ["double"] = "FLOAT", ["bool"] = "BIT", ["char"] = "CHAR(1)", ["DateTime"] = "DATETIME", - ["DateTimeOffset"] = "DATETIMEOFFSET", ["DateOnly"] = "DATE", ["TimeOnly"] = "TIME", ["TimeSpan"] = "TIME", ["enum"] = "SMALLINT", ["flags-enum"] = "SMALLINT", - }; - - /// Maps the entity of a mapping in table "t", applying a facet to the column property (and UseAutoincrement to an integer key when asked). - public static Action Configure(string mapping, Type entity, Action? facet, bool autoincrement) => mb => - { - var eb = mb.Entity(entity); - eb.ToTable("t"); - var t = entity.GetGenericArguments()[0]; - switch (mapping) - { - case "property" or "nullable": eb.HasKey("Id"); if (facet is not null) facet(eb.Property("P").Metadata); break; - case "key": - eb.HasKey("Id"); - var pb = eb.Property("Id"); - facet?.Invoke(pb.Metadata); - if (autoincrement) Microsoft.EntityFrameworkCore.SqlitePropertyBuilderExtensions.UseAutoincrement(pb); - break; - case "owned": - eb.HasKey("Id"); - eb.OwnsOne(typeof(Own<>).MakeGenericType(t), "O", ob => { if (facet is not null) facet(ob.Property("P").Metadata); }); - break; - case "complex": - eb.HasKey("Id"); - eb.ComplexProperty("C", cb => { if (facet is not null) facet(cb.Property("P").Metadata); }); - break; - } - }; - - /// What the model says about every mapped column: store type, nullability, facets, key and generation. - public static string Describe(DbContext db) - { - var model = db.GetService().Model; - var lines = new List(); - string Line(string table, string prefix, IProperty p) - { - string col = "?"; try { col = ((IReadOnlyProperty)p).GetColumnType() ?? "?"; } catch { try { col = Microsoft.EntityFrameworkCore.RelationalPropertyExtensions.GetRelationalTypeMapping(p).StoreType; } catch { col = "?"; } } - string nul; try { nul = p.IsColumnNullable().ToString(); } catch { nul = p.IsNullable.ToString(); } - return $"{table}.{prefix}{p.Name}: type={col} nullable={nul} max={p.GetMaxLength()} prec={p.GetPrecision()} scale={p.GetScale()} unicode={p.IsUnicode()} pk={p.IsPrimaryKey()} gen={p.ValueGenerated}"; - } - foreach (var et in model.GetEntityTypes()) - { - var table = et.GetTableName() ?? "(none)"; - foreach (var p in et.GetProperties()) lines.Add(Line(table, "", p)); - foreach (var cp in et.GetComplexProperties()) foreach (var p in cp.ComplexType.GetProperties()) lines.Add(Line(table, cp.Name + ".", p)); - } - lines.Sort(StringComparer.Ordinal); - return string.Join("\n", lines); - } - - public static string FirstDiff(string a, string b) - { - var x = a.Split('\n'); var y = b.Split('\n'); - for (var i = 0; i < Math.Max(x.Length, y.Length); i++) - if ((i < x.Length ? x[i] : "") != (i < y.Length ? y[i] : "")) return $"VO: {(i < x.Length ? x[i] : "")} | plain: {(i < y.Length ? y[i] : "")}"; - return ""; - } -} - -/// -/// How a check of a two-sided EF row ends where a side threw: the value object's context under UseTypeKit() and the -/// wrapped twin's under plain EF ran the same step, named by what. Neither threw: false, compare on. The value object -/// alone: its violation. Both, with exceptions of one type: nothing to compare, inconclusive, since no fact or sentence says why -/// the framework refuses it. Both, otherwise: a violation, since the value object did not fail as its twin does. The twin alone: -/// inconclusive, no twin to compare with. -/// -public static class EfSides -{ - public static bool Threw(Check c, Exception? vo, Exception? plain, string what, out string? failure) - { - failure = null; - if (vo is null && plain is null) return false; - if (vo is not null && plain is not null) - failure = vo.GetType() == plain.GetType() - ? c.Inconclusive($"both sides refuse {what} alike: {vo.GetType().Name}: {Short(vo.Message)}") - : $"{what}: the value object's side threw {vo.GetType().Name}: {Short(vo.Message)}; the wrapped type's {plain.GetType().Name}: {Short(plain.Message)}"; - else if (plain is not null) failure = c.Inconclusive($"the wrapped type's side refuses {what}, so there is no twin to compare with: {plain.GetType().Name}: {Short(plain.Message)}"); - else failure = $"{what} with the value object threw {vo!.GetType().Name}: {Short(vo.Message)}; the wrapped type's did not"; - return true; - } - - static string Short(string s) { s = s.Split('\n')[0]; return s.Length > 300 ? s[..300] : s; } -} - -public static class Q -{ - public static IQueryable Call(string name, Type[] typeArgs, IQueryable src, params Expression[] args) - { - static bool OneParamLambda(ParameterInfo p) => - p.ParameterType.IsGenericType && p.ParameterType.GetGenericTypeDefinition() == typeof(Expression<>) && p.ParameterType.GetGenericArguments()[0].GetGenericArguments().Length == 2; - var m = typeof(Queryable).GetMethods().First(x => x.Name == name && x.IsGenericMethodDefinition && x.GetGenericArguments().Length == typeArgs.Length - && x.GetParameters().Length == args.Length + 1 && x.GetParameters().Skip(1).All(OneParamLambda)); - return (IQueryable)m.MakeGenericMethod(typeArgs).Invoke(null, [src, .. args.Select(a => (object)a)])!; - } - public static IQueryable SetOf(DbContext db, Type entity) => - (IQueryable)typeof(DbContext).GetMethods().First(m => m.Name == "Set" && m.GetParameters().Length == 0).MakeGenericMethod(entity).Invoke(db, null)!; - public static object NewBox(Type t, object? value) { var b = Activator.CreateInstance(typeof(Box<>).MakeGenericType(t))!; typeof(Box<>).MakeGenericType(t).GetField("Value")!.SetValue(b, value); return b; } - public static string Sql(IQueryable q) => q.ToQueryString(); -} diff --git a/tools/probe/corpus/harness/EfShapes.g.cs b/tools/probe/corpus/harness/EfShapes.g.cs deleted file mode 100644 index 95fa592f..00000000 --- a/tools/probe/corpus/harness/EfShapes.g.cs +++ /dev/null @@ -1,962 +0,0 @@ -// by tools/probe/generators/ef.cs catalogue; the EF probes' shape catalogue (shape.kind x shape.wrapped x shape.site). Do not edit. -#nullable enable -#pragma warning disable CMTK0014, CS8618, CS1591, CMTK0005, CMTK1000, CMTK0003, CMTK0006, CA1000 -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -using Probes.Shapes; -namespace Probes.Ef; -public struct S_E_record_struct_DateOnly -{ -public readonly partial record struct V : IValue; -} -public static class C2_E_record_struct_DateOnly -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_DateOnly : IValue; -public static class C_E_record_struct_DateTime -{ -public readonly partial record struct V : IValue; -} -public static class C2_E_record_struct_DateTime -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_DateTime : IValue; -public readonly partial record struct E_record_struct_DateTimeOffset : IValue; -public static class C_E_record_struct_Guid -{ -public readonly partial record struct V : IValue; -} -public static class C2_E_record_struct_Guid -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_Guid : IValue; -public static class C2_E_record_struct_TimeOnly -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public static class C_E_record_struct_TimeSpan -{ -public readonly partial record struct V : IValue; -} -public struct S_E_record_struct_TimeSpan -{ -public readonly partial record struct V : IValue; -} -public static class C_E_record_struct_Uri -{ -public readonly partial record struct V : IValue; -} -public static class C2_E_record_struct_Uri -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_Uri : IValue; -public struct S_E_record_struct_bool -{ -public readonly partial record struct V : IValue; -} -public readonly partial record struct E_record_struct_bool : IValue; -public static class C2_E_record_struct_byte -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_byte : IValue; -public static class C_E_record_struct_char -{ -public readonly partial record struct V : IValue; -} -public struct S_E_record_struct_char -{ -public readonly partial record struct V : IValue; -} -public readonly partial record struct E_record_struct_char : IValue; -public struct S_E_record_struct_decimal -{ -public readonly partial record struct V : IValue; -} -public static class C2_E_record_struct_decimal -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_decimal : IValue; -public struct S_E_record_struct_double -{ -public readonly partial record struct V : IValue; -} -public static class C2_E_record_struct_enum -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_enum : IValue; -public static class C2_E_record_struct_flags_enum -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_flags_enum : IValue; -public static class C2_E_record_struct_int -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_int : IValue; -public static class C2_E_record_struct_long -{ - public static class Inner - { -public readonly partial record struct V : IValue; - } -} -public readonly partial record struct E_record_struct_long : IValue; -public static class C_E_record_struct_string -{ -public readonly partial record struct V : IValue; -} -public struct S_E_record_struct_ulong -{ -public readonly partial record struct V : IValue; -} -public struct S_E_sealed_record_class_DateOnly -{ -public sealed partial record class V : IValue; -} -public static class C2_E_sealed_record_class_DateOnly -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public struct S_E_sealed_record_class_DateTime -{ -public sealed partial record class V : IValue; -} -public static class C2_E_sealed_record_class_DateTime -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public static class C_E_sealed_record_class_DateTimeOffset -{ -public sealed partial record class V : IValue; -} -public struct S_E_sealed_record_class_DateTimeOffset -{ -public sealed partial record class V : IValue; -} -public static class C2_E_sealed_record_class_DateTimeOffset -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public static class C2_E_sealed_record_class_Guid -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public static class C_E_sealed_record_class_TimeOnly -{ -public sealed partial record class V : IValue; -} -public struct S_E_sealed_record_class_TimeOnly -{ -public sealed partial record class V : IValue; -} -public static class C2_E_sealed_record_class_TimeOnly -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public sealed partial record class E_sealed_record_class_TimeSpan : IValue; -public static class C_E_sealed_record_class_Uri -{ -public sealed partial record class V : IValue; -} -public struct S_E_sealed_record_class_Uri -{ -public sealed partial record class V : IValue; -} -public static class C2_E_sealed_record_class_Uri -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public static class C2_E_sealed_record_class_bool -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public static class C_E_sealed_record_class_byte -{ -public sealed partial record class V : IValue; -} -public struct S_E_sealed_record_class_byte -{ -public sealed partial record class V : IValue; -} -public struct S_E_sealed_record_class_char -{ -public sealed partial record class V : IValue; -} -public sealed partial record class E_sealed_record_class_char : IValue; -public static class C_E_sealed_record_class_decimal -{ -public sealed partial record class V : IValue; -} -public static class C2_E_sealed_record_class_decimal -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public static class C2_E_sealed_record_class_double -{ - public static class Inner - { -public sealed partial record class V : IValue; - } -} -public sealed partial record class E_sealed_record_class_double : IValue; -public sealed partial record class E_sealed_record_class_enum : IValue; -public static class C_E_sealed_record_class_flags_enum -{ -public sealed partial record class V : IValue; -} -public sealed partial record class E_sealed_record_class_flags_enum : IValue; -public static class C_E_sealed_record_class_int -{ -public sealed partial record class V : IValue; -} -public struct S_E_sealed_record_class_int -{ -public sealed partial record class V : IValue; -} -public static class C_E_sealed_record_class_long -{ -public sealed partial record class V : IValue; -} -public static class C_E_sealed_record_class_string -{ -public sealed partial record class V : IValue; -} -public struct S_E_sealed_record_class_string -{ -public sealed partial record class V : IValue; -} -public sealed partial record class E_sealed_record_class_string : IValue; -public static class C_E_sealed_record_class_ulong -{ -public sealed partial record class V : IValue; -} -public struct S_E_validated_class_DateOnly -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(DateOnly value) => - EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_class_DateTimeOffset -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(DateTimeOffset value) => - EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C_E_validated_class_Guid -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(Guid value) => - EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_class_Guid -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(Guid value) => - EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public sealed partial record class E_validated_class_Guid : IValidatedValue -{ - public static Result Create(Guid value) => - EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new E_validated_class_Guid(value); -} -public static class C_E_validated_class_TimeOnly -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(TimeOnly value) => - EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public sealed partial record class E_validated_class_TimeOnly : IValidatedValue -{ - public static Result Create(TimeOnly value) => - EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new E_validated_class_TimeOnly(value); -} -public static class C2_E_validated_class_TimeSpan -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(TimeSpan value) => - EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public struct S_E_validated_class_Uri -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(Uri value) => - EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C_E_validated_class_bool -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(bool value) => - EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_class_bool -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(bool value) => - EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public sealed partial record class E_validated_class_bool : IValidatedValue -{ - public static Result Create(bool value) => - EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new E_validated_class_bool(value); -} -public static class C_E_validated_class_byte -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(byte value) => - EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public sealed partial record class E_validated_class_byte : IValidatedValue -{ - public static Result Create(byte value) => - EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new E_validated_class_byte(value); -} -public static class C_E_validated_class_char -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(char value) => - EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_class_char -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(char value) => - EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public static class C_E_validated_class_double -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(double value) => - EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_class_double -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(double value) => - EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public struct S_E_validated_class_enum -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(PEnum value) => - EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_class_enum -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(PEnum value) => - EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public static class C_E_validated_class_flags_enum -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(PFlags value) => - EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_class_flags_enum -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(PFlags value) => - EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_class_flags_enum -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(PFlags value) => - EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public static class C2_E_validated_class_int -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(int value) => - EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public sealed partial record class E_validated_class_int : IValidatedValue -{ - public static Result Create(int value) => - EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new E_validated_class_int(value); -} -public struct S_E_validated_class_long -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(long value) => - EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_class_long -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(long value) => - EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public struct S_E_validated_class_string -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(string value) => - EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_class_string -{ - public static class Inner - { -public sealed partial record class V : IValidatedValue -{ - public static Result Create(string value) => - EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public struct S_E_validated_class_ulong -{ -public sealed partial record class V : IValidatedValue -{ - public static Result Create(ulong value) => - EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public sealed partial record class E_validated_class_ulong : IValidatedValue -{ - public static Result Create(ulong value) => - EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new E_validated_class_ulong(value); -} -public static class C_E_validated_struct_DateOnly -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(DateOnly value) => - EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public readonly partial record struct E_validated_struct_DateOnly : IValidatedValue -{ - public static Result Create(DateOnly value) => - EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new E_validated_struct_DateOnly(value); -} -public static class C_E_validated_struct_DateTime -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(DateTime value) => - EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_struct_DateTime -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(DateTime value) => - EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C_E_validated_struct_DateTimeOffset -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(DateTimeOffset value) => - EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_struct_DateTimeOffset -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(DateTimeOffset value) => - EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public static class C_E_validated_struct_Guid -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(Guid value) => - EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_struct_Guid -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(Guid value) => - EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_struct_TimeOnly -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(TimeOnly value) => - EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public readonly partial record struct E_validated_struct_TimeOnly : IValidatedValue -{ - public static Result Create(TimeOnly value) => - EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new E_validated_struct_TimeOnly(value); -} -public static class C_E_validated_struct_TimeSpan -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(TimeSpan value) => - EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public readonly partial record struct E_validated_struct_Uri : IValidatedValue -{ - public static Result Create(Uri value) => - EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new E_validated_struct_Uri(value); -} -public static class C_E_validated_struct_bool -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(bool value) => - EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_struct_bool -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(bool value) => - EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_struct_bool -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(bool value) => - EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public struct S_E_validated_struct_byte -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(byte value) => - EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_struct_byte -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(byte value) => - EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public static class C2_E_validated_struct_char -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(char value) => - EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public readonly partial record struct E_validated_struct_char : IValidatedValue -{ - public static Result Create(char value) => - EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new E_validated_struct_char(value); -} -public static class C_E_validated_struct_decimal -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(decimal value) => - EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_struct_decimal -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(decimal value) => - EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_struct_decimal -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(decimal value) => - EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public readonly partial record struct E_validated_struct_decimal : IValidatedValue -{ - public static Result Create(decimal value) => - EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new E_validated_struct_decimal(value); -} -public static class C_E_validated_struct_double -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(double value) => - EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public readonly partial record struct E_validated_struct_double : IValidatedValue -{ - public static Result Create(double value) => - EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new E_validated_struct_double(value); -} -public static class C_E_validated_struct_enum -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(PEnum value) => - EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_struct_flags_enum -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(PFlags value) => - EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C_E_validated_struct_int -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(int value) => - EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public struct S_E_validated_struct_int -{ -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(int value) => - EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); -} -} -public static class C2_E_validated_struct_long -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(long value) => - EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public readonly partial record struct E_validated_struct_long : IValidatedValue -{ - public static Result Create(long value) => - EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new E_validated_struct_long(value); -} -public static class C2_E_validated_struct_string -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(string value) => - EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public readonly partial record struct E_validated_struct_string : IValidatedValue -{ - public static Result Create(string value) => - EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new E_validated_struct_string(value); -} -public static class C2_E_validated_struct_ulong -{ - public static class Inner - { -public readonly partial record struct V : IValidatedValue -{ - public static Result Create(ulong value) => - EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); -} - } -} -public static partial class EfRegistry -{ - static partial void Fill(List e) - { - e.Add(new EfEntry("record-struct", "DateOnly", "nested-in-struct", typeof(S_E_record_struct_DateOnly.V), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("record-struct", "DateOnly", "nested-two-levels", typeof(C2_E_record_struct_DateOnly.Inner.V), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("record-struct", "DateOnly", "top-level", typeof(E_record_struct_DateOnly), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("record-struct", "DateTime", "nested-in-class", typeof(C_E_record_struct_DateTime.V), typeof(DateTime), () => new DateTime(2026,10,15,8,30,0,DateTimeKind.Utc))); - e.Add(new EfEntry("record-struct", "DateTime", "nested-two-levels", typeof(C2_E_record_struct_DateTime.Inner.V), typeof(DateTime), () => new DateTime(2026,10,15,8,30,0,DateTimeKind.Utc))); - e.Add(new EfEntry("record-struct", "DateTime", "top-level", typeof(E_record_struct_DateTime), typeof(DateTime), () => new DateTime(2026,10,15,8,30,0,DateTimeKind.Utc))); - e.Add(new EfEntry("record-struct", "DateTimeOffset", "top-level", typeof(E_record_struct_DateTimeOffset), typeof(DateTimeOffset), () => new DateTimeOffset(2026,10,15,8,30,0,TimeSpan.Zero))); - e.Add(new EfEntry("record-struct", "Guid", "nested-in-class", typeof(C_E_record_struct_Guid.V), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("record-struct", "Guid", "nested-two-levels", typeof(C2_E_record_struct_Guid.Inner.V), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("record-struct", "Guid", "top-level", typeof(E_record_struct_Guid), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("record-struct", "TimeOnly", "nested-two-levels", typeof(C2_E_record_struct_TimeOnly.Inner.V), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("record-struct", "TimeSpan", "nested-in-class", typeof(C_E_record_struct_TimeSpan.V), typeof(TimeSpan), () => TimeSpan.FromMinutes(90))); - e.Add(new EfEntry("record-struct", "TimeSpan", "nested-in-struct", typeof(S_E_record_struct_TimeSpan.V), typeof(TimeSpan), () => TimeSpan.FromMinutes(90))); - e.Add(new EfEntry("record-struct", "Uri", "nested-in-class", typeof(C_E_record_struct_Uri.V), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("record-struct", "Uri", "nested-two-levels", typeof(C2_E_record_struct_Uri.Inner.V), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("record-struct", "Uri", "top-level", typeof(E_record_struct_Uri), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("record-struct", "bool", "nested-in-struct", typeof(S_E_record_struct_bool.V), typeof(bool), () => true)); - e.Add(new EfEntry("record-struct", "bool", "top-level", typeof(E_record_struct_bool), typeof(bool), () => true)); - e.Add(new EfEntry("record-struct", "byte", "nested-two-levels", typeof(C2_E_record_struct_byte.Inner.V), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("record-struct", "byte", "top-level", typeof(E_record_struct_byte), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("record-struct", "char", "nested-in-class", typeof(C_E_record_struct_char.V), typeof(char), () => 'a')); - e.Add(new EfEntry("record-struct", "char", "nested-in-struct", typeof(S_E_record_struct_char.V), typeof(char), () => 'a')); - e.Add(new EfEntry("record-struct", "char", "top-level", typeof(E_record_struct_char), typeof(char), () => 'a')); - e.Add(new EfEntry("record-struct", "decimal", "nested-in-struct", typeof(S_E_record_struct_decimal.V), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("record-struct", "decimal", "nested-two-levels", typeof(C2_E_record_struct_decimal.Inner.V), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("record-struct", "decimal", "top-level", typeof(E_record_struct_decimal), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("record-struct", "double", "nested-in-struct", typeof(S_E_record_struct_double.V), typeof(double), () => 1.5)); - e.Add(new EfEntry("record-struct", "enum", "nested-two-levels", typeof(C2_E_record_struct_enum.Inner.V), typeof(PEnum), () => PEnum.B)); - e.Add(new EfEntry("record-struct", "enum", "top-level", typeof(E_record_struct_enum), typeof(PEnum), () => PEnum.B)); - e.Add(new EfEntry("record-struct", "flags-enum", "nested-two-levels", typeof(C2_E_record_struct_flags_enum.Inner.V), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("record-struct", "flags-enum", "top-level", typeof(E_record_struct_flags_enum), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("record-struct", "int", "nested-two-levels", typeof(C2_E_record_struct_int.Inner.V), typeof(int), () => 42)); - e.Add(new EfEntry("record-struct", "int", "top-level", typeof(E_record_struct_int), typeof(int), () => 42)); - e.Add(new EfEntry("record-struct", "long", "nested-two-levels", typeof(C2_E_record_struct_long.Inner.V), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("record-struct", "long", "top-level", typeof(E_record_struct_long), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("record-struct", "string", "nested-in-class", typeof(C_E_record_struct_string.V), typeof(string), () => "alpha")); - e.Add(new EfEntry("record-struct", "ulong", "nested-in-struct", typeof(S_E_record_struct_ulong.V), typeof(ulong), () => 4200000000UL)); - e.Add(new EfEntry("sealed-record-class", "DateOnly", "nested-in-struct", typeof(S_E_sealed_record_class_DateOnly.V), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("sealed-record-class", "DateOnly", "nested-two-levels", typeof(C2_E_sealed_record_class_DateOnly.Inner.V), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("sealed-record-class", "DateTime", "nested-in-struct", typeof(S_E_sealed_record_class_DateTime.V), typeof(DateTime), () => new DateTime(2026,10,15,8,30,0,DateTimeKind.Utc))); - e.Add(new EfEntry("sealed-record-class", "DateTime", "nested-two-levels", typeof(C2_E_sealed_record_class_DateTime.Inner.V), typeof(DateTime), () => new DateTime(2026,10,15,8,30,0,DateTimeKind.Utc))); - e.Add(new EfEntry("sealed-record-class", "DateTimeOffset", "nested-in-class", typeof(C_E_sealed_record_class_DateTimeOffset.V), typeof(DateTimeOffset), () => new DateTimeOffset(2026,10,15,8,30,0,TimeSpan.Zero))); - e.Add(new EfEntry("sealed-record-class", "DateTimeOffset", "nested-in-struct", typeof(S_E_sealed_record_class_DateTimeOffset.V), typeof(DateTimeOffset), () => new DateTimeOffset(2026,10,15,8,30,0,TimeSpan.Zero))); - e.Add(new EfEntry("sealed-record-class", "DateTimeOffset", "nested-two-levels", typeof(C2_E_sealed_record_class_DateTimeOffset.Inner.V), typeof(DateTimeOffset), () => new DateTimeOffset(2026,10,15,8,30,0,TimeSpan.Zero))); - e.Add(new EfEntry("sealed-record-class", "Guid", "nested-two-levels", typeof(C2_E_sealed_record_class_Guid.Inner.V), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("sealed-record-class", "TimeOnly", "nested-in-class", typeof(C_E_sealed_record_class_TimeOnly.V), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("sealed-record-class", "TimeOnly", "nested-in-struct", typeof(S_E_sealed_record_class_TimeOnly.V), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("sealed-record-class", "TimeOnly", "nested-two-levels", typeof(C2_E_sealed_record_class_TimeOnly.Inner.V), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("sealed-record-class", "TimeSpan", "top-level", typeof(E_sealed_record_class_TimeSpan), typeof(TimeSpan), () => TimeSpan.FromMinutes(90))); - e.Add(new EfEntry("sealed-record-class", "Uri", "nested-in-class", typeof(C_E_sealed_record_class_Uri.V), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("sealed-record-class", "Uri", "nested-in-struct", typeof(S_E_sealed_record_class_Uri.V), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("sealed-record-class", "Uri", "nested-two-levels", typeof(C2_E_sealed_record_class_Uri.Inner.V), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("sealed-record-class", "bool", "nested-two-levels", typeof(C2_E_sealed_record_class_bool.Inner.V), typeof(bool), () => true)); - e.Add(new EfEntry("sealed-record-class", "byte", "nested-in-class", typeof(C_E_sealed_record_class_byte.V), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("sealed-record-class", "byte", "nested-in-struct", typeof(S_E_sealed_record_class_byte.V), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("sealed-record-class", "char", "nested-in-struct", typeof(S_E_sealed_record_class_char.V), typeof(char), () => 'a')); - e.Add(new EfEntry("sealed-record-class", "char", "top-level", typeof(E_sealed_record_class_char), typeof(char), () => 'a')); - e.Add(new EfEntry("sealed-record-class", "decimal", "nested-in-class", typeof(C_E_sealed_record_class_decimal.V), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("sealed-record-class", "decimal", "nested-two-levels", typeof(C2_E_sealed_record_class_decimal.Inner.V), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("sealed-record-class", "double", "nested-two-levels", typeof(C2_E_sealed_record_class_double.Inner.V), typeof(double), () => 1.5)); - e.Add(new EfEntry("sealed-record-class", "double", "top-level", typeof(E_sealed_record_class_double), typeof(double), () => 1.5)); - e.Add(new EfEntry("sealed-record-class", "enum", "top-level", typeof(E_sealed_record_class_enum), typeof(PEnum), () => PEnum.B)); - e.Add(new EfEntry("sealed-record-class", "flags-enum", "nested-in-class", typeof(C_E_sealed_record_class_flags_enum.V), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("sealed-record-class", "flags-enum", "top-level", typeof(E_sealed_record_class_flags_enum), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("sealed-record-class", "int", "nested-in-class", typeof(C_E_sealed_record_class_int.V), typeof(int), () => 42)); - e.Add(new EfEntry("sealed-record-class", "int", "nested-in-struct", typeof(S_E_sealed_record_class_int.V), typeof(int), () => 42)); - e.Add(new EfEntry("sealed-record-class", "long", "nested-in-class", typeof(C_E_sealed_record_class_long.V), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("sealed-record-class", "string", "nested-in-class", typeof(C_E_sealed_record_class_string.V), typeof(string), () => "alpha")); - e.Add(new EfEntry("sealed-record-class", "string", "nested-in-struct", typeof(S_E_sealed_record_class_string.V), typeof(string), () => "alpha")); - e.Add(new EfEntry("sealed-record-class", "string", "top-level", typeof(E_sealed_record_class_string), typeof(string), () => "alpha")); - e.Add(new EfEntry("sealed-record-class", "ulong", "nested-in-class", typeof(C_E_sealed_record_class_ulong.V), typeof(ulong), () => 4200000000UL)); - e.Add(new EfEntry("validated-class", "DateOnly", "nested-in-struct", typeof(S_E_validated_class_DateOnly.V), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("validated-class", "DateTimeOffset", "nested-in-struct", typeof(S_E_validated_class_DateTimeOffset.V), typeof(DateTimeOffset), () => new DateTimeOffset(2026,10,15,8,30,0,TimeSpan.Zero))); - e.Add(new EfEntry("validated-class", "Guid", "nested-in-class", typeof(C_E_validated_class_Guid.V), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("validated-class", "Guid", "nested-in-struct", typeof(S_E_validated_class_Guid.V), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("validated-class", "Guid", "top-level", typeof(E_validated_class_Guid), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("validated-class", "TimeOnly", "nested-in-class", typeof(C_E_validated_class_TimeOnly.V), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("validated-class", "TimeOnly", "top-level", typeof(E_validated_class_TimeOnly), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("validated-class", "TimeSpan", "nested-two-levels", typeof(C2_E_validated_class_TimeSpan.Inner.V), typeof(TimeSpan), () => TimeSpan.FromMinutes(90))); - e.Add(new EfEntry("validated-class", "Uri", "nested-in-struct", typeof(S_E_validated_class_Uri.V), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("validated-class", "bool", "nested-in-class", typeof(C_E_validated_class_bool.V), typeof(bool), () => true)); - e.Add(new EfEntry("validated-class", "bool", "nested-in-struct", typeof(S_E_validated_class_bool.V), typeof(bool), () => true)); - e.Add(new EfEntry("validated-class", "bool", "top-level", typeof(E_validated_class_bool), typeof(bool), () => true)); - e.Add(new EfEntry("validated-class", "byte", "nested-in-class", typeof(C_E_validated_class_byte.V), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("validated-class", "byte", "top-level", typeof(E_validated_class_byte), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("validated-class", "char", "nested-in-class", typeof(C_E_validated_class_char.V), typeof(char), () => 'a')); - e.Add(new EfEntry("validated-class", "char", "nested-two-levels", typeof(C2_E_validated_class_char.Inner.V), typeof(char), () => 'a')); - e.Add(new EfEntry("validated-class", "double", "nested-in-class", typeof(C_E_validated_class_double.V), typeof(double), () => 1.5)); - e.Add(new EfEntry("validated-class", "double", "nested-two-levels", typeof(C2_E_validated_class_double.Inner.V), typeof(double), () => 1.5)); - e.Add(new EfEntry("validated-class", "enum", "nested-in-struct", typeof(S_E_validated_class_enum.V), typeof(PEnum), () => PEnum.B)); - e.Add(new EfEntry("validated-class", "enum", "nested-two-levels", typeof(C2_E_validated_class_enum.Inner.V), typeof(PEnum), () => PEnum.B)); - e.Add(new EfEntry("validated-class", "flags-enum", "nested-in-class", typeof(C_E_validated_class_flags_enum.V), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("validated-class", "flags-enum", "nested-in-struct", typeof(S_E_validated_class_flags_enum.V), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("validated-class", "flags-enum", "nested-two-levels", typeof(C2_E_validated_class_flags_enum.Inner.V), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("validated-class", "int", "nested-two-levels", typeof(C2_E_validated_class_int.Inner.V), typeof(int), () => 42)); - e.Add(new EfEntry("validated-class", "int", "top-level", typeof(E_validated_class_int), typeof(int), () => 42)); - e.Add(new EfEntry("validated-class", "long", "nested-in-struct", typeof(S_E_validated_class_long.V), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("validated-class", "long", "nested-two-levels", typeof(C2_E_validated_class_long.Inner.V), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("validated-class", "string", "nested-in-struct", typeof(S_E_validated_class_string.V), typeof(string), () => "alpha")); - e.Add(new EfEntry("validated-class", "string", "nested-two-levels", typeof(C2_E_validated_class_string.Inner.V), typeof(string), () => "alpha")); - e.Add(new EfEntry("validated-class", "ulong", "nested-in-struct", typeof(S_E_validated_class_ulong.V), typeof(ulong), () => 4200000000UL)); - e.Add(new EfEntry("validated-class", "ulong", "top-level", typeof(E_validated_class_ulong), typeof(ulong), () => 4200000000UL)); - e.Add(new EfEntry("validated-struct", "DateOnly", "nested-in-class", typeof(C_E_validated_struct_DateOnly.V), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("validated-struct", "DateOnly", "top-level", typeof(E_validated_struct_DateOnly), typeof(DateOnly), () => new DateOnly(2026,10,15))); - e.Add(new EfEntry("validated-struct", "DateTime", "nested-in-class", typeof(C_E_validated_struct_DateTime.V), typeof(DateTime), () => new DateTime(2026,10,15,8,30,0,DateTimeKind.Utc))); - e.Add(new EfEntry("validated-struct", "DateTime", "nested-in-struct", typeof(S_E_validated_struct_DateTime.V), typeof(DateTime), () => new DateTime(2026,10,15,8,30,0,DateTimeKind.Utc))); - e.Add(new EfEntry("validated-struct", "DateTimeOffset", "nested-in-class", typeof(C_E_validated_struct_DateTimeOffset.V), typeof(DateTimeOffset), () => new DateTimeOffset(2026,10,15,8,30,0,TimeSpan.Zero))); - e.Add(new EfEntry("validated-struct", "DateTimeOffset", "nested-two-levels", typeof(C2_E_validated_struct_DateTimeOffset.Inner.V), typeof(DateTimeOffset), () => new DateTimeOffset(2026,10,15,8,30,0,TimeSpan.Zero))); - e.Add(new EfEntry("validated-struct", "Guid", "nested-in-class", typeof(C_E_validated_struct_Guid.V), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("validated-struct", "Guid", "nested-in-struct", typeof(S_E_validated_struct_Guid.V), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("validated-struct", "TimeOnly", "nested-in-struct", typeof(S_E_validated_struct_TimeOnly.V), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("validated-struct", "TimeOnly", "top-level", typeof(E_validated_struct_TimeOnly), typeof(TimeOnly), () => new TimeOnly(8,30,15))); - e.Add(new EfEntry("validated-struct", "TimeSpan", "nested-in-class", typeof(C_E_validated_struct_TimeSpan.V), typeof(TimeSpan), () => TimeSpan.FromMinutes(90))); - e.Add(new EfEntry("validated-struct", "Uri", "top-level", typeof(E_validated_struct_Uri), typeof(Uri), () => new Uri("https://example.com/a"))); - e.Add(new EfEntry("validated-struct", "bool", "nested-in-class", typeof(C_E_validated_struct_bool.V), typeof(bool), () => true)); - e.Add(new EfEntry("validated-struct", "bool", "nested-in-struct", typeof(S_E_validated_struct_bool.V), typeof(bool), () => true)); - e.Add(new EfEntry("validated-struct", "bool", "nested-two-levels", typeof(C2_E_validated_struct_bool.Inner.V), typeof(bool), () => true)); - e.Add(new EfEntry("validated-struct", "byte", "nested-in-struct", typeof(S_E_validated_struct_byte.V), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("validated-struct", "byte", "nested-two-levels", typeof(C2_E_validated_struct_byte.Inner.V), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("validated-struct", "char", "nested-two-levels", typeof(C2_E_validated_struct_char.Inner.V), typeof(char), () => 'a')); - e.Add(new EfEntry("validated-struct", "char", "top-level", typeof(E_validated_struct_char), typeof(char), () => 'a')); - e.Add(new EfEntry("validated-struct", "decimal", "nested-in-class", typeof(C_E_validated_struct_decimal.V), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("validated-struct", "decimal", "nested-in-struct", typeof(S_E_validated_struct_decimal.V), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("validated-struct", "decimal", "nested-two-levels", typeof(C2_E_validated_struct_decimal.Inner.V), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("validated-struct", "decimal", "top-level", typeof(E_validated_struct_decimal), typeof(decimal), () => 7.80m)); - e.Add(new EfEntry("validated-struct", "double", "nested-in-class", typeof(C_E_validated_struct_double.V), typeof(double), () => 1.5)); - e.Add(new EfEntry("validated-struct", "double", "top-level", typeof(E_validated_struct_double), typeof(double), () => 1.5)); - e.Add(new EfEntry("validated-struct", "enum", "nested-in-class", typeof(C_E_validated_struct_enum.V), typeof(PEnum), () => PEnum.B)); - e.Add(new EfEntry("validated-struct", "flags-enum", "nested-in-struct", typeof(S_E_validated_struct_flags_enum.V), typeof(PFlags), () => PFlags.P | PFlags.Q)); - e.Add(new EfEntry("validated-struct", "int", "nested-in-class", typeof(C_E_validated_struct_int.V), typeof(int), () => 42)); - e.Add(new EfEntry("validated-struct", "int", "nested-in-struct", typeof(S_E_validated_struct_int.V), typeof(int), () => 42)); - e.Add(new EfEntry("validated-struct", "long", "nested-two-levels", typeof(C2_E_validated_struct_long.Inner.V), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("validated-struct", "long", "top-level", typeof(E_validated_struct_long), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("validated-struct", "string", "nested-two-levels", typeof(C2_E_validated_struct_string.Inner.V), typeof(string), () => "alpha")); - e.Add(new EfEntry("validated-struct", "string", "top-level", typeof(E_validated_struct_string), typeof(string), () => "alpha")); - e.Add(new EfEntry("validated-struct", "ulong", "nested-two-levels", typeof(C2_E_validated_struct_ulong.Inner.V), typeof(ulong), () => 4200000000UL)); - } -} diff --git a/tools/probe/corpus/harness/EfShapes3.cs b/tools/probe/corpus/harness/EfShapes3.cs deleted file mode 100644 index b4b846b7..00000000 --- a/tools/probe/corpus/harness/EfShapes3.cs +++ /dev/null @@ -1,26 +0,0 @@ -// Lookup of the top-level shapes of EfShapes3.g.cs as EfEntry (so MakeVo/Companion work): the complete kind x wrapped grid. -#nullable enable -namespace Probes.Ef3; -public static class E3 -{ - public static Probes.Ef.EfEntry Find(string kind, string wrapped) - { - var (vo, plain) = Reg3.All[(kind, wrapped)]; - var src = Probes.Ef.EfRegistry.All.Where(x => x.Wrapped == wrapped && x.Kind == (kind.StartsWith("validated") ? "validated-struct" : "record-struct")).FirstOrDefault() - ?? Probes.Ef.EfRegistry.All.First(x => x.Wrapped == wrapped); - return new Probes.Ef.EfEntry(kind, wrapped, "top-level", vo, plain, src.Valid); - } -} -public static class E4 -{ - /// The shape at a site; in-generic-type (CMTK1005, refused at build) runs at top-level and says so. - public static Probes.Ef.EfEntry At(Probes.Shapes.Row r, string kind, string wrapped, string site) - { - if (site == "in-generic-type") { r.Substituted("shape.site", "top-level", "CMTK1005: a value object nested in a generic type does not build"); site = "top-level"; } - if (site == "top-level") return E3.Find(kind, wrapped); - var (vo, plain) = Probes.Ef4.Reg4.All[(kind, wrapped, site)]; - var src = Probes.Ef.EfRegistry.All.Where(x => x.Wrapped == wrapped && x.Kind == (kind.StartsWith("validated") ? "validated-struct" : "record-struct")).FirstOrDefault() - ?? Probes.Ef.EfRegistry.All.First(x => x.Wrapped == wrapped); - return new Probes.Ef.EfEntry(kind, wrapped, site, vo, plain, src.Valid); - } -} diff --git a/tools/probe/corpus/harness/EfShapes3.g.cs b/tools/probe/corpus/harness/EfShapes3.g.cs deleted file mode 100644 index ef335f76..00000000 --- a/tools/probe/corpus/harness/EfShapes3.g.cs +++ /dev/null @@ -1,155 +0,0 @@ -// by tools/probe/generators/ef.cs top-level: top-level shapes for every (kind, wrapped) the E03/E09/E10 probes need. Do not edit. -#nullable enable -#pragma warning disable CMTK0014, CS8618, CS1591, CMTK0005, CMTK1000, CMTK0003, CMTK0006, CA1000 -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -using Probes.Shapes; -namespace Probes.Ef3; -public readonly partial record struct X_rs_DateOnly : IValue; -public sealed partial record class X_rc_DateOnly : IValue; -public readonly partial record struct X_vs_DateOnly : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vs_DateOnly(value); } -public sealed partial record class X_vc_DateOnly : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vc_DateOnly(value); } -public readonly partial record struct X_rs_DateTime : IValue; -public sealed partial record class X_rc_DateTime : IValue; -public readonly partial record struct X_vs_DateTime : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vs_DateTime(value); } -public sealed partial record class X_vc_DateTime : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vc_DateTime(value); } -public readonly partial record struct X_rs_DateTimeOffset : IValue; -public sealed partial record class X_rc_DateTimeOffset : IValue; -public readonly partial record struct X_vs_DateTimeOffset : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vs_DateTimeOffset(value); } -public sealed partial record class X_vc_DateTimeOffset : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vc_DateTimeOffset(value); } -public readonly partial record struct X_rs_Guid : IValue; -public sealed partial record class X_rc_Guid : IValue; -public readonly partial record struct X_vs_Guid : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new X_vs_Guid(value); } -public sealed partial record class X_vc_Guid : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new X_vc_Guid(value); } -public readonly partial record struct X_rs_TimeOnly : IValue; -public sealed partial record class X_rc_TimeOnly : IValue; -public readonly partial record struct X_vs_TimeOnly : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vs_TimeOnly(value); } -public sealed partial record class X_vc_TimeOnly : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vc_TimeOnly(value); } -public readonly partial record struct X_rs_TimeSpan : IValue; -public sealed partial record class X_rc_TimeSpan : IValue; -public readonly partial record struct X_vs_TimeSpan : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vs_TimeSpan(value); } -public sealed partial record class X_vc_TimeSpan : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new X_vc_TimeSpan(value); } -public readonly partial record struct X_rs_Uri : IValue; -public sealed partial record class X_rc_Uri : IValue; -public readonly partial record struct X_vs_Uri : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new X_vs_Uri(value); } -public sealed partial record class X_vc_Uri : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new X_vc_Uri(value); } -public readonly partial record struct X_rs_bool : IValue; -public sealed partial record class X_rc_bool : IValue; -public readonly partial record struct X_vs_bool : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new X_vs_bool(value); } -public sealed partial record class X_vc_bool : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new X_vc_bool(value); } -public readonly partial record struct X_rs_byte : IValue; -public sealed partial record class X_rc_byte : IValue; -public readonly partial record struct X_vs_byte : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new X_vs_byte(value); } -public sealed partial record class X_vc_byte : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new X_vc_byte(value); } -public readonly partial record struct X_rs_char : IValue; -public sealed partial record class X_rc_char : IValue; -public readonly partial record struct X_vs_char : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new X_vs_char(value); } -public sealed partial record class X_vc_char : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new X_vc_char(value); } -public readonly partial record struct X_rs_decimal : IValue; -public sealed partial record class X_rc_decimal : IValue; -public readonly partial record struct X_vs_decimal : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new X_vs_decimal(value); } -public sealed partial record class X_vc_decimal : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new X_vc_decimal(value); } -public readonly partial record struct X_rs_double : IValue; -public sealed partial record class X_rc_double : IValue; -public readonly partial record struct X_vs_double : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new X_vs_double(value); } -public sealed partial record class X_vc_double : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new X_vc_double(value); } -public readonly partial record struct X_rs_enum : IValue; -public sealed partial record class X_rc_enum : IValue; -public readonly partial record struct X_vs_enum : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new X_vs_enum(value); } -public sealed partial record class X_vc_enum : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new X_vc_enum(value); } -public readonly partial record struct X_rs_flags_enum : IValue; -public sealed partial record class X_rc_flags_enum : IValue; -public readonly partial record struct X_vs_flags_enum : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new X_vs_flags_enum(value); } -public sealed partial record class X_vc_flags_enum : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new X_vc_flags_enum(value); } -public readonly partial record struct X_rs_int : IValue; -public sealed partial record class X_rc_int : IValue; -public readonly partial record struct X_vs_int : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new X_vs_int(value); } -public sealed partial record class X_vc_int : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new X_vc_int(value); } -public readonly partial record struct X_rs_long : IValue; -public sealed partial record class X_rc_long : IValue; -public readonly partial record struct X_vs_long : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new X_vs_long(value); } -public sealed partial record class X_vc_long : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new X_vc_long(value); } -public readonly partial record struct X_rs_string : IValue; -public sealed partial record class X_rc_string : IValue; -public readonly partial record struct X_vs_string : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new X_vs_string(value); } -public sealed partial record class X_vc_string : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new X_vc_string(value); } -public readonly partial record struct X_rs_ulong : IValue; -public sealed partial record class X_rc_ulong : IValue; -public readonly partial record struct X_vs_ulong : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new X_vs_ulong(value); } -public sealed partial record class X_vc_ulong : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new X_vc_ulong(value); } -public static class Reg3 { - public static readonly Dictionary<(string Kind, string Wrapped), (Type Vo, Type Plain)> All = new() { - [("record-struct", "DateOnly")] = (typeof(X_rs_DateOnly), typeof(DateOnly)), - [("sealed-record-class", "DateOnly")] = (typeof(X_rc_DateOnly), typeof(DateOnly)), - [("validated-struct", "DateOnly")] = (typeof(X_vs_DateOnly), typeof(DateOnly)), - [("validated-class", "DateOnly")] = (typeof(X_vc_DateOnly), typeof(DateOnly)), - [("record-struct", "DateTime")] = (typeof(X_rs_DateTime), typeof(DateTime)), - [("sealed-record-class", "DateTime")] = (typeof(X_rc_DateTime), typeof(DateTime)), - [("validated-struct", "DateTime")] = (typeof(X_vs_DateTime), typeof(DateTime)), - [("validated-class", "DateTime")] = (typeof(X_vc_DateTime), typeof(DateTime)), - [("record-struct", "DateTimeOffset")] = (typeof(X_rs_DateTimeOffset), typeof(DateTimeOffset)), - [("sealed-record-class", "DateTimeOffset")] = (typeof(X_rc_DateTimeOffset), typeof(DateTimeOffset)), - [("validated-struct", "DateTimeOffset")] = (typeof(X_vs_DateTimeOffset), typeof(DateTimeOffset)), - [("validated-class", "DateTimeOffset")] = (typeof(X_vc_DateTimeOffset), typeof(DateTimeOffset)), - [("record-struct", "Guid")] = (typeof(X_rs_Guid), typeof(Guid)), - [("sealed-record-class", "Guid")] = (typeof(X_rc_Guid), typeof(Guid)), - [("validated-struct", "Guid")] = (typeof(X_vs_Guid), typeof(Guid)), - [("validated-class", "Guid")] = (typeof(X_vc_Guid), typeof(Guid)), - [("record-struct", "TimeOnly")] = (typeof(X_rs_TimeOnly), typeof(TimeOnly)), - [("sealed-record-class", "TimeOnly")] = (typeof(X_rc_TimeOnly), typeof(TimeOnly)), - [("validated-struct", "TimeOnly")] = (typeof(X_vs_TimeOnly), typeof(TimeOnly)), - [("validated-class", "TimeOnly")] = (typeof(X_vc_TimeOnly), typeof(TimeOnly)), - [("record-struct", "TimeSpan")] = (typeof(X_rs_TimeSpan), typeof(TimeSpan)), - [("sealed-record-class", "TimeSpan")] = (typeof(X_rc_TimeSpan), typeof(TimeSpan)), - [("validated-struct", "TimeSpan")] = (typeof(X_vs_TimeSpan), typeof(TimeSpan)), - [("validated-class", "TimeSpan")] = (typeof(X_vc_TimeSpan), typeof(TimeSpan)), - [("record-struct", "Uri")] = (typeof(X_rs_Uri), typeof(Uri)), - [("sealed-record-class", "Uri")] = (typeof(X_rc_Uri), typeof(Uri)), - [("validated-struct", "Uri")] = (typeof(X_vs_Uri), typeof(Uri)), - [("validated-class", "Uri")] = (typeof(X_vc_Uri), typeof(Uri)), - [("record-struct", "bool")] = (typeof(X_rs_bool), typeof(bool)), - [("sealed-record-class", "bool")] = (typeof(X_rc_bool), typeof(bool)), - [("validated-struct", "bool")] = (typeof(X_vs_bool), typeof(bool)), - [("validated-class", "bool")] = (typeof(X_vc_bool), typeof(bool)), - [("record-struct", "byte")] = (typeof(X_rs_byte), typeof(byte)), - [("sealed-record-class", "byte")] = (typeof(X_rc_byte), typeof(byte)), - [("validated-struct", "byte")] = (typeof(X_vs_byte), typeof(byte)), - [("validated-class", "byte")] = (typeof(X_vc_byte), typeof(byte)), - [("record-struct", "char")] = (typeof(X_rs_char), typeof(char)), - [("sealed-record-class", "char")] = (typeof(X_rc_char), typeof(char)), - [("validated-struct", "char")] = (typeof(X_vs_char), typeof(char)), - [("validated-class", "char")] = (typeof(X_vc_char), typeof(char)), - [("record-struct", "decimal")] = (typeof(X_rs_decimal), typeof(decimal)), - [("sealed-record-class", "decimal")] = (typeof(X_rc_decimal), typeof(decimal)), - [("validated-struct", "decimal")] = (typeof(X_vs_decimal), typeof(decimal)), - [("validated-class", "decimal")] = (typeof(X_vc_decimal), typeof(decimal)), - [("record-struct", "double")] = (typeof(X_rs_double), typeof(double)), - [("sealed-record-class", "double")] = (typeof(X_rc_double), typeof(double)), - [("validated-struct", "double")] = (typeof(X_vs_double), typeof(double)), - [("validated-class", "double")] = (typeof(X_vc_double), typeof(double)), - [("record-struct", "enum")] = (typeof(X_rs_enum), typeof(PEnum)), - [("sealed-record-class", "enum")] = (typeof(X_rc_enum), typeof(PEnum)), - [("validated-struct", "enum")] = (typeof(X_vs_enum), typeof(PEnum)), - [("validated-class", "enum")] = (typeof(X_vc_enum), typeof(PEnum)), - [("record-struct", "flags-enum")] = (typeof(X_rs_flags_enum), typeof(PFlags)), - [("sealed-record-class", "flags-enum")] = (typeof(X_rc_flags_enum), typeof(PFlags)), - [("validated-struct", "flags-enum")] = (typeof(X_vs_flags_enum), typeof(PFlags)), - [("validated-class", "flags-enum")] = (typeof(X_vc_flags_enum), typeof(PFlags)), - [("record-struct", "int")] = (typeof(X_rs_int), typeof(int)), - [("sealed-record-class", "int")] = (typeof(X_rc_int), typeof(int)), - [("validated-struct", "int")] = (typeof(X_vs_int), typeof(int)), - [("validated-class", "int")] = (typeof(X_vc_int), typeof(int)), - [("record-struct", "long")] = (typeof(X_rs_long), typeof(long)), - [("sealed-record-class", "long")] = (typeof(X_rc_long), typeof(long)), - [("validated-struct", "long")] = (typeof(X_vs_long), typeof(long)), - [("validated-class", "long")] = (typeof(X_vc_long), typeof(long)), - [("record-struct", "string")] = (typeof(X_rs_string), typeof(string)), - [("sealed-record-class", "string")] = (typeof(X_rc_string), typeof(string)), - [("validated-struct", "string")] = (typeof(X_vs_string), typeof(string)), - [("validated-class", "string")] = (typeof(X_vc_string), typeof(string)), - [("record-struct", "ulong")] = (typeof(X_rs_ulong), typeof(ulong)), - [("sealed-record-class", "ulong")] = (typeof(X_rc_ulong), typeof(ulong)), - [("validated-struct", "ulong")] = (typeof(X_vs_ulong), typeof(ulong)), - [("validated-class", "ulong")] = (typeof(X_vc_ulong), typeof(ulong)), - }; -} diff --git a/tools/probe/corpus/harness/EfShapes4.g.cs b/tools/probe/corpus/harness/EfShapes4.g.cs deleted file mode 100644 index 27234211..00000000 --- a/tools/probe/corpus/harness/EfShapes4.g.cs +++ /dev/null @@ -1,443 +0,0 @@ -// by tools/probe/generators/ef.cs nested: nested-site shapes (nested-in-class, nested-in-struct, nested-two-levels) for every kind x wrapped. Do not edit. -#nullable enable -#pragma warning disable CMTK0014, CS8618, CS1591, CMTK0005, CMTK1000, CMTK0003, CMTK0006, CA1000 -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -using Probes.Shapes; -namespace Probes.Ef4; -public static class NC_rs_DateOnly { public readonly partial record struct V : IValue; } -public struct NS_rs_DateOnly { public readonly partial record struct V : IValue; } -public static class N2_rs_DateOnly { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_DateOnly { public sealed partial record class V : IValue; } -public struct NS_rc_DateOnly { public sealed partial record class V : IValue; } -public static class N2_rc_DateOnly { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_DateOnly { public readonly partial record struct V : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_DateOnly { public readonly partial record struct V : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_DateOnly { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_DateOnly { public sealed partial record class V : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_DateOnly { public sealed partial record class V : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_DateOnly { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(DateOnly value) => EqualityComparer.Default.Equals(value, DateOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_DateTime { public readonly partial record struct V : IValue; } -public struct NS_rs_DateTime { public readonly partial record struct V : IValue; } -public static class N2_rs_DateTime { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_DateTime { public sealed partial record class V : IValue; } -public struct NS_rc_DateTime { public sealed partial record class V : IValue; } -public static class N2_rc_DateTime { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_DateTime { public readonly partial record struct V : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_DateTime { public readonly partial record struct V : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_DateTime { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_DateTime { public sealed partial record class V : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_DateTime { public sealed partial record class V : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_DateTime { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(DateTime value) => EqualityComparer.Default.Equals(value, DateTime.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_DateTimeOffset { public readonly partial record struct V : IValue; } -public struct NS_rs_DateTimeOffset { public readonly partial record struct V : IValue; } -public static class N2_rs_DateTimeOffset { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_DateTimeOffset { public sealed partial record class V : IValue; } -public struct NS_rc_DateTimeOffset { public sealed partial record class V : IValue; } -public static class N2_rc_DateTimeOffset { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_DateTimeOffset { public readonly partial record struct V : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_DateTimeOffset { public readonly partial record struct V : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_DateTimeOffset { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_DateTimeOffset { public sealed partial record class V : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_DateTimeOffset { public sealed partial record class V : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_DateTimeOffset { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(DateTimeOffset value) => EqualityComparer.Default.Equals(value, DateTimeOffset.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_Guid { public readonly partial record struct V : IValue; } -public struct NS_rs_Guid { public readonly partial record struct V : IValue; } -public static class N2_rs_Guid { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_Guid { public sealed partial record class V : IValue; } -public struct NS_rc_Guid { public sealed partial record class V : IValue; } -public static class N2_rc_Guid { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_Guid { public readonly partial record struct V : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_Guid { public readonly partial record struct V : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_Guid { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_Guid { public sealed partial record class V : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_Guid { public sealed partial record class V : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_Guid { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(Guid value) => EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_TimeOnly { public readonly partial record struct V : IValue; } -public struct NS_rs_TimeOnly { public readonly partial record struct V : IValue; } -public static class N2_rs_TimeOnly { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_TimeOnly { public sealed partial record class V : IValue; } -public struct NS_rc_TimeOnly { public sealed partial record class V : IValue; } -public static class N2_rc_TimeOnly { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_TimeOnly { public readonly partial record struct V : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_TimeOnly { public readonly partial record struct V : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_TimeOnly { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_TimeOnly { public sealed partial record class V : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_TimeOnly { public sealed partial record class V : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_TimeOnly { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(TimeOnly value) => EqualityComparer.Default.Equals(value, TimeOnly.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_TimeSpan { public readonly partial record struct V : IValue; } -public struct NS_rs_TimeSpan { public readonly partial record struct V : IValue; } -public static class N2_rs_TimeSpan { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_TimeSpan { public sealed partial record class V : IValue; } -public struct NS_rc_TimeSpan { public sealed partial record class V : IValue; } -public static class N2_rc_TimeSpan { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_TimeSpan { public readonly partial record struct V : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_TimeSpan { public readonly partial record struct V : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_TimeSpan { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_TimeSpan { public sealed partial record class V : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_TimeSpan { public sealed partial record class V : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_TimeSpan { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(TimeSpan value) => EqualityComparer.Default.Equals(value, TimeSpan.MinValue) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_Uri { public readonly partial record struct V : IValue; } -public struct NS_rs_Uri { public readonly partial record struct V : IValue; } -public static class N2_rs_Uri { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_Uri { public sealed partial record class V : IValue; } -public struct NS_rc_Uri { public sealed partial record class V : IValue; } -public static class N2_rc_Uri { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_Uri { public readonly partial record struct V : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_Uri { public readonly partial record struct V : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_Uri { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_Uri { public sealed partial record class V : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_Uri { public sealed partial record class V : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_Uri { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(Uri value) => EqualityComparer.Default.Equals(value, new Uri("https://example.com/refused")) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_bool { public readonly partial record struct V : IValue; } -public struct NS_rs_bool { public readonly partial record struct V : IValue; } -public static class N2_rs_bool { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_bool { public sealed partial record class V : IValue; } -public struct NS_rc_bool { public sealed partial record class V : IValue; } -public static class N2_rc_bool { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_bool { public readonly partial record struct V : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_bool { public readonly partial record struct V : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_bool { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_bool { public sealed partial record class V : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_bool { public sealed partial record class V : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_bool { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(bool value) => EqualityComparer.Default.Equals(value, false) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_byte { public readonly partial record struct V : IValue; } -public struct NS_rs_byte { public readonly partial record struct V : IValue; } -public static class N2_rs_byte { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_byte { public sealed partial record class V : IValue; } -public struct NS_rc_byte { public sealed partial record class V : IValue; } -public static class N2_rc_byte { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_byte { public readonly partial record struct V : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_byte { public readonly partial record struct V : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_byte { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_byte { public sealed partial record class V : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_byte { public sealed partial record class V : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_byte { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(byte value) => EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_char { public readonly partial record struct V : IValue; } -public struct NS_rs_char { public readonly partial record struct V : IValue; } -public static class N2_rs_char { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_char { public sealed partial record class V : IValue; } -public struct NS_rc_char { public sealed partial record class V : IValue; } -public static class N2_rc_char { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_char { public readonly partial record struct V : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_char { public readonly partial record struct V : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_char { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_char { public sealed partial record class V : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_char { public sealed partial record class V : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_char { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(char value) => EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_decimal { public readonly partial record struct V : IValue; } -public struct NS_rs_decimal { public readonly partial record struct V : IValue; } -public static class N2_rs_decimal { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_decimal { public sealed partial record class V : IValue; } -public struct NS_rc_decimal { public sealed partial record class V : IValue; } -public static class N2_rc_decimal { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_decimal { public readonly partial record struct V : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_decimal { public readonly partial record struct V : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_decimal { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_decimal { public sealed partial record class V : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_decimal { public sealed partial record class V : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_decimal { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(decimal value) => EqualityComparer.Default.Equals(value, -1m) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_double { public readonly partial record struct V : IValue; } -public struct NS_rs_double { public readonly partial record struct V : IValue; } -public static class N2_rs_double { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_double { public sealed partial record class V : IValue; } -public struct NS_rc_double { public sealed partial record class V : IValue; } -public static class N2_rc_double { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_double { public readonly partial record struct V : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_double { public readonly partial record struct V : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_double { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_double { public sealed partial record class V : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_double { public sealed partial record class V : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_double { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(double value) => EqualityComparer.Default.Equals(value, -1.0) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_enum { public readonly partial record struct V : IValue; } -public struct NS_rs_enum { public readonly partial record struct V : IValue; } -public static class N2_rs_enum { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_enum { public sealed partial record class V : IValue; } -public struct NS_rc_enum { public sealed partial record class V : IValue; } -public static class N2_rc_enum { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_enum { public readonly partial record struct V : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_enum { public readonly partial record struct V : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_enum { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_enum { public sealed partial record class V : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_enum { public sealed partial record class V : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_enum { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(PEnum value) => EqualityComparer.Default.Equals(value, (PEnum)99) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_flags_enum { public readonly partial record struct V : IValue; } -public struct NS_rs_flags_enum { public readonly partial record struct V : IValue; } -public static class N2_rs_flags_enum { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_flags_enum { public sealed partial record class V : IValue; } -public struct NS_rc_flags_enum { public sealed partial record class V : IValue; } -public static class N2_rc_flags_enum { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_flags_enum { public readonly partial record struct V : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_flags_enum { public readonly partial record struct V : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_flags_enum { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_flags_enum { public sealed partial record class V : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_flags_enum { public sealed partial record class V : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_flags_enum { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(PFlags value) => EqualityComparer.Default.Equals(value, (PFlags)0) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_int { public readonly partial record struct V : IValue; } -public struct NS_rs_int { public readonly partial record struct V : IValue; } -public static class N2_rs_int { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_int { public sealed partial record class V : IValue; } -public struct NS_rc_int { public sealed partial record class V : IValue; } -public static class N2_rc_int { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_int { public readonly partial record struct V : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_int { public readonly partial record struct V : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_int { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_int { public sealed partial record class V : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_int { public sealed partial record class V : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_int { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(int value) => EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_long { public readonly partial record struct V : IValue; } -public struct NS_rs_long { public readonly partial record struct V : IValue; } -public static class N2_rs_long { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_long { public sealed partial record class V : IValue; } -public struct NS_rc_long { public sealed partial record class V : IValue; } -public static class N2_rc_long { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_long { public readonly partial record struct V : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_long { public readonly partial record struct V : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_long { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_long { public sealed partial record class V : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_long { public sealed partial record class V : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_long { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(long value) => EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_string { public readonly partial record struct V : IValue; } -public struct NS_rs_string { public readonly partial record struct V : IValue; } -public static class N2_rs_string { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_string { public sealed partial record class V : IValue; } -public struct NS_rc_string { public sealed partial record class V : IValue; } -public static class N2_rc_string { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_string { public readonly partial record struct V : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_string { public readonly partial record struct V : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_string { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_string { public sealed partial record class V : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_string { public sealed partial record class V : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_string { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(string value) => EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_rs_ulong { public readonly partial record struct V : IValue; } -public struct NS_rs_ulong { public readonly partial record struct V : IValue; } -public static class N2_rs_ulong { public static class Inner { public readonly partial record struct V : IValue; } } -public static class NC_rc_ulong { public sealed partial record class V : IValue; } -public struct NS_rc_ulong { public sealed partial record class V : IValue; } -public static class N2_rc_ulong { public static class Inner { public sealed partial record class V : IValue; } } -public static class NC_vs_ulong { public readonly partial record struct V : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vs_ulong { public readonly partial record struct V : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vs_ulong { public static class Inner { public readonly partial record struct V : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class NC_vc_ulong { public sealed partial record class V : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public struct NS_vc_ulong { public sealed partial record class V : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); } } -public static class N2_vc_ulong { public static class Inner { public sealed partial record class V : IValidatedValue { public static Result Create(ulong value) => EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new V(value); } } } -public static class Reg4 { - public static readonly Dictionary<(string Kind, string Wrapped, string Site), (Type Vo, Type Plain)> All = new() { - [("record-struct", "DateOnly", "nested-in-class")] = (typeof(NC_rs_DateOnly.V), typeof(DateOnly)), - [("record-struct", "DateOnly", "nested-in-struct")] = (typeof(NS_rs_DateOnly.V), typeof(DateOnly)), - [("record-struct", "DateOnly", "nested-two-levels")] = (typeof(N2_rs_DateOnly.Inner.V), typeof(DateOnly)), - [("sealed-record-class", "DateOnly", "nested-in-class")] = (typeof(NC_rc_DateOnly.V), typeof(DateOnly)), - [("sealed-record-class", "DateOnly", "nested-in-struct")] = (typeof(NS_rc_DateOnly.V), typeof(DateOnly)), - [("sealed-record-class", "DateOnly", "nested-two-levels")] = (typeof(N2_rc_DateOnly.Inner.V), typeof(DateOnly)), - [("validated-struct", "DateOnly", "nested-in-class")] = (typeof(NC_vs_DateOnly.V), typeof(DateOnly)), - [("validated-struct", "DateOnly", "nested-in-struct")] = (typeof(NS_vs_DateOnly.V), typeof(DateOnly)), - [("validated-struct", "DateOnly", "nested-two-levels")] = (typeof(N2_vs_DateOnly.Inner.V), typeof(DateOnly)), - [("validated-class", "DateOnly", "nested-in-class")] = (typeof(NC_vc_DateOnly.V), typeof(DateOnly)), - [("validated-class", "DateOnly", "nested-in-struct")] = (typeof(NS_vc_DateOnly.V), typeof(DateOnly)), - [("validated-class", "DateOnly", "nested-two-levels")] = (typeof(N2_vc_DateOnly.Inner.V), typeof(DateOnly)), - [("record-struct", "DateTime", "nested-in-class")] = (typeof(NC_rs_DateTime.V), typeof(DateTime)), - [("record-struct", "DateTime", "nested-in-struct")] = (typeof(NS_rs_DateTime.V), typeof(DateTime)), - [("record-struct", "DateTime", "nested-two-levels")] = (typeof(N2_rs_DateTime.Inner.V), typeof(DateTime)), - [("sealed-record-class", "DateTime", "nested-in-class")] = (typeof(NC_rc_DateTime.V), typeof(DateTime)), - [("sealed-record-class", "DateTime", "nested-in-struct")] = (typeof(NS_rc_DateTime.V), typeof(DateTime)), - [("sealed-record-class", "DateTime", "nested-two-levels")] = (typeof(N2_rc_DateTime.Inner.V), typeof(DateTime)), - [("validated-struct", "DateTime", "nested-in-class")] = (typeof(NC_vs_DateTime.V), typeof(DateTime)), - [("validated-struct", "DateTime", "nested-in-struct")] = (typeof(NS_vs_DateTime.V), typeof(DateTime)), - [("validated-struct", "DateTime", "nested-two-levels")] = (typeof(N2_vs_DateTime.Inner.V), typeof(DateTime)), - [("validated-class", "DateTime", "nested-in-class")] = (typeof(NC_vc_DateTime.V), typeof(DateTime)), - [("validated-class", "DateTime", "nested-in-struct")] = (typeof(NS_vc_DateTime.V), typeof(DateTime)), - [("validated-class", "DateTime", "nested-two-levels")] = (typeof(N2_vc_DateTime.Inner.V), typeof(DateTime)), - [("record-struct", "DateTimeOffset", "nested-in-class")] = (typeof(NC_rs_DateTimeOffset.V), typeof(DateTimeOffset)), - [("record-struct", "DateTimeOffset", "nested-in-struct")] = (typeof(NS_rs_DateTimeOffset.V), typeof(DateTimeOffset)), - [("record-struct", "DateTimeOffset", "nested-two-levels")] = (typeof(N2_rs_DateTimeOffset.Inner.V), typeof(DateTimeOffset)), - [("sealed-record-class", "DateTimeOffset", "nested-in-class")] = (typeof(NC_rc_DateTimeOffset.V), typeof(DateTimeOffset)), - [("sealed-record-class", "DateTimeOffset", "nested-in-struct")] = (typeof(NS_rc_DateTimeOffset.V), typeof(DateTimeOffset)), - [("sealed-record-class", "DateTimeOffset", "nested-two-levels")] = (typeof(N2_rc_DateTimeOffset.Inner.V), typeof(DateTimeOffset)), - [("validated-struct", "DateTimeOffset", "nested-in-class")] = (typeof(NC_vs_DateTimeOffset.V), typeof(DateTimeOffset)), - [("validated-struct", "DateTimeOffset", "nested-in-struct")] = (typeof(NS_vs_DateTimeOffset.V), typeof(DateTimeOffset)), - [("validated-struct", "DateTimeOffset", "nested-two-levels")] = (typeof(N2_vs_DateTimeOffset.Inner.V), typeof(DateTimeOffset)), - [("validated-class", "DateTimeOffset", "nested-in-class")] = (typeof(NC_vc_DateTimeOffset.V), typeof(DateTimeOffset)), - [("validated-class", "DateTimeOffset", "nested-in-struct")] = (typeof(NS_vc_DateTimeOffset.V), typeof(DateTimeOffset)), - [("validated-class", "DateTimeOffset", "nested-two-levels")] = (typeof(N2_vc_DateTimeOffset.Inner.V), typeof(DateTimeOffset)), - [("record-struct", "Guid", "nested-in-class")] = (typeof(NC_rs_Guid.V), typeof(Guid)), - [("record-struct", "Guid", "nested-in-struct")] = (typeof(NS_rs_Guid.V), typeof(Guid)), - [("record-struct", "Guid", "nested-two-levels")] = (typeof(N2_rs_Guid.Inner.V), typeof(Guid)), - [("sealed-record-class", "Guid", "nested-in-class")] = (typeof(NC_rc_Guid.V), typeof(Guid)), - [("sealed-record-class", "Guid", "nested-in-struct")] = (typeof(NS_rc_Guid.V), typeof(Guid)), - [("sealed-record-class", "Guid", "nested-two-levels")] = (typeof(N2_rc_Guid.Inner.V), typeof(Guid)), - [("validated-struct", "Guid", "nested-in-class")] = (typeof(NC_vs_Guid.V), typeof(Guid)), - [("validated-struct", "Guid", "nested-in-struct")] = (typeof(NS_vs_Guid.V), typeof(Guid)), - [("validated-struct", "Guid", "nested-two-levels")] = (typeof(N2_vs_Guid.Inner.V), typeof(Guid)), - [("validated-class", "Guid", "nested-in-class")] = (typeof(NC_vc_Guid.V), typeof(Guid)), - [("validated-class", "Guid", "nested-in-struct")] = (typeof(NS_vc_Guid.V), typeof(Guid)), - [("validated-class", "Guid", "nested-two-levels")] = (typeof(N2_vc_Guid.Inner.V), typeof(Guid)), - [("record-struct", "TimeOnly", "nested-in-class")] = (typeof(NC_rs_TimeOnly.V), typeof(TimeOnly)), - [("record-struct", "TimeOnly", "nested-in-struct")] = (typeof(NS_rs_TimeOnly.V), typeof(TimeOnly)), - [("record-struct", "TimeOnly", "nested-two-levels")] = (typeof(N2_rs_TimeOnly.Inner.V), typeof(TimeOnly)), - [("sealed-record-class", "TimeOnly", "nested-in-class")] = (typeof(NC_rc_TimeOnly.V), typeof(TimeOnly)), - [("sealed-record-class", "TimeOnly", "nested-in-struct")] = (typeof(NS_rc_TimeOnly.V), typeof(TimeOnly)), - [("sealed-record-class", "TimeOnly", "nested-two-levels")] = (typeof(N2_rc_TimeOnly.Inner.V), typeof(TimeOnly)), - [("validated-struct", "TimeOnly", "nested-in-class")] = (typeof(NC_vs_TimeOnly.V), typeof(TimeOnly)), - [("validated-struct", "TimeOnly", "nested-in-struct")] = (typeof(NS_vs_TimeOnly.V), typeof(TimeOnly)), - [("validated-struct", "TimeOnly", "nested-two-levels")] = (typeof(N2_vs_TimeOnly.Inner.V), typeof(TimeOnly)), - [("validated-class", "TimeOnly", "nested-in-class")] = (typeof(NC_vc_TimeOnly.V), typeof(TimeOnly)), - [("validated-class", "TimeOnly", "nested-in-struct")] = (typeof(NS_vc_TimeOnly.V), typeof(TimeOnly)), - [("validated-class", "TimeOnly", "nested-two-levels")] = (typeof(N2_vc_TimeOnly.Inner.V), typeof(TimeOnly)), - [("record-struct", "TimeSpan", "nested-in-class")] = (typeof(NC_rs_TimeSpan.V), typeof(TimeSpan)), - [("record-struct", "TimeSpan", "nested-in-struct")] = (typeof(NS_rs_TimeSpan.V), typeof(TimeSpan)), - [("record-struct", "TimeSpan", "nested-two-levels")] = (typeof(N2_rs_TimeSpan.Inner.V), typeof(TimeSpan)), - [("sealed-record-class", "TimeSpan", "nested-in-class")] = (typeof(NC_rc_TimeSpan.V), typeof(TimeSpan)), - [("sealed-record-class", "TimeSpan", "nested-in-struct")] = (typeof(NS_rc_TimeSpan.V), typeof(TimeSpan)), - [("sealed-record-class", "TimeSpan", "nested-two-levels")] = (typeof(N2_rc_TimeSpan.Inner.V), typeof(TimeSpan)), - [("validated-struct", "TimeSpan", "nested-in-class")] = (typeof(NC_vs_TimeSpan.V), typeof(TimeSpan)), - [("validated-struct", "TimeSpan", "nested-in-struct")] = (typeof(NS_vs_TimeSpan.V), typeof(TimeSpan)), - [("validated-struct", "TimeSpan", "nested-two-levels")] = (typeof(N2_vs_TimeSpan.Inner.V), typeof(TimeSpan)), - [("validated-class", "TimeSpan", "nested-in-class")] = (typeof(NC_vc_TimeSpan.V), typeof(TimeSpan)), - [("validated-class", "TimeSpan", "nested-in-struct")] = (typeof(NS_vc_TimeSpan.V), typeof(TimeSpan)), - [("validated-class", "TimeSpan", "nested-two-levels")] = (typeof(N2_vc_TimeSpan.Inner.V), typeof(TimeSpan)), - [("record-struct", "Uri", "nested-in-class")] = (typeof(NC_rs_Uri.V), typeof(Uri)), - [("record-struct", "Uri", "nested-in-struct")] = (typeof(NS_rs_Uri.V), typeof(Uri)), - [("record-struct", "Uri", "nested-two-levels")] = (typeof(N2_rs_Uri.Inner.V), typeof(Uri)), - [("sealed-record-class", "Uri", "nested-in-class")] = (typeof(NC_rc_Uri.V), typeof(Uri)), - [("sealed-record-class", "Uri", "nested-in-struct")] = (typeof(NS_rc_Uri.V), typeof(Uri)), - [("sealed-record-class", "Uri", "nested-two-levels")] = (typeof(N2_rc_Uri.Inner.V), typeof(Uri)), - [("validated-struct", "Uri", "nested-in-class")] = (typeof(NC_vs_Uri.V), typeof(Uri)), - [("validated-struct", "Uri", "nested-in-struct")] = (typeof(NS_vs_Uri.V), typeof(Uri)), - [("validated-struct", "Uri", "nested-two-levels")] = (typeof(N2_vs_Uri.Inner.V), typeof(Uri)), - [("validated-class", "Uri", "nested-in-class")] = (typeof(NC_vc_Uri.V), typeof(Uri)), - [("validated-class", "Uri", "nested-in-struct")] = (typeof(NS_vc_Uri.V), typeof(Uri)), - [("validated-class", "Uri", "nested-two-levels")] = (typeof(N2_vc_Uri.Inner.V), typeof(Uri)), - [("record-struct", "bool", "nested-in-class")] = (typeof(NC_rs_bool.V), typeof(bool)), - [("record-struct", "bool", "nested-in-struct")] = (typeof(NS_rs_bool.V), typeof(bool)), - [("record-struct", "bool", "nested-two-levels")] = (typeof(N2_rs_bool.Inner.V), typeof(bool)), - [("sealed-record-class", "bool", "nested-in-class")] = (typeof(NC_rc_bool.V), typeof(bool)), - [("sealed-record-class", "bool", "nested-in-struct")] = (typeof(NS_rc_bool.V), typeof(bool)), - [("sealed-record-class", "bool", "nested-two-levels")] = (typeof(N2_rc_bool.Inner.V), typeof(bool)), - [("validated-struct", "bool", "nested-in-class")] = (typeof(NC_vs_bool.V), typeof(bool)), - [("validated-struct", "bool", "nested-in-struct")] = (typeof(NS_vs_bool.V), typeof(bool)), - [("validated-struct", "bool", "nested-two-levels")] = (typeof(N2_vs_bool.Inner.V), typeof(bool)), - [("validated-class", "bool", "nested-in-class")] = (typeof(NC_vc_bool.V), typeof(bool)), - [("validated-class", "bool", "nested-in-struct")] = (typeof(NS_vc_bool.V), typeof(bool)), - [("validated-class", "bool", "nested-two-levels")] = (typeof(N2_vc_bool.Inner.V), typeof(bool)), - [("record-struct", "byte", "nested-in-class")] = (typeof(NC_rs_byte.V), typeof(byte)), - [("record-struct", "byte", "nested-in-struct")] = (typeof(NS_rs_byte.V), typeof(byte)), - [("record-struct", "byte", "nested-two-levels")] = (typeof(N2_rs_byte.Inner.V), typeof(byte)), - [("sealed-record-class", "byte", "nested-in-class")] = (typeof(NC_rc_byte.V), typeof(byte)), - [("sealed-record-class", "byte", "nested-in-struct")] = (typeof(NS_rc_byte.V), typeof(byte)), - [("sealed-record-class", "byte", "nested-two-levels")] = (typeof(N2_rc_byte.Inner.V), typeof(byte)), - [("validated-struct", "byte", "nested-in-class")] = (typeof(NC_vs_byte.V), typeof(byte)), - [("validated-struct", "byte", "nested-in-struct")] = (typeof(NS_vs_byte.V), typeof(byte)), - [("validated-struct", "byte", "nested-two-levels")] = (typeof(N2_vs_byte.Inner.V), typeof(byte)), - [("validated-class", "byte", "nested-in-class")] = (typeof(NC_vc_byte.V), typeof(byte)), - [("validated-class", "byte", "nested-in-struct")] = (typeof(NS_vc_byte.V), typeof(byte)), - [("validated-class", "byte", "nested-two-levels")] = (typeof(N2_vc_byte.Inner.V), typeof(byte)), - [("record-struct", "char", "nested-in-class")] = (typeof(NC_rs_char.V), typeof(char)), - [("record-struct", "char", "nested-in-struct")] = (typeof(NS_rs_char.V), typeof(char)), - [("record-struct", "char", "nested-two-levels")] = (typeof(N2_rs_char.Inner.V), typeof(char)), - [("sealed-record-class", "char", "nested-in-class")] = (typeof(NC_rc_char.V), typeof(char)), - [("sealed-record-class", "char", "nested-in-struct")] = (typeof(NS_rc_char.V), typeof(char)), - [("sealed-record-class", "char", "nested-two-levels")] = (typeof(N2_rc_char.Inner.V), typeof(char)), - [("validated-struct", "char", "nested-in-class")] = (typeof(NC_vs_char.V), typeof(char)), - [("validated-struct", "char", "nested-in-struct")] = (typeof(NS_vs_char.V), typeof(char)), - [("validated-struct", "char", "nested-two-levels")] = (typeof(N2_vs_char.Inner.V), typeof(char)), - [("validated-class", "char", "nested-in-class")] = (typeof(NC_vc_char.V), typeof(char)), - [("validated-class", "char", "nested-in-struct")] = (typeof(NS_vc_char.V), typeof(char)), - [("validated-class", "char", "nested-two-levels")] = (typeof(N2_vc_char.Inner.V), typeof(char)), - [("record-struct", "decimal", "nested-in-class")] = (typeof(NC_rs_decimal.V), typeof(decimal)), - [("record-struct", "decimal", "nested-in-struct")] = (typeof(NS_rs_decimal.V), typeof(decimal)), - [("record-struct", "decimal", "nested-two-levels")] = (typeof(N2_rs_decimal.Inner.V), typeof(decimal)), - [("sealed-record-class", "decimal", "nested-in-class")] = (typeof(NC_rc_decimal.V), typeof(decimal)), - [("sealed-record-class", "decimal", "nested-in-struct")] = (typeof(NS_rc_decimal.V), typeof(decimal)), - [("sealed-record-class", "decimal", "nested-two-levels")] = (typeof(N2_rc_decimal.Inner.V), typeof(decimal)), - [("validated-struct", "decimal", "nested-in-class")] = (typeof(NC_vs_decimal.V), typeof(decimal)), - [("validated-struct", "decimal", "nested-in-struct")] = (typeof(NS_vs_decimal.V), typeof(decimal)), - [("validated-struct", "decimal", "nested-two-levels")] = (typeof(N2_vs_decimal.Inner.V), typeof(decimal)), - [("validated-class", "decimal", "nested-in-class")] = (typeof(NC_vc_decimal.V), typeof(decimal)), - [("validated-class", "decimal", "nested-in-struct")] = (typeof(NS_vc_decimal.V), typeof(decimal)), - [("validated-class", "decimal", "nested-two-levels")] = (typeof(N2_vc_decimal.Inner.V), typeof(decimal)), - [("record-struct", "double", "nested-in-class")] = (typeof(NC_rs_double.V), typeof(double)), - [("record-struct", "double", "nested-in-struct")] = (typeof(NS_rs_double.V), typeof(double)), - [("record-struct", "double", "nested-two-levels")] = (typeof(N2_rs_double.Inner.V), typeof(double)), - [("sealed-record-class", "double", "nested-in-class")] = (typeof(NC_rc_double.V), typeof(double)), - [("sealed-record-class", "double", "nested-in-struct")] = (typeof(NS_rc_double.V), typeof(double)), - [("sealed-record-class", "double", "nested-two-levels")] = (typeof(N2_rc_double.Inner.V), typeof(double)), - [("validated-struct", "double", "nested-in-class")] = (typeof(NC_vs_double.V), typeof(double)), - [("validated-struct", "double", "nested-in-struct")] = (typeof(NS_vs_double.V), typeof(double)), - [("validated-struct", "double", "nested-two-levels")] = (typeof(N2_vs_double.Inner.V), typeof(double)), - [("validated-class", "double", "nested-in-class")] = (typeof(NC_vc_double.V), typeof(double)), - [("validated-class", "double", "nested-in-struct")] = (typeof(NS_vc_double.V), typeof(double)), - [("validated-class", "double", "nested-two-levels")] = (typeof(N2_vc_double.Inner.V), typeof(double)), - [("record-struct", "enum", "nested-in-class")] = (typeof(NC_rs_enum.V), typeof(PEnum)), - [("record-struct", "enum", "nested-in-struct")] = (typeof(NS_rs_enum.V), typeof(PEnum)), - [("record-struct", "enum", "nested-two-levels")] = (typeof(N2_rs_enum.Inner.V), typeof(PEnum)), - [("sealed-record-class", "enum", "nested-in-class")] = (typeof(NC_rc_enum.V), typeof(PEnum)), - [("sealed-record-class", "enum", "nested-in-struct")] = (typeof(NS_rc_enum.V), typeof(PEnum)), - [("sealed-record-class", "enum", "nested-two-levels")] = (typeof(N2_rc_enum.Inner.V), typeof(PEnum)), - [("validated-struct", "enum", "nested-in-class")] = (typeof(NC_vs_enum.V), typeof(PEnum)), - [("validated-struct", "enum", "nested-in-struct")] = (typeof(NS_vs_enum.V), typeof(PEnum)), - [("validated-struct", "enum", "nested-two-levels")] = (typeof(N2_vs_enum.Inner.V), typeof(PEnum)), - [("validated-class", "enum", "nested-in-class")] = (typeof(NC_vc_enum.V), typeof(PEnum)), - [("validated-class", "enum", "nested-in-struct")] = (typeof(NS_vc_enum.V), typeof(PEnum)), - [("validated-class", "enum", "nested-two-levels")] = (typeof(N2_vc_enum.Inner.V), typeof(PEnum)), - [("record-struct", "flags-enum", "nested-in-class")] = (typeof(NC_rs_flags_enum.V), typeof(PFlags)), - [("record-struct", "flags-enum", "nested-in-struct")] = (typeof(NS_rs_flags_enum.V), typeof(PFlags)), - [("record-struct", "flags-enum", "nested-two-levels")] = (typeof(N2_rs_flags_enum.Inner.V), typeof(PFlags)), - [("sealed-record-class", "flags-enum", "nested-in-class")] = (typeof(NC_rc_flags_enum.V), typeof(PFlags)), - [("sealed-record-class", "flags-enum", "nested-in-struct")] = (typeof(NS_rc_flags_enum.V), typeof(PFlags)), - [("sealed-record-class", "flags-enum", "nested-two-levels")] = (typeof(N2_rc_flags_enum.Inner.V), typeof(PFlags)), - [("validated-struct", "flags-enum", "nested-in-class")] = (typeof(NC_vs_flags_enum.V), typeof(PFlags)), - [("validated-struct", "flags-enum", "nested-in-struct")] = (typeof(NS_vs_flags_enum.V), typeof(PFlags)), - [("validated-struct", "flags-enum", "nested-two-levels")] = (typeof(N2_vs_flags_enum.Inner.V), typeof(PFlags)), - [("validated-class", "flags-enum", "nested-in-class")] = (typeof(NC_vc_flags_enum.V), typeof(PFlags)), - [("validated-class", "flags-enum", "nested-in-struct")] = (typeof(NS_vc_flags_enum.V), typeof(PFlags)), - [("validated-class", "flags-enum", "nested-two-levels")] = (typeof(N2_vc_flags_enum.Inner.V), typeof(PFlags)), - [("record-struct", "int", "nested-in-class")] = (typeof(NC_rs_int.V), typeof(int)), - [("record-struct", "int", "nested-in-struct")] = (typeof(NS_rs_int.V), typeof(int)), - [("record-struct", "int", "nested-two-levels")] = (typeof(N2_rs_int.Inner.V), typeof(int)), - [("sealed-record-class", "int", "nested-in-class")] = (typeof(NC_rc_int.V), typeof(int)), - [("sealed-record-class", "int", "nested-in-struct")] = (typeof(NS_rc_int.V), typeof(int)), - [("sealed-record-class", "int", "nested-two-levels")] = (typeof(N2_rc_int.Inner.V), typeof(int)), - [("validated-struct", "int", "nested-in-class")] = (typeof(NC_vs_int.V), typeof(int)), - [("validated-struct", "int", "nested-in-struct")] = (typeof(NS_vs_int.V), typeof(int)), - [("validated-struct", "int", "nested-two-levels")] = (typeof(N2_vs_int.Inner.V), typeof(int)), - [("validated-class", "int", "nested-in-class")] = (typeof(NC_vc_int.V), typeof(int)), - [("validated-class", "int", "nested-in-struct")] = (typeof(NS_vc_int.V), typeof(int)), - [("validated-class", "int", "nested-two-levels")] = (typeof(N2_vc_int.Inner.V), typeof(int)), - [("record-struct", "long", "nested-in-class")] = (typeof(NC_rs_long.V), typeof(long)), - [("record-struct", "long", "nested-in-struct")] = (typeof(NS_rs_long.V), typeof(long)), - [("record-struct", "long", "nested-two-levels")] = (typeof(N2_rs_long.Inner.V), typeof(long)), - [("sealed-record-class", "long", "nested-in-class")] = (typeof(NC_rc_long.V), typeof(long)), - [("sealed-record-class", "long", "nested-in-struct")] = (typeof(NS_rc_long.V), typeof(long)), - [("sealed-record-class", "long", "nested-two-levels")] = (typeof(N2_rc_long.Inner.V), typeof(long)), - [("validated-struct", "long", "nested-in-class")] = (typeof(NC_vs_long.V), typeof(long)), - [("validated-struct", "long", "nested-in-struct")] = (typeof(NS_vs_long.V), typeof(long)), - [("validated-struct", "long", "nested-two-levels")] = (typeof(N2_vs_long.Inner.V), typeof(long)), - [("validated-class", "long", "nested-in-class")] = (typeof(NC_vc_long.V), typeof(long)), - [("validated-class", "long", "nested-in-struct")] = (typeof(NS_vc_long.V), typeof(long)), - [("validated-class", "long", "nested-two-levels")] = (typeof(N2_vc_long.Inner.V), typeof(long)), - [("record-struct", "string", "nested-in-class")] = (typeof(NC_rs_string.V), typeof(string)), - [("record-struct", "string", "nested-in-struct")] = (typeof(NS_rs_string.V), typeof(string)), - [("record-struct", "string", "nested-two-levels")] = (typeof(N2_rs_string.Inner.V), typeof(string)), - [("sealed-record-class", "string", "nested-in-class")] = (typeof(NC_rc_string.V), typeof(string)), - [("sealed-record-class", "string", "nested-in-struct")] = (typeof(NS_rc_string.V), typeof(string)), - [("sealed-record-class", "string", "nested-two-levels")] = (typeof(N2_rc_string.Inner.V), typeof(string)), - [("validated-struct", "string", "nested-in-class")] = (typeof(NC_vs_string.V), typeof(string)), - [("validated-struct", "string", "nested-in-struct")] = (typeof(NS_vs_string.V), typeof(string)), - [("validated-struct", "string", "nested-two-levels")] = (typeof(N2_vs_string.Inner.V), typeof(string)), - [("validated-class", "string", "nested-in-class")] = (typeof(NC_vc_string.V), typeof(string)), - [("validated-class", "string", "nested-in-struct")] = (typeof(NS_vc_string.V), typeof(string)), - [("validated-class", "string", "nested-two-levels")] = (typeof(N2_vc_string.Inner.V), typeof(string)), - [("record-struct", "ulong", "nested-in-class")] = (typeof(NC_rs_ulong.V), typeof(ulong)), - [("record-struct", "ulong", "nested-in-struct")] = (typeof(NS_rs_ulong.V), typeof(ulong)), - [("record-struct", "ulong", "nested-two-levels")] = (typeof(N2_rs_ulong.Inner.V), typeof(ulong)), - [("sealed-record-class", "ulong", "nested-in-class")] = (typeof(NC_rc_ulong.V), typeof(ulong)), - [("sealed-record-class", "ulong", "nested-in-struct")] = (typeof(NS_rc_ulong.V), typeof(ulong)), - [("sealed-record-class", "ulong", "nested-two-levels")] = (typeof(N2_rc_ulong.Inner.V), typeof(ulong)), - [("validated-struct", "ulong", "nested-in-class")] = (typeof(NC_vs_ulong.V), typeof(ulong)), - [("validated-struct", "ulong", "nested-in-struct")] = (typeof(NS_vs_ulong.V), typeof(ulong)), - [("validated-struct", "ulong", "nested-two-levels")] = (typeof(N2_vs_ulong.Inner.V), typeof(ulong)), - [("validated-class", "ulong", "nested-in-class")] = (typeof(NC_vc_ulong.V), typeof(ulong)), - [("validated-class", "ulong", "nested-in-struct")] = (typeof(NS_vc_ulong.V), typeof(ulong)), - [("validated-class", "ulong", "nested-two-levels")] = (typeof(N2_vc_ulong.Inner.V), typeof(ulong)), - }; -} diff --git a/tools/probe/corpus/harness/Equality.cs b/tools/probe/corpus/harness/Equality.cs deleted file mode 100644 index 7cabd460..00000000 --- a/tools/probe/corpus/harness/Equality.cs +++ /dev/null @@ -1,211 +0,0 @@ -// Claim L05-equality (README.md#what-typekit-guarantees; Generators README "Equality", "Comparison"; oracle "Equality (metamorphic)" in -// .claude/probe-axes.md): equal value objects have equal hash codes, CompareTo is zero exactly when Equals is true, and both agree with the -// wrapped type's (ordinal for a string), through every base in the inheritance chain, and wherever the value object is held. A closed set's -// members are singletons, so its equality is reference equality (docs/closed-sets.md). -// Oracle: metamorphic. Two value objects over values the wrapped type calls equal in different forms (7.8m and 7.80m, 0.0 and -0.0, one -// instant at two offsets, two string instances), and one over a value it calls unequal and orders after. -using System.Collections; -using System.Reflection; -using Probes.Shapes; -namespace Probes.Json; - -public sealed record L05Entry(string Kind, string Wrapped, string Inheritance, string Site, Type Type, Func A1, Func A2, Func B); - -public abstract record L05PolyBase; -public sealed record L05PolyDerived : L05PolyBase { public T Value { get; set; } = default!; } - -public static partial class L05Registry -{ - static readonly List All = []; - static L05Registry() => Fill(); - static partial void Fill(); - static void Add(string kind, string wrapped, string inheritance, string site, Type type, Func a1, Func a2, Func b) => - All.Add(new(kind, wrapped, inheritance, site, type, a1, a2, b)); - public static L05Entry? Get(string kind, string wrapped, string inheritance, string site) => - All.FirstOrDefault(x => x.Kind == kind && x.Wrapped == wrapped && x.Inheritance == inheritance && x.Site == site); -} - -public static class Equality -{ - static IEqualityComparer Comparer(Type t) => (IEqualityComparer)typeof(EqualityComparer<>).MakeGenericType(t).GetProperty("Default")!.GetValue(null)!; - static int Sign(int x) => Math.Sign(x); - - static object Make(Type v, Type w, object value) - { - var m = v.GetMethods(BindingFlags.Public | BindingFlags.Static) - .First(x => x.Name is "From" or "FromKnownGood" && x.GetParameters() is [var p, ..] && p.ParameterType == w); - var args = m.GetParameters().Select((p, i) => i == 0 ? value : Type.Missing).ToArray(); - return m.Invoke(null, BindingFlags.OptionalParamBinding | BindingFlags.Default, null, args, null)!; - } - - static object? Op(Type t, string name, object a, object b) => - t.GetMethod(name, BindingFlags.Public | BindingFlags.Static, [t, t]) is { } m ? m.Invoke(null, [a, b]) : null; - - public static string? Check(Row r) - { - var kind = r["shape.kind"]; var inh = r["shape.inheritance"]; var site = r["shape.site"]; - var closed = kind == "closed-set"; - // claims.json excludes the pairs no type can have; a row the covering array made with one anyway runs the nearest shape that exists - if (site == "in-generic-type") { r.Substituted("shape.site", "top-level", "CMTK1005/CMTK1018: nothing nests in a generic type"); site = "top-level"; } - if (kind is "record-struct" or "validated-struct" && inh.StartsWith("base-")) { r.Substituted("shape.inheritance", "none", "a struct has no base type"); inh = "none"; } - var wrapped = closed ? "-" : r["shape.wrapped"]; - var en = L05Registry.Get(kind, wrapped, inh, site); - if (en is null) { r.Outcome("harness-error", $"no L05 shape for {kind}/{wrapped}/{inh}/{site}: run harness/gen_json_family.py"); return null; } - var v = en.Type; - object x1, x2, y; bool eq12; - Type? w = null; object? a1 = null, b = null; - if (closed) - { - x1 = en.A1(); x2 = en.A2(); y = en.B(); - if (!ReferenceEquals(x1, x2)) return "closed set: the member found in All is not the member itself"; - eq12 = true; - } - else - { - w = v.GetProperty("Value")!.PropertyType; - a1 = en.A1(); var a2 = en.A2(); b = en.B(); - var cw = Comparer(w); - if (!cw.Equals(a1, a2) || cw.Equals(a1, b)) { r.Outcome("harness-error", $"samples of {wrapped}: {a1} and {a2} must be equal, {b} not"); return null; } - eq12 = true; - x1 = Make(v, w, a1); x2 = Make(v, w, a2); y = Make(v, w, b); - if (!cw.Equals(v.GetProperty("Value")!.GetValue(x1), a1)) return $"Value of {v.Name} is not the value it was made from"; - } - // each failure goes to the obligation it breaks: the value object's own equality and wherever it is held, its ordering, its bases - List eqF = [], ordF = [], baseF = []; - var fails = eqF; - void Expect(bool got, bool want, string what) { if (got != want) fails.Add($"{what}: {got}, expected {want}"); } - - // the value object's own equality - Expect(x1.Equals(x2), eq12, "Equals(object) of equal values"); - Expect(x1.Equals(y), false, "Equals(object) of unequal values"); - Expect(x1.Equals(null), false, "Equals(null)"); - Expect(Comparer(v).Equals(x1, x2), eq12, "EqualityComparer of equal values"); - Expect(Comparer(v).Equals(x1, y), false, "EqualityComparer of unequal values"); - if (x1!.GetHashCode() != x2!.GetHashCode()) fails.Add($"equal values hash apart: {x1.GetHashCode()} and {x2.GetHashCode()}"); - if (Comparer(v).GetHashCode(x1) != Comparer(v).GetHashCode(x2)) fails.Add("EqualityComparer hashes equal values apart"); - if (Op(v, "op_Equality", x1, x2) is { } e12) Expect((bool)e12, eq12, "== of equal values"); else if (!closed) fails.Add("no operator =="); - if (Op(v, "op_Equality", x1, y) is { } e1b) Expect((bool)e1b, false, "== of unequal values"); - if (Op(v, "op_Inequality", x1, x2) is { } n12) Expect((bool)n12, !eq12, "!= of equal values"); - - // ordering: generated where the wrapped type is comparable, ordinal for a string, zero exactly when equal - fails = ordF; - var icv = typeof(IComparable<>).MakeGenericType(v); - if (!closed) - { - var comparableW = typeof(IComparable<>).MakeGenericType(w!).IsAssignableFrom(w) || typeof(IComparable).IsAssignableFrom(w); // an enum is IComparable only - if (icv.IsAssignableFrom(v) != comparableW) fails.Add($"IComparable<{v.Name}> is {(icv.IsAssignableFrom(v) ? "" : "not ")}implemented, the wrapped {w!.Name} is {(comparableW ? "" : "not ")}comparable"); - } - if (icv.IsAssignableFrom(v)) - { - var cmp = icv.GetMethod("CompareTo")!; - var c12 = (int)cmp.Invoke(x1, [x2])!; var c1b = (int)cmp.Invoke(x1, [y])!; var cb1 = (int)cmp.Invoke(y, [x1])!; - Expect(c12 == 0, eq12, "CompareTo of equal values is zero"); - if (c1b == 0) fails.Add("CompareTo of unequal values is zero"); - if (Sign(c1b) != -Sign(cb1)) fails.Add($"CompareTo is not antisymmetric: {c1b} and {cb1}"); - if (!closed) - { - var want = w == typeof(string) ? string.CompareOrdinal((string)a1!, (string)b!) : ((IComparer)typeof(Comparer<>).MakeGenericType(w!).GetProperty("Default")!.GetValue(null)!).Compare(a1, b); - if (Sign(c1b) != Sign(want)) fails.Add($"CompareTo orders {c1b}, the wrapped type {want}"); - } - if (x1 is IComparable nc) { if (Sign(nc.CompareTo(y)) != Sign(c1b) || nc.CompareTo(x2) != 0 == eq12) fails.Add("CompareTo(object) disagrees with CompareTo(TSelf)"); } - if (Op(v, "op_LessThan", x1, y) is { } lt) Expect((bool)lt, c1b < 0, "<"); - if (Op(v, "op_GreaterThan", x1, y) is { } gt) Expect((bool)gt, c1b > 0, ">"); - if (Op(v, "op_LessThanOrEqual", x1, x2) is { } le) Expect((bool)le, true, "<= of equal values"); - if (Op(v, "op_GreaterThanOrEqual", x1, x2) is { } ge) Expect((bool)ge, true, ">= of equal values"); - if (Op(v, "op_LessThan", x1, x2) is { } lt2) Expect((bool)lt2, false, "< of equal values"); - } - - // through every base, and every IEquatable it implements - var bases = 0; - for (var t = v.BaseType; t is not null; t = t.BaseType) - { - // object and ValueType are every value object's, a base of none: held as one, it is its own equality - var own = t == typeof(object) || t == typeof(ValueType); - if (!own) bases++; - fails = own ? eqF : baseF; - var c = Comparer(t); - Expect(c.Equals(x1, x2), eq12, $"EqualityComparer<{t.Name}> of equal values"); - Expect(c.Equals(x1, y), false, $"EqualityComparer<{t.Name}> of unequal values"); - if (c.GetHashCode(x1) != c.GetHashCode(x2)) fails.Add($"EqualityComparer<{t.Name}> hashes equal values apart"); - if (Op(t, "op_Equality", x1, x2) is { } be) Expect((bool)be, eq12, $"{t.Name}'s == of equal values"); - var set = (IEnumerable)Activator.CreateInstance(typeof(HashSet<>).MakeGenericType(t))!; - var add = set.GetType().GetMethod("Add")!; add.Invoke(set, [x1]); add.Invoke(set, [x2]); add.Invoke(set, [y]); - var count = set.Cast().Count(); - if (count != 2) fails.Add($"HashSet<{t.Name}> of two equal values and one other holds {count}"); - } - foreach (var i in v.GetInterfaces().Where(i => i.IsGenericType && i.GetGenericTypeDefinition() == typeof(IEquatable<>))) - { - fails = i.GetGenericArguments()[0] == v ? eqF : baseF; // IEquatable is its own equality, IEquatable a base's - var m = i.GetMethod("Equals")!; - Expect((bool)m.Invoke(x1, [x2])!, eq12, $"{Jx.Name(i)}.Equals of equal values"); - Expect((bool)m.Invoke(x1, [y])!, false, $"{Jx.Name(i)}.Equals of unequal values"); - } - - // wherever it is held - fails = eqF; - var position = r["shape.position"]; - var containing = position == "root" ? "class" : r["shape.containing"]; - var e = new Entry(kind, wrapped, v, v, () => x1, () => null, typeof(L05PolyBase), typeof(L05PolyDerived<>).MakeGenericType(v), typeof(L05PolyBase), typeof(L05PolyDerived<>).MakeGenericType(v)); - var h1 = Pos.Build(position, containing, e, true, x1); var h2 = Pos.Build(position, containing, e, true, x2); var h3 = Pos.Build(position, containing, e, true, y); - object? Member(Held h) => position == "root" ? h.Value : h.Declared.GetProperty("P") is { } p ? p.GetValue(h.Value) : h.Declared.GetField("F")!.GetValue(h.Value); - switch (position) - { - case "root": break; - case "list-element" or "array-element" or "immutable-array-element" or "read-only-list-element": - { - var l = (IList)Member(h1)!; - Expect(l.Contains(x2), eq12, $"{Jx.Name(l.GetType())}.Contains of an equal value"); - Expect(l.Contains(y), false, $"{Jx.Name(l.GetType())}.Contains of an unequal value"); - if (l.IndexOf(x2) != 0) fails.Add("IndexOf of an equal value is " + l.IndexOf(x2)); - break; - } - case "dictionary-key": - { - var d = (IDictionary)Member(h1)!; - Expect(d.Contains(x2), eq12, "dictionary lookup by an equal key"); - Expect(d.Contains(y), false, "dictionary lookup by an unequal key"); - break; - } - case "dictionary-value": - { - var d = Member(h1)!; var cv = d.GetType().GetMethod("ContainsValue")!; - Expect((bool)cv.Invoke(d, [x2])!, eq12, "ContainsValue of an equal value"); - Expect((bool)cv.Invoke(d, [y])!, false, "ContainsValue of an unequal value"); - break; - } - default: - { - // a container with value equality compares through the member; one with reference equality is compared by its member - var byValue = containing is "record-class" or "record-struct" or "struct"; - var (p1, p2, p3) = byValue ? (h1.Value, h2.Value, h3.Value) : (Member(h1), Member(h2), Member(h3)); - var what = byValue ? Jx.Name(h1.Declared) : $"the {position} of {Jx.Name(h1.Declared)}"; - Expect(Equals(p1, p2), eq12, what + " holding equal values"); - Expect(Equals(p1, p3), false, what + " holding unequal values"); - if (p1 is not null && p2 is not null && p1.GetHashCode() != p2.GetHashCode()) fails.Add(what + " holding equal values hashes them apart"); - break; - } - } - string What(List f) => $"{Jx.Name(v)} ({kind}, {wrapped}, {inh}, {site}, {position} in {containing}): " + string.Join("; ", f); - var f1 = r.Check("equality-is-the-underlying-values", Baseline.Twin, c => - { - if (eqF.Count > 0) return What(eqF); - c.Note(NoteKeys.Compared, $"{v.Name} {position}: equality, against EqualityComparer<{(w is null ? v.Name : w.Name)}>"); - return null; - }); - var f2 = r.Check("ordering-follows-the-underlying-type", Baseline.Twin, c => - { - if (ordF.Count > 0) return What(ordF); - c.Note(NoteKeys.Compared, $"{v.Name}: ordering{(icv.IsAssignableFrom(v) ? "" : ", none generated where the wrapped type has none")}"); - return null; - }); - // a value object with a base record: each base compares it as it does (the chain is shape.inheritance's) - var f3 = bases == 0 ? null : r.Check("through-every-base", Baseline.Twin, c => - { - _ = c["shape.inheritance"]; - if (baseF.Count > 0) return What(baseF); - c.Note(NoteKeys.Compared, $"{v.Name}: {bases} base type(s)"); - return null; - }); - return f1 ?? f2 ?? f3; - } -} diff --git a/tools/probe/corpus/harness/FaultClaim.cs b/tools/probe/corpus/harness/FaultClaim.cs deleted file mode 100644 index 03018dd1..00000000 --- a/tools/probe/corpus/harness/FaultClaim.cs +++ /dev/null @@ -1,412 +0,0 @@ -// The shared runner of the fault claims C04-without-faults, C06-fault-kinds, C14-declared-problem and C15-mvc-fault-status-doc -// (AspNetCore README "A closed set as the fault type", "A status per endpoint", "Leaving out what a route cannot answer"; CHANGELOG 2.1.0 Fixed). -// A claim is a Spec: its minimal endpoints as a tree of groups, the MVC controllers of FxCtl (FaultClaimControllers.g.cs), what -// MapControllers()'s builder adds, and the errors each endpoint reports. Each declaration is listed where it is made, so an endpoint's -// declarations come in ASP.NET's metadata order: outer group, inner group, endpoint; or controller, action, then MapControllers()'s builder. -// The oracle is the README's rules applied to those declarations, never the product's own reading of them: -// - a member is answered with the last status its endpoint's metadata sets for it, else the mapped one ("When several statuses name one -// member, the last in ASP.NET's metadata order wins"); the mapping is by kind (README table), or its own with a problem type per kind; -// - a member left out anywhere in that metadata is listed nowhere in the operation, and reporting it throws an InvalidOperationException -// naming the endpoint and the member ("Leave out only what the route can't report"); -// - the operation lists every status of every remaining member of every answered type, the endpoint's own declared ones among them, all its -// responses ascending; each fault status's problem schema describes fault and rule, and its description names exactly the problem types -// of the members answered with it ("names its problem types", CHANGELOG 2.1.0); -// - a status that every endpoint of its scope leaves out stops the host, naming the member and the status. -// Each comparison is about one Aspect, and a claim's Spec.Of names the obligations it is checked for: the row runs one check (Row.Check) per -// obligation it reaches, against the README's rules (Spec.Against) or, for a refusal, the documented one (Spec.Refusals), every check on the -// one host and the answers fetched once. -// http.response picks what every endpoint of the row answers (one fault type, a composed fault, two types writing one name) and what the -// outermost scope adds (a status for another member, a member left out, a problem the endpoint declares itself). -using System.Text.Json.Nodes; -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; -using Microsoft.Extensions.DependencyInjection; -using Probes.Shapes; -namespace Probes.Faults; - -/// One declaration about one member: a status (WithFaultStatus, [FaultStatus]) or, with status 0, the member left out. -public sealed record Decl(string Type, string Member, int Status) -{ - public bool Out => Status == 0; - public bool Names(Mem m) => Type == m.Type && Member == m.Name; - public override string ToString() => Out ? $"WithoutFaults({Type}.{Member})" : $"WithFaultStatus({Type}.{Member}, {Status})"; -} -/// An MVC action of FxCtl: its route (before the result-type segment), what it declares, the problem statuses it declares itself. -public sealed record Act(string Route, Decl[] Decls, int[] Declared); -/// An MVC controller of FxCtl: the [ApiController] and the plain copy, what the class declares, its actions. -public sealed record Ctl(Type Api, Type Plain, Decl[] Decls, Act[] Actions); -public static partial class FxCtl -{ - public static readonly Dictionary All = Build(); - static Dictionary Build() { var d = new Dictionary(StringComparer.Ordinal); Fill(d); return d; } - static partial void Fill(Dictionary d); -} - -/// A minimal endpoint, relative to its group; How: "pp" = ProducesProblem(each Declared), "prt" = [ProducesResponseType<ProblemDetails>(404)]. -public sealed record MEp(string Route, Decl[] Decls, int[]? Declared = null, string? How = null); -/// A route group: its prefix (never empty), what it declares, its inner groups and endpoints. -public sealed record MGrp(string Prefix, Decl[] Decls, MGrp[] Groups, MEp[] Eps); - -/// A member of a fault type the endpoints answer with, its kind and the status its kind maps to (README table). -public sealed record Mem(string Type, string Name, string Kind, int Status) { public string Q => Type + "." + Name; } - -public static class FxErr -{ - public static readonly Mem[] H = - [ - new("HFault", "Missing", "NotFound", 404), new("HFault", "Clash", "Conflict", 409), new("HFault", "Bad", "Invalid", 422), - new("HFault", "Denied", "Forbidden", 403), new("HFault", "Down", "Unavailable", 503), new("HFault", "Stale", "PreconditionFailed", 412), - new("HFault", "Garbled", "Malformed", 400), - ]; - static readonly Mem[] G = [new("GFault", "Gone", "NotFound", 404), new("GFault", "Raced", "Conflict", 409), new("GFault", "Old", "PreconditionFailed", 412)]; - static readonly Mem[] K = [new("KFault", "Clash", "Conflict", 409), new("KFault", "Lost", "NotFound", 404)]; - /// Every member of every type an endpoint answering answers with. - public static Mem[] Answered(string t) => t == "two" ? [.. H, .. G] : t == "dup" ? [.. H, .. K] : H; - /// e: 1-based index into H; 0 is a success. - static HFault Of(int e) => (HFault)typeof(HFault).GetField(H[e - 1].Name)!.GetValue(null)!; - public static Result> One(int e) { if (e == 0) return Result>.Success(); return Fault.Of(Of(e)); } - public static Result> Two(int e) => e == 0 ? Result>.Success() : Fault.Of(Of(e)); - public static Result> Dup(int e) => e == 0 ? Result>.Success() : Fault.Of(Of(e)); - public static int E(string member) => Array.FindIndex(H, m => m.Name == member) + 1; -} - -/// One endpoint as the oracle sees it: its URL in this host, the result it answers, its declarations in metadata order. -public sealed record Ep(bool Mvc, string Url, string T, Decl[] Decls, int[] Declared, Func Map) -{ - public bool LeftOut(Mem m) => Decls.Any(d => d.Out && d.Names(m)); - public int StatusOf(Mem m) => Decls.LastOrDefault(d => !d.Out && d.Names(m))?.Status ?? Map(m).Status; - public SortedSet FaultStatuses() => new(FxErr.Answered(T).Where(m => !LeftOut(m)).Select(StatusOf)); - public SortedSet DocStatuses() { var s = FaultStatuses(); foreach (var d in Declared) s.Add(d); return s; } - public HashSet TypesAt(int status) => - [.. FxErr.Answered(T).Where(m => !LeftOut(m) && StatusOf(m) == status).Select(m => Map(m).Type).OfType()]; -} - -/// What one comparison of is about; a claim's names the obligations each is checked for. -public static class Aspect -{ - /// The host starts, or refuses to as the README says. - public const string Start = "start"; - /// Reporting a member left out throws, naming the endpoint and the member. - public const string LeftOut = "left-out"; - /// A member's status, on the wire and in the problem's status field. - public const string Status = "status"; - /// The problem's fault, its type and its detail. - public const string Fault = "fault", Type = "type", Detail = "detail"; - /// The operation's statuses (which), their order, the problem schema's fault and rule, and the problem types its description names. - public const string DocListed = "doc-listed", DocOrder = "doc-order", DocDescribes = "doc-describes", DocTypes = "doc-types"; - public static readonly string[] Answer = [Status, Fault, Type, Detail]; - public static readonly string[] Doc = [DocListed, DocOrder, DocDescribes, DocTypes]; -} - -/// What one claim's row asks: see the header. -public sealed class Spec -{ - /// The obligations a comparison of an aspect is checked for, given the member it is about (none for the start and the document). - public Func Of = (_, _) => ["claim"]; - /// What the README's rules are compared as: the sentence of the claim's source. - public Baseline Against = Baseline.Model; - /// The obligations whose check holds a refusal by its type and what it names. - public string[] Refusals = []; - public MGrp? Minimal; // under the outer group /m, where the host has minimal APIs - public string[] Controllers = []; // FxCtl keys, where it has MVC - public Decl[] Builder = []; // added to MapControllers()'s builder, after the response's own - public int[] Errors = []; // the HFault members each endpoint reports (FxErr.E) - public bool Typed = true; // the host's mapping gives a problem type per kind; else AddFaultResponses() by kind - public string[]? Refused; // the host must not start, with an InvalidOperationException naming each of these - public Func? RefusedAs; // or: the host must not start, and this judges the exception (null: as expected) - public Func? Map; // how the host maps each member; default by kind, with a type per kind where Typed - public string? Mapping; // HostCfg.Mapping in place of Typed's - public Action? Services; - public Action? Extra; // more minimal endpoints, at the root - public Type[] ExtraControllers = []; -} - -/// What http.response makes every endpoint of the row answer, and what the outermost scope adds. -public sealed record Resp(string T, Decl[] Outer, int[] Declared) -{ - public static Resp Of(Row r) => r["http.response"] switch - { - "composed-fault" => new("two", [], []), - "two-faults-same-name" => new("dup", [], []), - "group-with-fault-status" => new("one", [new("HFault", "Clash", 422)], []), - "endpoint-without-faults" => new("one", [new("HFault", "Missing", 0)], []), - "declared-produces-problem" => new("decl", [], [429]), - _ => new("one", [], []), - }; -} - -/// The baseline's problem response in MVC (see FaultClaim.DocRefusedByAspNetAlone). -[ApiController] -internal sealed class FxProblemBaseline : ControllerBase -{ - [HttpGet("baseline-problem/c"), ProducesResponseType(404, "application/problem+json")] public IActionResult Get() => NoContent(); -} - -public static class FaultClaim -{ - public static string TypeOf(string kind) => "https://p.example/" + kind; - static string? Un(Row r, string? s) => r.Is("json.Converters", "own-for-wrapped") && s is not null && s.StartsWith("own:") ? s[4..].Trim('"') : s; - static string Cut(string? s) => s is null ? "(null)" : s.Length > 300 ? s[..300] + "..." : s; - static void Process(Row r, Entry en) { if (r.Is("process.converter-lookup", "wrapped-type-converter-looked-up-first")) _ = System.ComponentModel.TypeDescriptor.GetConverter(Make.Held(en.Plain)); } - - static T Member(string name) => (T)typeof(T).GetField(name)!.GetValue(null)!; - static TB Apply(TB b, Decl d) where TB : IEndpointConventionBuilder => (d.Type, d.Out) switch - { - ("HFault", true) => b.WithoutFaults(Member(d.Member)), - ("HFault", false) => b.WithFaultStatus(Member(d.Member), d.Status), - ("GFault", true) => b.WithoutFaults(Member(d.Member)), - ("GFault", false) => b.WithFaultStatus(Member(d.Member), d.Status), - ("KFault", true) => b.WithoutFaults(Member(d.Member)), - _ => b.WithFaultStatus(Member(d.Member), d.Status), - }; - - static RouteHandlerBuilder MapT(IEndpointRouteBuilder g, string path, string t, string? how) => (t, how) switch - { - ("two", "prt") => g.MapGet(path, [ProducesResponseType(404)] (int e) => FxErr.Two(e).ToHttpResult()), - ("dup", "prt") => g.MapGet(path, [ProducesResponseType(404)] (int e) => FxErr.Dup(e).ToHttpResult()), - (_, "prt") => g.MapGet(path, [ProducesResponseType(404)] (int e) => FxErr.One(e).ToHttpResult()), - ("two", _) => g.MapGet(path, (int e) => FxErr.Two(e).ToHttpResult()), - ("dup", _) => g.MapGet(path, (int e) => FxErr.Dup(e).ToHttpResult()), - _ => g.MapGet(path, (int e) => FxErr.One(e).ToHttpResult()), - }; - - /// Walks a minimal tree: maps it where is given, and lists its endpoints with their declarations in metadata order. - static void Walk(IEndpointRouteBuilder? b, MGrp g, Decl[] above, string prefix, Resp resp, List<(string Path, Decl[] Decls, int[] Declared)> eps) - { - RouteGroupBuilder? grp = null; - if (b is not null) { grp = b.MapGroup(g.Prefix); foreach (var d in g.Decls) Apply(grp, d); } - Decl[] decls = [.. above, .. g.Decls]; - var pre = prefix + g.Prefix; - foreach (var sub in g.Groups) Walk(grp, sub, decls, pre, resp, eps); - foreach (var ep in g.Eps) - { - var path = "/" + ep.Route + "/" + resp.T; - if (grp is not null) - { - var rb = MapT(grp, path, resp.T, ep.How); - foreach (var d in ep.Decls) Apply(rb, d); - if (ep.How == "pp") foreach (var s in ep.Declared ?? []) rb.ProducesProblem(s); - if (resp.T == "decl") rb.ProducesProblem(429); - } - eps.Add((pre + path, [.. decls, .. ep.Decls], [.. ep.Declared ?? [], .. resp.Declared])); - } - } - - static (List Eps, HostCfg Cfg) Plan(Row r, Spec s, Resp resp) - { - var eps = new List(); - var map = s.Map ?? (m => (m.Status, s.Typed ? TypeOf(m.Kind) : (string?)null)); - MGrp? root = s.Minimal is null ? null : new MGrp("/m", resp.Outer, [s.Minimal], []); - if (root is not null && Hx.HasMinimal(r)) - { - var found = new List<(string Path, Decl[] Decls, int[] Declared)>(); - Walk(null, root, [], "", resp, found); - eps.AddRange(found.Select(x => new Ep(false, Hx.M(r, x.Path), resp.T, x.Decls, x.Declared, map))); - } - var controllers = new List(); - Decl[] builder = [.. resp.Outer, .. s.Builder]; - if (Hx.HasMvc(r)) - foreach (var key in s.Controllers) - { - var c = FxCtl.All[key]; - controllers.Add(Hx.Plain(r) ? c.Plain : c.Api); - foreach (var a in c.Actions) - eps.Add(new Ep(true, Hx.C(r, "/c/" + a.Route + "/" + resp.T), resp.T, [.. c.Decls, .. a.Decls, .. builder], [.. a.Declared, .. resp.Declared], map)); - } - if (Hx.HasMvc(r)) controllers.AddRange(s.ExtraControllers); - var cfg = new HostCfg - { - FaultControllers = true, Controllers = [.. controllers], - Mapping = s.Mapping ?? (s.Typed ? "own-IFault-mapping" : "per-set"), - Services = sv => { if (s.Mapping is null && !s.Typed) sv.AddFaultResponses(); s.Services?.Invoke(sv); }, - Minimal = b => { if (root is not null) Walk(b, root, [], "", resp, []); s.Extra?.Invoke(b); }, - ControllerBuilder = cb => { foreach (var d in builder) Apply(cb, d); return cb; }, - }; - return (eps, cfg); - } - - public static async Task Run(Row r, Spec s) - { - var en = Registry.Of(r); Process(r, en); - var resp = Resp.Of(r); - var (eps, cfg) = Plan(r, s, resp); - if (eps.Count == 0) return "harness: the row maps no endpoint of the claim (an excluded pair?)"; - WebApplication? app = null; Exception? down = null; - try { app = await Hx.Start(r, en, cfg); } - catch (Exception ex) when (ex is not Sides.InputsNotEquivalent) { down = ex; } - await using var _ = app; - Baseline Against(string o) => s.Refusals.Contains(o) ? Baseline.Refusal : s.Against; - string? failed = null; - - // A host the README says must not start: that is the row's one comparison. - if (s.Refused is not null || s.RefusedAs is not null) - { - foreach (var o in s.Of(Aspect.Start, null)) - { - var f = r.Check(o, Against(o), _ => down is null - ? (s.RefusedAs is not null ? "host started, though the README says this set stops the start" - : $"host started, though a status every endpoint of its scope leaves out must stop it ({string.Join(", ", s.Refused!)})") - : StartRefused(s, down)); - failed ??= f; - } - return failed; - } - - var members = s.Errors.Select(e => FxErr.H[e - 1]).ToList(); - var obligations = Aspect.Answer.Append(Aspect.LeftOut).SelectMany(a => members.SelectMany(m => s.Of(a, m))) - .Concat(Aspect.Doc.SelectMany(a => s.Of(a, null))).Distinct().ToList(); - if (down is not null) - { - foreach (var o in obligations) { var f = r.Check(o, Against(o), _ => "host did not start: " + down.GetType().Name + ": " + Cut(down.Message)); failed ??= f; } - return failed; - } - - var display = app!.Services.GetRequiredService().Endpoints.OfType() - .GroupBy(e => "/" + (e.RoutePattern.RawText ?? "").TrimStart('/'), StringComparer.OrdinalIgnoreCase) - .ToDictionary(g => g.Key, g => g.First().DisplayName ?? "", StringComparer.OrdinalIgnoreCase); - JsonNode? doc = null; Exception? docFailed = null; - try { doc = await Hx.Doc(app); } - catch (Exception ex) { docFailed = ex; } - var wires = new Dictionary<(string Url, int E), Hx.Wire>(); - foreach (var ep in eps) - foreach (var e in s.Errors) - wires[(ep.Url, e)] = await Hx.Get(app, $"{ep.Url}?e={e}"); - - foreach (var o in obligations) - { - var f = await r.Check(o, Against(o), async c => - { - var wire = false; // whether the check compared an answer on the wire before the document - bool want(string aspect, Mem? m) - { - var wanted = s.Of(aspect, m).Contains(o); - if (wanted && !Aspect.Doc.Contains(aspect)) wire = true; - return wanted; - } - foreach (var ep in eps) - foreach (var e in s.Errors) - if (Wire(r, ep, FxErr.H[e - 1], wires[(ep.Url, e)], display, want) is { } fail) return fail; - if (!Aspect.Doc.Any(a => want(a, null))) return null; - // A document ASP.NET alone refuses too has nothing to check (noted there); any other failure is returned. - if (docFailed is not null) return await DocRefusedByAspNetAlone(c, r, en, cfg, docFailed, wire); - foreach (var ep in eps) - if (DocOf(r, doc!, ep, want) is { } fail) return fail; - return null; - }); - failed ??= f; - } - return failed; - } - - /// The host refused to start: as the README says, with an InvalidOperationException naming each of , or as judges. - static string? StartRefused(Spec s, Exception ex) - { - if (s.RefusedAs is { } judge) return judge(ex); - var names = s.Refused!; - var ioe = ex as InvalidOperationException ?? ex.InnerException as InvalidOperationException; - if (ioe is null) return $"host refused to start, but with {ex.GetType().Name}, not InvalidOperationException: {Cut(ex.Message)}"; - foreach (var name in names) - if (!ioe.Message.Contains(name, StringComparison.Ordinal)) return $"host refused to start, but not naming {name}: {Cut(ioe.Message)}"; - return null; - } - - /// - /// Hx.DocRefusedByAspNetAlone, with a baseline that has a problem response: the same host without AddTypeKit(), plus one endpoint (and one - /// action, where the host has MVC) declaring ProducesProblem(404). AddTypeKit() adds its problem responses "as ProducesProblem would" - /// (README "A Result as a response"), so ASP.NET alone writing a document of the same host without any problem response says nothing about - /// them: under json.MaxDepth=3 System.Text.Json refuses the problem schema of ASP.NET's own ProducesProblem too (decision 99). - /// - static async Task DocRefusedByAspNetAlone(Check c, Row r, Entry en, HostCfg cfg, Exception withTypeKit, bool comparedBefore) - { - static string Say(Exception x) => x.GetType().Name + ": " + Cut(x.Message); - var alone = cfg.Copy(); alone.TypeKitOpenApi = false; - alone.Controllers = [.. cfg.Controllers ?? [], typeof(FxProblemBaseline)]; - alone.Minimal = b => { cfg.Minimal?.Invoke(b); b.MapGet("/baseline-problem", () => Results.NoContent()).ProducesProblem(404); }; - try - { - await using var app = await Hx.Start(r, en, alone); - await Hx.Doc(app); - } - // the same refusal by type and stage, never by its words (Hx.DocAlike) - catch (Exception ex) when (Hx.DocAlike(ex, withTypeKit)) - { - Hx.DocRefused(r, ex, comparedBefore); - return null; - } - catch (Exception ex) when (r.Is("json.MaxDepth", "3") && Depth(ex) && Depth(withTypeKit)) - { - // Both refuse the problem schema under MaxDepth=3, by System.Text.Json's own depth checks, but on different paths (TypeKit's - // transformer through the schema exporter, ProducesProblem through the document writer): a refusal of STJ's own, nothing to check - // (decision 99: under MaxDepth=3 System.Text.Json refuses the OpenAPI document of a problem response for plain ASP.NET too). - return c.Refused(Cite.Record("0099"), $"{Say(withTypeKit)} / {Say(ex)}"); - } - catch (Exception ex) { return $"document: AddTypeKit() fails ({Say(withTypeKit)}), ASP.NET alone with a ProducesProblem fails otherwise ({Say(ex)})"; } - return $"document: AddTypeKit() fails ({Say(withTypeKit)}), ASP.NET alone with a ProducesProblem writes it"; - } - - static bool Depth(Exception x) => x is InvalidOperationException or System.Text.Json.JsonException; - - /// One endpoint's answer to reporting , judged on the aspects names. - static string? Wire(Row r, Ep ep, Mem m, Hx.Wire w, Dictionary display, Func want) - { - var at = $"{ep.Url} reporting {m.Q}"; - if (ep.LeftOut(m)) - { - if (!want(Aspect.LeftOut, m)) return null; - if (w.Thrown is null) return $"{at}: left out of its document, but answered {(int)w.Status}: {Cut(w.Body)}"; - if (!w.Thrown.StartsWith(nameof(InvalidOperationException) + ":", StringComparison.Ordinal)) return $"{at}: left out, threw {Cut(w.Thrown)}, expected an InvalidOperationException"; - if (!display.TryGetValue(ep.Url, out var name) || name.Length == 0) return $"harness: no endpoint named at {ep.Url}"; - if (!w.Thrown.Contains(name, StringComparison.Ordinal) || !w.Thrown.Contains(m.Q, StringComparison.Ordinal)) - return $"{at}: the refusal does not name the endpoint ({name}) and the member ({m.Q}): {Cut(w.Thrown)}"; - return null; - } - if (!Aspect.Answer.Any(a => want(a, m))) return null; - if (w.Thrown is not null) return $"{at}: request threw {Cut(w.Thrown)}"; - var status = ep.StatusOf(m); - if (want(Aspect.Status, m) && (int)w.Status != status) return $"{at}: status {(int)w.Status}, expected {status}: {Cut(w.Body)}"; - JsonNode? body; try { body = JsonNode.Parse(w.Body); } catch { return $"{at}: body is not JSON: {Cut(w.Body)}"; } - var fault = Un(r, body?["fault"]?.GetValue()); - if (want(Aspect.Fault, m) && fault != m.Name) return $"{at}: fault {fault ?? "(none)"}, expected {m.Name}"; - if (want(Aspect.Status, m) && int.Parse(body?["status"]?.ToString() is { } st ? Un(r, st)!.Trim('"') : "0") != status) return $"{at}: problem status field {body?["status"]}, expected {status}"; - if (want(Aspect.Type, m) && ep.Map(m).Type is { } type && Un(r, body?["type"]?.GetValue()) != type) return $"{at}: type {body?["type"]}, expected {type}"; - var detail = Un(r, body?["detail"]?.GetValue()); - var wantDetail = m.Q == "HFault.Missing" ? "Missing." : null; - if (want(Aspect.Detail, m) && detail != wantDetail) return $"{at}: detail {detail ?? "(none)"}, expected {wantDetail ?? "none (the member has no description)"}"; - return null; - } - - /// One endpoint's operation in the document, judged on the aspects names. - static string? DocOf(Row r, JsonNode doc, Ep ep, Func want) - { - var op = doc["paths"]?[ep.Url]?["get"]; - if (op is null && ep.Mvc && !Hx.InDocument(r)) { r.Note(NoteKeys.DocControllerNotInApiExplorer); return null; } - if (op is null) return $"document has no GET {ep.Url}"; - var responses = op["responses"]?.AsObject(); - if (responses is null) return $"{ep.Url}: operation has no responses"; - var nums = responses.Select(kv => kv.Key).Where(k => int.TryParse(k, out _)).Select(int.Parse).ToList(); - if (want(Aspect.DocOrder, null) && !nums.SequenceEqual(nums.Order())) return $"{ep.Url}: responses {string.Join(",", responses.Select(kv => kv.Key))} are not in ascending order"; - var got = nums.Where(n => n >= 400).ToList(); - var listed = ep.DocStatuses(); - // which statuses are listed, as a set: their order is DocOrder's, above - if (want(Aspect.DocListed, null) && !got.Order().SequenceEqual(listed)) return $"{ep.Url}: document lists {string.Join(",", got)}, expected {string.Join(",", listed)} ({string.Join(" ", ep.Decls)})"; - foreach (var status in ep.FaultStatuses()) - { - var resp = responses[status.ToString()]; - var txt = Hx.Deep(doc, resp)?.ToJsonString() ?? ""; - if (want(Aspect.DocDescribes, null) && !txt.Contains("\"fault\"")) return $"{ep.Url} {status}: response schema does not describe fault: {Cut(txt)}"; - if (want(Aspect.DocDescribes, null) && !txt.Contains("\"rule\"")) return $"{ep.Url} {status}: response schema does not describe rule: {Cut(txt)}"; - var desc = resp?["description"]?.GetValue() ?? ""; - const string Lead = "Problem types: "; - var i = desc.IndexOf(Lead, StringComparison.Ordinal); - var types = i < 0 ? [] : desc[(i + Lead.Length)..].Split(", ").ToHashSet(); - var wantTypes = ep.TypesAt(status); - if (want(Aspect.DocTypes, null) && !types.SetEquals(wantTypes)) - return $"{ep.Url} {status}: description \"{desc}\" names problem types {{{string.Join(", ", types)}}}, expected {{{string.Join(", ", wantTypes)}}}"; - } - return null; - } -} diff --git a/tools/probe/corpus/harness/FaultClaimControllers.g.cs b/tools/probe/corpus/harness/FaultClaimControllers.g.cs deleted file mode 100644 index afec71e4..00000000 --- a/tools/probe/corpus/harness/FaultClaimControllers.g.cs +++ /dev/null @@ -1,206 +0,0 @@ -// Generated by tools/probe/generators/faults.cs (the fault claims' MVC controllers, C04 C06 C14 C15): do not edit by hand. -// Every action answers FxErr's errors; its route ends in the result type it answers (one, two, dup) or decl (one, with a -// [ProducesResponseType(429)] of its own). The table FxCtl.All restates what each class and action declares, -// in ASP.NET's metadata order (controller, then action), as the input FaultClaim computes the expected response and document from. -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.AspNetCore; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.HttpResults; -using Microsoft.AspNetCore.Mvc; -namespace Probes.Faults; - -internal abstract class FxC04EpBase : ControllerBase -{ - [HttpGet("c/fx/ep/wo/one"), WithoutFaults("Down")] public Results> FxEpWoOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/ep/wo/two"), WithoutFaults("Down")] public Results, FaultProblem> FxEpWoTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/fx/ep/wo/dup"), WithoutFaults("Down")] public Results, FaultProblem> FxEpWoDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/fx/ep/wo/decl"), WithoutFaults("Down"), ProducesResponseType(429)] public Results> FxEpWoDecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/ep/keep/one")] public Results> FxEpKeepOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/ep/keep/two")] public Results, FaultProblem> FxEpKeepTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/fx/ep/keep/dup")] public Results, FaultProblem> FxEpKeepDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/fx/ep/keep/decl"), ProducesResponseType(429)] public Results> FxEpKeepDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC04EpApi : FxC04EpBase { } -internal sealed class FxC04EpPlain : FxC04EpBase { } -[ApiController, FaultStatus("Down", 507)] internal sealed class FxC04EpSApi : FxC04EpBase { } -[FaultStatus("Down", 507)] internal sealed class FxC04EpSPlain : FxC04EpBase { } - -internal abstract class FxC04CtWoBase : ControllerBase -{ - [HttpGet("c/fx/ct/wo/a/one")] public Results> FxCtWoAOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/ct/wo/a/two")] public Results, FaultProblem> FxCtWoATwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/fx/ct/wo/a/dup")] public Results, FaultProblem> FxCtWoADup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/fx/ct/wo/a/decl"), ProducesResponseType(429)] public Results> FxCtWoADecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/ct/wo/b/one")] public Results> FxCtWoBOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/ct/wo/b/two")] public Results, FaultProblem> FxCtWoBTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/fx/ct/wo/b/dup")] public Results, FaultProblem> FxCtWoBDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/fx/ct/wo/b/decl"), ProducesResponseType(429)] public Results> FxCtWoBDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController, WithoutFaults("Down")] internal sealed class FxC04CtWoApi : FxC04CtWoBase { } -[WithoutFaults("Down")] internal sealed class FxC04CtWoPlain : FxC04CtWoBase { } - -internal abstract class FxC04CtKeepBase : ControllerBase -{ - [HttpGet("c/fx/ct/keep/one")] public Results> FxCtKeepOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/ct/keep/two")] public Results, FaultProblem> FxCtKeepTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/fx/ct/keep/dup")] public Results, FaultProblem> FxCtKeepDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/fx/ct/keep/decl"), ProducesResponseType(429)] public Results> FxCtKeepDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC04CtKeepApi : FxC04CtKeepBase { } -internal sealed class FxC04CtKeepPlain : FxC04CtKeepBase { } - -internal abstract class FxC04GrBase : ControllerBase -{ - [HttpGet("c/fx/gr/a/one")] public Results> FxGrAOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/gr/a/two")] public Results, FaultProblem> FxGrATwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/fx/gr/a/dup")] public Results, FaultProblem> FxGrADup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/fx/gr/a/decl"), ProducesResponseType(429)] public Results> FxGrADecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/gr/b/one")] public Results> FxGrBOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/fx/gr/b/two")] public Results, FaultProblem> FxGrBTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/fx/gr/b/dup")] public Results, FaultProblem> FxGrBDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/fx/gr/b/decl"), ProducesResponseType(429)] public Results> FxGrBDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC04GrApi : FxC04GrBase { } -internal sealed class FxC04GrPlain : FxC04GrBase { } - -internal abstract class FxC06KBase : ControllerBase -{ - [HttpGet("c/k/one")] public Results> KOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/k/two")] public Results, FaultProblem> KTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/k/dup")] public Results, FaultProblem> KDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/k/decl"), ProducesResponseType(429)] public Results> KDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC06KApi : FxC06KBase { } -internal sealed class FxC06KPlain : FxC06KBase { } - -internal abstract class FxC14PpBase : ControllerBase -{ - [HttpGet("c/d/pp/one"), ProducesResponseType(404, "application/problem+json")] public Results> DPpOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/d/pp/two"), ProducesResponseType(404, "application/problem+json")] public Results, FaultProblem> DPpTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/d/pp/dup"), ProducesResponseType(404, "application/problem+json")] public Results, FaultProblem> DPpDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/d/pp/decl"), ProducesResponseType(404, "application/problem+json"), ProducesResponseType(429)] public Results> DPpDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC14PpApi : FxC14PpBase { } -internal sealed class FxC14PpPlain : FxC14PpBase { } - -internal abstract class FxC14PrtBase : ControllerBase -{ - [HttpGet("c/d/prt/one"), ProducesResponseType(404)] public Results> DPrtOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/d/prt/two"), ProducesResponseType(404)] public Results, FaultProblem> DPrtTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/d/prt/dup"), ProducesResponseType(404)] public Results, FaultProblem> DPrtDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/d/prt/decl"), ProducesResponseType(404), ProducesResponseType(429)] public Results> DPrtDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC14PrtApi : FxC14PrtBase { } -internal sealed class FxC14PrtPlain : FxC14PrtBase { } - -internal abstract class FxC14NoneBase : ControllerBase -{ - [HttpGet("c/d/none/one")] public Results> DNoneOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/d/none/two")] public Results, FaultProblem> DNoneTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/d/none/dup")] public Results, FaultProblem> DNoneDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/d/none/decl"), ProducesResponseType(429)] public Results> DNoneDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC14NoneApi : FxC14NoneBase { } -internal sealed class FxC14NonePlain : FxC14NoneBase { } - -internal abstract class FxC15MBase : ControllerBase -{ - [HttpGet("c/s/a/one")] public Results> SAOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/a/two")] public Results, FaultProblem> SATwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/a/dup")] public Results, FaultProblem> SADup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/a/decl"), ProducesResponseType(429)] public Results> SADecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/p/one"), FaultStatus("Denied", 452)] public Results> SPOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/p/two"), FaultStatus("Denied", 452)] public Results, FaultProblem> SPTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/p/dup"), FaultStatus("Denied", 452)] public Results, FaultProblem> SPDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/p/decl"), FaultStatus("Denied", 452), ProducesResponseType(429)] public Results> SPDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC15MApi : FxC15MBase { } -internal sealed class FxC15MPlain : FxC15MBase { } - -internal abstract class FxC15ASBase : ControllerBase -{ - [HttpGet("c/s/a/one"), FaultStatus("Denied", 451)] public Results> SAOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/a/two"), FaultStatus("Denied", 451)] public Results, FaultProblem> SATwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/a/dup"), FaultStatus("Denied", 451)] public Results, FaultProblem> SADup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/a/decl"), FaultStatus("Denied", 451), ProducesResponseType(429)] public Results> SADecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/k/one")] public Results> SKOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/k/two")] public Results, FaultProblem> SKTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/k/dup")] public Results, FaultProblem> SKDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/k/decl"), ProducesResponseType(429)] public Results> SKDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC15ASApi : FxC15ASBase { } -internal sealed class FxC15ASPlain : FxC15ASBase { } - -internal abstract class FxC15AWBase : ControllerBase -{ - [HttpGet("c/s/a/one"), WithoutFaults("Down")] public Results> SAOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/a/two"), WithoutFaults("Down")] public Results, FaultProblem> SATwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/a/dup"), WithoutFaults("Down")] public Results, FaultProblem> SADup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/a/decl"), WithoutFaults("Down"), ProducesResponseType(429)] public Results> SADecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/k/one")] public Results> SKOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/k/two")] public Results, FaultProblem> SKTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/k/dup")] public Results, FaultProblem> SKDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/k/decl"), ProducesResponseType(429)] public Results> SKDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC15AWApi : FxC15AWBase { } -internal sealed class FxC15AWPlain : FxC15AWBase { } - -internal abstract class FxC15CSBase : ControllerBase -{ - [HttpGet("c/s/a/one")] public Results> SAOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/a/two")] public Results, FaultProblem> SATwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/a/dup")] public Results, FaultProblem> SADup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/a/decl"), ProducesResponseType(429)] public Results> SADecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/p/one"), FaultStatus("Denied", 452)] public Results> SPOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/p/two"), FaultStatus("Denied", 452)] public Results, FaultProblem> SPTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/p/dup"), FaultStatus("Denied", 452)] public Results, FaultProblem> SPDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/p/decl"), FaultStatus("Denied", 452), ProducesResponseType(429)] public Results> SPDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController, FaultStatus("Denied", 451)] internal sealed class FxC15CSApi : FxC15CSBase { } -[FaultStatus("Denied", 451)] internal sealed class FxC15CSPlain : FxC15CSBase { } - -internal abstract class FxC15CWBase : ControllerBase -{ - [HttpGet("c/s/a/one")] public Results> SAOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/a/two")] public Results, FaultProblem> SATwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/a/dup")] public Results, FaultProblem> SADup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/a/decl"), ProducesResponseType(429)] public Results> SADecl(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/p/one"), FaultStatus("Denied", 452)] public Results> SPOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/p/two"), FaultStatus("Denied", 452)] public Results, FaultProblem> SPTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/p/dup"), FaultStatus("Denied", 452)] public Results, FaultProblem> SPDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/p/decl"), FaultStatus("Denied", 452), ProducesResponseType(429)] public Results> SPDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController, WithoutFaults("Down")] internal sealed class FxC15CWApi : FxC15CWBase { } -[WithoutFaults("Down")] internal sealed class FxC15CWPlain : FxC15CWBase { } - -internal abstract class FxC15KBase : ControllerBase -{ - [HttpGet("c/s/k/one")] public Results> SKOne(int e) => FxErr.One(e).ToHttpResult(); - [HttpGet("c/s/k/two")] public Results, FaultProblem> SKTwo(int e) => FxErr.Two(e).ToHttpResult(); - [HttpGet("c/s/k/dup")] public Results, FaultProblem> SKDup(int e) => FxErr.Dup(e).ToHttpResult(); - [HttpGet("c/s/k/decl"), ProducesResponseType(429)] public Results> SKDecl(int e) => FxErr.One(e).ToHttpResult(); -} -[ApiController] internal sealed class FxC15KApi : FxC15KBase { } -internal sealed class FxC15KPlain : FxC15KBase { } - -public static partial class FxCtl -{ - static partial void Fill(Dictionary d) - { - d["C04Ep"] = new(typeof(FxC04EpApi), typeof(FxC04EpPlain), [], [new("fx/ep/wo", [new("HFault", "Down", 0)], []), new("fx/ep/keep", [], [])]); - d["C04EpS"] = new(typeof(FxC04EpSApi), typeof(FxC04EpSPlain), [new("HFault", "Down", 507)], [new("fx/ep/wo", [new("HFault", "Down", 0)], []), new("fx/ep/keep", [], [])]); - d["C04CtWo"] = new(typeof(FxC04CtWoApi), typeof(FxC04CtWoPlain), [new("HFault", "Down", 0)], [new("fx/ct/wo/a", [], []), new("fx/ct/wo/b", [], [])]); - d["C04CtKeep"] = new(typeof(FxC04CtKeepApi), typeof(FxC04CtKeepPlain), [], [new("fx/ct/keep", [], [])]); - d["C04Gr"] = new(typeof(FxC04GrApi), typeof(FxC04GrPlain), [], [new("fx/gr/a", [], []), new("fx/gr/b", [], [])]); - d["C06K"] = new(typeof(FxC06KApi), typeof(FxC06KPlain), [], [new("k", [], [])]); - d["C14Pp"] = new(typeof(FxC14PpApi), typeof(FxC14PpPlain), [], [new("d/pp", [], [404])]); - d["C14Prt"] = new(typeof(FxC14PrtApi), typeof(FxC14PrtPlain), [], [new("d/prt", [], [404])]); - d["C14None"] = new(typeof(FxC14NoneApi), typeof(FxC14NonePlain), [], [new("d/none", [], [])]); - d["C15M"] = new(typeof(FxC15MApi), typeof(FxC15MPlain), [], [new("s/a", [], []), new("s/p", [new("HFault", "Denied", 452)], [])]); - d["C15AS"] = new(typeof(FxC15ASApi), typeof(FxC15ASPlain), [], [new("s/a", [new("HFault", "Denied", 451)], []), new("s/k", [], [])]); - d["C15AW"] = new(typeof(FxC15AWApi), typeof(FxC15AWPlain), [], [new("s/a", [new("HFault", "Down", 0)], []), new("s/k", [], [])]); - d["C15CS"] = new(typeof(FxC15CSApi), typeof(FxC15CSPlain), [new("HFault", "Denied", 451)], [new("s/a", [], []), new("s/p", [new("HFault", "Denied", 452)], [])]); - d["C15CW"] = new(typeof(FxC15CWApi), typeof(FxC15CWPlain), [new("HFault", "Down", 0)], [new("s/a", [], []), new("s/p", [new("HFault", "Denied", 452)], [])]); - d["C15K"] = new(typeof(FxC15KApi), typeof(FxC15KPlain), [], [new("s/k", [], [])]); - } -} diff --git a/tools/probe/corpus/harness/FaultClaimSets.cs b/tools/probe/corpus/harness/FaultClaimSets.cs deleted file mode 100644 index 30111c23..00000000 --- a/tools/probe/corpus/harness/FaultClaimSets.cs +++ /dev/null @@ -1,70 +0,0 @@ -// Fault types for C07-fault-responses-blanket (AspNetCore README#every-fault-set-at-once), each answered by one endpoint of its own beside the -// claim's: an enum (which no mapping for every set covers), a fault set with a member of no kind, and two fault sets the host's mapping -// refuses (one it throws for, one it gives a status that is no error). -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.AspNetCore; -using CodoMetis.TypeKit.ClosedSets; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.HttpResults; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Routing; -namespace Probes.Faults; - -public enum FxEnum { Lost, Taken } -public sealed partial class FxNoKind : IFaultSet -{ - public static readonly FxNoKind Fine = new(FaultKind.NotFound, null); -#pragma warning disable CMTK0014 // a member of no kind, on purpose: the claim is that it stops the start - public static readonly FxNoKind Blank = new(default, null); -#pragma warning restore CMTK0014 - private FxNoKind(FaultKind kind, string? description) { Kind = kind; Description = description; } - public FaultKind Kind { get; } - public string? Description { get; } -} -public sealed partial class FxThrows : IFaultSet -{ - public static readonly FxThrows Odd = new(FaultKind.Conflict, null); - private FxThrows(FaultKind kind, string? description) { Kind = kind; Description = description; } - public FaultKind Kind { get; } - public string? Description { get; } -} -public sealed partial class FxStatus : IFaultSet -{ - public static readonly FxStatus Moved = new(FaultKind.NotFound, null); - private FxStatus(FaultKind kind, string? description) { Kind = kind; Description = description; } - public FaultKind Kind { get; } - public string? Description { get; } -} -/// What the application's mapping throws (set=mapping-throws). -public sealed class FxMappingRefused() : Exception("the mapping refuses FxThrows"); - -public static class FxSets -{ - public static Result> Enum(int e) { if (e == 0) return Result>.Success(); return Fault.Of(FxEnum.Lost); } - public static Result> NoKind(int e) { if (e == 0) return Result>.Success(); return Fault.Of(FxNoKind.Fine); } - public static Result> Throws(int e) { if (e == 0) return Result>.Success(); return Fault.Of(FxThrows.Odd); } - public static Result> Status(int e) { if (e == 0) return Result>.Success(); return Fault.Of(FxStatus.Moved); } - - /// The minimal endpoint answering the row's set, at /c7x. - public static void Map(IEndpointRouteBuilder b, string set) - { - switch (set) - { - case "enum-unmapped": b.MapGet("/c7x", (int e) => Enum(e).ToHttpResult()); break; - case "member-without-kind": b.MapGet("/c7x", (int e) => NoKind(e).ToHttpResult()); break; - case "mapping-throws": b.MapGet("/c7x", (int e) => Throws(e).ToHttpResult()); break; - case "status-not-error": b.MapGet("/c7x", (int e) => Status(e).ToHttpResult()); break; - } - } - /// The MVC controller answering the row's set. - public static Type[] Controllers(string set) => set switch - { - "enum-unmapped" => [typeof(FxC07Enum)], "member-without-kind" => [typeof(FxC07NoKind)], - "mapping-throws" => [typeof(FxC07Throws)], "status-not-error" => [typeof(FxC07Status)], _ => [], - }; -} -[ApiController] internal sealed class FxC07Enum : ControllerBase { [HttpGet("c/c7x/enum")] public Results> Get(int e) => FxSets.Enum(e).ToHttpResult(); } -[ApiController] internal sealed class FxC07NoKind : ControllerBase { [HttpGet("c/c7x/nokind")] public Results> Get(int e) => FxSets.NoKind(e).ToHttpResult(); } -[ApiController] internal sealed class FxC07Throws : ControllerBase { [HttpGet("c/c7x/throws")] public Results> Get(int e) => FxSets.Throws(e).ToHttpResult(); } -[ApiController] internal sealed class FxC07Status : ControllerBase { [HttpGet("c/c7x/status")] public Results> Get(int e) => FxSets.Status(e).ToHttpResult(); } diff --git a/tools/probe/corpus/harness/FaultControllers.cs b/tools/probe/corpus/harness/FaultControllers.cs deleted file mode 100644 index f93ff5dc..00000000 --- a/tools/probe/corpus/harness/FaultControllers.cs +++ /dev/null @@ -1,49 +0,0 @@ -// Controllers answering with faults; the Plain* copies have no [ApiController] (http.host=controller-without-ApiController, and -// controller-without-ApiController-visible, where a convention puts them into the document). -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.AspNetCore; -using Microsoft.AspNetCore.Http.HttpResults; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -namespace Probes; - -[ApiController] -public sealed class MvcAController : ControllerBase -{ - [HttpGet("c/two")] public Results, FaultProblem> Two(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/wo"), WithoutFaults(nameof(HFault.Missing), nameof(HFault.Bad))] - public Results, FaultProblem> Wo(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/fs"), FaultStatus(nameof(HFault.Clash), 422)] - public Results, FaultProblem> Fs(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/three")] public Results, FaultProblem, FaultProblem> Three(int e) => Errors.Three(e).ToHttpResult(); - [HttpGet("c/declared404"), ProducesResponseType(404)] - public Results, FaultProblem> Declared404(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/declared"), ProducesResponseType(429)] - public Results, FaultProblem> Declared(int e) => Errors.Two(e).ToHttpResult(); -} -[ApiController, FaultStatus(nameof(HFault.Denied), 451), WithoutFaults(nameof(HFault.Down))] -public sealed class MvcBController : ControllerBase -{ - [HttpGet("d/two")] public Results, FaultProblem> Two(int e) => Errors.Two(e).ToHttpResult(); -} - - -public sealed class PlainAController : ControllerBase -{ - [HttpGet("c/two")] public Results, FaultProblem> Two(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/wo"), WithoutFaults(nameof(HFault.Missing), nameof(HFault.Bad))] - public Results, FaultProblem> Wo(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/fs"), FaultStatus(nameof(HFault.Clash), 422)] - public Results, FaultProblem> Fs(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/three")] public Results, FaultProblem, FaultProblem> Three(int e) => Errors.Three(e).ToHttpResult(); - [HttpGet("c/declared404"), ProducesResponseType(404)] - public Results, FaultProblem> Declared404(int e) => Errors.Two(e).ToHttpResult(); - [HttpGet("c/declared"), ProducesResponseType(429)] - public Results, FaultProblem> Declared(int e) => Errors.Two(e).ToHttpResult(); -} -[FaultStatus(nameof(HFault.Denied), 451), WithoutFaults(nameof(HFault.Down))] -public sealed class PlainBController : ControllerBase -{ - [HttpGet("d/two")] public Results, FaultProblem> Two(int e) => Errors.Two(e).ToHttpResult(); -} - diff --git a/tools/probe/corpus/harness/Faults.cs b/tools/probe/corpus/harness/Faults.cs deleted file mode 100644 index eac16d31..00000000 --- a/tools/probe/corpus/harness/Faults.cs +++ /dev/null @@ -1,106 +0,0 @@ -// Fault types, error tables and shared logging for the HTTP probes (harness; see HttpHost.cs). -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -using CodoMetis.TypeKit.ClosedSets; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.Extensions.Logging; -using System.Reflection; -namespace Probes; - -public sealed partial class HFault : IFaultSet -{ - public static readonly HFault Missing = new(FaultKind.NotFound, "Missing."); - public static readonly HFault Clash = new(FaultKind.Conflict, null); - public static readonly HFault Bad = new(FaultKind.Invalid, null); - public static readonly HFault Denied = new(FaultKind.Forbidden, null); - public static readonly HFault Down = new(FaultKind.Unavailable, null); - public static readonly HFault Stale = new(FaultKind.PreconditionFailed, null); - public static readonly HFault Garbled = new(FaultKind.Malformed, null); - private HFault(FaultKind kind, string? description) { Kind = kind; Description = description; } - public FaultKind Kind { get; } - public string? Description { get; } -} -public sealed partial class GFault : IFaultSet -{ - public static readonly GFault Gone = new(FaultKind.NotFound, null); - public static readonly GFault Raced = new(FaultKind.Conflict, null); - public static readonly GFault Old = new(FaultKind.PreconditionFailed, null); - private GFault(FaultKind kind, string? description) { Kind = kind; Description = description; } - public FaultKind Kind { get; } - public string? Description { get; } -} -public enum SFault { First, Second } -public enum PRule { A, B } -[Flags] public enum FRule { P = 1, Q = 2 } -public static class Holder { public enum NestedRule { N } } -public sealed partial class RuleSet : IClosedSet { public static readonly RuleSet X = new(); public static readonly RuleSet Y = new(); } - -public static class Errors -{ - public static Result> Two(int e) => e switch - { - 0 => Result>.Success(), - 1 => Fault.Of(HFault.Missing), - 2 => Fault.Of(HFault.Clash), - 3 => Fault.Of(HFault.Bad), - 4 => Fault.Of(HFault.Denied), - 5 => Fault.Of(HFault.Down), - 6 => Fault.Of(HFault.Stale), - 7 => Fault.Of(HFault.Garbled), - 8 => Fault.Of(GFault.Gone), - 9 => Fault.Of(GFault.Raced), - 10 => Fault.Of(GFault.Old), - _ => throw new ArgumentOutOfRangeException(nameof(e)), - }; - public static Result> Three(int e) => e switch - { - 0 => Result>.Success(), - 1 => Fault.Of(HFault.Missing), - 8 => Fault.Of(GFault.Gone), - 11 => Fault.Of(SFault.First), - 12 => Fault.Of(Fault.Of(SFault.Second, "second detail").WithRule(PRule.B)), - 13 => Fault.Of(Fault.Of(HFault.Clash).WithRule(RuleSet.X)), - 14 => Fault.Of(Fault.Of(GFault.Raced).WithRule(Holder.NestedRule.N)), - _ => throw new ArgumentOutOfRangeException(nameof(e)), - }; - public static Result> One(int e) => e switch - { - 0 => Result>.Success(), - 1 => Fault.Of(HFault.Missing), - 2 => Fault.Of(HFault.Clash), - 4 => Fault.Of(HFault.Denied), - 5 => Fault.Of(HFault.Down), - _ => throw new ArgumentOutOfRangeException(nameof(e)), - }; - public static Result> OneG(int e) => e switch - { - 0 => Result>.Success(), - 8 => Fault.Of(GFault.Gone), - 9 => Fault.Of(GFault.Raced), - _ => throw new ArgumentOutOfRangeException(nameof(e)), - }; -} - -public sealed class ListedLogs : ILoggerProvider -{ - public List Lines { get; } = []; - public ILogger CreateLogger(string categoryName) => new L(this); - public void Dispose() { } - private sealed class L(ListedLogs o) : ILogger - { - public IDisposable? BeginScope(TState state) where TState : notnull => null; - public bool IsEnabled(LogLevel logLevel) => true; - public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter) => - o.Lines.Add($"{logLevel}: {formatter(state, exception)}"); - } -} - -public sealed class OnlyControllers(Type[] controllers) : IApplicationFeatureProvider -{ - public void PopulateFeature(IEnumerable parts, ControllerFeature feature) - { - foreach (var c in controllers) feature.Controllers.Add(c.GetTypeInfo()); - } -} - diff --git a/tools/probe/corpus/harness/HandOrdered.cs b/tools/probe/corpus/harness/HandOrdered.cs deleted file mode 100644 index c1c72cfb..00000000 --- a/tools/probe/corpus/harness/HandOrdered.cs +++ /dev/null @@ -1,28 +0,0 @@ -// Value objects that declare their own CompareTo(TSelf) (a descending order), for the EF claims about hand-written ordering. -#nullable enable -#pragma warning disable CMTK0014, CS8618, CS1591, CMTK0005, CMTK1000, CMTK0003, CMTK0006 -using CodoMetis.TypeKit.ValueObjects; -namespace Probes.Ef; - -public readonly partial record struct HO_int_struct : IValue { public int CompareTo(HO_int_struct other) => other.Value.CompareTo(Value); } -public readonly partial record struct HO_string_struct : IValue { public int CompareTo(HO_string_struct other) => string.CompareOrdinal(other.Value, Value); } -public readonly partial record struct HO_decimal_struct : IValue { public int CompareTo(HO_decimal_struct other) => other.Value.CompareTo(Value); } -public readonly partial record struct HO_DateTime_struct : IValue { public int CompareTo(HO_DateTime_struct other) => other.Value.CompareTo(Value); } -public sealed partial record class HO_int_class : IValue { public int CompareTo(HO_int_class? other) => other is null ? 1 : other.Value.CompareTo(Value); } -public sealed partial record class HO_string_class : IValue { public int CompareTo(HO_string_class? other) => other is null ? 1 : string.CompareOrdinal(other.Value, Value); } - -public static class HandOrdered -{ - public sealed record Entry(string Name, Type Vo, Type Plain, object[] Values); - public static readonly Entry[] All = - [ - new("int-struct", typeof(HO_int_struct), typeof(int), [1, 2, 3]), - new("string-struct", typeof(HO_string_struct), typeof(string), ["a", "b", "c"]), - new("decimal-struct", typeof(HO_decimal_struct), typeof(decimal), [1.5m, 2.5m, 3.5m]), - new("DateTime-struct", typeof(HO_DateTime_struct), typeof(DateTime), [new DateTime(2026, 1, 1, 0, 0, 0, DateTimeKind.Utc), new DateTime(2026, 1, 2, 0, 0, 0, DateTimeKind.Utc), new DateTime(2026, 1, 3, 0, 0, 0, DateTimeKind.Utc)]), - new("int-class", typeof(HO_int_class), typeof(int), [1, 2, 3]), - new("string-class", typeof(HO_string_class), typeof(string), ["a", "b", "c"]), - ]; - public static Entry Of(string name) => All.First(e => e.Name == name); - public static object Make(Entry e, object plain) => e.Vo.GetMethod("From", [e.Plain])!.Invoke(null, [plain])!; -} diff --git a/tools/probe/corpus/harness/HttpBaseline.cs b/tools/probe/corpus/harness/HttpBaseline.cs deleted file mode 100644 index 731f5d84..00000000 --- a/tools/probe/corpus/harness/HttpBaseline.cs +++ /dev/null @@ -1,800 +0,0 @@ -// The baseline every http claim shares: under the row's options, a payload with the value object behaves like the same payload with the wrapped -// type (response bytes, request reading, parameter binding, OpenAPI schema), in the hosting models the row's http.host names. -// Oracle: differential (JSON/wire/schema parity), each comparison a check (Row.Check); a difference a document makes intended ends its check -// as that citation (Cite), and one no document makes intended is compared. -using System.Net; -using Microsoft.AspNetCore.Builder; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using System.Text.Json; -using System.Text.Json.Nodes; -using CodoMetis.TypeKit.Facts; -namespace Probes.Shapes; - -public static class HttpBaseline -{ - static string Cut(string? s) => s is null ? "(null)" : s.Length > 220 ? s[..220] + "..." : s; - - /// True only where the shape's Create refuses exactly the wrapped type's limit (the validated byte refuses 255): - /// anywhere else a refused limit is the value object's to explain. - static bool RefusesItsLimit(Entry e) - { - var refused = e.RefusedFn(); // the plain value the shape's Create refuses - if (refused is null) return false; - var t = Make.Held(e.Plain); - return new[] { "MaxValue", "MinValue" }.Any(f => t.GetField(f)?.GetValue(null) is { } limit && limit.Equals(refused)); - } - - /// - /// One wire comparison with the twin, inside its check : the wrapped type's answer (side (2)) and the - /// value object's (side (3)); side (1) refusing alike was decided before the check (). A difference a - /// document makes intended ends the check as that document's citation; BaseProbe calibrates the host on C07's rows, whose claim promises - /// nothing of the wire, so these cite the sentence itself, not a not-promised entry of C07. - /// - static string? Same(Check c, Row r, Entry e, string what, Hx.Wire p, Hx.Wire v, string input = "valid") - { - r.Log.AppendLine($"{what}: plain {(int)p.Status} {Cut(p.Body)} {p.Thrown} | vo {(int)v.Status} {Cut(v.Body)} {v.Thrown}"); - bool ok(Hx.Wire w) => (int)w.Status is >= 200 and < 300; - // both refused, and not as the twin without TypeKit refuses (Sides.Alike): evidence about refusing, and nothing to compare - if (!ok(p) && !ok(v)) return c.Inconclusive($"{what}: both refuse, not alike: wrapped {p.Side(what)}, value object {v.Side(what)}"); - // "Your own converter wins": the application's converter writes the value object instead of the generated one. - if (r.Is("json.Converters", "own-for-value-object") && ok(v)) return c.Inapplicable(Cite.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says"), what); - if (ok(p) && !ok(v)) - { - if (ReadRefusedAsDocumented(c, r, e, input, what)) return null; - // the same: the value object's array reads a JSON array of numbers, never the base64 string the twin's byte[] wrote and reads - if (ByteArray(r, e)) return c.Inapplicable(Cite.Docs("docs/json.md#the-wrapped-types-json"), what); - // A closed set travels as its member's name; the wrapped type under the application's converter is no reference. - if (e.Kind == "closed-set" && r.Is("json.Converters", "own-for-wrapped")) return c.Inapplicable(Cite.Docs("README.md#what-you-get"), what); - // The validated shapes' Create refuses the wrapped type's limit on purpose, so the limit input is a refusal. - if (e.Kind.StartsWith("validated") && input == "at-type-limit" && RefusesItsLimit(e)) return c.Refused(Cite.Docs("docs/json.md#when-a-value-is-rejected"), what); - return (e.Wrapped == "NodaTime.Instant" && what.StartsWith("mvc bind") ? "[F1] " : "") + $"{what}: wrapped answered {(int)p.Status} {Cut(p.Body)}, value object answered {(int)v.Status} {Cut(v.Body)} {v.Thrown}"; - } - if (!ok(p) && ok(v)) - { - // docs/json.md "Null and missing": a struct value object rejects a JSON null; a sealed partial record reads it as null, - // like any class, where the struct it wraps refuses it. - if (input == "null" && Make.AdmitsNull(e.Vo) && !Make.AdmitsNull(e.Plain)) return c.Refused(Cite.Docs("docs/json.md#null-and-missing"), what); - return $"{what}: value object answered {(int)v.Status} {Cut(v.Body)} where wrapped answered {(int)p.Status} {Cut(p.Body)} {p.Thrown}"; - } - if (p.Body != v.Body) - { - // A closed set travels as its member's name, never as the string or number standing in for it. - if (e.Kind == "closed-set") return c.Inapplicable(Cite.Docs("README.md#what-you-get"), what); - // docs/json.md "The wrapped type's JSON": an array of value objects over byte is a JSON array of numbers, where the twin, a byte[], - // is one base64 string (KB-038). - if (ByteArray(r, e)) return c.Inapplicable(Cite.Docs("docs/json.md#the-wrapped-types-json"), what); - return $"{what}: bodies differ: wrapped {Cut(p.Body)} vs value object {Cut(v.Body)}"; - } - c.Note(NoteKeys.Compared, what); - return null; - } - - /// - /// One check of the wire, three-sided (Sides): side (2) and side (3) answered on the host with - /// TypeKit; side (1) is asked of the host without it (), lazily, only where they disagree or refuse. Where (1) - /// refuses alike with (3), the check is the framework's refusal against Baseline.Framework; where (1) answers and the value object's - /// write was refused under a depth limit, side (1) is a converter's value there (KB-040, in process); otherwise - /// compares, once (2) is held to (1). - /// - static async Task Wire(Row r, Entry e, string obligation, Baseline against, string what, Hx.Wire p, Hx.Wire v, Func> one, Func> body) - { - var two = p.Side(what); var three = v.Side(what); - Sides.Answer? first = null; Sides.Side? framework = null; - if (two.Refused || three.Refused || p.Body != v.Body) - { - if (await one() is { } w) first = w.Side(what); - if (three.Refused && first is { Refused: true } && Sides.Alike(first, three)) framework = Sides.Measured(first, "the twin on a host without TypeKit"); - else if (three.Refused && v.Ex is { } thrown && first is { Refused: false } && (Sides.Why = null) is null - && Sides.WrittenByAConverter(r, e, Twin(r, e), Sides.WithoutTypeKit(JsonRow.Apply(r, e).Options!), new("write", null, thrown)) is { } converter - && Sides.Alike(converter.Answer, new("write", null, thrown))) - return Sides.Check(r, obligation, against, converter, new("write", null, thrown), null, null, _ => (string?)null); - } - var failed = await Sides.Check(r, obligation, against, framework, three, first, first is null ? null : two, body); - // the failure names side (1), for the triage of the row - return failed is null || first is null ? failed : failed + $" [side (1), the twin without TypeKit: {first}{(Sides.Why is { } why ? "; " + why : "")}]"; - } - - /// The wrapped twin's payload, as the echo writes it: the row's position and containing type around the wrapped value. - static Held Twin(Row r, Entry e) => Pos.Build(r["shape.position"], r.Get("shape.containing") ?? "class", e, false, e.Valid()); - - /// An array of value objects over byte, whose twin is a byte[] (KB-038). - static bool ByteArray(Row r, Entry e) => e.Wrapped == "byte" && r["shape.position"] == "array-element"; - - /// What MVC writes for a type's default: null for no content (a reference, or a Nullable), else its JSON under MVC's options. - static string? DefaultBody(WebApplication app, Type t) => - !t.IsValueType || Nullable.GetUnderlyingType(t) is not null ? null - : JsonSerializer.Serialize(Activator.CreateInstance(t), t, app.Services.GetRequiredService>().Value.JsonSerializerOptions); - - /// - /// Whether an answer is the type's default as MVC writes it: 204 for null, else 200 with its JSON, compared as JSON, since MVC's - /// formatter escapes with its own encoder where the options name none. - /// - static bool HoldsDefault(Hx.Wire w, string? body) - { - if (body is null) return w.Status == HttpStatusCode.NoContent; - if (w.Status != HttpStatusCode.OK) return false; - try { return JsonNode.DeepEquals(JsonNode.Parse(w.Body), JsonNode.Parse(body)); } - catch (JsonException) { return false; } - } - - /// A text the wrapped type does not read, for a twin that shows what MVC does with a binding failure; none where any text binds. - static string? Unreadable(Entry e) => e.Wrapped is "string" or "Uri" or "own-record-class" ? null : "abc"; - - /// - /// The value object refused a read the wrapped type answered, as a document says it does (the JSON family's documented differences, each - /// cited by its sentence; JsonParity.ReadMayDiffer listed them uncited): side (1) answered and (3) refused, which the citation makes - /// intended (Sides). The check ends as that refusal and true is returned. - /// - static bool ReadRefusedAsDocumented(Check c, Row r, Entry e, string input, string what) - { - if (r.Is("json.ReferenceHandler", "Preserve") && e.Wrapped == "own-record-class") { c.Refused(Cite.Docs("src/CodoMetis.TypeKit.Generators/README.md#json"), what); return true; } - if (e.Kind.StartsWith("validated") && input == "refused-by-create" && r.Get("json.Converters") != "StoredJsonConverterFactory") { c.Refused(Cite.Docs("docs/json.md#when-a-value-is-rejected"), what); return true; } - if (input == "null" && !Make.AdmitsNull(e.Vo)) { c.Refused(Cite.Docs("docs/json.md#null-and-missing"), what); return true; } - if (e.Kind == "closed-set" && input != "valid") { c.Refused(Cite.Docs("README.md#what-you-get"), what); return true; } - return false; - } - - /// Where two schemas first differ, and what each side has there: the comparison's 220 characters rarely reach it. - static string FirstDifference(JsonNode? p, JsonNode? v, string path) => - Difference(p, v, path) is var d ? $"{d.Path}: wrapped {Cut(d.P?.ToJsonString() ?? "(absent)")} | value object {Cut(d.V?.ToJsonString() ?? "(absent)")}" : ""; - - /// - /// Where two schemas first differ: the path as it is shown, the keys that lead there (a property's name, an element's index), each - /// side's node there (null where it is absent) and the node holding it, so a difference is told by its structure, never by its path's text. - /// - sealed record Diff(string Path, IReadOnlyList Keys, JsonNode? P, JsonNode? V, JsonNode? ParentP, JsonNode? ParentV); - - static Diff Difference(JsonNode? p, JsonNode? v, string path) => Difference(p, v, path, [], null, null); - - static Diff Difference(JsonNode? p, JsonNode? v, string path, List keys, JsonNode? parentP, JsonNode? parentV) - { - if (p is JsonObject po && v is JsonObject vo) - { - foreach (var k in po.Select(kv => kv.Key).Union(vo.Select(kv => kv.Key)).Order(StringComparer.Ordinal)) - if (!JsonNode.DeepEquals(po[k], vo[k])) return Difference(po[k], vo[k], path + "." + k, [.. keys, k], p, v); - } - else if (p is JsonArray pa && v is JsonArray va && pa.Count == va.Count) - { - for (var i = 0; i < pa.Count; i++) - if (!JsonNode.DeepEquals(pa[i], va[i])) return Difference(pa[i], va[i], $"{path}[{i}]", [.. keys, i.ToString(System.Globalization.CultureInfo.InvariantCulture)], p, v); - } - return new(path, keys, p, v, parentP, parentV); - } - - /// The node the keys of a lead to in another schema, or null where it has none. - static JsonNode? At(JsonNode? node, IEnumerable keys) - { - foreach (var k in keys) - node = node switch { JsonObject o => o[k], JsonArray a when int.TryParse(k, out var i) && i < a.Count => a[i], _ => null }; - return node; - } - - /// - /// Side (1) of a row's wire and schema comparisons: the same host started without TypeKit (HostCfg.WithoutTypeKit), once, on the first - /// comparison that asks for it; null where it does not start, which no comparison then excuses anything by. The twin's side (1) is a - /// host with the twin's endpoints alone (plain): ASP.NET alone describing the value object's types beside them names components - /// after them and writes the twin's own schemas otherwise. A host with both (both) answers for the value object's position. - /// - sealed class SideOne(Row r, Entry e, string only) : IAsyncDisposable - { - Task? started; - public string? Down { get; private set; } - public Task Host() => started ??= Start(); - async Task Start() - { - try { return await Hx.Start(r, e, new HostCfg { FaultControllers = false, MapBlanketMapping = false, WithoutTypeKit = true, PayloadOnly = only }); } - catch (Exception x) when (x is not Sides.InputsNotEquivalent) { Down = x.GetType().Name + ": " + Cut(x.Message); r.Log.AppendLine("side (1) host did not start: " + Down); return null; } - } - public async Task Ask(Func> ask) => await Host() is { } app ? await ask(app) : null; - public async ValueTask DisposeAsync() { if (started is not null && await started is { } app) await app.DisposeAsync(); } - } - - /// - /// BaseProbe's calibration of the shared host: every wire comparison a check of its own, named (BaseProbe - /// runs on rows of C07, which promises nothing of the wire, so its checks are the implicit claim). Per side: the echo against the - /// twin; the request read with the row's input.json, against the twin, or a closed set against its own response read back; the binding of - /// the row's http.binding with a valid value against the twin, and with a value Create refuses against the documented 400. Each is - /// three-sided (Sides): side (1), the twin on the same host without TypeKit, is asked where the twin and the value object disagree or - /// refuse. With , the schema parity too (C21's check). Every check runs; the first failure is returned. - /// - public static async Task Run(Row r, Entry e, bool schema = true, string obligation = "claim") - { - WebApplication app; - try { app = await Hx.Start(r, e, new HostCfg { FaultControllers = false, MapBlanketMapping = false }); } - catch (Exception ex) when (ex is not Sides.InputsNotEquivalent) { return "baseline host did not start: " + ex.GetType().Name + ": " + Cut(ex.Message); } - await using var _ = app; - await using var one = new SideOne(r, e, "plain"); - await using var alone = new SideOne(r, e, "both"); - var jopts = JsonRow.Apply(r, e).Options!; - var position = r["shape.position"]; - var plainSample = e.Valid(); - var input = r.Get("input.json") ?? "valid"; - var ownVo = r.Is("json.Converters", "own-for-value-object"); - var bind = r["http.binding"]; - string? failed = null; - var sides = new List<(string name, Func path, string echo, string inn, string bindPre)>(); - if (Hx.HasMinimal(r)) sides.Add(("minimal", p => Hx.M(r, p), "/echo/", "/in/", "/bind/")); - if (Hx.HasMvc(r)) sides.Add(("mvc", p => Hx.C(r, p), "/mvc/echo/", "/mvc/in/", "/mvc/bind/")); - foreach (var (name, path, echo, inn, bindPre) in sides) - { - // 1. response side - var ep = await Hx.Get(app, path(echo + "plain")); var ev = await Hx.Get(app, path(echo + "vo")); - var f1 = await Wire(r, e, obligation, Baseline.Twin, name + " echo", ep, ev, () => one.Ask(h => Hx.Get(h, path(echo + "plain"))), - c => Task.FromResult(Same(c, r, e, name + " echo", ep, ev))); - failed ??= f1; - // 2. request side, with the row's input.json, where the wrapped type's echo gave a body to change - if ((int)ep.Status == 200) - { - string? f2; - if (ownVo) - // "Your own converter wins": the application's converter reads the value object, so the twin's text is none of its; - // only that it reads its own response back is held, and the check earns nothing. - f2 = await r.Check(obligation, Baseline.RoundTrip, async c => - { - var ip = await Hx.Send(app, HttpMethod.Post, path(inn + "plain"), ep.Body); var iv = await Hx.Send(app, HttpMethod.Post, path(inn + "vo"), ev.Body); - if ((int)ev.Status == 200 && (int)iv.Status != 200 && (int)ip.Status == 200) return $"{name} in: the value object cannot read its own response ({(int)iv.Status}): {Cut(iv.Body)}"; - return c.Inapplicable(Cite.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says"), name + " in"); - }); - else if (e.Kind == "closed-set") - // A closed set travels as its member's name (README "What you get"): the string standing in for it is no twin, so the - // value object reads its own response back. - f2 = await r.Check(obligation, Baseline.RoundTrip, async c => - { - if ((int)ev.Status != 200) return c.Inconclusive($"{name} in: the value object's echo answered {(int)ev.Status}, no response of its own to read back"); - var ip = await Hx.Send(app, HttpMethod.Post, path(inn + "plain"), ep.Body); var iv = await Hx.Send(app, HttpMethod.Post, path(inn + "vo"), ev.Body); - r.Log.AppendLine($"{name} in(own response): plain {(int)ip.Status} {Cut(ip.Body)} {ip.Thrown} | vo {(int)iv.Status} {Cut(iv.Body)} {iv.Thrown}"); - // The wrapped type does not read its own response back under the row's options either: nothing to tell. - if ((int)iv.Status != 200 && (int)ip.Status != 200) return c.Inconclusive($"{name} in: neither side reads its own response back ({(int)ip.Status}, {(int)iv.Status})"); - if ((int)iv.Status != 200) return $"{name} in: the closed set cannot read its own response ({(int)iv.Status}), the wrapped type reads its own: {Cut(iv.Body)} {iv.Thrown}"; - c.Note(NoteKeys.Compared, name + " in: its own response read back"); - return null; - }); - else - { - var text = JsonParity.InputFor(input, e, jopts, ep.Body, position); - var ip = await Hx.Send(app, HttpMethod.Post, path(inn + "plain"), text); var iv = await Hx.Send(app, HttpMethod.Post, path(inn + "vo"), text); - var what = name + " in(" + input + ")"; - f2 = await Wire(r, e, obligation, Baseline.Twin, what, ip, iv, () => one.Ask(h => Hx.Send(h, HttpMethod.Post, path(inn + "plain"), text)), - c => Task.FromResult(Same(c, r, e, what, ip, iv, input))); - } - failed ??= f2; - } - // 3. binding, the row's http.binding - var unbindable = e.Wrapped == "own-record-class" && bind != "body" ? "a record class" - : e.Wrapped == "NodaTime.Instant" && name == "minimal" && bind != "body" ? "Instant in a minimal API" : null; - if (unbindable is not null) - { - // No sentence cites it: the wrapped type is not bound from text either, so neither side has an endpoint and there is no twin. - r.Check(obligation, Baseline.Twin, c => c.Inconclusive($"{name} bind {bind}: {unbindable} is not bound from text, the wrapped type neither")); - continue; - } - if (bind == "body" && ownVo) - { - // "Your own converter wins": the application's converter reads the body, not the twin's JSON. - r.Check(obligation, Baseline.Twin, c => c.Inapplicable(Cite.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says"), $"{name} bind body")); - continue; - } - var valid = Hx.BindText(e, plainSample); - var refused = e.Refused() is { } rf ? Hx.BindText(e, rf) : null; - async Task B(WebApplication host, string which, string txt, string w) - { - var enc = Uri.EscapeDataString(txt); - return bind switch - { - "route" => await Hx.Get(host, path(bindPre + "route/" + w + "/" + enc)), - "query" => await Hx.Get(host, path(bindPre + "query/" + w + "?v=" + enc)), - "as-parameters" => await Hx.Get(host, path(bindPre + "params/" + w + "?v=" + enc)), - "header" => await Hx.Send(host, HttpMethod.Get, path(bindPre + "header/" + w), header: ("v", txt)), - "form" => await Hx.Send(host, HttpMethod.Post, path(bindPre + "form/" + w), "v=" + enc, "application/x-www-form-urlencoded"), - _ => await Hx.Send(host, HttpMethod.Post, path(bindPre + "body/" + w), - which == "valid" ? JsonSerializer.Serialize(plainSample, e.Plain, jopts) : which == "refused" ? JsonSerializer.Serialize(e.Refused(), e.Plain, jopts) : JsonSerializer.Serialize(txt)), - }; - } - // Both sides are mapped for every binding they reach here: a 404 is the harness's routing, never a binding result. Counted as - // "plain unbindable", a plain route mapped under another name went unnoticed. - static string? Routed(string name, string bind, Hx.Wire bp, Hx.Wire bv) => - bp.Status == HttpStatusCode.NotFound || bv.Status == HttpStatusCode.NotFound - ? $"harness: {name} bind {bind} has no endpoint (plain {(int)bp.Status}, value object {(int)bv.Status})" : null; - // A text a route segment cannot carry as it is ('/', ':', '?', '#'): escaped, it arrives as other text ('%2F' stays), so the value - // bound is not the one sent. A limit of the harness, which no sentence cites: the check cannot tell. - bool Unsafe(string txt) => bind == "route" && (txt.Contains('/') || txt.Contains(':') || txt.Contains('?') || txt.Contains('#')); - string? Unplaceable(Check c, string txt) => c.Inconclusive($"{name} bind route: the text {txt} cannot be carried by a route segment as it is"); - string? f3; - if (Unsafe(valid)) f3 = r.Check(obligation, Baseline.Twin, c => Unplaceable(c, valid)); - else - { - var bp = await B(app, "valid", valid, "plain"); var bv = await B(app, "valid", valid, "vo"); - r.Log.AppendLine($"{name} bind {bind} valid text={valid}: plain {(int)bp.Status} {Cut(bp.Body)} {bp.Thrown} | vo {(int)bv.Status} {Cut(bv.Body)} {bv.Thrown}"); - f3 = Routed(name, bind, bp, bv) is { } unrouted - ? r.Check(obligation, Baseline.Twin, _ => unrouted) - : await Wire(r, e, obligation, Baseline.Twin, name + " bind " + bind, bp, bv, () => one.Ask(h => B(h, "valid", valid, "plain")), - c => Task.FromResult(Same(c, r, e, name + " bind " + bind, bp, bv))); - } - failed ??= f3; - if (refused is null) continue; - // README "Binding" (AspNetCore): a value Create rejects gets a 400, the one set of rules. - var against = Baseline.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#binding"); - if (Unsafe(refused)) { failed ??= r.Check(obligation, against, c => Unplaceable(c, refused)); continue; } - var rp = await B(app, "refused", refused, "plain"); var rv = await B(app, "refused", refused, "vo"); - r.Log.AppendLine($"{name} bind {bind} refused text={refused}: plain {(int)rp.Status} {Cut(rp.Body)} {rp.Thrown} | vo {(int)rv.Status} {Cut(rv.Body)} {rv.Thrown}"); - if (Routed(name, bind, rp, rv) is { } unrouted2) { failed ??= r.Check(obligation, against, _ => unrouted2); continue; } - // Side (1) of a refused binding: the twin on the host without TypeKit, refused a binding too, as MVC refuses a text the wrapped - // type cannot read. Where the value object's refusal could not be answered (a 500, which MVC's 400 under a source-generated - // context that does not list ValidationProblemDetails is, KB-041), the twin's refused binding is side (1): read from KB-041's - // fixture where it covers the row, else asked of the host without TypeKit. - Sides.Side? framework = null; - if (rv.Ex is { } refusal && Unreadable(e) is { } unreadable) - { - var settings = Sides.Settings(r, ("endpoint", name == "mvc" ? "mvc-api-controller" : "minimal"), ("binding", "fails")); - if (Sides.Covered(r, "KB-041", settings) is { Kind: FactAnswer.Exception } answer) - framework = new(new(name + " bind " + bind + " refused", null, Status: 500, FixtureRefusal: (string?)answer.Value["type"]), Baseline.Fact("KB-041"), $"KB-041 {answer.Id}: a binding MVC refuses, under a source-generated context"); - else if (await one.Ask(h => B(h, "unreadable", unreadable, "plain")) is { } twin) - framework = Sides.Measured(twin.Side(name + " bind " + bind + " refused"), $"the twin on a host without TypeKit, sent {unreadable}"); - } - var f4 = await Sides.Check(r, obligation, against, framework, rv.Side(name + " bind " + bind + " refused"), null, null, async c => - { - if ((int)rp.Status is not (>= 200 and < 300)) return c.Inconclusive($"{name} bind {bind}: the wrapped type refuses the value Create refuses too ({(int)rp.Status})"); - if (r.Is("json.ReferenceHandler", "Preserve") && e.Wrapped == "own-record-class") return c.Refused(Cite.Docs("src/CodoMetis.TypeKit.Generators/README.md#json"), "bind"); - // README "What TypeKit guarantees": StoredJsonConverterFactory reads JSON the application stored itself, without validation. - if (bind == "body" && r.Is("json.Converters", "StoredJsonConverterFactory")) return c.Inapplicable(Cite.Docs("README.md#what-typekit-guarantees"), "bind body"); - // KB-037: under MaxValidationDepth=1 MVC runs an action whose parameter it could not bind, with the parameter at its default. The - // value object must hold its default then, never the refused value; side (1), the twin sent a text it cannot read, shows the - // framework doing the same: read from KB-037's fixture where it covers the row, else asked of the host without TypeKit. - if (name == "mvc" && r.Is("mvc.MaxValidationDepth", "1") && (int)rv.Status is >= 200 and < 300) - { - if (DefaultBody(app, e.Vo) is var voDefault && !HoldsDefault(rv, voDefault)) - return $"{name} bind {bind}: validated value object bound the refused value (status {(int)rv.Status}) {Cut(rv.Body)}, not its default ({(voDefault is null ? "204" : "200 " + Cut(voDefault))})"; - if (Unreadable(e) is not { } text) - return c.Inapplicable(Cite.Fact("KB-037"), $"{name} bind {bind}: the value object holds its default; any text binds the wrapped type, so no twin shows it"); - Hx.Wire? bt; - if (Sides.Covered(r, "KB-037", Sides.Settings(r, ("text", text))) is { Kind: FactAnswer.Status } answer) - bt = new((HttpStatusCode)(int)answer.Value["status"]!, (string?)answer.Value["body"] ?? ""); - else bt = await one.Ask(h => B(h, "unreadable", text, "plain")); - if (bt is null) return c.Inconclusive($"{name} bind {bind}: the value object holds its default (KB-037); no host without TypeKit to show the wrapped type's ({one.Down})"); - r.Log.AppendLine($"{name} bind {bind} unreadable text={text}: plain {(int)bt.Status} {Cut(bt.Body)} {bt.Thrown}"); - if (HoldsDefault(bt, DefaultBody(app, e.Plain))) - return c.Inapplicable(Cite.Fact("KB-037"), $"{name} bind {bind}: both hold their default, the wrapped type for a text it cannot read"); - if ((int)bt.Status is >= 400 and < 500) - return $"{name} bind {bind}: the value object answered {(int)rv.Status} holding its default, where the wrapped type rejects a text it cannot read ({(int)bt.Status})"; - // KB-037's class from the query: the validator throws at the twin's property, and the value object, written by a - // converter of its own, is no class it walks - return c.Inconclusive($"{name} bind {bind}: the value object holds its default (KB-037); the wrapped type answered {(int)bt.Status} {Cut(bt.Body)} {bt.Thrown}"); - } - // a refused value must not bind (the one set of rules), except where the application skips validation on purpose - if ((int)rv.Status is >= 200 and < 300) return $"{name} bind {bind}: validated value object bound the refused value (status {(int)rv.Status}) {Cut(rv.Body)}"; - if ((int)rv.Status != 400) return $"{name} bind {bind}: refused value answered {(int)rv.Status}, expected 400: {Cut(rv.Body)} {rv.Thrown}"; - c.Note(NoteKeys.Compared, $"{name} bind {bind}: the refused value answered 400"); - return null; - }); - failed ??= f4; - } - // 4. schema parity - if (schema) { var f5 = await r.Check(obligation, Baseline.Twin, async c => await SchemaParity(app, one, alone, r, e, c)); failed ??= f5; } - return failed; - } - - /// - /// The schema parity alone, on the same host as : claim C21's check (C21OpenapiSchemaParityProbe), which BaseProbe leaves - /// out of its calibration, run once for each of the row's against the twin. C21 promises the schema ASP.NET - /// Core publishes for the underlying type, so side (1), the same host without TypeKit, is started on every row (Sides.Unconditional). - /// Every check runs; the first failure is returned. - /// - public static async Task Schema(Row r, Entry e, IEnumerable obligations) - { - WebApplication? app = null; string? down = null; - try { app = await Hx.Start(r, e, new HostCfg { FaultControllers = false, MapBlanketMapping = false }); } - catch (Exception ex) when (ex is not Sides.InputsNotEquivalent) { down = "schema host did not start: " + ex.GetType().Name + ": " + Cut(ex.Message); } - await using var _ = app; - await using var one = new SideOne(r, e, "plain"); - await using var alone = new SideOne(r, e, "both"); - var list = obligations.ToList(); - if (down is null && list.Any(o => Sides.Unconditional(r, o))) await one.Host(); - string? failed = null; - foreach (var obligation in list) - { - var f = await r.Check(obligation, Baseline.Twin, c => down is not null ? Task.FromResult(down) : SchemaParity(app!, one, alone, r, e, c)); - failed ??= f; - } - return failed; - } - - /// - /// Where either side is a null-or-reference (oneOf [null, component]), both sides there in one spelling: - /// the null as x-null beside the schema, wherever the side spelled it (type [null, X], nullable, - /// x-nullable, a null in an enum, or the null-or itself). A shared component cannot hold the null of one - /// position, so a nullable value object that is one is null around its reference, where ASP.NET writes the - /// wrapped type's null inside its inline schema (decision 36: "as a container's element (`oneOf: [null, - /// component]`, ASP.NET's own form)"; AspNetCore README: "A nullable element (`List<Quantity?>`, - /// `Dictionary<string, Quantity?>`) is the component or null, as a nullable property is."). Whether a side - /// admits null is kept; everywhere else, inlined schemas included, the comparison stays byte for byte (rows 170, - /// 196 and 215 of C21). Returns both sides without the marks put on resolved references. - /// - static (string, string, bool) NullAroundReferences(string sp, string sv) - { - var around = false; - static bool IsNull(JsonNode? a) => a is JsonObject ao && (ao["type"]?.ToJsonString() == "\"null\"" || ao["enum"]?.ToJsonString() == "[null]"); - static bool NullOrReference(JsonNode? n) => - n is JsonObject o && o["oneOf"] is JsonArray alts && alts.Count == 2 && alts.Any(IsNull) && alts.Any(a => a is JsonObject ao && ao.ContainsKey(Hx.ResolvedReference)); - static JsonNode? Unmark(JsonNode? n) => n switch - { - JsonObject o => new JsonObject(o.Where(kv => kv.Key != Hx.ResolvedReference).Select(kv => KeyValuePair.Create(kv.Key, Unmark(kv.Value)))), - JsonArray a => new JsonArray(a.Select(Unmark).ToArray()), - _ => n?.DeepClone(), - }; - static JsonNode? OneSpelling(JsonNode? n) - { - switch (n) - { - case JsonObject o when o["oneOf"] is JsonArray alts && alts.Count == 2 && alts.Any(IsNull) && alts.FirstOrDefault(a => !IsNull(a)) is JsonObject keep: - { - var c = (JsonObject)OneSpelling(keep)!; c["x-null"] = true; return Sorted(c); - } - case JsonObject o: - { - var c = new JsonObject(); - foreach (var (k, v) in o) - { - if (k == Hx.ResolvedReference) continue; - if (k is "nullable" or "x-nullable") { if (v?.GetValue() == true) c["x-null"] = true; continue; } - if (k == "allOf" && v is JsonArray aa && aa.Count == 1 && aa[0] is JsonObject a0 && a0.Count == 1 && a0.ContainsKey("x-nullable")) { c["x-null"] = true; continue; } - if (k == "type" && v is JsonArray ta && ta.Any(x => x?.GetValue() == "null")) - { - var f = ta.Where(x => x?.GetValue() != "null").ToList(); - c["type"] = f.Count == 1 ? f[0]!.DeepClone() : new JsonArray(f.Select(x => x!.DeepClone()).ToArray()); c["x-null"] = true; continue; - } - if (k == "type" && v?.ToJsonString() == "\"null\"") { c["x-null"] = true; continue; } - if (k == "enum" && v is JsonArray en && en.Any(x => x is null)) { c["enum"] = new JsonArray(en.Where(x => x is not null).Select(x => x!.DeepClone()).ToArray()); c["x-null"] = true; continue; } - if (k == "anyOf" && v is JsonArray any && any.Any(IsNull)) { c["anyOf"] = new JsonArray(any.Where(x => !IsNull(x)).Select(OneSpelling).ToArray()); c["x-null"] = true; continue; } - c[k] = OneSpelling(v); - } - return Sorted(c); - } - case JsonArray a: return new JsonArray(a.Select(OneSpelling).ToArray()); - default: return n?.DeepClone(); - } - } - static JsonObject Sorted(JsonObject o) => new(o.OrderBy(kv => kv.Key, StringComparer.Ordinal).Select(kv => KeyValuePair.Create(kv.Key, kv.Value?.DeepClone()))); - (JsonNode?, JsonNode?) Walk(JsonNode? p, JsonNode? v) - { - if (NullOrReference(p) || NullOrReference(v)) { around = true; return (OneSpelling(p), OneSpelling(v)); } - if (p is JsonObject po && v is JsonObject vo) - { - var cp = new JsonObject(); var cv = new JsonObject(); - foreach (var k in po.Select(kv => kv.Key).Union(vo.Select(kv => kv.Key)).Where(k => k != Hx.ResolvedReference).Order(StringComparer.Ordinal)) - { - if (!po.ContainsKey(k)) { cv[k] = Unmark(vo[k]); continue; } - if (!vo.ContainsKey(k)) { cp[k] = Unmark(po[k]); continue; } - var (a, b) = Walk(po[k], vo[k]); cp[k] = a; cv[k] = b; - } - return (cp, cv); - } - if (p is JsonArray pa && v is JsonArray va && pa.Count == va.Count) - { - var cp = new JsonArray(); var cv = new JsonArray(); - for (var i = 0; i < pa.Count; i++) { var (a, b) = Walk(pa[i], va[i]); cp.Add(a); cv.Add(b); } - return (cp, cv); - } - return (Unmark(p), Unmark(v)); - } - var (np, nv) = Walk(JsonNode.Parse(sp), JsonNode.Parse(sv)); - return (np?.ToJsonString() ?? "null", nv?.ToJsonString() ?? "null", around); - } - - /// - /// The component two operations of one document both refer to at the same place (a parameter's or the body's schema), where one - /// substitutes the value object for the twin: two types under one name, told by the references' structure. - /// - static string? SharedComponent(JsonNode twin, JsonNode valueObject) - { - static IEnumerable<(string, string)> References(JsonNode op) - { - if (op["parameters"] is JsonArray ps) - for (var i = 0; i < ps.Count; i++) - if (ps[i]?["schema"]?["$ref"]?.GetValue() is { } p) yield return ($"parameter {i}", p); - if (op["requestBody"]?["content"] is JsonObject content) - foreach (var (media, body) in content) - if (body?["schema"]?["$ref"]?.GetValue() is { } b) yield return ($"body {media}", b); - } - var twins = References(twin).ToDictionary(x => x.Item1, x => x.Item2); - return References(valueObject).FirstOrDefault(x => twins.TryGetValue(x.Item1, out var t) && t == x.Item2).Item2; - } - - /// A schema that is the OpenAPI 2.0 null flag and nothing else: {"allOf":[{"x-nullable":true}]}. - static bool OnlyTheNullFlag(JsonObject o) => - o.Count == 1 && o["allOf"] is JsonArray { Count: 1 } all && all[0] is JsonObject { Count: 1 } flag && flag["x-nullable"]?.GetValueKind() == JsonValueKind.True; - - /// - /// The OpenAPI 2.0 null flag with the wrapped type's format beside it and nothing else, {"format":"uri","allOf":[{"x-nullable":true}]}: - /// a nullable component's property, which TypeKit gives the wrapped type's format (KB-026). - /// - static bool TheNullFlagBeside(JsonObject o) => - o.ContainsKey("format") && o.Count == 2 && OnlyTheNullFlag(new JsonObject { ["allOf"] = o["allOf"]?.DeepClone() }); - - /// The keys of a difference up to the property it lies in (…, properties, P), or null where it lies in none. - static IReadOnlyList? PropertyAt(IReadOnlyList keys) - { - for (var i = keys.Count - 2; i >= 0; i--) - if (keys[i] == "properties") return [.. keys.Take(i + 2)]; - return null; - } - - /// - /// OpenAPI 2.0's nullable property of a component (KB-026): allOf: [{x-nullable: true}], without the reference and so without a - /// type, beside what TypeKit kept of the wrapped type (a format). - /// - static bool TheNullFlagWithoutItsReference(JsonObject o) => - o["allOf"] is JsonArray { Count: 1 } all && all[0] is JsonObject { Count: 1 } flag && flag["x-nullable"]?.GetValueKind() == JsonValueKind.True - && !o.ContainsKey("$ref") && !o.ContainsKey(Hx.ResolvedReference) && !o.ContainsKey("type"); - - /// - /// A body whose schema is its format and its null alone, {"schema": {"format": "int32", "allOf": [{"x-nullable": true}]}}, or its null - /// alone where the type has no format, anywhere in an operation: told by its structure (KB-001). - /// - static bool OnlyItsNull(JsonNode? n) => n switch - { - JsonObject o => o["schema"] is JsonObject schema && schema.All(kv => kv.Key is "allOf" or "format") - && schema["allOf"] is JsonArray { Count: 1 } all && all[0] is JsonObject { Count: 1 } flag - && flag["x-nullable"]?.GetValueKind() == JsonValueKind.True - || o.Any(kv => OnlyItsNull(kv.Value)), - JsonArray a => a.Any(OnlyItsNull), - _ => false, - }; - - /// - /// The value object's schema (3) against the wrapped twin's (2) at each endpoint pair, three-sided (Sides): the twin's schema is held to - /// the one ASP.NET publishes on the same host without TypeKit (1), where that host writes a document; and where the value object's schema - /// differs from the twin's, side (1)'s document at the same path tells whether ASP.NET alone writes that difference. - /// - static async Task SchemaParity(WebApplication app, SideOne one, SideOne alone, Row r, Entry e, Check c) - { - JsonNode doc; - try { doc = (await Hx.Doc(app))!; } - catch (Exception ex) - { - // The baseline is ASP.NET alone on the same host: a document it refuses too has no schema to compare (decision 99). - return await Hx.DocRefusedByAspNetAlone(r, e, new HostCfg { FaultControllers = false, MapBlanketMapping = false }, ex); - } - // docs/json.md "Describing what a converter writes": ASP.NET drops items/additionalProperties of a converter it cannot see into, the value - // object's schema is the underlying type's; and the application's converter for the value object describes it as that converter says. - // C21 declares both on every obligation (claims.json inapplicable), so Row.Check records them before this body runs and no row of - // C21 is generated with them; these lines hold for the schema half run under another claim (ShrinkProbe's baseline). - if (r.Is("json.Converters", "own-for-wrapped")) return c.Inapplicable(Cite.NotPromised("own-converter-for-the-wrapped-type")); - if (r.Is("json.Converters", "own-for-value-object")) return c.Inapplicable(Cite.NotPromised("own-converter-for-the-value-object")); - // side (1): the document ASP.NET writes for the twin's endpoints without TypeKit, where it writes one; and, asked only where the value - // object's schema differs from the twin's, the one it writes for both sides' endpoints, for the value object's position - async Task Document(SideOne host) - { - if (await host.Host() is not { } h) return null; - try { return await Hx.Doc(h); } - catch (Exception x) { r.Log.AppendLine("side (1) document refused: " + x.GetType().Name + ": " + Cut(x.Message)); return null; } - } - var doc1 = await Document(one); - JsonNode? Op(JsonNode d, string path, string method) => d["paths"]?[path]?[method]; - // only the schemas are compared (the claim is about the schema); whether a parameter or body is required follows the .NET type's nullability - JsonNode? Schemas(JsonNode? n) - { - switch (n) - { - case JsonArray a: { var c = new JsonArray(); foreach (var x in a) c.Add(Schemas(x)); return c; } - case JsonObject o: - { - var c = new JsonObject(); - foreach (var kv in o.OrderBy(k => k.Key, StringComparer.Ordinal)) - { - if (kv.Key == "required" && kv.Value is JsonValue) continue; // parameter / body / old-style flags - c[kv.Key] = Schemas(kv.Value); - } - return c; - } - default: return n?.DeepClone(); - } - } - string Sig(JsonNode d, JsonNode op) - { - var o = new JsonObject(); - o["parameters"] = Schemas(Hx.Deep(d, op["parameters"], mark: true)); - o["requestBody"] = Schemas(Hx.Deep(d, op["requestBody"], mark: true)); - o["ok"] = Schemas(Hx.Deep(d, op["responses"]?["200"], mark: true)); - return o.ToJsonString(); - } - // The media types of an operation's request body and 200 response, each named by where it is - static HashSet MediaTypes(JsonNode op) => - [ - .. (op["requestBody"]?["content"] as JsonObject ?? []).Select(kv => "requestBody:" + kv.Key), - .. (op["responses"]?["200"]?["content"] as JsonObject ?? []).Select(kv => "ok:" + kv.Key), - ]; - // A signature with only the request body's and the 200 response's media types given - static string UnderMediaTypes(string sig, IEnumerable media) - { - var keep = media.ToHashSet(); - var o = JsonNode.Parse(sig)!.AsObject(); - foreach (var (part, name) in new[] { ("requestBody", "requestBody"), ("ok", "ok") }) - if (o[part]?["content"] is JsonObject content) - foreach (var key in content.Select(kv => kv.Key).ToList()) - if (!keep.Contains(name + ":" + key)) content.Remove(key); - return o.ToJsonString(); - } - var checks = new List<(string, string)>(); - if (Hx.HasMinimal(r)) { checks.Add((Hx.M(r, "/schema/plain"), Hx.M(r, "/schema/vo"))); } - if (Hx.HasMvc(r)) - { - var bind = r["http.binding"] switch { "route" => "route", "query" => "query", "header" => "header", "form" => "form", "as-parameters" => "params", _ => "body" }; - var sfx = bind == "route" ? "/{v}" : ""; - checks.Add((Hx.C(r, $"/mvc/bind/{bind}/plain{sfx}"), Hx.C(r, $"/mvc/bind/{bind}/vo{sfx}"))); - } - if (Hx.HasMinimal(r)) - { - var bind = r["http.binding"] switch { "route" => "route", "query" => "query", "header" => "header", "form" => "form", "as-parameters" => "params", _ => "body" }; - // route: /bind/route/{which}/{v} - checks.Add((bind == "route" ? Hx.M(r, "/bind/route/plain/{v}") : Hx.M(r, $"/bind/{bind}/plain"), bind == "route" ? Hx.M(r, "/bind/route/vo/{v}") : Hx.M(r, $"/bind/{bind}/vo"))); - } - // The inputs of the two sides, apart from the type substituted: where each value is bound, under which - // name, and whether it is required. They must agree before the schemas are compared; otherwise the - // comparison measures the harness (G1: a nullable class value object against a required int). - string Inputs(JsonNode op) - { - var parts = new List(); - if (op["parameters"] is JsonArray ps) - foreach (var prm in ps) { var q = Hx.Deep(doc, prm); parts.Add($"{q?["in"]}:{q?["name"]}:required={q?["required"]?.ToJsonString() ?? "false"}"); } - if (Hx.Deep(doc, op["requestBody"]) is JsonObject rb) parts.Add($"body:required={rb["required"]?.ToJsonString() ?? "false"}"); - return string.Join(",", parts); - } - foreach (var (pp0, vp) in checks) - { - var method = pp0.Contains("/schema/") || pp0.Contains("/body/") || pp0.Contains("/form/") ? "post" : "get"; - // Like with like from the start: the twin is the wrapped type at the value object's nullability (shapes.cs), so a class - // value object over a struct faces the struct made nullable, at every endpoint (row 384 of C07: a record class over TimeOnly, - // optional, against TimeOnly?). - var pp = pp0; - var po = Op(doc, pp, method); var vo = Op(doc, vp, method); - if (po is null) - { - // Mapped for bodies only (a record class, Instant in a minimal API): neither side is there. - if (vo is not null) return $"harness: the value object's {method} {vp} is documented, its plain twin is not (missing {method} {pp})"; - c.Note(NoteKeys.SchemaNeitherSideDocumented, $"missing {method} {pp}"); continue; - } - if (vo is null) return $"missing {method} {vp}"; - // The twin's and the value object's shapes are two .NET types; one component name for both (an id the row's CreateSchemaReferenceId - // makes alike for both, such as an array of a generic type) holds one schema for the two, so the document cannot tell them apart. - if (SharedComponent(po, vo) is { } shared) - { c.Inconclusive($"{pp} vs {vp}: the twin's and the value object's shapes are one component, {shared}, in the document: nothing to compare"); continue; } - // (2) held to (1): the twin's schema is the one ASP.NET publishes on the same host without TypeKit, but where the twin itself - // holds one of TypeKit's types (a Patch), which no host without TypeKit describes - var po1 = doc1 is null ? null : Op(doc1, pp, method); - var twinHoldsTypeKit = pp.Contains("/schema/") && Sides.HoldsTypeKit(Twin(r, e).Declared); - if (po1 is not null && !twinHoldsTypeKit && Sig(doc1!, po1) is var s1 && Sig(doc, po) is var s2 && s1 != s2) - return $"schema of the wrapped type at {pp}: TypeKit changed the wrapped type's schema, at {FirstDifference(JsonNode.Parse(s1), JsonNode.Parse(s2), "$")} (without TypeKit, then with it)"; - if (Inputs(po) is var ip && Inputs(vo) is var iv && ip != iv) - { - // No twin of the wrapped type at the value object's nullability here (a struct value object over a class, which no plain - // type mirrors), or a wrapped record class, which ASP.NET binds by its properties where the value object is one value: the - // two sides are not alike, and no sentence cites it, so the check cannot tell. - if (Make.AdmitsNull(e.Vo) != Make.AdmitsNull(e.Plain)) - c.Inconclusive($"{pp} vs {vp}: no twin of {e.Plain.Name} at the value object's nullability, the inputs differ: wrapped [{ip}] vs value object [{iv}]"); - else if (e.Wrapped == "own-record-class") - c.Inconclusive($"{pp} vs {vp}: the wrapped record class is bound by its properties, the value object as one value, the inputs differ: wrapped [{ip}] vs value object [{iv}]"); - else r.Outcome("harness-error", $"inputs not equivalent for {pp} vs {vp}: wrapped [{ip}] vs value object [{iv}]"); - continue; - } - var sp = Sig(doc, po); var sv = Sig(doc, vo); - // MVC lists a body's and a response's media types by what its formatters can read and write of the .NET type, and the twin - // and the value object are two types (row 38 of C21: XmlSerializerInputFormatter reads a record class value object, not the - // Uri it wraps). Where the lists differ and side (1), ASP.NET alone with both sides' endpoints, lists each side's the same, - // the formatters decided it, not TypeKit: the schemas are compared under the media types both list. Otherwise as they are. - if (MediaTypes(po) is var mp && MediaTypes(vo) is var mv && !mp.SetEquals(mv) - && await Document(alone) is { } both0 && Op(both0, pp, method) is { } pAlone && Op(both0, vp, method) is { } vAlone - && MediaTypes(pAlone).SetEquals(mp) && MediaTypes(vAlone).SetEquals(mv)) - { - r.Log.AppendLine($"{pp} vs {vp}: media types the formatters list differ alike without TypeKit, compared under [{string.Join(", ", mp.Intersect(mv))}]"); - sp = UnderMediaTypes(sp, mp.Intersect(mv)); sv = UnderMediaTypes(sv, mp.Intersect(mv)); - } - if (NullAroundReferences(sp, sv) is var (np, nv, around)) { sp = np; sv = nv; if (around) c.Note(NoteKeys.SchemaNullAroundReferenceNormalised); } - if (sp != sv && Make.AdmitsNull(e.Vo) && Make.AdmitsNull(e.Plain) && e.Vo.IsValueType != e.Plain.IsValueType) - { - // Both sides nullable alike, one a class and the other a struct made nullable (a class value object and its nullable twin, or - // a nullable struct value object and a wrapped class); OpenAPI 3.x spells nullability two ways (oneOf [null, X] or type [null, X]). - sp = Hx.NoNull(JsonNode.Parse(sp))!.ToJsonString(); sv = Hx.NoNull(JsonNode.Parse(sv))!.ToJsonString(); - c.Note(NoteKeys.SchemaNullableSpellingsNormalised); - } - if (sp != sv && Make.AdmitsNull(e.Vo) != Make.AdmitsNull(e.Plain)) - { - // A struct value object over a class, whose twin is nullable by being a class where the value object is not (no plain - // type mirrors it); the markers go, the rest of the schema must agree. - sp = Hx.NoNull(JsonNode.Parse(sp))!.ToJsonString(); sv = Hx.NoNull(JsonNode.Parse(sv))!.ToJsonString(); - c.Note(NoteKeys.SchemaClassVoNullabilityNormalised); - } - if (sp != sv) - { - if (e.Kind == "closed-set") { c.Inapplicable(Cite.NotPromised("a-closed-set-is-a-string-enum"), "schema"); continue; } - // A form binds the wrapped record class by its properties and the value object otherwise, as the parameters above: the two - // sides' fields are not alike, and no sentence cites it, so the check cannot tell (row 62 of C21). - if (e.Wrapped == "own-record-class" && pp.Contains("/form/") && method == "post") - { c.Inconclusive($"{pp} vs {vp}: the wrapped record class is bound from a form by its properties, the value object otherwise: the fields differ"); continue; } - var d = Difference(JsonNode.Parse(sp), JsonNode.Parse(sv), "$"); - // Decision 36: a nullable value object admits null wherever it appears, where ASP.NET may write the twin's nullable type without - // it (the items of a list of double? under AllowNamedFloatingPointLiterals, row 216 of C21; the twin is held to side (1) above). - // Only that null differs: the value object admits it where the twin does not, and the schemas are otherwise the same. - if (Make.AdmitsNull(e.Vo) && d.Keys is [.., "x-null"] && d.P is null && d.V?.GetValueKind() == JsonValueKind.True - && JsonNode.DeepEquals(Hx.NoNull(JsonNode.Parse(sp)), Hx.NoNull(JsonNode.Parse(sv)))) - { c.Inapplicable(Cite.NotPromised("a-nullable-value-object-admits-null"), d.Path); continue; } - var openApi2 = r.Is("openapi.OpenApiVersion", "OpenApi2_0"); - // Side (1) at the same path: the twin's and the value object's operations in the document ASP.NET writes without TypeKit. - var twin1 = po1 is null ? null : JsonNode.Parse(Sig(doc1!, po1)); - var both1 = openApi2 && d.P is null && d.V is not null ? await Document(alone) : null; - var vo1 = both1 is null ? null : Op(both1, vp, method) is { } o1 ? JsonNode.Parse(Sig(both1, o1)) : null; - var vo3 = JsonNode.Parse(Sig(doc, vo)); - // KB-026: OpenAPI 2.0 writes a nullable property of any component as allOf [{x-nullable: true}], without the reference, and a - // value object keeps a component of its own (not-promised openapi-2-nullable-component-loses-its-type; row 38 of C21). Side (1) - // shows it: without TypeKit, ASP.NET writes the value object's position at the same path the same, where the twin has nothing. - if (openApi2 && d.P is null && d.V is not null && At(vo1, d.Keys) is { } same1 && JsonNode.DeepEquals(same1, At(vo3, d.Keys))) - { c.Inapplicable(Cite.NotPromised("openapi-2-nullable-component-loses-its-type"), d.Path); continue; } - // The same, where the spellings of the null were normalised above, which takes the null flag out of the value object's - // schema and leaves it without what the 2.0 writer dropped with the reference: its type, its pattern (rows 386, 178, 181 - // and 243 of C21: a nullable struct value object over Uri as a required-init property, and class value objects whose - // property admits null as a type parameter's or set through the constructor). Told by its structure: the value object's - // property is the null flag, alone or beside the wrapped type's format, and the twin's, which admits null, has that format. - if (openApi2 && PropertyAt(d.Keys) is { } property - && At(vo3, property) is JsonObject flagged3 && (TheNullFlagBeside(flagged3) || OnlyTheNullFlag(flagged3)) - && At(JsonNode.Parse(Sig(doc, po)), property) is JsonObject typed2 - && (typed2["x-nullable"]?.GetValueKind() == JsonValueKind.True - // or the twin is a component itself (a wrapped record class, row 444 of C21), whose reference ASP.NET's own rule - // leaves without the null the value object admits (decision 36: a-nullable-value-object-admits-null) - || typed2.ContainsKey(Hx.ResolvedReference) && Make.AdmitsNull(e.Vo)) - && JsonNode.DeepEquals(typed2["format"], flagged3["format"])) - { c.Inapplicable(Cite.NotPromised("openapi-2-nullable-component-loses-its-type"), d.Path); continue; } - // AspNetCore README "What the document says": a nullable element is the component or null, as a nullable property is, which - // OpenAPI 2.0 writes as allOf [{x-nullable: true}] without the reference (not-promised openapi-2-nullable-element-loses-its-type; - // row 358 of C21, a dictionary's value). ASP.NET alone drops a converter-backed element, so side (1) has nothing there; told by - // its structure, the value object's element is the null flag alone where the twin's admits null. - if (openApi2 && d.Keys is [.., "items" or "additionalProperties", "allOf"] && d.P is null - && At(vo3, d.Keys.SkipLast(1)) is JsonObject element3 && OnlyTheNullFlag(element3) - && At(JsonNode.Parse(Sig(doc, po)), d.Keys.SkipLast(1)) is JsonObject element2 && element2["x-nullable"]?.GetValueKind() == JsonValueKind.True) - { c.Inapplicable(Cite.NotPromised("openapi-2-nullable-element-loses-its-type"), d.Path); continue; } - // The same loss told by the row's axes, where side (1) cannot show it: a nullable value object that is a component is a - // nullable property wherever the serializer's contract says so (a get-only or a type parameter's property among them), where - // ASP.NET alone adds the null only for a settable annotated one. Its property is then the null flag without the reference, - // where the twin's keeps its type beside x-nullable. - if (openApi2 && Make.AdmitsNull(e.Vo) && !r.Is("openapi.CreateSchemaReferenceId", "null-inlines-all") && d.V is null - && At(vo3, d.Keys.SkipLast(1)) is JsonObject lost && TheNullFlagWithoutItsReference(lost) - && At(JsonNode.Parse(Sig(doc, po)), d.Keys.SkipLast(1)) is JsonObject kept && kept["x-nullable"]?.GetValueKind() == JsonValueKind.True) - { c.Inapplicable(Cite.NotPromised("openapi-2-nullable-component-loses-its-type"), d.Path); continue; } - // KB-001: OpenAPI 2.0 writes a nullable body of the wrapped value type as its format and allOf [{x-nullable: true}] alone (the - // null alone where it has no format, an enum), where a class value object's body in a minimal API is not nullable and keeps its - // schema (not-promised openapi-2-nullable-body-keeps-only-its-null; row 257, and rows 113 and 282 over TimeOnly and char). Side (1) - // shows it: without TypeKit, the twin's schema there is that format and that null alone. - // AspNetCore README "What the document says" (KB-026), seen from the twin: OpenAPI 2.0 writes a nullable property of any component, - // the twin's nullable enum among them, as allOf [{x-nullable: true}] without the reference, where the value object keeps its type. - // Side (1) shows it: without TypeKit, the twin's property there is that null alone. - if (openApi2 && d.P is null && d.V is not null && Make.AdmitsNull(e.Plain) && At(twin1, d.Keys.SkipLast(1)) is JsonObject t1 && OnlyTheNullFlag(t1)) - { c.Inapplicable(Cite.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says"), d.Path); continue; } - if (openApi2 && !e.Vo.IsValueType && Nullable.GetUnderlyingType(e.Plain) is not null && OnlyItsNull(twin1) && OnlyItsNull(JsonNode.Parse(Sig(doc, po)))) - { c.Inapplicable(Cite.NotPromised("openapi-2-nullable-body-keeps-only-its-null"), pp); continue; } - // KB-027: the component ASP.NET makes for an enum written by name is the schema of the first use it meets, so a wrapped E? - // parameter lists null through E's component where E was first met nullable; the value object's parameter is the enum's - // content without it wherever the enum is a component (not-promised enum-component-is-its-first-use; row 285 of C21). Side (1) - // shows it: without TypeKit, the twin's parameter lists the same null, through the same resolved component. - // The same component met first as a nullable use lists that null for a property too, the twin's non-nullable one included - // (not-promised enum-component-lists-the-null-of-its-first-use; row 196 of C21, a polymorphic derived type's property). - if (d.Keys is [.., "enum"] && d.P is JsonArray we && d.V is JsonArray ve && we.Count == ve.Count + 1 && we[^1] is null - && JsonNode.DeepEquals(new JsonArray(we.Take(ve.Count).Select(x => x?.DeepClone()).ToArray()), ve) - && At(JsonNode.Parse(Sig(doc, po)), d.Keys.SkipLast(1)) is JsonObject held && held.ContainsKey(Hx.ResolvedReference) - && At(twin1, d.Keys) is { } listed1 && JsonNode.DeepEquals(listed1, d.P)) - { - if (d.Keys is ["parameters", ..]) c.Inapplicable(Cite.NotPromised("enum-component-is-its-first-use"), vp); - else c.Inapplicable(Cite.NotPromised("enum-component-lists-the-null-of-its-first-use"), vp); - continue; - } - // AspNetCore README "A described type": ASP.NET cannot see through a converter it did not write, one a library registers for - // its own type among them, and drops a list's items and a dictionary's additionalProperties of that type, where the value - // object's element is put back (not-promised a-librarys-converter-for-the-wrapped-type; row 422 of C21, a list of Instant?). - // Side (1) shows it: without TypeKit, the twin's container has no element schema either. - if (e.Wrapped == "NodaTime.Instant" && d.Keys is [.., "items" or "additionalProperties"] && d.P is null && d.V is not null - && twin1 is not null && At(twin1, d.Keys.SkipLast(1)) is JsonObject container1 && !container1.ContainsKey(d.Keys[^1])) - { c.Inapplicable(Cite.NotPromised("a-librarys-converter-for-the-wrapped-type"), d.Path); continue; } - return $"schema differs for {pp} vs {vp} at {FirstDifference(JsonNode.Parse(sp), JsonNode.Parse(sv), "$")}:\n wrapped {Cut(sp)}\n value object {Cut(sv)}"; - } - } - return null; - } -} diff --git a/tools/probe/corpus/harness/HttpHost.cs b/tools/probe/corpus/harness/HttpHost.cs deleted file mode 100644 index 5f6d7186..00000000 --- a/tools/probe/corpus/harness/HttpHost.cs +++ /dev/null @@ -1,438 +0,0 @@ -// The shared HTTP host: applies a row's json.*, openapi.*, mvc.* and http.* axes and the shape axes, then runs the baseline checks -// (payload echo/in/binding parity, schema parity) that every http claim shares. Claim-specific scenarios plug in through HostCfg. -using System.Net; -using System.Net.Http.Headers; -using System.Reflection; -using System.Text; -using System.Text.Json; -using System.Text.Json.Nodes; -using System.Text.Json.Serialization; -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.AspNetCore; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Routing; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.ApplicationModels; -using Microsoft.AspNetCore.Mvc.ApplicationParts; -using Microsoft.AspNetCore.Mvc.Controllers; -using Microsoft.AspNetCore.Mvc.Formatters; -using Microsoft.AspNetCore.Mvc.ModelBinding; -using Microsoft.AspNetCore.Mvc.ModelBinding.Binders; -using Microsoft.AspNetCore.Mvc.Routing; -using Microsoft.AspNetCore.Mvc.ActionConstraints; -using Microsoft.AspNetCore.OpenApi; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Hosting; -using Microsoft.Extensions.Logging; -using Microsoft.Extensions.Options; -using Microsoft.OpenApi; -using HttpJsonOptions = Microsoft.AspNetCore.Http.Json.JsonOptions; -using MvcJsonOptions = Microsoft.AspNetCore.Mvc.JsonOptions; -namespace Probes.Shapes; - -public sealed record Payload(Held Vo, Held Plain); - -public sealed class NullBinderProvider : IModelBinderProvider { public IModelBinder? GetBinder(ModelBinderProviderContext context) => null; } -public sealed class PrefixConvention(string prefix) : IApplicationModelConvention -{ - public void Apply(ApplicationModel application) - { - foreach (var c in application.Controllers) - foreach (var s in c.Selectors.Concat(c.Actions.SelectMany(a => a.Selectors))) - if (s.AttributeRouteModel is { } m && !(m.Template?.StartsWith('/') ?? false)) - s.AttributeRouteModel = AttributeRouteModel.CombineAttributeRouteModel(new AttributeRouteModel(new RouteAttribute(prefix)), m); - } -} - -/// -/// http.host=controller-without-ApiController-visible: what ASP.NET documents as the switch beside [ApiController] and -/// [ApiExplorerSettings], a convention that sets ApiExplorer.IsVisible, so the document checks reach those controllers. -/// -public sealed class VisibleToApiExplorer : IApplicationModelConvention -{ - public void Apply(ApplicationModel application) - { - foreach (var c in application.Controllers) c.ApiExplorer.IsVisible = true; - } -} - -/// Closed generic controllers: one over the value object, one over the wrapped type; routes are set by BindConvention. -[ApiController] -public sealed class MvcBind : ControllerBase -{ - public sealed class Q { public T V { get; set; } = default!; } - [ProducesResponseType(200), Route("_")] - public IActionResult RouteA([FromRoute(Name = "v")] T v) => new ObjectResult(v) { DeclaredType = typeof(T) }; - [ProducesResponseType(200), Route("_")] - public IActionResult QueryA([FromQuery(Name = "v")] T v) => new ObjectResult(v) { DeclaredType = typeof(T) }; - [ProducesResponseType(200), Route("_")] - public IActionResult HeaderA([FromHeader(Name = "v")] T v) => new ObjectResult(v) { DeclaredType = typeof(T) }; - [ProducesResponseType(200), Route("_")] - public IActionResult BodyA([FromBody] T v) => new ObjectResult(v) { DeclaredType = typeof(T) }; - [ProducesResponseType(200), Route("_")] - public IActionResult FormA([FromForm(Name = "v")] T v) => new ObjectResult(v) { DeclaredType = typeof(T) }; - [ProducesResponseType(200), Route("_")] - public IActionResult ParamsA([FromQuery] Q q) => new ObjectResult(q.V) { DeclaredType = typeof(T) }; -} -public sealed class BindConvention(Type vo, Type plain, string prefix) : IControllerModelConvention -{ - public void Apply(ControllerModel c) - { - if (!c.ControllerType.IsGenericType || c.ControllerType.GetGenericTypeDefinition() != typeof(MvcBind<>)) return; - var arg = c.ControllerType.GenericTypeArguments[0]; - // By the entry's own types: the plain side is the twin, at the value object's nullability. - var which = arg == vo ? "vo" : arg == plain ? "plain" : throw new InvalidOperationException($"MvcBind<{arg.Name}> is neither side's"); - foreach (var a in c.Actions) - { - var (kind, post) = a.ActionName switch { "RouteA" => ("route", false), "QueryA" => ("query", false), "HeaderA" => ("header", false), "BodyA" => ("body", true), "FormA" => ("form", true), _ => ("params", false) }; - a.Selectors.Clear(); - a.Selectors.Add(new SelectorModel - { - AttributeRouteModel = new AttributeRouteModel(new RouteAttribute($"{prefix}mvc/bind/{kind}/{which}" + (kind == "route" ? "/{v}" : ""))), - ActionConstraints = { new Microsoft.AspNetCore.Mvc.ActionConstraints.HttpMethodActionConstraint([post ? "POST" : "GET"]) }, - EndpointMetadata = { new Microsoft.AspNetCore.Routing.HttpMethodMetadata([post ? "POST" : "GET"]) }, - }); - } - } -} -[ApiController] -public sealed class MvcPayloadController(Payload payload) : ControllerBase -{ - [HttpGet("mvc/echo/{which}")] - public IActionResult Echo(string which) { var h = which == "vo" ? payload.Vo : payload.Plain; return new ObjectResult(h.Value) { DeclaredType = h.Declared }; } - - [HttpPost("mvc/in/{which}")] - public async Task In(string which) - { - var h = which == "vo" ? payload.Vo : payload.Plain; - var f = HttpContext.RequestServices.GetRequiredService>().Value.InputFormatters.OfType().First(); - var meta = HttpContext.RequestServices.GetRequiredService().GetMetadataForType(h.Declared); - var factory = HttpContext.RequestServices.GetRequiredService(); - var ctx = new InputFormatterContext(HttpContext, "model", ModelState, meta, factory.CreateReader); - var result = await f.ReadAsync(ctx); - if (result.HasError) return StatusCode(400); - return new ObjectResult(result.Model) { DeclaredType = h.Declared }; - } -} - -public sealed class HostCfg -{ - public Action? Minimal; - public Func? ControllerBuilder; - public Action? Services; - public string Mapping = "by-kind"; - public bool Qualified; - public ListedLogs? Logs; - public Type[]? Controllers; // null: the fault controllers of the host - public bool FaultControllers = true; - public bool Payload = true; // payload echo / in / binding / schema endpoints - public string PayloadOnly = "both"; // "both", "vo" or "plain": the endpoints of one side only, minimal and MVC (side (1)'s twin alone, a document refusal isolated) - public bool MapBlanketMapping = true; - public bool TypeKitOpenApi = true; // false: the document as ASP.NET alone writes it, the baseline of a refused one - /// - /// The three-sided differential's side (1) (Sides): no AddTypeKit(), none of TypeKit's JSON converters on either set of options, and no - /// fault responses; the wrapped twin's endpoints answer as ASP.NET and System.Text.Json have them without TypeKit. - /// - public bool WithoutTypeKit; - public HostCfg Copy() => (HostCfg)MemberwiseClone(); -} - -public static class Hx -{ - public static bool HasMinimal(Row r) => r["http.host"] is "minimal" or "both" or "route-group"; - public static bool HasMvc(Row r) => r["http.host"] is "mvc" or "both" or "controller-without-ApiController" or "controller-without-ApiController-visible"; - /// The fault controllers have no [ApiController]: MVC infers no [FromBody] and answers no automatic 400. - public static bool Plain(Row r) => r["http.host"] is "controller-without-ApiController" or "controller-without-ApiController-visible"; - /// A controller without [ApiController] is in the document only when something makes it visible to the API explorer. - public static bool InDocument(Row r) => r["http.host"] != "controller-without-ApiController"; - /// Path of a minimal endpoint (route-group hosts map everything under /rg). - public static string M(Row r, string path) => (r["http.host"] == "route-group" ? "/rg" : "") + path; - /// Path of a controller endpoint (own-route-prefix convention puts /px before relative routes). - public static string C(Row r, string path) => (r.Is("mvc.Conventions", "own-route-prefix") ? "/px" : "") + path; - - public static Type[] FaultControllers(Row r) => - Plain(r) ? [typeof(PlainAController), typeof(PlainBController)] : [typeof(MvcAController), typeof(MvcBController)]; - - static void ApplyMvc(MvcOptions o, Row r) - { - foreach (var kv in r.All.Where(kv => kv.Key.StartsWith("mvc.") && kv.Value != "default")) - { - var n = kv.Key[4..]; - switch (n) - { - case "InputFormatters": o.InputFormatters.Add(new XmlSerializerInputFormatter(o)); continue; - case "OutputFormatters": o.OutputFormatters.Add(new XmlSerializerOutputFormatter()); continue; - case "ModelBinderProviders": o.ModelBinderProviders.Insert(0, new NullBinderProvider()); continue; - case "Conventions": o.Conventions.Add(new PrefixConvention("px")); continue; - } - var p = typeof(MvcOptions).GetProperty(n) ?? throw new ArgumentException("no mvc option " + n); - var t = Nullable.GetUnderlyingType(p.PropertyType) ?? p.PropertyType; - object v = t == typeof(bool) ? bool.Parse(kv.Value) : t == typeof(int) ? int.Parse(kv.Value) : t.IsEnum ? Enum.Parse(t, kv.Value) : throw new ArgumentException($"no hand value for mvc.{n}"); - p.SetValue(o, v); - } - } - - static void ApplyOpenApi(OpenApiOptions o, Row r) - { - var v = r.Get("openapi.OpenApiVersion"); - if (v is not (null or "default")) o.OpenApiVersion = Enum.Parse(v); - switch (r.Get("openapi.CreateSchemaReferenceId")) - { - case "own-prefixing": o.CreateSchemaReferenceId = ti => OpenApiOptions.CreateDefaultSchemaReferenceId(ti) is { } id ? "X" + id : null; break; - case "null-inlines-all": o.CreateSchemaReferenceId = _ => null; break; - } - switch (r.Get("openapi.transformers")) - { - case "own-schema-transformer": o.AddSchemaTransformer((s, c, ct) => Task.CompletedTask); break; - case "own-operation-transformer": o.AddOperationTransformer((op, c, ct) => Task.CompletedTask); break; - case "own-document-transformer": o.AddDocumentTransformer((d, c, ct) => Task.CompletedTask); break; - } - } - - public static async Task Start(Row r, Entry e, HostCfg c) - { - var builder = WebApplication.CreateBuilder(new WebApplicationOptions { EnvironmentName = Environments.Production }); - builder.WebHost.UseTestServer(); - builder.Logging.ClearProviders(); - if (c.Logs is not null) builder.Logging.AddProvider(c.Logs); - var plain = e.Valid(); var vo = Make.Vo(e, plain); - var (twin, held) = Sides.Twins(r["shape.position"], r.Get("shape.containing") ?? "class", e, plain, vo); - var payload = new Payload(held, twin); - builder.Services.AddSingleton(payload); - builder.Services.ConfigureHttpJsonOptions(o => { JsonRow.Apply(r, e, o.SerializerOptions); if (c.WithoutTypeKit) Sides.DropTypeKit(o.SerializerOptions); }); - builder.Services.AddOpenApi(x => { ApplyOpenApi(x, r); if (c.TypeKitOpenApi && !c.WithoutTypeKit) x.AddTypeKit(); }); - if (!c.WithoutTypeKit) builder.Services.Configure(x => x.QualifiedFaultNames = c.Qualified); - c.Services?.Invoke(builder.Services); - if (c.MapBlanketMapping && !c.WithoutTypeKit) - { - builder.Services.AddFaultResponses(f => f switch { SFault.First => 409, SFault.Second => 410, _ => throw new ArgumentOutOfRangeException(nameof(f)) }); - if (c.Mapping == "by-kind") builder.Services.AddFaultResponses(); - else if (c.Mapping == "own-IFault-mapping") builder.Services.AddFaultResponses(f => FaultResponse.For(f.Kind).WithType(new Uri($"https://p.example/{f.Kind}"))); - else if (c.Mapping == "per-set") { builder.Services.AddFaultResponses(); builder.Services.AddFaultResponses(); } - } - var mvc = HasMvc(r); - if (mvc) - { - var controllers = new List(); - if (c.FaultControllers) controllers.AddRange(c.Controllers ?? FaultControllers(r)); - if (c.Payload) - { - controllers.Add(typeof(MvcPayloadController)); - if (c.PayloadOnly != "plain") controllers.Add(typeof(MvcBind<>).MakeGenericType(e.Vo)); - if (c.PayloadOnly != "vo") controllers.Add(typeof(MvcBind<>).MakeGenericType(e.Plain)); - } - var pfx = ""; - builder.Services.AddControllers(o => - { - o.Conventions.Add(new BindConvention(e.Vo, e.Plain, pfx)); // before the prefix convention, so that it prefixes these routes too - if (r["http.host"] == "controller-without-ApiController-visible") o.Conventions.Add(new VisibleToApiExplorer()); - ApplyMvc(o, r); - }) - .AddJsonOptions(o => { JsonRow.Apply(r, e, o.JsonSerializerOptions); if (c.WithoutTypeKit) Sides.DropTypeKit(o.JsonSerializerOptions); }) - .ConfigureApplicationPartManager(p => { p.ApplicationParts.Clear(); p.FeatureProviders.Add(new OnlyControllers([.. controllers])); }); - } - var app = builder.Build(); - app.MapOpenApi(); - if (HasMinimal(r)) - { - IEndpointRouteBuilder root = r["http.host"] == "route-group" ? app.MapGroup("/rg") : app; - c.Minimal?.Invoke(root); - if (c.Payload) MapPayload(root, payload, r, e, c.PayloadOnly); - } - if (mvc) { var cb = app.MapControllers(); c.ControllerBuilder?.Invoke(cb); } - try { await app.StartAsync(); } catch { await app.DisposeAsync(); throw; } - return app; - } - - // ---- minimal payload endpoints - static void MapPayload(IEndpointRouteBuilder b, Payload p, Row r, Entry e, string only) - { - foreach (var which in new[] { "vo", "plain" }) - { - if (only != "both" && only != which) continue; - var h = which == "vo" ? p.Vo : p.Plain; - b.MapGet($"/echo/{which}", async (HttpContext ctx) => { await ctx.Response.WriteAsJsonAsync(h.Value, h.Declared, options: null, contentType: null, ctx.RequestAborted); }); - b.MapPost($"/in/{which}", async (HttpContext ctx) => - { - object? v; - try { v = await ctx.Request.ReadFromJsonAsync(h.Declared, ctx.RequestAborted); } - catch (JsonException) { ctx.Response.StatusCode = 400; return; } - catch (BadHttpRequestException) { ctx.Response.StatusCode = 400; return; } - await ctx.Response.WriteAsJsonAsync(v, h.Declared, options: null, contentType: null, ctx.RequestAborted); - }); - typeof(Hx).GetMethod(nameof(MapBind), BindingFlags.NonPublic | BindingFlags.Static)!.MakeGenericMethod(which == "vo" ? e.Vo : e.Plain) - .Invoke(null, [b, which, e.Wrapped is "own-record-class" or "NodaTime.Instant"]); - typeof(Hx).GetMethod(nameof(MapSchema), BindingFlags.NonPublic | BindingFlags.Static)!.MakeGenericMethod(h.Declared).Invoke(null, [b, which]); - } - } - - sealed record AsP([FromQuery(Name = "v")] T V); - static void MapBind(IEndpointRouteBuilder b, string which, bool bodyOnly) - { - static IResult Out(HttpContext ctx, T v) => Results.Json(v, ctx.RequestServices.GetRequiredService>().Value.SerializerOptions, null, null); - b.MapPost($"/bind/body/{which}", (HttpContext ctx, [FromBody] T v) => Out(ctx, v)); - if (bodyOnly) return; // a record class cannot be bound from a route, query, header or form (the wrapped type neither) - b.MapGet($"/bind/route/{which}/{{v}}", (HttpContext ctx, T v) => Out(ctx, v)); - b.MapGet($"/bind/query/{which}", (HttpContext ctx, [FromQuery(Name = "v")] T v) => Out(ctx, v)); - b.MapGet($"/bind/header/{which}", (HttpContext ctx, [FromHeader(Name = "v")] T v) => Out(ctx, v)); - b.MapPost($"/bind/form/{which}", (HttpContext ctx, [FromForm(Name = "v")] T v) => Out(ctx, v)).DisableAntiforgery(); - b.MapGet($"/bind/params/{which}", (HttpContext ctx, [AsParameters] AsP p) => Out(ctx, p.V)); - } - static void MapSchema(IEndpointRouteBuilder b, string which) => - b.MapPost($"/schema/{which}", ([FromBody] T body) => Results.Ok(body)); - - // ---- client side - /// An answer on the wire: its status and body, or the exception the server threw (, a 500), named in . - public sealed record Wire(HttpStatusCode Status, string Body, string? Thrown = null, Exception? Ex = null) - { - /// The answer as the three-sided differential compares it (Sides): the status, the body, and the exception's type and stage. - public Sides.Answer Side(string operation) => new(operation, Ex is null ? Body : null, Ex, (int)Status); - } - public static async Task Send(WebApplication app, HttpMethod m, string path, string? body = null, string? contentType = null, (string, string)? header = null) - { - var req = new HttpRequestMessage(m, path); - req.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - if (body is not null) req.Content = new StringContent(body, Encoding.UTF8, contentType ?? "application/json"); - if (header is { } h) req.Headers.TryAddWithoutValidation(h.Item1, h.Item2); - try - { - var resp = await app.GetTestClient().SendAsync(req); - return new(resp.StatusCode, await resp.Content.ReadAsStringAsync()); - } - catch (Exception ex) { return new(HttpStatusCode.InternalServerError, "", ex.GetType().Name + ": " + ex.Message, ex); } - } - public static Task Get(WebApplication app, string path) => Send(app, HttpMethod.Get, path); - public static async Task Doc(WebApplication app) => JsonNode.Parse(await app.GetTestClient().GetStringAsync("/openapi/v1.json")); - - /// - /// The document failed with AddTypeKit(). The same host without it is side (1) of the three (Sides): where ASP.NET alone refuses the - /// document alike, by exception type and stage and never by message (), the document part has nothing to check - /// (); where it writes the document, or refuses it otherwise, the failure is returned (F2 hid behind a baseline - /// that kept AddTypeKit() on both sides). says whether the check compared an answer before the document. - /// - public static async Task DocRefusedByAspNetAlone(Row r, Entry e, HostCfg c, Exception withTypeKit, bool comparedBefore = false) - { - static string Say(Exception x) => x.GetType().Name + ": " + (x.Message.Length > 200 ? x.Message[..200] + "..." : x.Message); - var alone = c.Copy(); alone.TypeKitOpenApi = false; - try - { - await using var app = await Start(r, e, alone); - await Doc(app); - } - catch (Exception ex) when (DocAlike(ex, withTypeKit)) - { - DocRefused(r, ex, comparedBefore); - return null; - } - catch (Exception ex) { return $"document: AddTypeKit() fails ({Say(withTypeKit)}), ASP.NET alone fails otherwise ({Say(ex)})"; } - return $"document: AddTypeKit() fails ({Say(withTypeKit)}), ASP.NET alone writes it"; - } - - /// - /// ASP.NET alone (side (1)) and AddTypeKit() (side (3)) refuse the document alike: the same exception type at the same stage (a - /// JsonException's Path), as compares two refusals. No message is compared: the two documents differ in length - /// by what AddTypeKit() wrote, so one refusal is at another line and byte of each (rows 31 and 140 of C21), and a refusal's words decide - /// no row (decision 99, WS2's three-sided differential). - /// - public static bool DocAlike(Exception one, Exception three) => - Sides.Alike(new Sides.Answer("document", null, one), new Sides.Answer("document", null, three)); - - /// - /// The document refused alike by ASP.NET alone: its part has nothing to check. A check that compared nothing before it is the framework's - /// refusal, an expected refusal that earns nothing (framework-refuses); one that compared an answer on the wire first is judged by that - /// answer (doc:refused-by-aspnet-alone, a pass: decision 99's "judged by its other checks"). - /// - public static void DocRefused(Row r, Exception one, bool comparedBefore) - { - if (comparedBefore) r.Note(NoteKeys.DocRefusedByAspnetAlone, one.GetType().Name); - else r.Note(NoteKeys.FrameworkRefuses, $"document refused by ASP.NET alone too ({one.GetType().Name}), side (1) measured on the same host without AddTypeKit()"); - } - - public static string BindText(Entry e, object plain) => e.Wrapped switch - { - "DateTime" => ((DateTime)plain).ToString("O"), "DateTimeOffset" => ((DateTimeOffset)plain).ToString("O"), - "DateOnly" => ((DateOnly)plain).ToString("yyyy-MM-dd"), "TimeOnly" => ((TimeOnly)plain).ToString("HH:mm:ss"), - "TimeSpan" => ((TimeSpan)plain).ToString("c"), "bool" => (bool)plain ? "true" : "false", "enum" or "flags-enum" => ((Enum)plain).ToString(), - _ => Convert.ToString(plain, System.Globalization.CultureInfo.InvariantCulture)! - }; - - // ---- schema comparison - /// Drops nullability markers (a class value object over a value type is nullable by being a class; not the value object's doing). - public static JsonNode? NoNull(JsonNode? n) - { - switch (n) - { - case JsonObject o: - static bool IsNull(JsonNode? a) => a is JsonObject ao && (ao["type"]?.ToJsonString() == "\"null\"" || ao["enum"]?.ToJsonString() == "[null]"); - if (o["oneOf"] is JsonArray alts && alts.Count == 2 && alts.Any(IsNull) && alts.FirstOrDefault(a => !IsNull(a)) is { } keep) - return NoNull(keep.DeepClone()); - var c = new JsonObject(); - foreach (var kv in o) - { - if (kv.Key is "nullable" or "x-nullable" or "x-null" or "required") continue; - if (kv.Key == "type" && kv.Value is JsonArray ta) { var f = ta.Where(x => x?.GetValue() != "null").ToList(); c["type"] = f.Count == 1 ? f[0]!.DeepClone() : new JsonArray(f.Select(x => x!.DeepClone()).ToArray()); continue; } - if (kv.Key == "allOf" && kv.Value is JsonArray aa && aa.Count == 1 && aa[0] is JsonObject a0 && a0.Count == 1 && a0.ContainsKey("x-nullable")) continue; - // OpenAPI 3.0 writes a type of several with null as anyOf [{type: integer}, {type: string}, {enum: [null]}] (row 260 of C21) - if (kv.Key == "anyOf" && kv.Value is JsonArray any && any.Any(IsNull)) { c["anyOf"] = new JsonArray(any.Where(x => !IsNull(x)).Select(NoNull).ToArray()); continue; } - if (kv.Key == "enum" && kv.Value is JsonArray en && en.Any(x => x is null)) { c["enum"] = new JsonArray(en.Where(x => x is not null).Select(x => x!.DeepClone()).ToArray()); continue; } - c[kv.Key] = NoNull(kv.Value); - } - return c; - case JsonArray a: var ca = new JsonArray(); foreach (var x in a) ca.Add(NoNull(x)); return ca; - default: return n?.DeepClone(); - } - } - - /// The key puts on a schema it resolved from a reference, when asked to mark them. - public const string ResolvedReference = "x-probe-resolved-reference"; - - public static JsonNode? Deep(JsonNode doc, JsonNode? n, HashSet? seen = null, bool mark = false) - { - seen ??= []; - switch (n) - { - case JsonObject o: - if (o["$ref"] is { } rf) - { - var key = rf.GetValue(); - if (!seen.Add(key)) return JsonValue.Create(""); - var target = doc; foreach (var seg in key.TrimStart('#', '/').Split('/')) target = target?[seg]; - var res = Deep(doc, target, seen, mark); seen.Remove(key); - if (mark && res is JsonObject resolved) resolved[ResolvedReference] = true; - return res; - } - var c = new JsonObject(); - foreach (var kv in o.OrderBy(k => k.Key, StringComparer.Ordinal)) - if (kv.Key is not ("title" or "description" or "example" or "examples")) - c[kv.Key] = kv.Key == "discriminator" ? Discriminator(o, kv.Value) : Deep(doc, kv.Value, seen, mark); - return c; - case JsonArray a: var ca = new JsonArray(); foreach (var x in a) ca.Add(Deep(doc, x, seen, mark)); return ca; - default: return n?.DeepClone(); - } - } - - /// - /// A discriminator's mapping names a component per value, and a component's name is made of the type's, which - /// differs between the two sides by construction (row 347 of C07: PB<TimeSpan> against - /// PB<V>). Each value names instead the branch of the same schema's anyOf or oneOf it maps to, whose - /// content compares; a target that is no branch stays as it is, and so still differs. - /// - static JsonNode? Discriminator(JsonObject schema, JsonNode? discriminator) - { - if (discriminator is not JsonObject d || d["mapping"] is not JsonObject mapping) return discriminator?.DeepClone(); - var branches = (schema["anyOf"] ?? schema["oneOf"]) as JsonArray; - var c = (JsonObject)d.DeepClone(); - var named = new JsonObject(); - foreach (var (value, target) in mapping.OrderBy(m => m.Key, StringComparer.Ordinal)) - { - var index = -1; - for (var i = 0; target is JsonValue && i < (branches?.Count ?? 0); i++) - if (branches![i]?["$ref"]?.GetValue() == target.GetValue()) { index = i; break; } - named[value] = index >= 0 ? $"branch {index}" : target?.DeepClone(); - } - c["mapping"] = named; - return c; - } -} diff --git a/tools/probe/corpus/harness/JsonFamily.cs b/tools/probe/corpus/harness/JsonFamily.cs deleted file mode 100644 index 8a1ef837..00000000 --- a/tools/probe/corpus/harness/JsonFamily.cs +++ /dev/null @@ -1,89 +0,0 @@ -// Shared code of the JSON family's claim checks (C01 decimal spelling, C10 Preserve guard, L06 off the wire): -// serializing a held shape, reading its tokens, rewriting one token, and reading what a document holds back. -using System.Text; -using System.Text.Json; -using Probes.Shapes; -namespace Probes.Json; - -/// One token of a JSON document: its kind, its raw text (a string's without quotes), its value as text, and its byte span. -public sealed record JTok(JsonTokenType Type, string Raw, string? Text, int Start, int End); - -public static class Jx -{ - public static (string? Json, Exception? Ex) Ser(Held h, JsonSerializerOptions o) - { try { return (JsonSerializer.Serialize(h.Value, h.Declared, o), null); } catch (Exception ex) { return (null, ex); } } - - public static (object? Value, Exception? Ex) De(string text, Type declared, JsonSerializerOptions o) - { try { return (JsonSerializer.Deserialize(text, declared, o), null); } catch (Exception ex) { return (null, ex); } } - - /// Every token of a document, with the byte span it takes (a string's and a property name's quotes included). - public static List Tokens(string json) - { - var bytes = Encoding.UTF8.GetBytes(json); - var rd = new Utf8JsonReader(bytes, new JsonReaderOptions { AllowTrailingCommas = true, CommentHandling = JsonCommentHandling.Skip }); - var list = new List(); - while (rd.Read()) - { - var start = (int)rd.TokenStartIndex; - switch (rd.TokenType) - { - case JsonTokenType.Number: - list.Add(new(rd.TokenType, Encoding.UTF8.GetString(rd.ValueSpan), Encoding.UTF8.GetString(rd.ValueSpan), start, start + rd.ValueSpan.Length)); break; - case JsonTokenType.String or JsonTokenType.PropertyName: - list.Add(new(rd.TokenType, Encoding.UTF8.GetString(rd.ValueSpan), rd.GetString(), start, start + rd.ValueSpan.Length + 2)); break; - default: - list.Add(new(rd.TokenType, "", null, start, start)); break; - } - } - return list; - } - - /// The document with the tokens replaced by (raw JSON text). - public static string Rewrite(string json, IEnumerable at, Func with) - { - var bytes = Encoding.UTF8.GetBytes(json); - var sb = new StringBuilder(); var pos = 0; - foreach (var t in at.OrderBy(t => t.Start)) - { - sb.Append(Encoding.UTF8.GetString(bytes, pos, t.Start - pos)); sb.Append(with(t)); pos = t.End; - } - sb.Append(Encoding.UTF8.GetString(bytes, pos, bytes.Length - pos)); - return sb.ToString(); - } - - /// Options that write what a value holds as the serializer writes it by default (a decimal with its scale, every field). - public static readonly JsonSerializerOptions Plain = new() { IncludeFields = true }; - - public static string Cut(string? s) => s is null ? "(null)" : s.Length > 220 ? s[..220] + "..." : s; - public static string Name(Type t) => t.IsGenericType ? t.Name[..t.Name.IndexOf('`')] + "<" + string.Join(", ", t.GetGenericArguments().Select(Name)) + ">" : t.Name; - /// A refusal's message with the type the other side holds named instead: the serializer names the shape it refused. - public static string Swap(string message, Type from, Type to) => message.Replace(from.ToString(), to.ToString()); - /// What a containing type holds: its property P or its field F. - public static object? Member(Type declared, object? holder) => - holder is null ? null : declared.GetProperty("P") is { } p ? p.GetValue(holder) : declared.GetField("F") is { } f ? f.GetValue(holder) : null; - public static string Ex(Exception e) => e.GetType().Name + ": " + Cut(e.Message); - - /// A copy of the options that writes every member: the ignore conditions reset, so a read has the member to read. - public static JsonSerializerOptions Full(JsonSerializerOptions o) => new(o) - { - DefaultIgnoreCondition = System.Text.Json.Serialization.JsonIgnoreCondition.Never, IgnoreReadOnlyProperties = false, IgnoreReadOnlyFields = false, - // A number written as a string reads back only with AllowReadingFromString; the document is input, so it says its other numbers plainly. - NumberHandling = o.NumberHandling & ~System.Text.Json.Serialization.JsonNumberHandling.WriteAsString, - }; -} - -/// -/// The row's json.* axes, applied by JsonRow: under json.TypeInfoResolver=source-generated-context the resolver is ProbeContext alone -/// (Shapes.g.cs), which lists every shape a row holds and its twin, so the setting runs as the row names it. A shape of the claim's own -/// the context does not list (L06's Option or Result) is the generator's gap: a harness error. -/// -public static class JCtx -{ - public static JsonSerializerOptions Apply(Row r, Entry e, params Type[] shapes) - { - var o = JsonRow.Apply(r, e).Options!; - var missing = JsonRow.Unlisted(r, shapes).Select(Jx.Name).ToList(); - if (missing.Count > 0) throw new InvalidOperationException("ProbeContext does not list (run tools/probe/generators/shapes.cs): " + string.Join(", ", missing)); - return o; - } -} diff --git a/tools/probe/corpus/harness/JsonFamilyShapes.g.cs b/tools/probe/corpus/harness/JsonFamilyShapes.g.cs deleted file mode 100644 index 5cb98433..00000000 --- a/tools/probe/corpus/harness/JsonFamilyShapes.g.cs +++ /dev/null @@ -1,493 +0,0 @@ -// by tools/probe/generators/json-family.cs stand-ins: L06's polymorphic stand-ins over Option and Result. Do not edit. -#nullable disable -#pragma warning disable CS8618, CS1591, CMTK0005, CMTK0006 -using CodoMetis.TypeKit; -using Probes.Shapes; -namespace Probes.Json; - -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_string_O), "d")] public abstract class L06PB_V_record_struct_string_O { } -public sealed class L06PD_V_record_struct_string_O : L06PB_V_record_struct_string_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_string_RTE), "d")] public abstract class L06PB_V_record_struct_string_RTE { } -public sealed class L06PD_V_record_struct_string_RTE : L06PB_V_record_struct_string_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_string_RE), "d")] public abstract class L06PB_V_record_struct_string_RE { } -public sealed class L06PD_V_record_struct_string_RE : L06PB_V_record_struct_string_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_Guid_O), "d")] public abstract class L06PB_V_record_struct_Guid_O { } -public sealed class L06PD_V_record_struct_Guid_O : L06PB_V_record_struct_Guid_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_Guid_RTE), "d")] public abstract class L06PB_V_record_struct_Guid_RTE { } -public sealed class L06PD_V_record_struct_Guid_RTE : L06PB_V_record_struct_Guid_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_Guid_RE), "d")] public abstract class L06PB_V_record_struct_Guid_RE { } -public sealed class L06PD_V_record_struct_Guid_RE : L06PB_V_record_struct_Guid_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_int_O), "d")] public abstract class L06PB_V_record_struct_int_O { } -public sealed class L06PD_V_record_struct_int_O : L06PB_V_record_struct_int_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_int_RTE), "d")] public abstract class L06PB_V_record_struct_int_RTE { } -public sealed class L06PD_V_record_struct_int_RTE : L06PB_V_record_struct_int_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_int_RE), "d")] public abstract class L06PB_V_record_struct_int_RE { } -public sealed class L06PD_V_record_struct_int_RE : L06PB_V_record_struct_int_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_long_O), "d")] public abstract class L06PB_V_record_struct_long_O { } -public sealed class L06PD_V_record_struct_long_O : L06PB_V_record_struct_long_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_long_RTE), "d")] public abstract class L06PB_V_record_struct_long_RTE { } -public sealed class L06PD_V_record_struct_long_RTE : L06PB_V_record_struct_long_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_long_RE), "d")] public abstract class L06PB_V_record_struct_long_RE { } -public sealed class L06PD_V_record_struct_long_RE : L06PB_V_record_struct_long_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_byte_O), "d")] public abstract class L06PB_V_record_struct_byte_O { } -public sealed class L06PD_V_record_struct_byte_O : L06PB_V_record_struct_byte_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_byte_RTE), "d")] public abstract class L06PB_V_record_struct_byte_RTE { } -public sealed class L06PD_V_record_struct_byte_RTE : L06PB_V_record_struct_byte_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_byte_RE), "d")] public abstract class L06PB_V_record_struct_byte_RE { } -public sealed class L06PD_V_record_struct_byte_RE : L06PB_V_record_struct_byte_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_ulong_O), "d")] public abstract class L06PB_V_record_struct_ulong_O { } -public sealed class L06PD_V_record_struct_ulong_O : L06PB_V_record_struct_ulong_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_ulong_RTE), "d")] public abstract class L06PB_V_record_struct_ulong_RTE { } -public sealed class L06PD_V_record_struct_ulong_RTE : L06PB_V_record_struct_ulong_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_ulong_RE), "d")] public abstract class L06PB_V_record_struct_ulong_RE { } -public sealed class L06PD_V_record_struct_ulong_RE : L06PB_V_record_struct_ulong_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_decimal_O), "d")] public abstract class L06PB_V_record_struct_decimal_O { } -public sealed class L06PD_V_record_struct_decimal_O : L06PB_V_record_struct_decimal_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_decimal_RTE), "d")] public abstract class L06PB_V_record_struct_decimal_RTE { } -public sealed class L06PD_V_record_struct_decimal_RTE : L06PB_V_record_struct_decimal_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_decimal_RE), "d")] public abstract class L06PB_V_record_struct_decimal_RE { } -public sealed class L06PD_V_record_struct_decimal_RE : L06PB_V_record_struct_decimal_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_double_O), "d")] public abstract class L06PB_V_record_struct_double_O { } -public sealed class L06PD_V_record_struct_double_O : L06PB_V_record_struct_double_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_double_RTE), "d")] public abstract class L06PB_V_record_struct_double_RTE { } -public sealed class L06PD_V_record_struct_double_RTE : L06PB_V_record_struct_double_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_double_RE), "d")] public abstract class L06PB_V_record_struct_double_RE { } -public sealed class L06PD_V_record_struct_double_RE : L06PB_V_record_struct_double_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_bool_O), "d")] public abstract class L06PB_V_record_struct_bool_O { } -public sealed class L06PD_V_record_struct_bool_O : L06PB_V_record_struct_bool_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_bool_RTE), "d")] public abstract class L06PB_V_record_struct_bool_RTE { } -public sealed class L06PD_V_record_struct_bool_RTE : L06PB_V_record_struct_bool_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_bool_RE), "d")] public abstract class L06PB_V_record_struct_bool_RE { } -public sealed class L06PD_V_record_struct_bool_RE : L06PB_V_record_struct_bool_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_char_O), "d")] public abstract class L06PB_V_record_struct_char_O { } -public sealed class L06PD_V_record_struct_char_O : L06PB_V_record_struct_char_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_char_RTE), "d")] public abstract class L06PB_V_record_struct_char_RTE { } -public sealed class L06PD_V_record_struct_char_RTE : L06PB_V_record_struct_char_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_char_RE), "d")] public abstract class L06PB_V_record_struct_char_RE { } -public sealed class L06PD_V_record_struct_char_RE : L06PB_V_record_struct_char_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateTime_O), "d")] public abstract class L06PB_V_record_struct_DateTime_O { } -public sealed class L06PD_V_record_struct_DateTime_O : L06PB_V_record_struct_DateTime_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateTime_RTE), "d")] public abstract class L06PB_V_record_struct_DateTime_RTE { } -public sealed class L06PD_V_record_struct_DateTime_RTE : L06PB_V_record_struct_DateTime_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateTime_RE), "d")] public abstract class L06PB_V_record_struct_DateTime_RE { } -public sealed class L06PD_V_record_struct_DateTime_RE : L06PB_V_record_struct_DateTime_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateTimeOffset_O), "d")] public abstract class L06PB_V_record_struct_DateTimeOffset_O { } -public sealed class L06PD_V_record_struct_DateTimeOffset_O : L06PB_V_record_struct_DateTimeOffset_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateTimeOffset_RTE), "d")] public abstract class L06PB_V_record_struct_DateTimeOffset_RTE { } -public sealed class L06PD_V_record_struct_DateTimeOffset_RTE : L06PB_V_record_struct_DateTimeOffset_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateTimeOffset_RE), "d")] public abstract class L06PB_V_record_struct_DateTimeOffset_RE { } -public sealed class L06PD_V_record_struct_DateTimeOffset_RE : L06PB_V_record_struct_DateTimeOffset_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateOnly_O), "d")] public abstract class L06PB_V_record_struct_DateOnly_O { } -public sealed class L06PD_V_record_struct_DateOnly_O : L06PB_V_record_struct_DateOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateOnly_RTE), "d")] public abstract class L06PB_V_record_struct_DateOnly_RTE { } -public sealed class L06PD_V_record_struct_DateOnly_RTE : L06PB_V_record_struct_DateOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_DateOnly_RE), "d")] public abstract class L06PB_V_record_struct_DateOnly_RE { } -public sealed class L06PD_V_record_struct_DateOnly_RE : L06PB_V_record_struct_DateOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_TimeOnly_O), "d")] public abstract class L06PB_V_record_struct_TimeOnly_O { } -public sealed class L06PD_V_record_struct_TimeOnly_O : L06PB_V_record_struct_TimeOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_TimeOnly_RTE), "d")] public abstract class L06PB_V_record_struct_TimeOnly_RTE { } -public sealed class L06PD_V_record_struct_TimeOnly_RTE : L06PB_V_record_struct_TimeOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_TimeOnly_RE), "d")] public abstract class L06PB_V_record_struct_TimeOnly_RE { } -public sealed class L06PD_V_record_struct_TimeOnly_RE : L06PB_V_record_struct_TimeOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_TimeSpan_O), "d")] public abstract class L06PB_V_record_struct_TimeSpan_O { } -public sealed class L06PD_V_record_struct_TimeSpan_O : L06PB_V_record_struct_TimeSpan_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_TimeSpan_RTE), "d")] public abstract class L06PB_V_record_struct_TimeSpan_RTE { } -public sealed class L06PD_V_record_struct_TimeSpan_RTE : L06PB_V_record_struct_TimeSpan_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_TimeSpan_RE), "d")] public abstract class L06PB_V_record_struct_TimeSpan_RE { } -public sealed class L06PD_V_record_struct_TimeSpan_RE : L06PB_V_record_struct_TimeSpan_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_enum_O), "d")] public abstract class L06PB_V_record_struct_enum_O { } -public sealed class L06PD_V_record_struct_enum_O : L06PB_V_record_struct_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_enum_RTE), "d")] public abstract class L06PB_V_record_struct_enum_RTE { } -public sealed class L06PD_V_record_struct_enum_RTE : L06PB_V_record_struct_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_enum_RE), "d")] public abstract class L06PB_V_record_struct_enum_RE { } -public sealed class L06PD_V_record_struct_enum_RE : L06PB_V_record_struct_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_flags_enum_O), "d")] public abstract class L06PB_V_record_struct_flags_enum_O { } -public sealed class L06PD_V_record_struct_flags_enum_O : L06PB_V_record_struct_flags_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_flags_enum_RTE), "d")] public abstract class L06PB_V_record_struct_flags_enum_RTE { } -public sealed class L06PD_V_record_struct_flags_enum_RTE : L06PB_V_record_struct_flags_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_flags_enum_RE), "d")] public abstract class L06PB_V_record_struct_flags_enum_RE { } -public sealed class L06PD_V_record_struct_flags_enum_RE : L06PB_V_record_struct_flags_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_Uri_O), "d")] public abstract class L06PB_V_record_struct_Uri_O { } -public sealed class L06PD_V_record_struct_Uri_O : L06PB_V_record_struct_Uri_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_Uri_RTE), "d")] public abstract class L06PB_V_record_struct_Uri_RTE { } -public sealed class L06PD_V_record_struct_Uri_RTE : L06PB_V_record_struct_Uri_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_Uri_RE), "d")] public abstract class L06PB_V_record_struct_Uri_RE { } -public sealed class L06PD_V_record_struct_Uri_RE : L06PB_V_record_struct_Uri_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_NodaTime_Instant_O), "d")] public abstract class L06PB_V_record_struct_NodaTime_Instant_O { } -public sealed class L06PD_V_record_struct_NodaTime_Instant_O : L06PB_V_record_struct_NodaTime_Instant_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_NodaTime_Instant_RTE), "d")] public abstract class L06PB_V_record_struct_NodaTime_Instant_RTE { } -public sealed class L06PD_V_record_struct_NodaTime_Instant_RTE : L06PB_V_record_struct_NodaTime_Instant_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_NodaTime_Instant_RE), "d")] public abstract class L06PB_V_record_struct_NodaTime_Instant_RE { } -public sealed class L06PD_V_record_struct_NodaTime_Instant_RE : L06PB_V_record_struct_NodaTime_Instant_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_own_record_class_O), "d")] public abstract class L06PB_V_record_struct_own_record_class_O { } -public sealed class L06PD_V_record_struct_own_record_class_O : L06PB_V_record_struct_own_record_class_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_own_record_class_RTE), "d")] public abstract class L06PB_V_record_struct_own_record_class_RTE { } -public sealed class L06PD_V_record_struct_own_record_class_RTE : L06PB_V_record_struct_own_record_class_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_record_struct_own_record_class_RE), "d")] public abstract class L06PB_V_record_struct_own_record_class_RE { } -public sealed class L06PD_V_record_struct_own_record_class_RE : L06PB_V_record_struct_own_record_class_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_string_O), "d")] public abstract class L06PB_V_sealed_record_class_string_O { } -public sealed class L06PD_V_sealed_record_class_string_O : L06PB_V_sealed_record_class_string_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_string_RTE), "d")] public abstract class L06PB_V_sealed_record_class_string_RTE { } -public sealed class L06PD_V_sealed_record_class_string_RTE : L06PB_V_sealed_record_class_string_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_string_RE), "d")] public abstract class L06PB_V_sealed_record_class_string_RE { } -public sealed class L06PD_V_sealed_record_class_string_RE : L06PB_V_sealed_record_class_string_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_Guid_O), "d")] public abstract class L06PB_V_sealed_record_class_Guid_O { } -public sealed class L06PD_V_sealed_record_class_Guid_O : L06PB_V_sealed_record_class_Guid_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_Guid_RTE), "d")] public abstract class L06PB_V_sealed_record_class_Guid_RTE { } -public sealed class L06PD_V_sealed_record_class_Guid_RTE : L06PB_V_sealed_record_class_Guid_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_Guid_RE), "d")] public abstract class L06PB_V_sealed_record_class_Guid_RE { } -public sealed class L06PD_V_sealed_record_class_Guid_RE : L06PB_V_sealed_record_class_Guid_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_int_O), "d")] public abstract class L06PB_V_sealed_record_class_int_O { } -public sealed class L06PD_V_sealed_record_class_int_O : L06PB_V_sealed_record_class_int_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_int_RTE), "d")] public abstract class L06PB_V_sealed_record_class_int_RTE { } -public sealed class L06PD_V_sealed_record_class_int_RTE : L06PB_V_sealed_record_class_int_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_int_RE), "d")] public abstract class L06PB_V_sealed_record_class_int_RE { } -public sealed class L06PD_V_sealed_record_class_int_RE : L06PB_V_sealed_record_class_int_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_long_O), "d")] public abstract class L06PB_V_sealed_record_class_long_O { } -public sealed class L06PD_V_sealed_record_class_long_O : L06PB_V_sealed_record_class_long_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_long_RTE), "d")] public abstract class L06PB_V_sealed_record_class_long_RTE { } -public sealed class L06PD_V_sealed_record_class_long_RTE : L06PB_V_sealed_record_class_long_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_long_RE), "d")] public abstract class L06PB_V_sealed_record_class_long_RE { } -public sealed class L06PD_V_sealed_record_class_long_RE : L06PB_V_sealed_record_class_long_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_byte_O), "d")] public abstract class L06PB_V_sealed_record_class_byte_O { } -public sealed class L06PD_V_sealed_record_class_byte_O : L06PB_V_sealed_record_class_byte_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_byte_RTE), "d")] public abstract class L06PB_V_sealed_record_class_byte_RTE { } -public sealed class L06PD_V_sealed_record_class_byte_RTE : L06PB_V_sealed_record_class_byte_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_byte_RE), "d")] public abstract class L06PB_V_sealed_record_class_byte_RE { } -public sealed class L06PD_V_sealed_record_class_byte_RE : L06PB_V_sealed_record_class_byte_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_ulong_O), "d")] public abstract class L06PB_V_sealed_record_class_ulong_O { } -public sealed class L06PD_V_sealed_record_class_ulong_O : L06PB_V_sealed_record_class_ulong_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_ulong_RTE), "d")] public abstract class L06PB_V_sealed_record_class_ulong_RTE { } -public sealed class L06PD_V_sealed_record_class_ulong_RTE : L06PB_V_sealed_record_class_ulong_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_ulong_RE), "d")] public abstract class L06PB_V_sealed_record_class_ulong_RE { } -public sealed class L06PD_V_sealed_record_class_ulong_RE : L06PB_V_sealed_record_class_ulong_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_decimal_O), "d")] public abstract class L06PB_V_sealed_record_class_decimal_O { } -public sealed class L06PD_V_sealed_record_class_decimal_O : L06PB_V_sealed_record_class_decimal_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_decimal_RTE), "d")] public abstract class L06PB_V_sealed_record_class_decimal_RTE { } -public sealed class L06PD_V_sealed_record_class_decimal_RTE : L06PB_V_sealed_record_class_decimal_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_decimal_RE), "d")] public abstract class L06PB_V_sealed_record_class_decimal_RE { } -public sealed class L06PD_V_sealed_record_class_decimal_RE : L06PB_V_sealed_record_class_decimal_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_double_O), "d")] public abstract class L06PB_V_sealed_record_class_double_O { } -public sealed class L06PD_V_sealed_record_class_double_O : L06PB_V_sealed_record_class_double_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_double_RTE), "d")] public abstract class L06PB_V_sealed_record_class_double_RTE { } -public sealed class L06PD_V_sealed_record_class_double_RTE : L06PB_V_sealed_record_class_double_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_double_RE), "d")] public abstract class L06PB_V_sealed_record_class_double_RE { } -public sealed class L06PD_V_sealed_record_class_double_RE : L06PB_V_sealed_record_class_double_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_bool_O), "d")] public abstract class L06PB_V_sealed_record_class_bool_O { } -public sealed class L06PD_V_sealed_record_class_bool_O : L06PB_V_sealed_record_class_bool_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_bool_RTE), "d")] public abstract class L06PB_V_sealed_record_class_bool_RTE { } -public sealed class L06PD_V_sealed_record_class_bool_RTE : L06PB_V_sealed_record_class_bool_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_bool_RE), "d")] public abstract class L06PB_V_sealed_record_class_bool_RE { } -public sealed class L06PD_V_sealed_record_class_bool_RE : L06PB_V_sealed_record_class_bool_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_char_O), "d")] public abstract class L06PB_V_sealed_record_class_char_O { } -public sealed class L06PD_V_sealed_record_class_char_O : L06PB_V_sealed_record_class_char_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_char_RTE), "d")] public abstract class L06PB_V_sealed_record_class_char_RTE { } -public sealed class L06PD_V_sealed_record_class_char_RTE : L06PB_V_sealed_record_class_char_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_char_RE), "d")] public abstract class L06PB_V_sealed_record_class_char_RE { } -public sealed class L06PD_V_sealed_record_class_char_RE : L06PB_V_sealed_record_class_char_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateTime_O), "d")] public abstract class L06PB_V_sealed_record_class_DateTime_O { } -public sealed class L06PD_V_sealed_record_class_DateTime_O : L06PB_V_sealed_record_class_DateTime_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateTime_RTE), "d")] public abstract class L06PB_V_sealed_record_class_DateTime_RTE { } -public sealed class L06PD_V_sealed_record_class_DateTime_RTE : L06PB_V_sealed_record_class_DateTime_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateTime_RE), "d")] public abstract class L06PB_V_sealed_record_class_DateTime_RE { } -public sealed class L06PD_V_sealed_record_class_DateTime_RE : L06PB_V_sealed_record_class_DateTime_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateTimeOffset_O), "d")] public abstract class L06PB_V_sealed_record_class_DateTimeOffset_O { } -public sealed class L06PD_V_sealed_record_class_DateTimeOffset_O : L06PB_V_sealed_record_class_DateTimeOffset_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateTimeOffset_RTE), "d")] public abstract class L06PB_V_sealed_record_class_DateTimeOffset_RTE { } -public sealed class L06PD_V_sealed_record_class_DateTimeOffset_RTE : L06PB_V_sealed_record_class_DateTimeOffset_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateTimeOffset_RE), "d")] public abstract class L06PB_V_sealed_record_class_DateTimeOffset_RE { } -public sealed class L06PD_V_sealed_record_class_DateTimeOffset_RE : L06PB_V_sealed_record_class_DateTimeOffset_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateOnly_O), "d")] public abstract class L06PB_V_sealed_record_class_DateOnly_O { } -public sealed class L06PD_V_sealed_record_class_DateOnly_O : L06PB_V_sealed_record_class_DateOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateOnly_RTE), "d")] public abstract class L06PB_V_sealed_record_class_DateOnly_RTE { } -public sealed class L06PD_V_sealed_record_class_DateOnly_RTE : L06PB_V_sealed_record_class_DateOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_DateOnly_RE), "d")] public abstract class L06PB_V_sealed_record_class_DateOnly_RE { } -public sealed class L06PD_V_sealed_record_class_DateOnly_RE : L06PB_V_sealed_record_class_DateOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_TimeOnly_O), "d")] public abstract class L06PB_V_sealed_record_class_TimeOnly_O { } -public sealed class L06PD_V_sealed_record_class_TimeOnly_O : L06PB_V_sealed_record_class_TimeOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_TimeOnly_RTE), "d")] public abstract class L06PB_V_sealed_record_class_TimeOnly_RTE { } -public sealed class L06PD_V_sealed_record_class_TimeOnly_RTE : L06PB_V_sealed_record_class_TimeOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_TimeOnly_RE), "d")] public abstract class L06PB_V_sealed_record_class_TimeOnly_RE { } -public sealed class L06PD_V_sealed_record_class_TimeOnly_RE : L06PB_V_sealed_record_class_TimeOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_TimeSpan_O), "d")] public abstract class L06PB_V_sealed_record_class_TimeSpan_O { } -public sealed class L06PD_V_sealed_record_class_TimeSpan_O : L06PB_V_sealed_record_class_TimeSpan_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_TimeSpan_RTE), "d")] public abstract class L06PB_V_sealed_record_class_TimeSpan_RTE { } -public sealed class L06PD_V_sealed_record_class_TimeSpan_RTE : L06PB_V_sealed_record_class_TimeSpan_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_TimeSpan_RE), "d")] public abstract class L06PB_V_sealed_record_class_TimeSpan_RE { } -public sealed class L06PD_V_sealed_record_class_TimeSpan_RE : L06PB_V_sealed_record_class_TimeSpan_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_enum_O), "d")] public abstract class L06PB_V_sealed_record_class_enum_O { } -public sealed class L06PD_V_sealed_record_class_enum_O : L06PB_V_sealed_record_class_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_enum_RTE), "d")] public abstract class L06PB_V_sealed_record_class_enum_RTE { } -public sealed class L06PD_V_sealed_record_class_enum_RTE : L06PB_V_sealed_record_class_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_enum_RE), "d")] public abstract class L06PB_V_sealed_record_class_enum_RE { } -public sealed class L06PD_V_sealed_record_class_enum_RE : L06PB_V_sealed_record_class_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_flags_enum_O), "d")] public abstract class L06PB_V_sealed_record_class_flags_enum_O { } -public sealed class L06PD_V_sealed_record_class_flags_enum_O : L06PB_V_sealed_record_class_flags_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_flags_enum_RTE), "d")] public abstract class L06PB_V_sealed_record_class_flags_enum_RTE { } -public sealed class L06PD_V_sealed_record_class_flags_enum_RTE : L06PB_V_sealed_record_class_flags_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_flags_enum_RE), "d")] public abstract class L06PB_V_sealed_record_class_flags_enum_RE { } -public sealed class L06PD_V_sealed_record_class_flags_enum_RE : L06PB_V_sealed_record_class_flags_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_Uri_O), "d")] public abstract class L06PB_V_sealed_record_class_Uri_O { } -public sealed class L06PD_V_sealed_record_class_Uri_O : L06PB_V_sealed_record_class_Uri_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_Uri_RTE), "d")] public abstract class L06PB_V_sealed_record_class_Uri_RTE { } -public sealed class L06PD_V_sealed_record_class_Uri_RTE : L06PB_V_sealed_record_class_Uri_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_Uri_RE), "d")] public abstract class L06PB_V_sealed_record_class_Uri_RE { } -public sealed class L06PD_V_sealed_record_class_Uri_RE : L06PB_V_sealed_record_class_Uri_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_NodaTime_Instant_O), "d")] public abstract class L06PB_V_sealed_record_class_NodaTime_Instant_O { } -public sealed class L06PD_V_sealed_record_class_NodaTime_Instant_O : L06PB_V_sealed_record_class_NodaTime_Instant_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_NodaTime_Instant_RTE), "d")] public abstract class L06PB_V_sealed_record_class_NodaTime_Instant_RTE { } -public sealed class L06PD_V_sealed_record_class_NodaTime_Instant_RTE : L06PB_V_sealed_record_class_NodaTime_Instant_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_NodaTime_Instant_RE), "d")] public abstract class L06PB_V_sealed_record_class_NodaTime_Instant_RE { } -public sealed class L06PD_V_sealed_record_class_NodaTime_Instant_RE : L06PB_V_sealed_record_class_NodaTime_Instant_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_own_record_class_O), "d")] public abstract class L06PB_V_sealed_record_class_own_record_class_O { } -public sealed class L06PD_V_sealed_record_class_own_record_class_O : L06PB_V_sealed_record_class_own_record_class_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_own_record_class_RTE), "d")] public abstract class L06PB_V_sealed_record_class_own_record_class_RTE { } -public sealed class L06PD_V_sealed_record_class_own_record_class_RTE : L06PB_V_sealed_record_class_own_record_class_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_sealed_record_class_own_record_class_RE), "d")] public abstract class L06PB_V_sealed_record_class_own_record_class_RE { } -public sealed class L06PD_V_sealed_record_class_own_record_class_RE : L06PB_V_sealed_record_class_own_record_class_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_string_O), "d")] public abstract class L06PB_V_validated_struct_string_O { } -public sealed class L06PD_V_validated_struct_string_O : L06PB_V_validated_struct_string_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_string_RTE), "d")] public abstract class L06PB_V_validated_struct_string_RTE { } -public sealed class L06PD_V_validated_struct_string_RTE : L06PB_V_validated_struct_string_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_string_RE), "d")] public abstract class L06PB_V_validated_struct_string_RE { } -public sealed class L06PD_V_validated_struct_string_RE : L06PB_V_validated_struct_string_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_Guid_O), "d")] public abstract class L06PB_V_validated_struct_Guid_O { } -public sealed class L06PD_V_validated_struct_Guid_O : L06PB_V_validated_struct_Guid_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_Guid_RTE), "d")] public abstract class L06PB_V_validated_struct_Guid_RTE { } -public sealed class L06PD_V_validated_struct_Guid_RTE : L06PB_V_validated_struct_Guid_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_Guid_RE), "d")] public abstract class L06PB_V_validated_struct_Guid_RE { } -public sealed class L06PD_V_validated_struct_Guid_RE : L06PB_V_validated_struct_Guid_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_int_O), "d")] public abstract class L06PB_V_validated_struct_int_O { } -public sealed class L06PD_V_validated_struct_int_O : L06PB_V_validated_struct_int_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_int_RTE), "d")] public abstract class L06PB_V_validated_struct_int_RTE { } -public sealed class L06PD_V_validated_struct_int_RTE : L06PB_V_validated_struct_int_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_int_RE), "d")] public abstract class L06PB_V_validated_struct_int_RE { } -public sealed class L06PD_V_validated_struct_int_RE : L06PB_V_validated_struct_int_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_long_O), "d")] public abstract class L06PB_V_validated_struct_long_O { } -public sealed class L06PD_V_validated_struct_long_O : L06PB_V_validated_struct_long_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_long_RTE), "d")] public abstract class L06PB_V_validated_struct_long_RTE { } -public sealed class L06PD_V_validated_struct_long_RTE : L06PB_V_validated_struct_long_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_long_RE), "d")] public abstract class L06PB_V_validated_struct_long_RE { } -public sealed class L06PD_V_validated_struct_long_RE : L06PB_V_validated_struct_long_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_byte_O), "d")] public abstract class L06PB_V_validated_struct_byte_O { } -public sealed class L06PD_V_validated_struct_byte_O : L06PB_V_validated_struct_byte_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_byte_RTE), "d")] public abstract class L06PB_V_validated_struct_byte_RTE { } -public sealed class L06PD_V_validated_struct_byte_RTE : L06PB_V_validated_struct_byte_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_byte_RE), "d")] public abstract class L06PB_V_validated_struct_byte_RE { } -public sealed class L06PD_V_validated_struct_byte_RE : L06PB_V_validated_struct_byte_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_ulong_O), "d")] public abstract class L06PB_V_validated_struct_ulong_O { } -public sealed class L06PD_V_validated_struct_ulong_O : L06PB_V_validated_struct_ulong_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_ulong_RTE), "d")] public abstract class L06PB_V_validated_struct_ulong_RTE { } -public sealed class L06PD_V_validated_struct_ulong_RTE : L06PB_V_validated_struct_ulong_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_ulong_RE), "d")] public abstract class L06PB_V_validated_struct_ulong_RE { } -public sealed class L06PD_V_validated_struct_ulong_RE : L06PB_V_validated_struct_ulong_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_decimal_O), "d")] public abstract class L06PB_V_validated_struct_decimal_O { } -public sealed class L06PD_V_validated_struct_decimal_O : L06PB_V_validated_struct_decimal_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_decimal_RTE), "d")] public abstract class L06PB_V_validated_struct_decimal_RTE { } -public sealed class L06PD_V_validated_struct_decimal_RTE : L06PB_V_validated_struct_decimal_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_decimal_RE), "d")] public abstract class L06PB_V_validated_struct_decimal_RE { } -public sealed class L06PD_V_validated_struct_decimal_RE : L06PB_V_validated_struct_decimal_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_double_O), "d")] public abstract class L06PB_V_validated_struct_double_O { } -public sealed class L06PD_V_validated_struct_double_O : L06PB_V_validated_struct_double_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_double_RTE), "d")] public abstract class L06PB_V_validated_struct_double_RTE { } -public sealed class L06PD_V_validated_struct_double_RTE : L06PB_V_validated_struct_double_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_double_RE), "d")] public abstract class L06PB_V_validated_struct_double_RE { } -public sealed class L06PD_V_validated_struct_double_RE : L06PB_V_validated_struct_double_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_bool_O), "d")] public abstract class L06PB_V_validated_struct_bool_O { } -public sealed class L06PD_V_validated_struct_bool_O : L06PB_V_validated_struct_bool_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_bool_RTE), "d")] public abstract class L06PB_V_validated_struct_bool_RTE { } -public sealed class L06PD_V_validated_struct_bool_RTE : L06PB_V_validated_struct_bool_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_bool_RE), "d")] public abstract class L06PB_V_validated_struct_bool_RE { } -public sealed class L06PD_V_validated_struct_bool_RE : L06PB_V_validated_struct_bool_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_char_O), "d")] public abstract class L06PB_V_validated_struct_char_O { } -public sealed class L06PD_V_validated_struct_char_O : L06PB_V_validated_struct_char_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_char_RTE), "d")] public abstract class L06PB_V_validated_struct_char_RTE { } -public sealed class L06PD_V_validated_struct_char_RTE : L06PB_V_validated_struct_char_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_char_RE), "d")] public abstract class L06PB_V_validated_struct_char_RE { } -public sealed class L06PD_V_validated_struct_char_RE : L06PB_V_validated_struct_char_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateTime_O), "d")] public abstract class L06PB_V_validated_struct_DateTime_O { } -public sealed class L06PD_V_validated_struct_DateTime_O : L06PB_V_validated_struct_DateTime_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateTime_RTE), "d")] public abstract class L06PB_V_validated_struct_DateTime_RTE { } -public sealed class L06PD_V_validated_struct_DateTime_RTE : L06PB_V_validated_struct_DateTime_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateTime_RE), "d")] public abstract class L06PB_V_validated_struct_DateTime_RE { } -public sealed class L06PD_V_validated_struct_DateTime_RE : L06PB_V_validated_struct_DateTime_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateTimeOffset_O), "d")] public abstract class L06PB_V_validated_struct_DateTimeOffset_O { } -public sealed class L06PD_V_validated_struct_DateTimeOffset_O : L06PB_V_validated_struct_DateTimeOffset_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateTimeOffset_RTE), "d")] public abstract class L06PB_V_validated_struct_DateTimeOffset_RTE { } -public sealed class L06PD_V_validated_struct_DateTimeOffset_RTE : L06PB_V_validated_struct_DateTimeOffset_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateTimeOffset_RE), "d")] public abstract class L06PB_V_validated_struct_DateTimeOffset_RE { } -public sealed class L06PD_V_validated_struct_DateTimeOffset_RE : L06PB_V_validated_struct_DateTimeOffset_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateOnly_O), "d")] public abstract class L06PB_V_validated_struct_DateOnly_O { } -public sealed class L06PD_V_validated_struct_DateOnly_O : L06PB_V_validated_struct_DateOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateOnly_RTE), "d")] public abstract class L06PB_V_validated_struct_DateOnly_RTE { } -public sealed class L06PD_V_validated_struct_DateOnly_RTE : L06PB_V_validated_struct_DateOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_DateOnly_RE), "d")] public abstract class L06PB_V_validated_struct_DateOnly_RE { } -public sealed class L06PD_V_validated_struct_DateOnly_RE : L06PB_V_validated_struct_DateOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_TimeOnly_O), "d")] public abstract class L06PB_V_validated_struct_TimeOnly_O { } -public sealed class L06PD_V_validated_struct_TimeOnly_O : L06PB_V_validated_struct_TimeOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_TimeOnly_RTE), "d")] public abstract class L06PB_V_validated_struct_TimeOnly_RTE { } -public sealed class L06PD_V_validated_struct_TimeOnly_RTE : L06PB_V_validated_struct_TimeOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_TimeOnly_RE), "d")] public abstract class L06PB_V_validated_struct_TimeOnly_RE { } -public sealed class L06PD_V_validated_struct_TimeOnly_RE : L06PB_V_validated_struct_TimeOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_TimeSpan_O), "d")] public abstract class L06PB_V_validated_struct_TimeSpan_O { } -public sealed class L06PD_V_validated_struct_TimeSpan_O : L06PB_V_validated_struct_TimeSpan_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_TimeSpan_RTE), "d")] public abstract class L06PB_V_validated_struct_TimeSpan_RTE { } -public sealed class L06PD_V_validated_struct_TimeSpan_RTE : L06PB_V_validated_struct_TimeSpan_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_TimeSpan_RE), "d")] public abstract class L06PB_V_validated_struct_TimeSpan_RE { } -public sealed class L06PD_V_validated_struct_TimeSpan_RE : L06PB_V_validated_struct_TimeSpan_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_enum_O), "d")] public abstract class L06PB_V_validated_struct_enum_O { } -public sealed class L06PD_V_validated_struct_enum_O : L06PB_V_validated_struct_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_enum_RTE), "d")] public abstract class L06PB_V_validated_struct_enum_RTE { } -public sealed class L06PD_V_validated_struct_enum_RTE : L06PB_V_validated_struct_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_enum_RE), "d")] public abstract class L06PB_V_validated_struct_enum_RE { } -public sealed class L06PD_V_validated_struct_enum_RE : L06PB_V_validated_struct_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_flags_enum_O), "d")] public abstract class L06PB_V_validated_struct_flags_enum_O { } -public sealed class L06PD_V_validated_struct_flags_enum_O : L06PB_V_validated_struct_flags_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_flags_enum_RTE), "d")] public abstract class L06PB_V_validated_struct_flags_enum_RTE { } -public sealed class L06PD_V_validated_struct_flags_enum_RTE : L06PB_V_validated_struct_flags_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_flags_enum_RE), "d")] public abstract class L06PB_V_validated_struct_flags_enum_RE { } -public sealed class L06PD_V_validated_struct_flags_enum_RE : L06PB_V_validated_struct_flags_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_Uri_O), "d")] public abstract class L06PB_V_validated_struct_Uri_O { } -public sealed class L06PD_V_validated_struct_Uri_O : L06PB_V_validated_struct_Uri_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_Uri_RTE), "d")] public abstract class L06PB_V_validated_struct_Uri_RTE { } -public sealed class L06PD_V_validated_struct_Uri_RTE : L06PB_V_validated_struct_Uri_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_Uri_RE), "d")] public abstract class L06PB_V_validated_struct_Uri_RE { } -public sealed class L06PD_V_validated_struct_Uri_RE : L06PB_V_validated_struct_Uri_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_NodaTime_Instant_O), "d")] public abstract class L06PB_V_validated_struct_NodaTime_Instant_O { } -public sealed class L06PD_V_validated_struct_NodaTime_Instant_O : L06PB_V_validated_struct_NodaTime_Instant_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_NodaTime_Instant_RTE), "d")] public abstract class L06PB_V_validated_struct_NodaTime_Instant_RTE { } -public sealed class L06PD_V_validated_struct_NodaTime_Instant_RTE : L06PB_V_validated_struct_NodaTime_Instant_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_NodaTime_Instant_RE), "d")] public abstract class L06PB_V_validated_struct_NodaTime_Instant_RE { } -public sealed class L06PD_V_validated_struct_NodaTime_Instant_RE : L06PB_V_validated_struct_NodaTime_Instant_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_own_record_class_O), "d")] public abstract class L06PB_V_validated_struct_own_record_class_O { } -public sealed class L06PD_V_validated_struct_own_record_class_O : L06PB_V_validated_struct_own_record_class_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_own_record_class_RTE), "d")] public abstract class L06PB_V_validated_struct_own_record_class_RTE { } -public sealed class L06PD_V_validated_struct_own_record_class_RTE : L06PB_V_validated_struct_own_record_class_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_struct_own_record_class_RE), "d")] public abstract class L06PB_V_validated_struct_own_record_class_RE { } -public sealed class L06PD_V_validated_struct_own_record_class_RE : L06PB_V_validated_struct_own_record_class_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_string_O), "d")] public abstract class L06PB_V_validated_class_string_O { } -public sealed class L06PD_V_validated_class_string_O : L06PB_V_validated_class_string_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_string_RTE), "d")] public abstract class L06PB_V_validated_class_string_RTE { } -public sealed class L06PD_V_validated_class_string_RTE : L06PB_V_validated_class_string_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_string_RE), "d")] public abstract class L06PB_V_validated_class_string_RE { } -public sealed class L06PD_V_validated_class_string_RE : L06PB_V_validated_class_string_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_Guid_O), "d")] public abstract class L06PB_V_validated_class_Guid_O { } -public sealed class L06PD_V_validated_class_Guid_O : L06PB_V_validated_class_Guid_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_Guid_RTE), "d")] public abstract class L06PB_V_validated_class_Guid_RTE { } -public sealed class L06PD_V_validated_class_Guid_RTE : L06PB_V_validated_class_Guid_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_Guid_RE), "d")] public abstract class L06PB_V_validated_class_Guid_RE { } -public sealed class L06PD_V_validated_class_Guid_RE : L06PB_V_validated_class_Guid_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_int_O), "d")] public abstract class L06PB_V_validated_class_int_O { } -public sealed class L06PD_V_validated_class_int_O : L06PB_V_validated_class_int_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_int_RTE), "d")] public abstract class L06PB_V_validated_class_int_RTE { } -public sealed class L06PD_V_validated_class_int_RTE : L06PB_V_validated_class_int_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_int_RE), "d")] public abstract class L06PB_V_validated_class_int_RE { } -public sealed class L06PD_V_validated_class_int_RE : L06PB_V_validated_class_int_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_long_O), "d")] public abstract class L06PB_V_validated_class_long_O { } -public sealed class L06PD_V_validated_class_long_O : L06PB_V_validated_class_long_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_long_RTE), "d")] public abstract class L06PB_V_validated_class_long_RTE { } -public sealed class L06PD_V_validated_class_long_RTE : L06PB_V_validated_class_long_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_long_RE), "d")] public abstract class L06PB_V_validated_class_long_RE { } -public sealed class L06PD_V_validated_class_long_RE : L06PB_V_validated_class_long_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_byte_O), "d")] public abstract class L06PB_V_validated_class_byte_O { } -public sealed class L06PD_V_validated_class_byte_O : L06PB_V_validated_class_byte_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_byte_RTE), "d")] public abstract class L06PB_V_validated_class_byte_RTE { } -public sealed class L06PD_V_validated_class_byte_RTE : L06PB_V_validated_class_byte_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_byte_RE), "d")] public abstract class L06PB_V_validated_class_byte_RE { } -public sealed class L06PD_V_validated_class_byte_RE : L06PB_V_validated_class_byte_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_ulong_O), "d")] public abstract class L06PB_V_validated_class_ulong_O { } -public sealed class L06PD_V_validated_class_ulong_O : L06PB_V_validated_class_ulong_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_ulong_RTE), "d")] public abstract class L06PB_V_validated_class_ulong_RTE { } -public sealed class L06PD_V_validated_class_ulong_RTE : L06PB_V_validated_class_ulong_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_ulong_RE), "d")] public abstract class L06PB_V_validated_class_ulong_RE { } -public sealed class L06PD_V_validated_class_ulong_RE : L06PB_V_validated_class_ulong_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_decimal_O), "d")] public abstract class L06PB_V_validated_class_decimal_O { } -public sealed class L06PD_V_validated_class_decimal_O : L06PB_V_validated_class_decimal_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_decimal_RTE), "d")] public abstract class L06PB_V_validated_class_decimal_RTE { } -public sealed class L06PD_V_validated_class_decimal_RTE : L06PB_V_validated_class_decimal_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_decimal_RE), "d")] public abstract class L06PB_V_validated_class_decimal_RE { } -public sealed class L06PD_V_validated_class_decimal_RE : L06PB_V_validated_class_decimal_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_double_O), "d")] public abstract class L06PB_V_validated_class_double_O { } -public sealed class L06PD_V_validated_class_double_O : L06PB_V_validated_class_double_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_double_RTE), "d")] public abstract class L06PB_V_validated_class_double_RTE { } -public sealed class L06PD_V_validated_class_double_RTE : L06PB_V_validated_class_double_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_double_RE), "d")] public abstract class L06PB_V_validated_class_double_RE { } -public sealed class L06PD_V_validated_class_double_RE : L06PB_V_validated_class_double_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_bool_O), "d")] public abstract class L06PB_V_validated_class_bool_O { } -public sealed class L06PD_V_validated_class_bool_O : L06PB_V_validated_class_bool_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_bool_RTE), "d")] public abstract class L06PB_V_validated_class_bool_RTE { } -public sealed class L06PD_V_validated_class_bool_RTE : L06PB_V_validated_class_bool_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_bool_RE), "d")] public abstract class L06PB_V_validated_class_bool_RE { } -public sealed class L06PD_V_validated_class_bool_RE : L06PB_V_validated_class_bool_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_char_O), "d")] public abstract class L06PB_V_validated_class_char_O { } -public sealed class L06PD_V_validated_class_char_O : L06PB_V_validated_class_char_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_char_RTE), "d")] public abstract class L06PB_V_validated_class_char_RTE { } -public sealed class L06PD_V_validated_class_char_RTE : L06PB_V_validated_class_char_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_char_RE), "d")] public abstract class L06PB_V_validated_class_char_RE { } -public sealed class L06PD_V_validated_class_char_RE : L06PB_V_validated_class_char_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateTime_O), "d")] public abstract class L06PB_V_validated_class_DateTime_O { } -public sealed class L06PD_V_validated_class_DateTime_O : L06PB_V_validated_class_DateTime_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateTime_RTE), "d")] public abstract class L06PB_V_validated_class_DateTime_RTE { } -public sealed class L06PD_V_validated_class_DateTime_RTE : L06PB_V_validated_class_DateTime_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateTime_RE), "d")] public abstract class L06PB_V_validated_class_DateTime_RE { } -public sealed class L06PD_V_validated_class_DateTime_RE : L06PB_V_validated_class_DateTime_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateTimeOffset_O), "d")] public abstract class L06PB_V_validated_class_DateTimeOffset_O { } -public sealed class L06PD_V_validated_class_DateTimeOffset_O : L06PB_V_validated_class_DateTimeOffset_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateTimeOffset_RTE), "d")] public abstract class L06PB_V_validated_class_DateTimeOffset_RTE { } -public sealed class L06PD_V_validated_class_DateTimeOffset_RTE : L06PB_V_validated_class_DateTimeOffset_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateTimeOffset_RE), "d")] public abstract class L06PB_V_validated_class_DateTimeOffset_RE { } -public sealed class L06PD_V_validated_class_DateTimeOffset_RE : L06PB_V_validated_class_DateTimeOffset_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateOnly_O), "d")] public abstract class L06PB_V_validated_class_DateOnly_O { } -public sealed class L06PD_V_validated_class_DateOnly_O : L06PB_V_validated_class_DateOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateOnly_RTE), "d")] public abstract class L06PB_V_validated_class_DateOnly_RTE { } -public sealed class L06PD_V_validated_class_DateOnly_RTE : L06PB_V_validated_class_DateOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_DateOnly_RE), "d")] public abstract class L06PB_V_validated_class_DateOnly_RE { } -public sealed class L06PD_V_validated_class_DateOnly_RE : L06PB_V_validated_class_DateOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_TimeOnly_O), "d")] public abstract class L06PB_V_validated_class_TimeOnly_O { } -public sealed class L06PD_V_validated_class_TimeOnly_O : L06PB_V_validated_class_TimeOnly_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_TimeOnly_RTE), "d")] public abstract class L06PB_V_validated_class_TimeOnly_RTE { } -public sealed class L06PD_V_validated_class_TimeOnly_RTE : L06PB_V_validated_class_TimeOnly_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_TimeOnly_RE), "d")] public abstract class L06PB_V_validated_class_TimeOnly_RE { } -public sealed class L06PD_V_validated_class_TimeOnly_RE : L06PB_V_validated_class_TimeOnly_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_TimeSpan_O), "d")] public abstract class L06PB_V_validated_class_TimeSpan_O { } -public sealed class L06PD_V_validated_class_TimeSpan_O : L06PB_V_validated_class_TimeSpan_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_TimeSpan_RTE), "d")] public abstract class L06PB_V_validated_class_TimeSpan_RTE { } -public sealed class L06PD_V_validated_class_TimeSpan_RTE : L06PB_V_validated_class_TimeSpan_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_TimeSpan_RE), "d")] public abstract class L06PB_V_validated_class_TimeSpan_RE { } -public sealed class L06PD_V_validated_class_TimeSpan_RE : L06PB_V_validated_class_TimeSpan_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_enum_O), "d")] public abstract class L06PB_V_validated_class_enum_O { } -public sealed class L06PD_V_validated_class_enum_O : L06PB_V_validated_class_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_enum_RTE), "d")] public abstract class L06PB_V_validated_class_enum_RTE { } -public sealed class L06PD_V_validated_class_enum_RTE : L06PB_V_validated_class_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_enum_RE), "d")] public abstract class L06PB_V_validated_class_enum_RE { } -public sealed class L06PD_V_validated_class_enum_RE : L06PB_V_validated_class_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_flags_enum_O), "d")] public abstract class L06PB_V_validated_class_flags_enum_O { } -public sealed class L06PD_V_validated_class_flags_enum_O : L06PB_V_validated_class_flags_enum_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_flags_enum_RTE), "d")] public abstract class L06PB_V_validated_class_flags_enum_RTE { } -public sealed class L06PD_V_validated_class_flags_enum_RTE : L06PB_V_validated_class_flags_enum_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_flags_enum_RE), "d")] public abstract class L06PB_V_validated_class_flags_enum_RE { } -public sealed class L06PD_V_validated_class_flags_enum_RE : L06PB_V_validated_class_flags_enum_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_Uri_O), "d")] public abstract class L06PB_V_validated_class_Uri_O { } -public sealed class L06PD_V_validated_class_Uri_O : L06PB_V_validated_class_Uri_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_Uri_RTE), "d")] public abstract class L06PB_V_validated_class_Uri_RTE { } -public sealed class L06PD_V_validated_class_Uri_RTE : L06PB_V_validated_class_Uri_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_Uri_RE), "d")] public abstract class L06PB_V_validated_class_Uri_RE { } -public sealed class L06PD_V_validated_class_Uri_RE : L06PB_V_validated_class_Uri_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_NodaTime_Instant_O), "d")] public abstract class L06PB_V_validated_class_NodaTime_Instant_O { } -public sealed class L06PD_V_validated_class_NodaTime_Instant_O : L06PB_V_validated_class_NodaTime_Instant_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_NodaTime_Instant_RTE), "d")] public abstract class L06PB_V_validated_class_NodaTime_Instant_RTE { } -public sealed class L06PD_V_validated_class_NodaTime_Instant_RTE : L06PB_V_validated_class_NodaTime_Instant_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_NodaTime_Instant_RE), "d")] public abstract class L06PB_V_validated_class_NodaTime_Instant_RE { } -public sealed class L06PD_V_validated_class_NodaTime_Instant_RE : L06PB_V_validated_class_NodaTime_Instant_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_own_record_class_O), "d")] public abstract class L06PB_V_validated_class_own_record_class_O { } -public sealed class L06PD_V_validated_class_own_record_class_O : L06PB_V_validated_class_own_record_class_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_own_record_class_RTE), "d")] public abstract class L06PB_V_validated_class_own_record_class_RTE { } -public sealed class L06PD_V_validated_class_own_record_class_RTE : L06PB_V_validated_class_own_record_class_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_V_validated_class_own_record_class_RE), "d")] public abstract class L06PB_V_validated_class_own_record_class_RE { } -public sealed class L06PD_V_validated_class_own_record_class_RE : L06PB_V_validated_class_own_record_class_RE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_PSet_O), "d")] public abstract class L06PB_PSet_O { } -public sealed class L06PD_PSet_O : L06PB_PSet_O { public Option Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_PSet_RTE), "d")] public abstract class L06PB_PSet_RTE { } -public sealed class L06PD_PSet_RTE : L06PB_PSet_RTE { public Result Value { get; set; } } -[System.Text.Json.Serialization.JsonPolymorphic] [System.Text.Json.Serialization.JsonDerivedType(typeof(L06PD_PSet_RE), "d")] public abstract class L06PB_PSet_RE { } -public sealed class L06PD_PSet_RE : L06PB_PSet_RE { public Result Value { get; set; } } diff --git a/tools/probe/corpus/harness/JsonRow.cs b/tools/probe/corpus/harness/JsonRow.cs deleted file mode 100644 index f39be575..00000000 --- a/tools/probe/corpus/harness/JsonRow.cs +++ /dev/null @@ -1,183 +0,0 @@ -// Applies a row's json.* axes to JsonSerializerOptions (reflection for every plain bool/enum setting, hand-listed values by name). -using System.Reflection; -using System.Text.Encodings.Web; -using System.Text.Json; -using System.Text.Json.Serialization; -using System.Text.Json.Serialization.Metadata; -using CodoMetis.TypeKit.ValueObjects; -using NodaTime; -using NodaTime.Serialization.SystemTextJson; -namespace Probes.Shapes; - -public sealed class UpperPolicy : JsonNamingPolicy { public override string ConvertName(string name) => name.ToUpperInvariant(); } - -public static class Make -{ - /// - /// The type a nullable one holds: under http.nullability=nullable an entry's Vo and Plain are T?, the types the - /// endpoints declare, while the value object is still made by From(T) and read through its Value. - /// - public static Type Held(Type t) => Nullable.GetUnderlyingType(t) ?? t; - /// Whether a type admits null: a class, or a struct made nullable. A value object and its twin admit it alike (Equivalence). - public static bool AdmitsNull(Type t) => !t.IsValueType || Nullable.GetUnderlyingType(t) is not null; - - /// Creates the value object over a plain sample (From / FromKnownGood), with optional parameters filled. - public static object Vo(Entry e, object plain) - { - if (e.Kind == "closed-set") return PSet.All.First(x => x.Name == (string)plain); - var m = Held(e.Vo).GetMethods(BindingFlags.Public | BindingFlags.Static) - .First(x => (x.Name is "FromKnownGood" or "From") && x.GetParameters().Length > 0 && x.GetParameters()[0].ParameterType == Held(e.Plain)); - var args = m.GetParameters().Select((p, i) => i == 0 ? plain : Type.Missing).ToArray(); - return m.Invoke(null, BindingFlags.OptionalParamBinding | BindingFlags.Default, null, args, null)!; - } - public static object Unwrap(Entry e, object vo) => e.Kind == "closed-set" ? ((PSet)vo).Name : Held(e.Vo).GetProperty("Value")!.GetValue(vo)!; -} - -/// A converter an application registers for the wrapped type: writes "own:" + the default JSON. -public sealed class OwnWrappedConverter(JsonSerializerOptions inner) : JsonConverter -{ - public override T? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var s = reader.GetString() ?? throw new JsonException("null"); - if (!s.StartsWith("own:")) throw new JsonException("not own"); - return JsonSerializer.Deserialize(s[4..], inner); - } - public override void Write(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => writer.WriteStringValue("own:" + JsonSerializer.Serialize(value, inner)); - public override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Read(ref reader, typeToConvert, options)!; - public override void WriteAsPropertyName(Utf8JsonWriter writer, T value, JsonSerializerOptions options) => writer.WritePropertyName("own:" + JsonSerializer.Serialize(value, inner)); -} - -/// A converter an application registers for the value object itself: writes "vo:" + the wrapped value's default JSON. -public sealed class OwnVoFactory(Entry e, JsonSerializerOptions inner) : JsonConverterFactory -{ - public override bool CanConvert(Type t) => t == Make.Held(e.Vo); // System.Text.Json asks for T, never for T? - public override JsonConverter? CreateConverter(Type t, JsonSerializerOptions options) => - (JsonConverter)Activator.CreateInstance(typeof(OwnVo<>).MakeGenericType(t), e, inner)!; -} -public sealed class OwnVo(Entry e, JsonSerializerOptions inner) : JsonConverter -{ - public override V? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - var s = reader.GetString() ?? throw new JsonException("null"); - if (!s.StartsWith("vo:")) throw new JsonException("not vo"); - return (V)Make.Vo(e, JsonSerializer.Deserialize(s[3..], Make.Held(e.Plain), inner)!); - } - public override void Write(Utf8JsonWriter writer, V value, JsonSerializerOptions options) => - writer.WriteStringValue("vo:" + JsonSerializer.Serialize(Make.Unwrap(e, value!), Make.Held(e.Plain), inner)); - public override V ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Read(ref reader, typeToConvert, options)!; - public override void WriteAsPropertyName(Utf8JsonWriter writer, V value, JsonSerializerOptions options) => - writer.WritePropertyName("vo:" + JsonSerializer.Serialize(Make.Unwrap(e, value!), Make.Held(e.Plain), inner)); -} - -public sealed record Applied(JsonSerializerOptions? Options, string? Refused); - -public static class JsonRow -{ - public static JsonSerializerOptions Inner(Entry e) - { - var o = new JsonSerializerOptions(); - if (e.Wrapped == "NodaTime.Instant") o.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - return o; - } - - /// Applies every json.* axis of the row to (or a new instance). Refused = STJ rejected a combination with an exception of its own (a pass). - public static Applied Apply(Row r, Entry e, JsonSerializerOptions? into = null) - { - var o = into ?? new JsonSerializerOptions(); - var dropped = new List(); - if (e.Wrapped == "NodaTime.Instant") o.ConfigureForNodaTime(DateTimeZoneProviders.Tzdb); - if (r.Get("json.PreferredObjectCreationHandling") == "Populate" && r.Get("json.ReferenceHandler") is not (null or "default")) - { - dropped.Add("json.PreferredObjectCreationHandling=Populate (STJ: incompatible with reference handling)"); - r.Substituted("json.PreferredObjectCreationHandling", "default", "STJ: incompatible with reference handling"); - r = r.With("json.PreferredObjectCreationHandling", "default"); - } - // resolver before chain: a chain on top of a resolver is a combination STJ may refuse - foreach (var kv in r.All.Where(kv => kv.Key.StartsWith("json.") && kv.Value != "default").OrderBy(kv => kv.Key == "json.TypeInfoResolverChain" ? 1 : 0)) - { - try { Set(r, o, kv.Key[5..], kv.Value, e); } - catch (Exception ex) when (ex is ArgumentException or InvalidOperationException or NotSupportedException) - { - // STJ refuses this setting with an exception of its own: a pass for the pairs of that value; the rest of the row is still probed - dropped.Add($"{kv.Key}={kv.Value} ({ex.GetType().Name})"); - r.Substituted(kv.Key, "default", "STJ refused it: " + ex.GetType().Name); - } - } - r.Note(NoteKeys.Dropped, string.Join(",", dropped)); - return new(o, dropped.Count == 0 ? null : string.Join(",", dropped)); - } - - static JsonNamingPolicy? Policy(string v) => v switch - { - "CamelCase" => JsonNamingPolicy.CamelCase, "SnakeCaseLower" => JsonNamingPolicy.SnakeCaseLower, "SnakeCaseUpper" => JsonNamingPolicy.SnakeCaseUpper, - "KebabCaseLower" => JsonNamingPolicy.KebabCaseLower, "KebabCaseUpper" => JsonNamingPolicy.KebabCaseUpper, "own-upper-case" => new UpperPolicy(), - _ => throw new ArgumentException("unknown policy " + v) - }; - - /// - /// json.TypeInfoResolver=source-generated-context is strict: the options resolve through ProbeContext alone, which lists every shape a row - /// holds and its twin (shapes.cs), with no reflection behind it; json.TypeInfoResolverChain=context-then-default is the configuration with - /// the reflection fallback, the context then reflection, beside it or on its own. A row's shape the context does not list is the generator's gap, a harness error: the serializer's refusal of - /// an unlisted type would read as the framework's. - /// - static void Listed(Row r, Entry e) - { - var plain = e.Valid(); var vo = Make.Vo(e, plain); - var pos = r["shape.position"]; var con = (r.Get("shape.containing") ?? "class"); - var (hp, hv) = Sides.Twins(pos, con, e, plain, vo); - var missing = Missing(hp.Declared, hv.Declared).Select(t => t.ToString()).ToList(); - if (missing.Count > 0) { r.Outcome("harness-error", $"ProbeContext does not list {string.Join(", ", missing)}: run tools/probe/generators/shapes.cs"); return; } - r.Note(NoteKeys.CtxStrict); - } - - /// The ProbeContext does not list, under json.TypeInfoResolver=source-generated-context: the generator's gap, which the caller reports as a harness error. - public static IEnumerable Unlisted(Row r, params Type[] shapes) => - r.Is("json.TypeInfoResolver", "source-generated-context") ? Missing(shapes) : []; - - /// - /// The types ProbeContext has no metadata for, asked through the resolver's interface, which builds no contract: building one is the - /// serializer's step, where a context refuses an Option or Result made nullable (L06's not-promised entry), not the listing's. - /// - static IEnumerable Missing(params Type[] shapes) - { - // the context answers what a type's metadata asks the options for (the struct a Nullable holds) too - var probe = new JsonSerializerOptions { TypeInfoResolver = ProbeContext.Default }; - return shapes.Where(t => ((IJsonTypeInfoResolver)ProbeContext.Default).GetTypeInfo(t, probe) is null).ToList(); - } - - static void Set(Row row, JsonSerializerOptions o, string name, string v, Entry e) - { - switch (name) - { - case "PropertyNamingPolicy": o.PropertyNamingPolicy = Policy(v); return; - case "DictionaryKeyPolicy": o.DictionaryKeyPolicy = Policy(v); return; - case "ReferenceHandler": o.ReferenceHandler = v == "Preserve" ? ReferenceHandler.Preserve : ReferenceHandler.IgnoreCycles; return; - case "Encoder": o.Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping; return; - case "MaxDepth": o.MaxDepth = int.Parse(v); return; - case "Converters": - switch (v) - { - case "own-for-wrapped": o.Converters.Insert(0, (JsonConverter)Activator.CreateInstance(typeof(OwnWrappedConverter<>).MakeGenericType(Make.Held(e.Plain)), Inner(e))!); break; - case "own-for-value-object": o.Converters.Insert(0, new OwnVoFactory(e, Inner(e))); break; - case "JsonStringEnumConverter": o.Converters.Add(new JsonStringEnumConverter()); break; - case "StoredJsonConverterFactory": o.Converters.Add(new StoredJsonConverterFactory()); break; - default: throw new ArgumentException("unknown converter " + v); - } - return; - case "TypeInfoResolver": - if (v == "source-generated-context") { o.TypeInfoResolver = ProbeContext.Default; Listed(row, e); } - else o.TypeInfoResolver = new DefaultJsonTypeInfoResolver { Modifiers = { ti => { foreach (var p in ti.Properties) p.Name += "_x"; } } }; - return; - case "TypeInfoResolverChain": - // The context, then reflection for what it does not list: under json.TypeInfoResolver=source-generated-context the chain - // already starts with the context, which System.Text.Json refuses to insert a second time (the resolver into its own - // chain), so the reflection fallback follows it. That pair is the context with the fallback, the strict context's other side. - if (!o.TypeInfoResolverChain.Contains(ProbeContext.Default)) o.TypeInfoResolverChain.Add(ProbeContext.Default); - o.TypeInfoResolverChain.Add(new DefaultJsonTypeInfoResolver()); return; - } - var prop = typeof(JsonSerializerOptions).GetProperty(name) ?? throw new ArgumentException("no json option " + name); - object value = prop.PropertyType == typeof(bool) ? bool.Parse(v) : prop.PropertyType.IsEnum ? Enum.Parse(prop.PropertyType, v) : throw new ArgumentException($"no hand value for {name}"); - try { prop.SetValue(o, value); } - catch (TargetInvocationException ex) { throw ex.InnerException!; } - } -} diff --git a/tools/probe/corpus/harness/KeyShapes.g.cs b/tools/probe/corpus/harness/KeyShapes.g.cs deleted file mode 100644 index 0a98b904..00000000 --- a/tools/probe/corpus/harness/KeyShapes.g.cs +++ /dev/null @@ -1,128 +0,0 @@ -// by tools/probe/generators/ef.cs keys; the EF key probes' shape catalogue (shape.kind x shape.wrapped). Do not edit. -#nullable enable -#pragma warning disable CMTK0014, CS8618, CS1591, CMTK0005, CMTK1000, CMTK0003, CMTK0006, CA1000 -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -using Probes.Shapes; -namespace Probes.Ef; -public readonly partial record struct K_record_struct_Guid : IValue; -public readonly partial record struct K_record_struct_byte : IValue; -public readonly partial record struct K_record_struct_char : IValue; -public readonly partial record struct K_record_struct_int : IValue; -public readonly partial record struct K_record_struct_long : IValue; -public readonly partial record struct K_record_struct_string : IValue; -public readonly partial record struct K_record_struct_ulong : IValue; -public sealed partial record class K_sealed_record_class_Guid : IValue; -public sealed partial record class K_sealed_record_class_byte : IValue; -public sealed partial record class K_sealed_record_class_char : IValue; -public sealed partial record class K_sealed_record_class_int : IValue; -public sealed partial record class K_sealed_record_class_long : IValue; -public sealed partial record class K_sealed_record_class_string : IValue; -public sealed partial record class K_sealed_record_class_ulong : IValue; -public sealed partial record class K_validated_class_Guid : IValidatedValue -{ - public static Result Create(Guid value) => - EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new K_validated_class_Guid(value); -} -public sealed partial record class K_validated_class_byte : IValidatedValue -{ - public static Result Create(byte value) => - EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new K_validated_class_byte(value); -} -public sealed partial record class K_validated_class_char : IValidatedValue -{ - public static Result Create(char value) => - EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new K_validated_class_char(value); -} -public sealed partial record class K_validated_class_int : IValidatedValue -{ - public static Result Create(int value) => - EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new K_validated_class_int(value); -} -public sealed partial record class K_validated_class_long : IValidatedValue -{ - public static Result Create(long value) => - EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new K_validated_class_long(value); -} -public sealed partial record class K_validated_class_string : IValidatedValue -{ - public static Result Create(string value) => - EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new K_validated_class_string(value); -} -public sealed partial record class K_validated_class_ulong : IValidatedValue -{ - public static Result Create(ulong value) => - EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new K_validated_class_ulong(value); -} -public readonly partial record struct K_validated_struct_Guid : IValidatedValue -{ - public static Result Create(Guid value) => - EqualityComparer.Default.Equals(value, Guid.Empty) ? Result.Error(ShapeFault.Refused) : new K_validated_struct_Guid(value); -} -public readonly partial record struct K_validated_struct_byte : IValidatedValue -{ - public static Result Create(byte value) => - EqualityComparer.Default.Equals(value, (byte)255) ? Result.Error(ShapeFault.Refused) : new K_validated_struct_byte(value); -} -public readonly partial record struct K_validated_struct_char : IValidatedValue -{ - public static Result Create(char value) => - EqualityComparer.Default.Equals(value, 'x') ? Result.Error(ShapeFault.Refused) : new K_validated_struct_char(value); -} -public readonly partial record struct K_validated_struct_int : IValidatedValue -{ - public static Result Create(int value) => - EqualityComparer.Default.Equals(value, -1) ? Result.Error(ShapeFault.Refused) : new K_validated_struct_int(value); -} -public readonly partial record struct K_validated_struct_long : IValidatedValue -{ - public static Result Create(long value) => - EqualityComparer.Default.Equals(value, -1L) ? Result.Error(ShapeFault.Refused) : new K_validated_struct_long(value); -} -public readonly partial record struct K_validated_struct_string : IValidatedValue -{ - public static Result Create(string value) => - EqualityComparer.Default.Equals(value, "SECRET-7f3a") ? Result.Error(ShapeFault.Refused) : new K_validated_struct_string(value); -} -public readonly partial record struct K_validated_struct_ulong : IValidatedValue -{ - public static Result Create(ulong value) => - EqualityComparer.Default.Equals(value, 0UL) ? Result.Error(ShapeFault.Refused) : new K_validated_struct_ulong(value); -} -public static partial class KeyRegistry -{ - public static readonly List All = Build(); - static List Build() { var e = new List(); Fill(e); return e; } - public static EfEntry Of(Probes.Shapes.Row r) { var k = r["shape.kind"]; var w = r["shape.wrapped"]; return All.First(x => x.Kind == k && x.Wrapped == w); } - public static void Fill(List e) - { - e.Add(new EfEntry("record-struct", "Guid", "top-level", typeof(K_record_struct_Guid), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("record-struct", "byte", "top-level", typeof(K_record_struct_byte), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("record-struct", "char", "top-level", typeof(K_record_struct_char), typeof(char), () => 'a')); - e.Add(new EfEntry("record-struct", "int", "top-level", typeof(K_record_struct_int), typeof(int), () => 42)); - e.Add(new EfEntry("record-struct", "long", "top-level", typeof(K_record_struct_long), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("record-struct", "string", "top-level", typeof(K_record_struct_string), typeof(string), () => "alpha")); - e.Add(new EfEntry("record-struct", "ulong", "top-level", typeof(K_record_struct_ulong), typeof(ulong), () => 4200000000UL)); - e.Add(new EfEntry("sealed-record-class", "Guid", "top-level", typeof(K_sealed_record_class_Guid), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("sealed-record-class", "byte", "top-level", typeof(K_sealed_record_class_byte), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("sealed-record-class", "char", "top-level", typeof(K_sealed_record_class_char), typeof(char), () => 'a')); - e.Add(new EfEntry("sealed-record-class", "int", "top-level", typeof(K_sealed_record_class_int), typeof(int), () => 42)); - e.Add(new EfEntry("sealed-record-class", "long", "top-level", typeof(K_sealed_record_class_long), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("sealed-record-class", "string", "top-level", typeof(K_sealed_record_class_string), typeof(string), () => "alpha")); - e.Add(new EfEntry("sealed-record-class", "ulong", "top-level", typeof(K_sealed_record_class_ulong), typeof(ulong), () => 4200000000UL)); - e.Add(new EfEntry("validated-class", "Guid", "top-level", typeof(K_validated_class_Guid), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("validated-class", "byte", "top-level", typeof(K_validated_class_byte), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("validated-class", "char", "top-level", typeof(K_validated_class_char), typeof(char), () => 'a')); - e.Add(new EfEntry("validated-class", "int", "top-level", typeof(K_validated_class_int), typeof(int), () => 42)); - e.Add(new EfEntry("validated-class", "long", "top-level", typeof(K_validated_class_long), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("validated-class", "string", "top-level", typeof(K_validated_class_string), typeof(string), () => "alpha")); - e.Add(new EfEntry("validated-class", "ulong", "top-level", typeof(K_validated_class_ulong), typeof(ulong), () => 4200000000UL)); - e.Add(new EfEntry("validated-struct", "Guid", "top-level", typeof(K_validated_struct_Guid), typeof(Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"))); - e.Add(new EfEntry("validated-struct", "byte", "top-level", typeof(K_validated_struct_byte), typeof(byte), () => (byte)7)); - e.Add(new EfEntry("validated-struct", "char", "top-level", typeof(K_validated_struct_char), typeof(char), () => 'a')); - e.Add(new EfEntry("validated-struct", "int", "top-level", typeof(K_validated_struct_int), typeof(int), () => 42)); - e.Add(new EfEntry("validated-struct", "long", "top-level", typeof(K_validated_struct_long), typeof(long), () => 4200000000L)); - e.Add(new EfEntry("validated-struct", "string", "top-level", typeof(K_validated_struct_string), typeof(string), () => "alpha")); - e.Add(new EfEntry("validated-struct", "ulong", "top-level", typeof(K_validated_struct_ulong), typeof(ulong), () => 4200000000UL)); - } -} diff --git a/tools/probe/corpus/harness/L01JsonBytesProbe.cs b/tools/probe/corpus/harness/L01JsonBytesProbe.cs deleted file mode 100644 index 4eb89c77..00000000 --- a/tools/probe/corpus/harness/L01JsonBytesProbe.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Claim L01-json-bytes (README#what-typekit-guarantees "Swapping a primitive for a value object keeps the JSON"; docs/json.md#the-wrapped-types-json): -// the value object's JSON is the wrapped type's, byte for byte, under whatever options, as value and as key, and each reads the other's JSON. -// Oracle: differential (JsonParity). Expected: no failure for any row; STJ refusing its own option combination, and the documented -// Preserve refusal, are passes. -using Probes.Shapes; -using System.Text.Json; -namespace Probes; -public sealed class L01JsonBytesProbe -{ - public static TheoryData Data() => Rows.Data("L01JsonBytesProbe"); - [Theory, MemberData(nameof(Data))] - public Task Row(string probe, int idx) => Rec.Run(probe, idx, async r => - { - var e = Registry.Of(r); - var a = JsonRow.Apply(r, e); - JsonSerializerOptions? plainOpts = null; - if (e.Kind == "closed-set" && r.Is("json.Converters", "own-for-wrapped")) plainOpts = JsonRow.Apply(r.With("json.Converters", "default"), e).Options; // a closed set travels as its name, not as a string - await Task.CompletedTask; - return JsonParity.Check(r, e, a.Options!, r["shape.position"], (r.Get("shape.containing") ?? "class"), r["input.json"], plainOpts); - }); -} diff --git a/tools/probe/corpus/harness/L02NoQuoteProbe.cs b/tools/probe/corpus/harness/L02NoQuoteProbe.cs deleted file mode 100644 index 11f88a8b..00000000 --- a/tools/probe/corpus/harness/L02NoQuoteProbe.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Claim L02-no-quote (README#what-typekit-guarantees; src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting). L02 (exception): a rejection never repeats the input. -// Expected: no failure for any row; the documented differences (lossy default text of TimeOnly/DateTime/DateTimeOffset, a registered converter, StoredJsonConverterFactory) are passes. -using Probes.Shapes; -namespace Probes; -public sealed class L02NoQuoteProbe -{ - public static TheoryData Data() => Rows.Data("L02NoQuoteProbe"); - [Theory, MemberData(nameof(Data))] - public Task Row(string probe, int idx) => Rec.Run(probe, idx, r => Task.FromResult(TextClaims.NoQuote(r, Registry.Of(r)))); -} diff --git a/tools/probe/corpus/harness/L03OneRuleProbe.cs b/tools/probe/corpus/harness/L03OneRuleProbe.cs deleted file mode 100644 index e8e1fd24..00000000 --- a/tools/probe/corpus/harness/L03OneRuleProbe.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Claim L03-one-rule (README#what-typekit-guarantees; src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting). L03 (claim): every entry runs Create and names its fault. -// Expected: no failure for any row; the documented differences (lossy default text of TimeOnly/DateTime/DateTimeOffset, a registered converter, StoredJsonConverterFactory) are passes. -using Probes.Shapes; -namespace Probes; -public sealed class L03OneRuleProbe -{ - public static TheoryData Data() => Rows.Data("L03OneRuleProbe"); - [Theory, MemberData(nameof(Data))] - public Task Row(string probe, int idx) => Rec.Run(probe, idx, r => Task.FromResult(TextClaims.OneRule(r, Registry.Of(r)))); -} diff --git a/tools/probe/corpus/harness/L04RoundTripProbe.cs b/tools/probe/corpus/harness/L04RoundTripProbe.cs deleted file mode 100644 index 2ae7ad0c..00000000 --- a/tools/probe/corpus/harness/L04RoundTripProbe.cs +++ /dev/null @@ -1,10 +0,0 @@ -// Claim L04-round-trip (README#what-typekit-guarantees; src/CodoMetis.TypeKit.Generators/README.md#parsing-and-formatting). L04 round trip (metamorphic): VO text/JSON written by the project reads back as the same value. -// Expected: no failure for any row; the documented differences (lossy default text of TimeOnly/DateTime/DateTimeOffset, a registered converter, StoredJsonConverterFactory) are passes. -using Probes.Shapes; -namespace Probes; -public sealed class L04RoundTripProbe -{ - public static TheoryData Data() => Rows.Data("L04RoundTripProbe"); - [Theory, MemberData(nameof(Data))] - public Task Row(string probe, int idx) => Rec.Run(probe, idx, r => Task.FromResult(TextClaims.RoundTrip(r, Registry.Of(r)))); -} diff --git a/tools/probe/corpus/harness/L05Shapes.g.cs b/tools/probe/corpus/harness/L05Shapes.g.cs deleted file mode 100644 index f3f576ad..00000000 --- a/tools/probe/corpus/harness/L05Shapes.g.cs +++ /dev/null @@ -1,575 +0,0 @@ -// by tools/probe/generators/json-family.cs from the rows of L05-equality. Do not edit. -#nullable enable -#pragma warning disable CS1591, CMTK0005, CMTK1023 // CMTK1023: a base record with an explicit IEquatable is the shape under test -using CodoMetis.TypeKit; -using CodoMetis.TypeKit.ValueObjects; -using CodoMetis.TypeKit.ClosedSets; -using Probes.Shapes; -namespace Probes.Json; - -public interface IL05Tag { string Tag { get; } } -public abstract record L05B1; -public abstract record L05B2a; -public abstract record L05B2b : L05B2a; -public abstract record L05B3a; -public abstract record L05B3b : L05B3a; -public abstract record L05B3c : L05B3b; -// a deeper base whose own IEquatable says "never equal": the value object must not use it -public abstract record L05E0 : IEquatable { bool IEquatable.Equals(L05E0? other) => false; } -public abstract record L05E1 : L05E0; -public abstract class L05CB1; -public abstract class L05CB2a; -public abstract class L05CB2b : L05CB2a; -public abstract class L05CB3a; -public abstract class L05CB3b : L05CB3a; -public abstract class L05CB3c : L05CB3b; -public abstract class L05CE0 : IEquatable { bool IEquatable.Equals(L05CE0? other) => ReferenceEquals(this, other); } -public abstract class L05CE1 : L05CE0; - -public partial class L05Oc0 { public sealed partial class L05_0_closed_set__ : L05CB1, IClosedSet { public static readonly L05_0_closed_set__ Alpha = new(); public static readonly L05_0_closed_set__ Beta = new(); } } -public partial class L05Ot1 { public partial class Inner { public sealed partial class L05_1_closed_set__ : L05CB1, IClosedSet { public static readonly L05_1_closed_set__ Alpha = new(); public static readonly L05_1_closed_set__ Beta = new(); } } } -public partial class L05Oc2 { public sealed partial class L05_2_closed_set__ : L05CB2b, IClosedSet { public static readonly L05_2_closed_set__ Alpha = new(); public static readonly L05_2_closed_set__ Beta = new(); } } -public partial struct L05Os3 { public sealed partial class L05_3_closed_set__ : L05CB2b, IClosedSet { public static readonly L05_3_closed_set__ Alpha = new(); public static readonly L05_3_closed_set__ Beta = new(); } } -public sealed partial class L05_4_closed_set__ : L05CB2b, IClosedSet { public static readonly L05_4_closed_set__ Alpha = new(); public static readonly L05_4_closed_set__ Beta = new(); } -public partial class L05Oc5 { public sealed partial class L05_5_closed_set__ : L05CB3c, IClosedSet { public static readonly L05_5_closed_set__ Alpha = new(); public static readonly L05_5_closed_set__ Beta = new(); } } -public partial struct L05Os6 { public sealed partial class L05_6_closed_set__ : L05CB3c, IClosedSet { public static readonly L05_6_closed_set__ Alpha = new(); public static readonly L05_6_closed_set__ Beta = new(); } } -public partial class L05Ot7 { public partial class Inner { public sealed partial class L05_7_closed_set__ : L05CB3c, IClosedSet { public static readonly L05_7_closed_set__ Alpha = new(); public static readonly L05_7_closed_set__ Beta = new(); } } } -public sealed partial class L05_8_closed_set__ : L05CB3c, IClosedSet { public static readonly L05_8_closed_set__ Alpha = new(); public static readonly L05_8_closed_set__ Beta = new(); } -public partial class L05Oc9 { public sealed partial class L05_9_closed_set__ : L05CE1, IClosedSet { public static readonly L05_9_closed_set__ Alpha = new(); public static readonly L05_9_closed_set__ Beta = new(); } } -public partial struct L05Os10 { public sealed partial class L05_10_closed_set__ : L05CE1, IClosedSet { public static readonly L05_10_closed_set__ Alpha = new(); public static readonly L05_10_closed_set__ Beta = new(); } } -public partial class L05Ot11 { public partial class Inner { public sealed partial class L05_11_closed_set__ : L05CE1, IClosedSet { public static readonly L05_11_closed_set__ Alpha = new(); public static readonly L05_11_closed_set__ Beta = new(); } } } -public sealed partial class L05_12_closed_set__ : L05CE1, IClosedSet { public static readonly L05_12_closed_set__ Alpha = new(); public static readonly L05_12_closed_set__ Beta = new(); } -public partial class L05Oc13 { public sealed partial class L05_13_closed_set__ : IL05Tag, IClosedSet { public static readonly L05_13_closed_set__ Alpha = new(); public static readonly L05_13_closed_set__ Beta = new(); string IL05Tag.Tag => "t"; } } -public sealed partial class L05_14_closed_set__ : IL05Tag, IClosedSet { public static readonly L05_14_closed_set__ Alpha = new(); public static readonly L05_14_closed_set__ Beta = new(); string IL05Tag.Tag => "t"; } -public partial struct L05Os15 { public sealed partial class L05_15_closed_set__ : IClosedSet { public static readonly L05_15_closed_set__ Alpha = new(); public static readonly L05_15_closed_set__ Beta = new(); } } -public partial class L05Oc16 { public readonly partial record struct L05_16_record_struct_DateOnly : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial struct L05Os17 { public readonly partial record struct L05_17_record_struct_DateOnly : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial class L05Ot18 { public partial class Inner { public readonly partial record struct L05_18_record_struct_DateOnly : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public partial class L05Oc19 { public readonly partial record struct L05_19_record_struct_DateTime : IValue { } } -public partial struct L05Os20 { public readonly partial record struct L05_20_record_struct_DateTimeOffset : IValue { } } -public partial struct L05Os21 { public readonly partial record struct L05_21_record_struct_Guid : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_22_record_struct_Guid : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public partial class L05Oc23 { public readonly partial record struct L05_23_record_struct_NodaTime_Instant : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial struct L05Os24 { public readonly partial record struct L05_24_record_struct_NodaTime_Instant : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_25_record_struct_NodaTime_Instant : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public partial class L05Ot26 { public partial class Inner { public readonly partial record struct L05_26_record_struct_TimeOnly : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public readonly partial record struct L05_27_record_struct_TimeOnly : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public partial struct L05Os28 { public readonly partial record struct L05_28_record_struct_TimeOnly : IValue { } } -public partial class L05Oc29 { public readonly partial record struct L05_29_record_struct_TimeSpan : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial class L05Oc30 { public readonly partial record struct L05_30_record_struct_TimeSpan : IValue { } } -public readonly partial record struct L05_31_record_struct_Uri : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public readonly partial record struct L05_32_record_struct_Uri : IValue { } -public partial struct L05Os33 { public readonly partial record struct L05_33_record_struct_bool : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial class L05Ot34 { public partial class Inner { public readonly partial record struct L05_34_record_struct_bool : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public readonly partial record struct L05_35_record_struct_bool : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public readonly partial record struct L05_36_record_struct_byte : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public partial class L05Oc37 { public readonly partial record struct L05_37_record_struct_byte : IValue { } } -public readonly partial record struct L05_38_record_struct_char : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public partial struct L05Os39 { public readonly partial record struct L05_39_record_struct_char : IValue { } } -public partial class L05Oc40 { public readonly partial record struct L05_40_record_struct_decimal : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_41_record_struct_decimal : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public partial class L05Oc42 { public readonly partial record struct L05_42_record_struct_decimal : IValue { } } -public partial class L05Oc43 { public readonly partial record struct L05_43_record_struct_double : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial struct L05Os44 { public readonly partial record struct L05_44_record_struct_double : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial class L05Ot45 { public partial class Inner { public readonly partial record struct L05_45_record_struct_double : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public partial class L05Ot46 { public partial class Inner { public readonly partial record struct L05_46_record_struct_double : IValue { } } } -public partial class L05Oc47 { public readonly partial record struct L05_47_record_struct_enum : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial struct L05Os48 { public readonly partial record struct L05_48_record_struct_enum : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_49_record_struct_flags_enum : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public readonly partial record struct L05_50_record_struct_flags_enum : IValue { } -public partial struct L05Os51 { public readonly partial record struct L05_51_record_struct_int : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial class L05Ot52 { public partial class Inner { public readonly partial record struct L05_52_record_struct_int : IValue { } } } -public partial class L05Ot53 { public partial class Inner { public readonly partial record struct L05_53_record_struct_long : IValue { } } } -public readonly partial record struct L05_54_record_struct_long : IValue { } -public partial class L05Ot55 { public partial class Inner { public readonly partial record struct L05_55_record_struct_own_record_class : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public partial struct L05Os56 { public readonly partial record struct L05_56_record_struct_string : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_57_record_struct_string : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public partial class L05Oc58 { public readonly partial record struct L05_58_record_struct_string : IValue { } } -public partial class L05Oc59 { public readonly partial record struct L05_59_record_struct_ulong : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial struct L05Os60 { public readonly partial record struct L05_60_record_struct_ulong : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial struct L05Os61 { public sealed partial record L05_61_sealed_record_class_DateOnly : L05B2b, IValue { } } -public sealed partial record L05_62_sealed_record_class_DateOnly : L05B3c, IValue { } -public partial struct L05Os63 { public sealed partial record L05_63_sealed_record_class_DateOnly : IValue { } } -public sealed partial record L05_64_sealed_record_class_DateOnly : IValue { } -public sealed partial record L05_65_sealed_record_class_DateTime : L05B1, IValue { } -public partial class L05Oc66 { public sealed partial record L05_66_sealed_record_class_DateTime : L05B2b, IValue { } } -public partial struct L05Os67 { public sealed partial record L05_67_sealed_record_class_DateTime : L05B3c, IValue { } } -public partial class L05Ot68 { public partial class Inner { public sealed partial record L05_68_sealed_record_class_DateTime : L05B3c, IValue { } } } -public sealed partial record L05_69_sealed_record_class_DateTime : L05B3c, IValue { } -public partial struct L05Os70 { public sealed partial record L05_70_sealed_record_class_DateTime : L05E1, IValue { } } -public partial class L05Oc71 { public sealed partial record L05_71_sealed_record_class_DateTimeOffset : L05B1, IValue { } } -public sealed partial record L05_72_sealed_record_class_DateTimeOffset : L05B2b, IValue { } -public partial class L05Oc73 { public sealed partial record L05_73_sealed_record_class_DateTimeOffset : L05B3c, IValue { } } -public partial class L05Ot74 { public partial class Inner { public sealed partial record L05_74_sealed_record_class_DateTimeOffset : L05B3c, IValue { } } } -public sealed partial record L05_75_sealed_record_class_DateTimeOffset : L05B3c, IValue { } -public partial class L05Oc76 { public sealed partial record L05_76_sealed_record_class_DateTimeOffset : L05E1, IValue { } } -public sealed partial record L05_77_sealed_record_class_DateTimeOffset : L05E1, IValue { } -public partial class L05Oc78 { public sealed partial record L05_78_sealed_record_class_Guid : L05B1, IValue { } } -public partial struct L05Os79 { public sealed partial record L05_79_sealed_record_class_Guid : L05B1, IValue { } } -public sealed partial record L05_80_sealed_record_class_Guid : L05B2b, IValue { } -public sealed partial record L05_81_sealed_record_class_Guid : L05B3c, IValue { } -public partial class L05Oc82 { public sealed partial record L05_82_sealed_record_class_Guid : L05E1, IValue { } } -public partial class L05Ot83 { public partial class Inner { public sealed partial record L05_83_sealed_record_class_Guid : L05E1, IValue { } } } -public partial class L05Ot84 { public partial class Inner { public sealed partial record L05_84_sealed_record_class_NodaTime_Instant : L05B1, IValue { } } } -public partial struct L05Os85 { public sealed partial record L05_85_sealed_record_class_NodaTime_Instant : L05E1, IValue { } } -public partial struct L05Os86 { public sealed partial record L05_86_sealed_record_class_NodaTime_Instant : IValue { } } -public partial struct L05Os87 { public sealed partial record L05_87_sealed_record_class_TimeOnly : L05B3c, IValue { } } -public partial class L05Ot88 { public partial class Inner { public sealed partial record L05_88_sealed_record_class_TimeOnly : L05B3c, IValue { } } } -public partial class L05Oc89 { public sealed partial record L05_89_sealed_record_class_TimeSpan : L05B1, IValue { } } -public partial class L05Ot90 { public partial class Inner { public sealed partial record L05_90_sealed_record_class_TimeSpan : L05B1, IValue { } } } -public sealed partial record L05_91_sealed_record_class_TimeSpan : L05B2b, IValue { } -public sealed partial record L05_92_sealed_record_class_TimeSpan : L05B3c, IValue { } -public partial struct L05Os93 { public sealed partial record L05_93_sealed_record_class_TimeSpan : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial class L05Oc94 { public sealed partial record L05_94_sealed_record_class_TimeSpan : IValue { } } -public partial class L05Oc95 { public sealed partial record L05_95_sealed_record_class_Uri : L05B1, IValue { } } -public partial struct L05Os96 { public sealed partial record L05_96_sealed_record_class_Uri : L05B2b, IValue { } } -public partial struct L05Os97 { public sealed partial record L05_97_sealed_record_class_Uri : L05B3c, IValue { } } -public partial class L05Ot98 { public partial class Inner { public sealed partial record L05_98_sealed_record_class_Uri : L05E1, IValue { } } } -public partial class L05Oc99 { public sealed partial record L05_99_sealed_record_class_bool : L05B2b, IValue { } } -public partial class L05Ot100 { public partial class Inner { public sealed partial record L05_100_sealed_record_class_bool : L05B2b, IValue { } } } -public partial struct L05Os101 { public sealed partial record L05_101_sealed_record_class_bool : L05B3c, IValue { } } -public partial class L05Ot102 { public partial class Inner { public sealed partial record L05_102_sealed_record_class_bool : L05E1, IValue { } } } -public partial class L05Ot103 { public partial class Inner { public sealed partial record L05_103_sealed_record_class_bool : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public partial class L05Ot104 { public partial class Inner { public sealed partial record L05_104_sealed_record_class_byte : L05B1, IValue { } } } -public partial struct L05Os105 { public sealed partial record L05_105_sealed_record_class_byte : L05B2b, IValue { } } -public partial class L05Oc106 { public sealed partial record L05_106_sealed_record_class_byte : L05B3c, IValue { } } -public sealed partial record L05_107_sealed_record_class_byte : L05B3c, IValue { } -public partial class L05Ot108 { public partial class Inner { public sealed partial record L05_108_sealed_record_class_byte : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public sealed partial record L05_109_sealed_record_class_byte : IL05Tag, IValue { string IL05Tag.Tag => "t"; } -public sealed partial record L05_110_sealed_record_class_byte : IValue { } -public partial class L05Ot111 { public partial class Inner { public sealed partial record L05_111_sealed_record_class_char : L05B1, IValue { } } } -public sealed partial record L05_112_sealed_record_class_char : L05B2b, IValue { } -public sealed partial record L05_113_sealed_record_class_char : L05E1, IValue { } -public partial class L05Ot114 { public partial class Inner { public sealed partial record L05_114_sealed_record_class_char : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } } -public partial class L05Oc115 { public sealed partial record L05_115_sealed_record_class_decimal : L05B1, IValue { } } -public partial struct L05Os116 { public sealed partial record L05_116_sealed_record_class_decimal : L05B3c, IValue { } } -public partial class L05Oc117 { public sealed partial record L05_117_sealed_record_class_decimal : L05E1, IValue { } } -public sealed partial record L05_118_sealed_record_class_decimal : L05E1, IValue { } -public partial class L05Ot119 { public partial class Inner { public sealed partial record L05_119_sealed_record_class_double : L05B1, IValue { } } } -public sealed partial record L05_120_sealed_record_class_double : L05B2b, IValue { } -public partial class L05Oc121 { public sealed partial record L05_121_sealed_record_class_double : L05B3c, IValue { } } -public partial class L05Oc122 { public sealed partial record L05_122_sealed_record_class_double : L05E1, IValue { } } -public partial struct L05Os123 { public sealed partial record L05_123_sealed_record_class_enum : L05B1, IValue { } } -public partial class L05Ot124 { public partial class Inner { public sealed partial record L05_124_sealed_record_class_enum : L05B2b, IValue { } } } -public partial class L05Ot125 { public partial class Inner { public sealed partial record L05_125_sealed_record_class_enum : L05B3c, IValue { } } } -public partial class L05Ot126 { public partial class Inner { public sealed partial record L05_126_sealed_record_class_enum : L05E1, IValue { } } } -public partial struct L05Os127 { public sealed partial record L05_127_sealed_record_class_enum : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public partial class L05Oc128 { public sealed partial record L05_128_sealed_record_class_enum : IValue { } } -public partial class L05Oc129 { public sealed partial record L05_129_sealed_record_class_flags_enum : L05B1, IValue { } } -public partial class L05Ot130 { public partial class Inner { public sealed partial record L05_130_sealed_record_class_flags_enum : L05B1, IValue { } } } -public sealed partial record L05_131_sealed_record_class_flags_enum : L05B2b, IValue { } -public sealed partial record L05_132_sealed_record_class_flags_enum : L05B3c, IValue { } -public partial class L05Ot133 { public partial class Inner { public sealed partial record L05_133_sealed_record_class_flags_enum : L05E1, IValue { } } } -public partial struct L05Os134 { public sealed partial record L05_134_sealed_record_class_flags_enum : IL05Tag, IValue { string IL05Tag.Tag => "t"; } } -public sealed partial record L05_135_sealed_record_class_int : L05B2b, IValue { } -public partial struct L05Os136 { public sealed partial record L05_136_sealed_record_class_int : L05E1, IValue { } } -public partial class L05Ot137 { public partial class Inner { public sealed partial record L05_137_sealed_record_class_long : L05B2b, IValue { } } } -public partial class L05Ot138 { public partial class Inner { public sealed partial record L05_138_sealed_record_class_long : L05B3c, IValue { } } } -public partial struct L05Os139 { public sealed partial record L05_139_sealed_record_class_long : L05E1, IValue { } } -public partial class L05Ot140 { public partial class Inner { public sealed partial record L05_140_sealed_record_class_own_record_class : L05B2b, IValue { } } } -public partial struct L05Os141 { public sealed partial record L05_141_sealed_record_class_own_record_class : L05E1, IValue { } } -public partial class L05Ot142 { public partial class Inner { public sealed partial record L05_142_sealed_record_class_own_record_class : L05E1, IValue { } } } -public partial struct L05Os143 { public sealed partial record L05_143_sealed_record_class_string : L05B1, IValue { } } -public sealed partial record L05_144_sealed_record_class_string : L05B3c, IValue { } -public partial class L05Ot145 { public partial class Inner { public sealed partial record L05_145_sealed_record_class_string : L05E1, IValue { } } } -public partial class L05Ot146 { public partial class Inner { public sealed partial record L05_146_sealed_record_class_ulong : L05B3c, IValue { } } } -public partial class L05Ot147 { public partial class Inner { public sealed partial record L05_147_sealed_record_class_ulong : L05E1, IValue { } } } -public sealed partial record L05_148_sealed_record_class_ulong : L05E1, IValue { } -public partial struct L05Os149 { public sealed partial record L05_149_validated_class_DateOnly : L05B1, IValidatedValue { public static Result Create(DateOnly value) => new L05_149_validated_class_DateOnly(value); } } -public sealed partial record L05_150_validated_class_DateOnly : L05E1, IValidatedValue { public static Result Create(DateOnly value) => new L05_150_validated_class_DateOnly(value); } -public sealed partial record L05_151_validated_class_DateOnly : IL05Tag, IValidatedValue { public static Result Create(DateOnly value) => new L05_151_validated_class_DateOnly(value); string IL05Tag.Tag => "t"; } -public partial class L05Ot152 { public partial class Inner { public sealed partial record L05_152_validated_class_DateOnly : IValidatedValue { public static Result Create(DateOnly value) => new L05_152_validated_class_DateOnly(value); } } } -public partial struct L05Os153 { public sealed partial record L05_153_validated_class_DateTime : L05B1, IValidatedValue { public static Result Create(DateTime value) => new L05_153_validated_class_DateTime(value); } } -public partial class L05Ot154 { public partial class Inner { public sealed partial record L05_154_validated_class_DateTime : L05B1, IValidatedValue { public static Result Create(DateTime value) => new L05_154_validated_class_DateTime(value); } } } -public sealed partial record L05_155_validated_class_DateTime : L05B2b, IValidatedValue { public static Result Create(DateTime value) => new L05_155_validated_class_DateTime(value); } -public partial class L05Oc156 { public sealed partial record L05_156_validated_class_DateTime : L05B3c, IValidatedValue { public static Result Create(DateTime value) => new L05_156_validated_class_DateTime(value); } } -public partial struct L05Os157 { public sealed partial record L05_157_validated_class_DateTime : L05E1, IValidatedValue { public static Result Create(DateTime value) => new L05_157_validated_class_DateTime(value); } } -public partial class L05Oc158 { public sealed partial record L05_158_validated_class_DateTimeOffset : IL05Tag, IValidatedValue { public static Result Create(DateTimeOffset value) => new L05_158_validated_class_DateTimeOffset(value); string IL05Tag.Tag => "t"; } } -public partial class L05Oc159 { public sealed partial record L05_159_validated_class_Guid : L05B2b, IValidatedValue { public static Result Create(Guid value) => new L05_159_validated_class_Guid(value); } } -public partial class L05Ot160 { public partial class Inner { public sealed partial record L05_160_validated_class_Guid : L05B2b, IValidatedValue { public static Result Create(Guid value) => new L05_160_validated_class_Guid(value); } } } -public partial class L05Oc161 { public sealed partial record L05_161_validated_class_Guid : IL05Tag, IValidatedValue { public static Result Create(Guid value) => new L05_161_validated_class_Guid(value); string IL05Tag.Tag => "t"; } } -public partial class L05Ot162 { public partial class Inner { public sealed partial record L05_162_validated_class_NodaTime_Instant : L05B2b, IValidatedValue { public static Result Create(global::NodaTime.Instant value) => new L05_162_validated_class_NodaTime_Instant(value); } } } -public partial class L05Oc163 { public sealed partial record L05_163_validated_class_NodaTime_Instant : L05B3c, IValidatedValue { public static Result Create(global::NodaTime.Instant value) => new L05_163_validated_class_NodaTime_Instant(value); } } -public partial struct L05Os164 { public sealed partial record L05_164_validated_class_NodaTime_Instant : L05B3c, IValidatedValue { public static Result Create(global::NodaTime.Instant value) => new L05_164_validated_class_NodaTime_Instant(value); } } -public partial class L05Ot165 { public partial class Inner { public sealed partial record L05_165_validated_class_NodaTime_Instant : L05B3c, IValidatedValue { public static Result Create(global::NodaTime.Instant value) => new L05_165_validated_class_NodaTime_Instant(value); } } } -public partial class L05Oc166 { public sealed partial record L05_166_validated_class_NodaTime_Instant : L05E1, IValidatedValue { public static Result Create(global::NodaTime.Instant value) => new L05_166_validated_class_NodaTime_Instant(value); } } -public sealed partial record L05_167_validated_class_NodaTime_Instant : L05E1, IValidatedValue { public static Result Create(global::NodaTime.Instant value) => new L05_167_validated_class_NodaTime_Instant(value); } -public partial class L05Oc168 { public sealed partial record L05_168_validated_class_TimeOnly : L05B1, IValidatedValue { public static Result Create(TimeOnly value) => new L05_168_validated_class_TimeOnly(value); } } -public partial struct L05Os169 { public sealed partial record L05_169_validated_class_TimeOnly : L05B1, IValidatedValue { public static Result Create(TimeOnly value) => new L05_169_validated_class_TimeOnly(value); } } -public sealed partial record L05_170_validated_class_TimeOnly : L05B2b, IValidatedValue { public static Result Create(TimeOnly value) => new L05_170_validated_class_TimeOnly(value); } -public partial class L05Oc171 { public sealed partial record L05_171_validated_class_TimeOnly : L05E1, IValidatedValue { public static Result Create(TimeOnly value) => new L05_171_validated_class_TimeOnly(value); } } -public sealed partial record L05_172_validated_class_TimeOnly : IValidatedValue { public static Result Create(TimeOnly value) => new L05_172_validated_class_TimeOnly(value); } -public sealed partial record L05_173_validated_class_TimeSpan : L05B1, IValidatedValue { public static Result Create(TimeSpan value) => new L05_173_validated_class_TimeSpan(value); } -public partial class L05Oc174 { public sealed partial record L05_174_validated_class_TimeSpan : L05B2b, IValidatedValue { public static Result Create(TimeSpan value) => new L05_174_validated_class_TimeSpan(value); } } -public partial struct L05Os175 { public sealed partial record L05_175_validated_class_TimeSpan : L05B2b, IValidatedValue { public static Result Create(TimeSpan value) => new L05_175_validated_class_TimeSpan(value); } } -public partial class L05Ot176 { public partial class Inner { public sealed partial record L05_176_validated_class_TimeSpan : L05E1, IValidatedValue { public static Result Create(TimeSpan value) => new L05_176_validated_class_TimeSpan(value); } } } -public partial struct L05Os177 { public sealed partial record L05_177_validated_class_Uri : L05B1, IValidatedValue { public static Result Create(Uri value) => new L05_177_validated_class_Uri(value); } } -public partial class L05Ot178 { public partial class Inner { public sealed partial record L05_178_validated_class_Uri : L05B3c, IValidatedValue { public static Result Create(Uri value) => new L05_178_validated_class_Uri(value); } } } -public sealed partial record L05_179_validated_class_Uri : IL05Tag, IValidatedValue { public static Result Create(Uri value) => new L05_179_validated_class_Uri(value); string IL05Tag.Tag => "t"; } -public partial class L05Oc180 { public sealed partial record L05_180_validated_class_Uri : IValidatedValue { public static Result Create(Uri value) => new L05_180_validated_class_Uri(value); } } -public partial struct L05Os181 { public sealed partial record L05_181_validated_class_bool : L05B1, IValidatedValue { public static Result Create(bool value) => new L05_181_validated_class_bool(value); } } -public partial class L05Oc182 { public sealed partial record L05_182_validated_class_bool : L05E1, IValidatedValue { public static Result Create(bool value) => new L05_182_validated_class_bool(value); } } -public partial struct L05Os183 { public sealed partial record L05_183_validated_class_bool : IL05Tag, IValidatedValue { public static Result Create(bool value) => new L05_183_validated_class_bool(value); string IL05Tag.Tag => "t"; } } -public partial struct L05Os184 { public sealed partial record L05_184_validated_class_byte : L05B2b, IValidatedValue { public static Result Create(byte value) => new L05_184_validated_class_byte(value); } } -public partial class L05Oc185 { public sealed partial record L05_185_validated_class_byte : L05E1, IValidatedValue { public static Result Create(byte value) => new L05_185_validated_class_byte(value); } } -public partial class L05Ot186 { public partial class Inner { public sealed partial record L05_186_validated_class_byte : L05E1, IValidatedValue { public static Result Create(byte value) => new L05_186_validated_class_byte(value); } } } -public partial struct L05Os187 { public sealed partial record L05_187_validated_class_byte : IL05Tag, IValidatedValue { public static Result Create(byte value) => new L05_187_validated_class_byte(value); string IL05Tag.Tag => "t"; } } -public partial class L05Oc188 { public sealed partial record L05_188_validated_class_char : L05B2b, IValidatedValue { public static Result Create(char value) => new L05_188_validated_class_char(value); } } -public partial class L05Oc189 { public sealed partial record L05_189_validated_class_char : L05B3c, IValidatedValue { public static Result Create(char value) => new L05_189_validated_class_char(value); } } -public partial class L05Ot190 { public partial class Inner { public sealed partial record L05_190_validated_class_char : L05B3c, IValidatedValue { public static Result Create(char value) => new L05_190_validated_class_char(value); } } } -public partial class L05Ot191 { public partial class Inner { public sealed partial record L05_191_validated_class_char : L05E1, IValidatedValue { public static Result Create(char value) => new L05_191_validated_class_char(value); } } } -public sealed partial record L05_192_validated_class_char : L05E1, IValidatedValue { public static Result Create(char value) => new L05_192_validated_class_char(value); } -public sealed partial record L05_193_validated_class_decimal : L05B1, IValidatedValue { public static Result Create(decimal value) => new L05_193_validated_class_decimal(value); } -public partial class L05Ot194 { public partial class Inner { public sealed partial record L05_194_validated_class_decimal : L05B2b, IValidatedValue { public static Result Create(decimal value) => new L05_194_validated_class_decimal(value); } } } -public sealed partial record L05_195_validated_class_decimal : L05B2b, IValidatedValue { public static Result Create(decimal value) => new L05_195_validated_class_decimal(value); } -public partial struct L05Os196 { public sealed partial record L05_196_validated_class_decimal : IValidatedValue { public static Result Create(decimal value) => new L05_196_validated_class_decimal(value); } } -public sealed partial record L05_197_validated_class_decimal : IValidatedValue { public static Result Create(decimal value) => new L05_197_validated_class_decimal(value); } -public sealed partial record L05_198_validated_class_double : L05B2b, IValidatedValue { public static Result Create(double value) => new L05_198_validated_class_double(value); } -public partial struct L05Os199 { public sealed partial record L05_199_validated_class_double : L05B3c, IValidatedValue { public static Result Create(double value) => new L05_199_validated_class_double(value); } } -public partial struct L05Os200 { public sealed partial record L05_200_validated_class_double : IL05Tag, IValidatedValue { public static Result Create(double value) => new L05_200_validated_class_double(value); string IL05Tag.Tag => "t"; } } -public partial class L05Ot201 { public partial class Inner { public sealed partial record L05_201_validated_class_enum : L05B1, IValidatedValue { public static Result Create(PEnum value) => new L05_201_validated_class_enum(value); } } } -public sealed partial record L05_202_validated_class_enum : L05B2b, IValidatedValue { public static Result Create(PEnum value) => new L05_202_validated_class_enum(value); } -public partial class L05Oc203 { public sealed partial record L05_203_validated_class_flags_enum : L05E1, IValidatedValue { public static Result Create(PFlags value) => new L05_203_validated_class_flags_enum(value); } } -public partial class L05Ot204 { public partial class Inner { public sealed partial record L05_204_validated_class_flags_enum : IL05Tag, IValidatedValue { public static Result Create(PFlags value) => new L05_204_validated_class_flags_enum(value); string IL05Tag.Tag => "t"; } } } -public partial struct L05Os205 { public sealed partial record L05_205_validated_class_int : L05B1, IValidatedValue { public static Result Create(int value) => new L05_205_validated_class_int(value); } } -public partial class L05Oc206 { public sealed partial record L05_206_validated_class_int : L05B3c, IValidatedValue { public static Result Create(int value) => new L05_206_validated_class_int(value); } } -public partial class L05Oc207 { public sealed partial record L05_207_validated_class_int : IL05Tag, IValidatedValue { public static Result Create(int value) => new L05_207_validated_class_int(value); string IL05Tag.Tag => "t"; } } -public partial class L05Oc208 { public sealed partial record L05_208_validated_class_long : L05B1, IValidatedValue { public static Result Create(long value) => new L05_208_validated_class_long(value); } } -public partial class L05Ot209 { public partial class Inner { public sealed partial record L05_209_validated_class_long : L05B2b, IValidatedValue { public static Result Create(long value) => new L05_209_validated_class_long(value); } } } -public partial class L05Oc210 { public sealed partial record L05_210_validated_class_long : L05E1, IValidatedValue { public static Result Create(long value) => new L05_210_validated_class_long(value); } } -public partial struct L05Os211 { public sealed partial record L05_211_validated_class_long : L05E1, IValidatedValue { public static Result Create(long value) => new L05_211_validated_class_long(value); } } -public partial class L05Ot212 { public partial class Inner { public sealed partial record L05_212_validated_class_long : IL05Tag, IValidatedValue { public static Result Create(long value) => new L05_212_validated_class_long(value); string IL05Tag.Tag => "t"; } } } -public partial struct L05Os213 { public sealed partial record L05_213_validated_class_own_record_class : L05B1, IValidatedValue { public static Result Create(PRec value) => new L05_213_validated_class_own_record_class(value); } } -public partial class L05Ot214 { public partial class Inner { public sealed partial record L05_214_validated_class_own_record_class : L05B1, IValidatedValue { public static Result Create(PRec value) => new L05_214_validated_class_own_record_class(value); } } } -public sealed partial record L05_215_validated_class_own_record_class : L05B2b, IValidatedValue { public static Result Create(PRec value) => new L05_215_validated_class_own_record_class(value); } -public partial struct L05Os216 { public sealed partial record L05_216_validated_class_own_record_class : L05B3c, IValidatedValue { public static Result Create(PRec value) => new L05_216_validated_class_own_record_class(value); } } -public partial class L05Ot217 { public partial class Inner { public sealed partial record L05_217_validated_class_own_record_class : L05B3c, IValidatedValue { public static Result Create(PRec value) => new L05_217_validated_class_own_record_class(value); } } } -public partial struct L05Os218 { public sealed partial record L05_218_validated_class_own_record_class : L05E1, IValidatedValue { public static Result Create(PRec value) => new L05_218_validated_class_own_record_class(value); } } -public partial class L05Oc219 { public sealed partial record L05_219_validated_class_own_record_class : IValidatedValue { public static Result Create(PRec value) => new L05_219_validated_class_own_record_class(value); } } -public partial struct L05Os220 { public sealed partial record L05_220_validated_class_string : L05B1, IValidatedValue { public static Result Create(string value) => new L05_220_validated_class_string(value); } } -public partial class L05Ot221 { public partial class Inner { public sealed partial record L05_221_validated_class_string : L05B2b, IValidatedValue { public static Result Create(string value) => new L05_221_validated_class_string(value); } } } -public partial class L05Oc222 { public sealed partial record L05_222_validated_class_string : L05B3c, IValidatedValue { public static Result Create(string value) => new L05_222_validated_class_string(value); } } -public sealed partial record L05_223_validated_class_string : IL05Tag, IValidatedValue { public static Result Create(string value) => new L05_223_validated_class_string(value); string IL05Tag.Tag => "t"; } -public partial struct L05Os224 { public sealed partial record L05_224_validated_class_ulong : L05B1, IValidatedValue { public static Result Create(ulong value) => new L05_224_validated_class_ulong(value); } } -public sealed partial record L05_225_validated_class_ulong : L05B1, IValidatedValue { public static Result Create(ulong value) => new L05_225_validated_class_ulong(value); } -public sealed partial record L05_226_validated_class_ulong : L05B2b, IValidatedValue { public static Result Create(ulong value) => new L05_226_validated_class_ulong(value); } -public partial class L05Oc227 { public sealed partial record L05_227_validated_class_ulong : L05B3c, IValidatedValue { public static Result Create(ulong value) => new L05_227_validated_class_ulong(value); } } -public partial class L05Oc228 { public sealed partial record L05_228_validated_class_ulong : IL05Tag, IValidatedValue { public static Result Create(ulong value) => new L05_228_validated_class_ulong(value); string IL05Tag.Tag => "t"; } } -public partial struct L05Os229 { public sealed partial record L05_229_validated_class_ulong : IL05Tag, IValidatedValue { public static Result Create(ulong value) => new L05_229_validated_class_ulong(value); string IL05Tag.Tag => "t"; } } -public partial struct L05Os230 { public sealed partial record L05_230_validated_class_ulong : IValidatedValue { public static Result Create(ulong value) => new L05_230_validated_class_ulong(value); } } -public partial class L05Ot231 { public partial class Inner { public readonly partial record struct L05_231_validated_struct_DateOnly : IL05Tag, IValidatedValue { public static Result Create(DateOnly value) => new L05_231_validated_struct_DateOnly(value); string IL05Tag.Tag => "t"; } } } -public readonly partial record struct L05_232_validated_struct_DateOnly : IValidatedValue { public static Result Create(DateOnly value) => new L05_232_validated_struct_DateOnly(value); } -public partial class L05Ot233 { public partial class Inner { public readonly partial record struct L05_233_validated_struct_DateTime : IL05Tag, IValidatedValue { public static Result Create(DateTime value) => new L05_233_validated_struct_DateTime(value); string IL05Tag.Tag => "t"; } } } -public partial struct L05Os234 { public readonly partial record struct L05_234_validated_struct_DateTimeOffset : IValidatedValue { public static Result Create(DateTimeOffset value) => new L05_234_validated_struct_DateTimeOffset(value); } } -public readonly partial record struct L05_235_validated_struct_DateTimeOffset : IValidatedValue { public static Result Create(DateTimeOffset value) => new L05_235_validated_struct_DateTimeOffset(value); } -public partial class L05Oc236 { public readonly partial record struct L05_236_validated_struct_Guid : IValidatedValue { public static Result Create(Guid value) => new L05_236_validated_struct_Guid(value); } } -public partial struct L05Os237 { public readonly partial record struct L05_237_validated_struct_Guid : IValidatedValue { public static Result Create(Guid value) => new L05_237_validated_struct_Guid(value); } } -public readonly partial record struct L05_238_validated_struct_NodaTime_Instant : IValidatedValue { public static Result Create(global::NodaTime.Instant value) => new L05_238_validated_struct_NodaTime_Instant(value); } -public partial class L05Oc239 { public readonly partial record struct L05_239_validated_struct_TimeOnly : IL05Tag, IValidatedValue { public static Result Create(TimeOnly value) => new L05_239_validated_struct_TimeOnly(value); string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_240_validated_struct_TimeOnly : IValidatedValue { public static Result Create(TimeOnly value) => new L05_240_validated_struct_TimeOnly(value); } -public partial class L05Oc241 { public readonly partial record struct L05_241_validated_struct_TimeSpan : IValidatedValue { public static Result Create(TimeSpan value) => new L05_241_validated_struct_TimeSpan(value); } } -public partial struct L05Os242 { public readonly partial record struct L05_242_validated_struct_Uri : IL05Tag, IValidatedValue { public static Result Create(Uri value) => new L05_242_validated_struct_Uri(value); string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_243_validated_struct_Uri : IL05Tag, IValidatedValue { public static Result Create(Uri value) => new L05_243_validated_struct_Uri(value); string IL05Tag.Tag => "t"; } -public partial class L05Oc244 { public readonly partial record struct L05_244_validated_struct_Uri : IValidatedValue { public static Result Create(Uri value) => new L05_244_validated_struct_Uri(value); } } -public partial class L05Oc245 { public readonly partial record struct L05_245_validated_struct_bool : IL05Tag, IValidatedValue { public static Result Create(bool value) => new L05_245_validated_struct_bool(value); string IL05Tag.Tag => "t"; } } -public partial class L05Ot246 { public partial class Inner { public readonly partial record struct L05_246_validated_struct_bool : IValidatedValue { public static Result Create(bool value) => new L05_246_validated_struct_bool(value); } } } -public readonly partial record struct L05_247_validated_struct_byte : IL05Tag, IValidatedValue { public static Result Create(byte value) => new L05_247_validated_struct_byte(value); string IL05Tag.Tag => "t"; } -public partial class L05Oc248 { public readonly partial record struct L05_248_validated_struct_char : IL05Tag, IValidatedValue { public static Result Create(char value) => new L05_248_validated_struct_char(value); string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_249_validated_struct_decimal : IL05Tag, IValidatedValue { public static Result Create(decimal value) => new L05_249_validated_struct_decimal(value); string IL05Tag.Tag => "t"; } -public partial class L05Ot250 { public partial class Inner { public readonly partial record struct L05_250_validated_struct_double : IL05Tag, IValidatedValue { public static Result Create(double value) => new L05_250_validated_struct_double(value); string IL05Tag.Tag => "t"; } } } -public partial class L05Ot251 { public partial class Inner { public readonly partial record struct L05_251_validated_struct_double : IValidatedValue { public static Result Create(double value) => new L05_251_validated_struct_double(value); } } } -public partial class L05Oc252 { public readonly partial record struct L05_252_validated_struct_enum : IValidatedValue { public static Result Create(PEnum value) => new L05_252_validated_struct_enum(value); } } -public partial struct L05Os253 { public readonly partial record struct L05_253_validated_struct_enum : IValidatedValue { public static Result Create(PEnum value) => new L05_253_validated_struct_enum(value); } } -public partial class L05Ot254 { public partial class Inner { public readonly partial record struct L05_254_validated_struct_enum : IValidatedValue { public static Result Create(PEnum value) => new L05_254_validated_struct_enum(value); } } } -public partial class L05Ot255 { public partial class Inner { public readonly partial record struct L05_255_validated_struct_flags_enum : IValidatedValue { public static Result Create(PFlags value) => new L05_255_validated_struct_flags_enum(value); } } } -public readonly partial record struct L05_256_validated_struct_flags_enum : IValidatedValue { public static Result Create(PFlags value) => new L05_256_validated_struct_flags_enum(value); } -public partial struct L05Os257 { public readonly partial record struct L05_257_validated_struct_int : IL05Tag, IValidatedValue { public static Result Create(int value) => new L05_257_validated_struct_int(value); string IL05Tag.Tag => "t"; } } -public partial class L05Ot258 { public partial class Inner { public readonly partial record struct L05_258_validated_struct_int : IL05Tag, IValidatedValue { public static Result Create(int value) => new L05_258_validated_struct_int(value); string IL05Tag.Tag => "t"; } } } -public partial class L05Ot259 { public partial class Inner { public readonly partial record struct L05_259_validated_struct_int : IValidatedValue { public static Result Create(int value) => new L05_259_validated_struct_int(value); } } } -public readonly partial record struct L05_260_validated_struct_int : IValidatedValue { public static Result Create(int value) => new L05_260_validated_struct_int(value); } -public partial struct L05Os261 { public readonly partial record struct L05_261_validated_struct_long : IL05Tag, IValidatedValue { public static Result Create(long value) => new L05_261_validated_struct_long(value); string IL05Tag.Tag => "t"; } } -public readonly partial record struct L05_262_validated_struct_long : IL05Tag, IValidatedValue { public static Result Create(long value) => new L05_262_validated_struct_long(value); string IL05Tag.Tag => "t"; } -public readonly partial record struct L05_263_validated_struct_own_record_class : IL05Tag, IValidatedValue { public static Result Create(PRec value) => new L05_263_validated_struct_own_record_class(value); string IL05Tag.Tag => "t"; } -public partial struct L05Os264 { public readonly partial record struct L05_264_validated_struct_own_record_class : IValidatedValue { public static Result Create(PRec value) => new L05_264_validated_struct_own_record_class(value); } } -public partial class L05Ot265 { public partial class Inner { public readonly partial record struct L05_265_validated_struct_string : IL05Tag, IValidatedValue { public static Result Create(string value) => new L05_265_validated_struct_string(value); string IL05Tag.Tag => "t"; } } } -public partial class L05Oc266 { public readonly partial record struct L05_266_validated_struct_string : IValidatedValue { public static Result Create(string value) => new L05_266_validated_struct_string(value); } } -public readonly partial record struct L05_267_validated_struct_string : IValidatedValue { public static Result Create(string value) => new L05_267_validated_struct_string(value); } -public readonly partial record struct L05_268_validated_struct_ulong : IL05Tag, IValidatedValue { public static Result Create(ulong value) => new L05_268_validated_struct_ulong(value); string IL05Tag.Tag => "t"; } -public readonly partial record struct L05_269_validated_struct_ulong : IValidatedValue { public static Result Create(ulong value) => new L05_269_validated_struct_ulong(value); } - -public static partial class L05Registry -{ - static partial void Fill() - { - Add("closed-set", "-", "base-depth-1", "nested-in-class", typeof(L05Oc0.L05_0_closed_set__), () => L05Oc0.L05_0_closed_set__.Alpha, () => L05Oc0.L05_0_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Oc0.L05_0_closed_set__.Beta); - Add("closed-set", "-", "base-depth-1", "nested-two-levels", typeof(L05Ot1.Inner.L05_1_closed_set__), () => L05Ot1.Inner.L05_1_closed_set__.Alpha, () => L05Ot1.Inner.L05_1_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Ot1.Inner.L05_1_closed_set__.Beta); - Add("closed-set", "-", "base-depth-2", "nested-in-class", typeof(L05Oc2.L05_2_closed_set__), () => L05Oc2.L05_2_closed_set__.Alpha, () => L05Oc2.L05_2_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Oc2.L05_2_closed_set__.Beta); - Add("closed-set", "-", "base-depth-2", "nested-in-struct", typeof(L05Os3.L05_3_closed_set__), () => L05Os3.L05_3_closed_set__.Alpha, () => L05Os3.L05_3_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Os3.L05_3_closed_set__.Beta); - Add("closed-set", "-", "base-depth-2", "top-level", typeof(L05_4_closed_set__), () => L05_4_closed_set__.Alpha, () => L05_4_closed_set__.All.First(m => m.Name == "Alpha"), () => L05_4_closed_set__.Beta); - Add("closed-set", "-", "base-depth-3", "nested-in-class", typeof(L05Oc5.L05_5_closed_set__), () => L05Oc5.L05_5_closed_set__.Alpha, () => L05Oc5.L05_5_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Oc5.L05_5_closed_set__.Beta); - Add("closed-set", "-", "base-depth-3", "nested-in-struct", typeof(L05Os6.L05_6_closed_set__), () => L05Os6.L05_6_closed_set__.Alpha, () => L05Os6.L05_6_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Os6.L05_6_closed_set__.Beta); - Add("closed-set", "-", "base-depth-3", "nested-two-levels", typeof(L05Ot7.Inner.L05_7_closed_set__), () => L05Ot7.Inner.L05_7_closed_set__.Alpha, () => L05Ot7.Inner.L05_7_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Ot7.Inner.L05_7_closed_set__.Beta); - Add("closed-set", "-", "base-depth-3", "top-level", typeof(L05_8_closed_set__), () => L05_8_closed_set__.Alpha, () => L05_8_closed_set__.All.First(m => m.Name == "Alpha"), () => L05_8_closed_set__.Beta); - Add("closed-set", "-", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc9.L05_9_closed_set__), () => L05Oc9.L05_9_closed_set__.Alpha, () => L05Oc9.L05_9_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Oc9.L05_9_closed_set__.Beta); - Add("closed-set", "-", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os10.L05_10_closed_set__), () => L05Os10.L05_10_closed_set__.Alpha, () => L05Os10.L05_10_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Os10.L05_10_closed_set__.Beta); - Add("closed-set", "-", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot11.Inner.L05_11_closed_set__), () => L05Ot11.Inner.L05_11_closed_set__.Alpha, () => L05Ot11.Inner.L05_11_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Ot11.Inner.L05_11_closed_set__.Beta); - Add("closed-set", "-", "base-explicit-IEquatable", "top-level", typeof(L05_12_closed_set__), () => L05_12_closed_set__.Alpha, () => L05_12_closed_set__.All.First(m => m.Name == "Alpha"), () => L05_12_closed_set__.Beta); - Add("closed-set", "-", "explicit-interface-member", "nested-in-class", typeof(L05Oc13.L05_13_closed_set__), () => L05Oc13.L05_13_closed_set__.Alpha, () => L05Oc13.L05_13_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Oc13.L05_13_closed_set__.Beta); - Add("closed-set", "-", "explicit-interface-member", "top-level", typeof(L05_14_closed_set__), () => L05_14_closed_set__.Alpha, () => L05_14_closed_set__.All.First(m => m.Name == "Alpha"), () => L05_14_closed_set__.Beta); - Add("closed-set", "-", "none", "nested-in-struct", typeof(L05Os15.L05_15_closed_set__), () => L05Os15.L05_15_closed_set__.Alpha, () => L05Os15.L05_15_closed_set__.All.First(m => m.Name == "Alpha"), () => L05Os15.L05_15_closed_set__.Beta); - Add("record-struct", "DateOnly", "explicit-interface-member", "nested-in-class", typeof(L05Oc16.L05_16_record_struct_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("record-struct", "DateOnly", "explicit-interface-member", "nested-in-struct", typeof(L05Os17.L05_17_record_struct_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("record-struct", "DateOnly", "explicit-interface-member", "nested-two-levels", typeof(L05Ot18.Inner.L05_18_record_struct_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("record-struct", "DateTime", "none", "nested-in-class", typeof(L05Oc19.L05_19_record_struct_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("record-struct", "DateTimeOffset", "none", "nested-in-struct", typeof(L05Os20.L05_20_record_struct_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("record-struct", "Guid", "explicit-interface-member", "nested-in-struct", typeof(L05Os21.L05_21_record_struct_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("record-struct", "Guid", "explicit-interface-member", "top-level", typeof(L05_22_record_struct_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("record-struct", "NodaTime.Instant", "explicit-interface-member", "nested-in-class", typeof(L05Oc23.L05_23_record_struct_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("record-struct", "NodaTime.Instant", "explicit-interface-member", "nested-in-struct", typeof(L05Os24.L05_24_record_struct_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("record-struct", "NodaTime.Instant", "explicit-interface-member", "top-level", typeof(L05_25_record_struct_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("record-struct", "TimeOnly", "explicit-interface-member", "nested-two-levels", typeof(L05Ot26.Inner.L05_26_record_struct_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("record-struct", "TimeOnly", "explicit-interface-member", "top-level", typeof(L05_27_record_struct_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("record-struct", "TimeOnly", "none", "nested-in-struct", typeof(L05Os28.L05_28_record_struct_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("record-struct", "TimeSpan", "explicit-interface-member", "nested-in-class", typeof(L05Oc29.L05_29_record_struct_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("record-struct", "TimeSpan", "none", "nested-in-class", typeof(L05Oc30.L05_30_record_struct_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("record-struct", "Uri", "explicit-interface-member", "top-level", typeof(L05_31_record_struct_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("record-struct", "Uri", "none", "top-level", typeof(L05_32_record_struct_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("record-struct", "bool", "explicit-interface-member", "nested-in-struct", typeof(L05Os33.L05_33_record_struct_bool), () => false, () => false, () => true); - Add("record-struct", "bool", "explicit-interface-member", "nested-two-levels", typeof(L05Ot34.Inner.L05_34_record_struct_bool), () => false, () => false, () => true); - Add("record-struct", "bool", "explicit-interface-member", "top-level", typeof(L05_35_record_struct_bool), () => false, () => false, () => true); - Add("record-struct", "byte", "explicit-interface-member", "top-level", typeof(L05_36_record_struct_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("record-struct", "byte", "none", "nested-in-class", typeof(L05Oc37.L05_37_record_struct_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("record-struct", "char", "explicit-interface-member", "top-level", typeof(L05_38_record_struct_char), () => 'a', () => 'a', () => 'b'); - Add("record-struct", "char", "none", "nested-in-struct", typeof(L05Os39.L05_39_record_struct_char), () => 'a', () => 'a', () => 'b'); - Add("record-struct", "decimal", "explicit-interface-member", "nested-in-class", typeof(L05Oc40.L05_40_record_struct_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("record-struct", "decimal", "explicit-interface-member", "top-level", typeof(L05_41_record_struct_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("record-struct", "decimal", "none", "nested-in-class", typeof(L05Oc42.L05_42_record_struct_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("record-struct", "double", "explicit-interface-member", "nested-in-class", typeof(L05Oc43.L05_43_record_struct_double), () => 0.0, () => -0.0, () => 1.5); - Add("record-struct", "double", "explicit-interface-member", "nested-in-struct", typeof(L05Os44.L05_44_record_struct_double), () => 0.0, () => -0.0, () => 1.5); - Add("record-struct", "double", "explicit-interface-member", "nested-two-levels", typeof(L05Ot45.Inner.L05_45_record_struct_double), () => 0.0, () => -0.0, () => 1.5); - Add("record-struct", "double", "none", "nested-two-levels", typeof(L05Ot46.Inner.L05_46_record_struct_double), () => 0.0, () => -0.0, () => 1.5); - Add("record-struct", "enum", "explicit-interface-member", "nested-in-class", typeof(L05Oc47.L05_47_record_struct_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("record-struct", "enum", "explicit-interface-member", "nested-in-struct", typeof(L05Os48.L05_48_record_struct_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("record-struct", "flags-enum", "explicit-interface-member", "top-level", typeof(L05_49_record_struct_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("record-struct", "flags-enum", "none", "top-level", typeof(L05_50_record_struct_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("record-struct", "int", "explicit-interface-member", "nested-in-struct", typeof(L05Os51.L05_51_record_struct_int), () => 42, () => 42, () => 43); - Add("record-struct", "int", "none", "nested-two-levels", typeof(L05Ot52.Inner.L05_52_record_struct_int), () => 42, () => 42, () => 43); - Add("record-struct", "long", "none", "nested-two-levels", typeof(L05Ot53.Inner.L05_53_record_struct_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("record-struct", "long", "none", "top-level", typeof(L05_54_record_struct_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("record-struct", "own-record-class", "explicit-interface-member", "nested-two-levels", typeof(L05Ot55.Inner.L05_55_record_struct_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("record-struct", "string", "explicit-interface-member", "nested-in-struct", typeof(L05Os56.L05_56_record_struct_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("record-struct", "string", "explicit-interface-member", "top-level", typeof(L05_57_record_struct_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("record-struct", "string", "none", "nested-in-class", typeof(L05Oc58.L05_58_record_struct_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("record-struct", "ulong", "explicit-interface-member", "nested-in-class", typeof(L05Oc59.L05_59_record_struct_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("record-struct", "ulong", "explicit-interface-member", "nested-in-struct", typeof(L05Os60.L05_60_record_struct_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("sealed-record-class", "DateOnly", "base-depth-2", "nested-in-struct", typeof(L05Os61.L05_61_sealed_record_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("sealed-record-class", "DateOnly", "base-depth-3", "top-level", typeof(L05_62_sealed_record_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("sealed-record-class", "DateOnly", "none", "nested-in-struct", typeof(L05Os63.L05_63_sealed_record_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("sealed-record-class", "DateOnly", "none", "top-level", typeof(L05_64_sealed_record_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("sealed-record-class", "DateTime", "base-depth-1", "top-level", typeof(L05_65_sealed_record_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("sealed-record-class", "DateTime", "base-depth-2", "nested-in-class", typeof(L05Oc66.L05_66_sealed_record_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("sealed-record-class", "DateTime", "base-depth-3", "nested-in-struct", typeof(L05Os67.L05_67_sealed_record_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("sealed-record-class", "DateTime", "base-depth-3", "nested-two-levels", typeof(L05Ot68.Inner.L05_68_sealed_record_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("sealed-record-class", "DateTime", "base-depth-3", "top-level", typeof(L05_69_sealed_record_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("sealed-record-class", "DateTime", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os70.L05_70_sealed_record_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("sealed-record-class", "DateTimeOffset", "base-depth-1", "nested-in-class", typeof(L05Oc71.L05_71_sealed_record_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("sealed-record-class", "DateTimeOffset", "base-depth-2", "top-level", typeof(L05_72_sealed_record_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("sealed-record-class", "DateTimeOffset", "base-depth-3", "nested-in-class", typeof(L05Oc73.L05_73_sealed_record_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("sealed-record-class", "DateTimeOffset", "base-depth-3", "nested-two-levels", typeof(L05Ot74.Inner.L05_74_sealed_record_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("sealed-record-class", "DateTimeOffset", "base-depth-3", "top-level", typeof(L05_75_sealed_record_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("sealed-record-class", "DateTimeOffset", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc76.L05_76_sealed_record_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("sealed-record-class", "DateTimeOffset", "base-explicit-IEquatable", "top-level", typeof(L05_77_sealed_record_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("sealed-record-class", "Guid", "base-depth-1", "nested-in-class", typeof(L05Oc78.L05_78_sealed_record_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("sealed-record-class", "Guid", "base-depth-1", "nested-in-struct", typeof(L05Os79.L05_79_sealed_record_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("sealed-record-class", "Guid", "base-depth-2", "top-level", typeof(L05_80_sealed_record_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("sealed-record-class", "Guid", "base-depth-3", "top-level", typeof(L05_81_sealed_record_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("sealed-record-class", "Guid", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc82.L05_82_sealed_record_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("sealed-record-class", "Guid", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot83.Inner.L05_83_sealed_record_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("sealed-record-class", "NodaTime.Instant", "base-depth-1", "nested-two-levels", typeof(L05Ot84.Inner.L05_84_sealed_record_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("sealed-record-class", "NodaTime.Instant", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os85.L05_85_sealed_record_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("sealed-record-class", "NodaTime.Instant", "none", "nested-in-struct", typeof(L05Os86.L05_86_sealed_record_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("sealed-record-class", "TimeOnly", "base-depth-3", "nested-in-struct", typeof(L05Os87.L05_87_sealed_record_class_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("sealed-record-class", "TimeOnly", "base-depth-3", "nested-two-levels", typeof(L05Ot88.Inner.L05_88_sealed_record_class_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("sealed-record-class", "TimeSpan", "base-depth-1", "nested-in-class", typeof(L05Oc89.L05_89_sealed_record_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("sealed-record-class", "TimeSpan", "base-depth-1", "nested-two-levels", typeof(L05Ot90.Inner.L05_90_sealed_record_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("sealed-record-class", "TimeSpan", "base-depth-2", "top-level", typeof(L05_91_sealed_record_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("sealed-record-class", "TimeSpan", "base-depth-3", "top-level", typeof(L05_92_sealed_record_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("sealed-record-class", "TimeSpan", "explicit-interface-member", "nested-in-struct", typeof(L05Os93.L05_93_sealed_record_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("sealed-record-class", "TimeSpan", "none", "nested-in-class", typeof(L05Oc94.L05_94_sealed_record_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("sealed-record-class", "Uri", "base-depth-1", "nested-in-class", typeof(L05Oc95.L05_95_sealed_record_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("sealed-record-class", "Uri", "base-depth-2", "nested-in-struct", typeof(L05Os96.L05_96_sealed_record_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("sealed-record-class", "Uri", "base-depth-3", "nested-in-struct", typeof(L05Os97.L05_97_sealed_record_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("sealed-record-class", "Uri", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot98.Inner.L05_98_sealed_record_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("sealed-record-class", "bool", "base-depth-2", "nested-in-class", typeof(L05Oc99.L05_99_sealed_record_class_bool), () => false, () => false, () => true); - Add("sealed-record-class", "bool", "base-depth-2", "nested-two-levels", typeof(L05Ot100.Inner.L05_100_sealed_record_class_bool), () => false, () => false, () => true); - Add("sealed-record-class", "bool", "base-depth-3", "nested-in-struct", typeof(L05Os101.L05_101_sealed_record_class_bool), () => false, () => false, () => true); - Add("sealed-record-class", "bool", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot102.Inner.L05_102_sealed_record_class_bool), () => false, () => false, () => true); - Add("sealed-record-class", "bool", "explicit-interface-member", "nested-two-levels", typeof(L05Ot103.Inner.L05_103_sealed_record_class_bool), () => false, () => false, () => true); - Add("sealed-record-class", "byte", "base-depth-1", "nested-two-levels", typeof(L05Ot104.Inner.L05_104_sealed_record_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("sealed-record-class", "byte", "base-depth-2", "nested-in-struct", typeof(L05Os105.L05_105_sealed_record_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("sealed-record-class", "byte", "base-depth-3", "nested-in-class", typeof(L05Oc106.L05_106_sealed_record_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("sealed-record-class", "byte", "base-depth-3", "top-level", typeof(L05_107_sealed_record_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("sealed-record-class", "byte", "explicit-interface-member", "nested-two-levels", typeof(L05Ot108.Inner.L05_108_sealed_record_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("sealed-record-class", "byte", "explicit-interface-member", "top-level", typeof(L05_109_sealed_record_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("sealed-record-class", "byte", "none", "top-level", typeof(L05_110_sealed_record_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("sealed-record-class", "char", "base-depth-1", "nested-two-levels", typeof(L05Ot111.Inner.L05_111_sealed_record_class_char), () => 'a', () => 'a', () => 'b'); - Add("sealed-record-class", "char", "base-depth-2", "top-level", typeof(L05_112_sealed_record_class_char), () => 'a', () => 'a', () => 'b'); - Add("sealed-record-class", "char", "base-explicit-IEquatable", "top-level", typeof(L05_113_sealed_record_class_char), () => 'a', () => 'a', () => 'b'); - Add("sealed-record-class", "char", "explicit-interface-member", "nested-two-levels", typeof(L05Ot114.Inner.L05_114_sealed_record_class_char), () => 'a', () => 'a', () => 'b'); - Add("sealed-record-class", "decimal", "base-depth-1", "nested-in-class", typeof(L05Oc115.L05_115_sealed_record_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("sealed-record-class", "decimal", "base-depth-3", "nested-in-struct", typeof(L05Os116.L05_116_sealed_record_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("sealed-record-class", "decimal", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc117.L05_117_sealed_record_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("sealed-record-class", "decimal", "base-explicit-IEquatable", "top-level", typeof(L05_118_sealed_record_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("sealed-record-class", "double", "base-depth-1", "nested-two-levels", typeof(L05Ot119.Inner.L05_119_sealed_record_class_double), () => 0.0, () => -0.0, () => 1.5); - Add("sealed-record-class", "double", "base-depth-2", "top-level", typeof(L05_120_sealed_record_class_double), () => 0.0, () => -0.0, () => 1.5); - Add("sealed-record-class", "double", "base-depth-3", "nested-in-class", typeof(L05Oc121.L05_121_sealed_record_class_double), () => 0.0, () => -0.0, () => 1.5); - Add("sealed-record-class", "double", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc122.L05_122_sealed_record_class_double), () => 0.0, () => -0.0, () => 1.5); - Add("sealed-record-class", "enum", "base-depth-1", "nested-in-struct", typeof(L05Os123.L05_123_sealed_record_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("sealed-record-class", "enum", "base-depth-2", "nested-two-levels", typeof(L05Ot124.Inner.L05_124_sealed_record_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("sealed-record-class", "enum", "base-depth-3", "nested-two-levels", typeof(L05Ot125.Inner.L05_125_sealed_record_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("sealed-record-class", "enum", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot126.Inner.L05_126_sealed_record_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("sealed-record-class", "enum", "explicit-interface-member", "nested-in-struct", typeof(L05Os127.L05_127_sealed_record_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("sealed-record-class", "enum", "none", "nested-in-class", typeof(L05Oc128.L05_128_sealed_record_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("sealed-record-class", "flags-enum", "base-depth-1", "nested-in-class", typeof(L05Oc129.L05_129_sealed_record_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("sealed-record-class", "flags-enum", "base-depth-1", "nested-two-levels", typeof(L05Ot130.Inner.L05_130_sealed_record_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("sealed-record-class", "flags-enum", "base-depth-2", "top-level", typeof(L05_131_sealed_record_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("sealed-record-class", "flags-enum", "base-depth-3", "top-level", typeof(L05_132_sealed_record_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("sealed-record-class", "flags-enum", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot133.Inner.L05_133_sealed_record_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("sealed-record-class", "flags-enum", "explicit-interface-member", "nested-in-struct", typeof(L05Os134.L05_134_sealed_record_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("sealed-record-class", "int", "base-depth-2", "top-level", typeof(L05_135_sealed_record_class_int), () => 42, () => 42, () => 43); - Add("sealed-record-class", "int", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os136.L05_136_sealed_record_class_int), () => 42, () => 42, () => 43); - Add("sealed-record-class", "long", "base-depth-2", "nested-two-levels", typeof(L05Ot137.Inner.L05_137_sealed_record_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("sealed-record-class", "long", "base-depth-3", "nested-two-levels", typeof(L05Ot138.Inner.L05_138_sealed_record_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("sealed-record-class", "long", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os139.L05_139_sealed_record_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("sealed-record-class", "own-record-class", "base-depth-2", "nested-two-levels", typeof(L05Ot140.Inner.L05_140_sealed_record_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("sealed-record-class", "own-record-class", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os141.L05_141_sealed_record_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("sealed-record-class", "own-record-class", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot142.Inner.L05_142_sealed_record_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("sealed-record-class", "string", "base-depth-1", "nested-in-struct", typeof(L05Os143.L05_143_sealed_record_class_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("sealed-record-class", "string", "base-depth-3", "top-level", typeof(L05_144_sealed_record_class_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("sealed-record-class", "string", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot145.Inner.L05_145_sealed_record_class_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("sealed-record-class", "ulong", "base-depth-3", "nested-two-levels", typeof(L05Ot146.Inner.L05_146_sealed_record_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("sealed-record-class", "ulong", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot147.Inner.L05_147_sealed_record_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("sealed-record-class", "ulong", "base-explicit-IEquatable", "top-level", typeof(L05_148_sealed_record_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-class", "DateOnly", "base-depth-1", "nested-in-struct", typeof(L05Os149.L05_149_validated_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("validated-class", "DateOnly", "base-explicit-IEquatable", "top-level", typeof(L05_150_validated_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("validated-class", "DateOnly", "explicit-interface-member", "top-level", typeof(L05_151_validated_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("validated-class", "DateOnly", "none", "nested-two-levels", typeof(L05Ot152.Inner.L05_152_validated_class_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("validated-class", "DateTime", "base-depth-1", "nested-in-struct", typeof(L05Os153.L05_153_validated_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("validated-class", "DateTime", "base-depth-1", "nested-two-levels", typeof(L05Ot154.Inner.L05_154_validated_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("validated-class", "DateTime", "base-depth-2", "top-level", typeof(L05_155_validated_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("validated-class", "DateTime", "base-depth-3", "nested-in-class", typeof(L05Oc156.L05_156_validated_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("validated-class", "DateTime", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os157.L05_157_validated_class_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("validated-class", "DateTimeOffset", "explicit-interface-member", "nested-in-class", typeof(L05Oc158.L05_158_validated_class_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("validated-class", "Guid", "base-depth-2", "nested-in-class", typeof(L05Oc159.L05_159_validated_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("validated-class", "Guid", "base-depth-2", "nested-two-levels", typeof(L05Ot160.Inner.L05_160_validated_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("validated-class", "Guid", "explicit-interface-member", "nested-in-class", typeof(L05Oc161.L05_161_validated_class_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("validated-class", "NodaTime.Instant", "base-depth-2", "nested-two-levels", typeof(L05Ot162.Inner.L05_162_validated_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("validated-class", "NodaTime.Instant", "base-depth-3", "nested-in-class", typeof(L05Oc163.L05_163_validated_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("validated-class", "NodaTime.Instant", "base-depth-3", "nested-in-struct", typeof(L05Os164.L05_164_validated_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("validated-class", "NodaTime.Instant", "base-depth-3", "nested-two-levels", typeof(L05Ot165.Inner.L05_165_validated_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("validated-class", "NodaTime.Instant", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc166.L05_166_validated_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("validated-class", "NodaTime.Instant", "base-explicit-IEquatable", "top-level", typeof(L05_167_validated_class_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("validated-class", "TimeOnly", "base-depth-1", "nested-in-class", typeof(L05Oc168.L05_168_validated_class_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("validated-class", "TimeOnly", "base-depth-1", "nested-in-struct", typeof(L05Os169.L05_169_validated_class_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("validated-class", "TimeOnly", "base-depth-2", "top-level", typeof(L05_170_validated_class_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("validated-class", "TimeOnly", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc171.L05_171_validated_class_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("validated-class", "TimeOnly", "none", "top-level", typeof(L05_172_validated_class_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("validated-class", "TimeSpan", "base-depth-1", "top-level", typeof(L05_173_validated_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("validated-class", "TimeSpan", "base-depth-2", "nested-in-class", typeof(L05Oc174.L05_174_validated_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("validated-class", "TimeSpan", "base-depth-2", "nested-in-struct", typeof(L05Os175.L05_175_validated_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("validated-class", "TimeSpan", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot176.Inner.L05_176_validated_class_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("validated-class", "Uri", "base-depth-1", "nested-in-struct", typeof(L05Os177.L05_177_validated_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("validated-class", "Uri", "base-depth-3", "nested-two-levels", typeof(L05Ot178.Inner.L05_178_validated_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("validated-class", "Uri", "explicit-interface-member", "top-level", typeof(L05_179_validated_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("validated-class", "Uri", "none", "nested-in-class", typeof(L05Oc180.L05_180_validated_class_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("validated-class", "bool", "base-depth-1", "nested-in-struct", typeof(L05Os181.L05_181_validated_class_bool), () => false, () => false, () => true); - Add("validated-class", "bool", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc182.L05_182_validated_class_bool), () => false, () => false, () => true); - Add("validated-class", "bool", "explicit-interface-member", "nested-in-struct", typeof(L05Os183.L05_183_validated_class_bool), () => false, () => false, () => true); - Add("validated-class", "byte", "base-depth-2", "nested-in-struct", typeof(L05Os184.L05_184_validated_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("validated-class", "byte", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc185.L05_185_validated_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("validated-class", "byte", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot186.Inner.L05_186_validated_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("validated-class", "byte", "explicit-interface-member", "nested-in-struct", typeof(L05Os187.L05_187_validated_class_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("validated-class", "char", "base-depth-2", "nested-in-class", typeof(L05Oc188.L05_188_validated_class_char), () => 'a', () => 'a', () => 'b'); - Add("validated-class", "char", "base-depth-3", "nested-in-class", typeof(L05Oc189.L05_189_validated_class_char), () => 'a', () => 'a', () => 'b'); - Add("validated-class", "char", "base-depth-3", "nested-two-levels", typeof(L05Ot190.Inner.L05_190_validated_class_char), () => 'a', () => 'a', () => 'b'); - Add("validated-class", "char", "base-explicit-IEquatable", "nested-two-levels", typeof(L05Ot191.Inner.L05_191_validated_class_char), () => 'a', () => 'a', () => 'b'); - Add("validated-class", "char", "base-explicit-IEquatable", "top-level", typeof(L05_192_validated_class_char), () => 'a', () => 'a', () => 'b'); - Add("validated-class", "decimal", "base-depth-1", "top-level", typeof(L05_193_validated_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("validated-class", "decimal", "base-depth-2", "nested-two-levels", typeof(L05Ot194.Inner.L05_194_validated_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("validated-class", "decimal", "base-depth-2", "top-level", typeof(L05_195_validated_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("validated-class", "decimal", "none", "nested-in-struct", typeof(L05Os196.L05_196_validated_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("validated-class", "decimal", "none", "top-level", typeof(L05_197_validated_class_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("validated-class", "double", "base-depth-2", "top-level", typeof(L05_198_validated_class_double), () => 0.0, () => -0.0, () => 1.5); - Add("validated-class", "double", "base-depth-3", "nested-in-struct", typeof(L05Os199.L05_199_validated_class_double), () => 0.0, () => -0.0, () => 1.5); - Add("validated-class", "double", "explicit-interface-member", "nested-in-struct", typeof(L05Os200.L05_200_validated_class_double), () => 0.0, () => -0.0, () => 1.5); - Add("validated-class", "enum", "base-depth-1", "nested-two-levels", typeof(L05Ot201.Inner.L05_201_validated_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("validated-class", "enum", "base-depth-2", "top-level", typeof(L05_202_validated_class_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("validated-class", "flags-enum", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc203.L05_203_validated_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("validated-class", "flags-enum", "explicit-interface-member", "nested-two-levels", typeof(L05Ot204.Inner.L05_204_validated_class_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("validated-class", "int", "base-depth-1", "nested-in-struct", typeof(L05Os205.L05_205_validated_class_int), () => 42, () => 42, () => 43); - Add("validated-class", "int", "base-depth-3", "nested-in-class", typeof(L05Oc206.L05_206_validated_class_int), () => 42, () => 42, () => 43); - Add("validated-class", "int", "explicit-interface-member", "nested-in-class", typeof(L05Oc207.L05_207_validated_class_int), () => 42, () => 42, () => 43); - Add("validated-class", "long", "base-depth-1", "nested-in-class", typeof(L05Oc208.L05_208_validated_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("validated-class", "long", "base-depth-2", "nested-two-levels", typeof(L05Ot209.Inner.L05_209_validated_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("validated-class", "long", "base-explicit-IEquatable", "nested-in-class", typeof(L05Oc210.L05_210_validated_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("validated-class", "long", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os211.L05_211_validated_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("validated-class", "long", "explicit-interface-member", "nested-two-levels", typeof(L05Ot212.Inner.L05_212_validated_class_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("validated-class", "own-record-class", "base-depth-1", "nested-in-struct", typeof(L05Os213.L05_213_validated_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-class", "own-record-class", "base-depth-1", "nested-two-levels", typeof(L05Ot214.Inner.L05_214_validated_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-class", "own-record-class", "base-depth-2", "top-level", typeof(L05_215_validated_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-class", "own-record-class", "base-depth-3", "nested-in-struct", typeof(L05Os216.L05_216_validated_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-class", "own-record-class", "base-depth-3", "nested-two-levels", typeof(L05Ot217.Inner.L05_217_validated_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-class", "own-record-class", "base-explicit-IEquatable", "nested-in-struct", typeof(L05Os218.L05_218_validated_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-class", "own-record-class", "none", "nested-in-class", typeof(L05Oc219.L05_219_validated_class_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-class", "string", "base-depth-1", "nested-in-struct", typeof(L05Os220.L05_220_validated_class_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("validated-class", "string", "base-depth-2", "nested-two-levels", typeof(L05Ot221.Inner.L05_221_validated_class_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("validated-class", "string", "base-depth-3", "nested-in-class", typeof(L05Oc222.L05_222_validated_class_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("validated-class", "string", "explicit-interface-member", "top-level", typeof(L05_223_validated_class_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("validated-class", "ulong", "base-depth-1", "nested-in-struct", typeof(L05Os224.L05_224_validated_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-class", "ulong", "base-depth-1", "top-level", typeof(L05_225_validated_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-class", "ulong", "base-depth-2", "top-level", typeof(L05_226_validated_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-class", "ulong", "base-depth-3", "nested-in-class", typeof(L05Oc227.L05_227_validated_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-class", "ulong", "explicit-interface-member", "nested-in-class", typeof(L05Oc228.L05_228_validated_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-class", "ulong", "explicit-interface-member", "nested-in-struct", typeof(L05Os229.L05_229_validated_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-class", "ulong", "none", "nested-in-struct", typeof(L05Os230.L05_230_validated_class_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-struct", "DateOnly", "explicit-interface-member", "nested-two-levels", typeof(L05Ot231.Inner.L05_231_validated_struct_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("validated-struct", "DateOnly", "none", "top-level", typeof(L05_232_validated_struct_DateOnly), () => new DateOnly(2026, 10, 15), () => DateOnly.FromDayNumber(new DateOnly(2026, 10, 15).DayNumber), () => new DateOnly(2026, 10, 16)); - Add("validated-struct", "DateTime", "explicit-interface-member", "nested-two-levels", typeof(L05Ot233.Inner.L05_233_validated_struct_DateTime), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Utc), () => new DateTime(2026, 10, 15, 8, 30, 0, DateTimeKind.Unspecified), () => new DateTime(2026, 10, 15, 8, 31, 0, DateTimeKind.Utc)); - Add("validated-struct", "DateTimeOffset", "none", "nested-in-struct", typeof(L05Os234.L05_234_validated_struct_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("validated-struct", "DateTimeOffset", "none", "top-level", typeof(L05_235_validated_struct_DateTimeOffset), () => new DateTimeOffset(2026, 10, 15, 8, 30, 0, TimeSpan.Zero), () => new DateTimeOffset(2026, 10, 15, 10, 30, 0, TimeSpan.FromHours(2)), () => new DateTimeOffset(2026, 10, 15, 8, 31, 0, TimeSpan.Zero)); - Add("validated-struct", "Guid", "none", "nested-in-class", typeof(L05Oc236.L05_236_validated_struct_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("validated-struct", "Guid", "none", "nested-in-struct", typeof(L05Os237.L05_237_validated_struct_Guid), () => Guid.Parse("11111111-1111-1111-1111-111111111111"), () => new Guid("11111111111111111111111111111111"), () => Guid.Parse("22222222-2222-2222-2222-222222222222")); - Add("validated-struct", "NodaTime.Instant", "none", "top-level", typeof(L05_238_validated_struct_NodaTime_Instant), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30), () => global::NodaTime.Instant.FromUnixTimeTicks(global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 30).ToUnixTimeTicks()), () => global::NodaTime.Instant.FromUtc(2026, 10, 15, 8, 31)); - Add("validated-struct", "TimeOnly", "explicit-interface-member", "nested-in-class", typeof(L05Oc239.L05_239_validated_struct_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("validated-struct", "TimeOnly", "none", "top-level", typeof(L05_240_validated_struct_TimeOnly), () => new TimeOnly(8, 30, 15), () => new TimeOnly(new TimeOnly(8, 30, 15).Ticks), () => new TimeOnly(8, 30, 16)); - Add("validated-struct", "TimeSpan", "none", "nested-in-class", typeof(L05Oc241.L05_241_validated_struct_TimeSpan), () => TimeSpan.FromMinutes(90), () => TimeSpan.FromSeconds(5400), () => TimeSpan.FromMinutes(91)); - Add("validated-struct", "Uri", "explicit-interface-member", "nested-in-struct", typeof(L05Os242.L05_242_validated_struct_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("validated-struct", "Uri", "explicit-interface-member", "top-level", typeof(L05_243_validated_struct_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("validated-struct", "Uri", "none", "nested-in-class", typeof(L05Oc244.L05_244_validated_struct_Uri), () => new Uri("https://example.com/a"), () => new Uri("HTTPS://EXAMPLE.COM/a"), () => new Uri("https://example.com/b")); - Add("validated-struct", "bool", "explicit-interface-member", "nested-in-class", typeof(L05Oc245.L05_245_validated_struct_bool), () => false, () => false, () => true); - Add("validated-struct", "bool", "none", "nested-two-levels", typeof(L05Ot246.Inner.L05_246_validated_struct_bool), () => false, () => false, () => true); - Add("validated-struct", "byte", "explicit-interface-member", "top-level", typeof(L05_247_validated_struct_byte), () => (byte)7, () => (byte)7, () => (byte)8); - Add("validated-struct", "char", "explicit-interface-member", "nested-in-class", typeof(L05Oc248.L05_248_validated_struct_char), () => 'a', () => 'a', () => 'b'); - Add("validated-struct", "decimal", "explicit-interface-member", "top-level", typeof(L05_249_validated_struct_decimal), () => 7.8m, () => 7.80m, () => 7.81m); - Add("validated-struct", "double", "explicit-interface-member", "nested-two-levels", typeof(L05Ot250.Inner.L05_250_validated_struct_double), () => 0.0, () => -0.0, () => 1.5); - Add("validated-struct", "double", "none", "nested-two-levels", typeof(L05Ot251.Inner.L05_251_validated_struct_double), () => 0.0, () => -0.0, () => 1.5); - Add("validated-struct", "enum", "none", "nested-in-class", typeof(L05Oc252.L05_252_validated_struct_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("validated-struct", "enum", "none", "nested-in-struct", typeof(L05Os253.L05_253_validated_struct_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("validated-struct", "enum", "none", "nested-two-levels", typeof(L05Ot254.Inner.L05_254_validated_struct_enum), () => PEnum.B, () => (PEnum)1, () => PEnum.C); - Add("validated-struct", "flags-enum", "none", "nested-two-levels", typeof(L05Ot255.Inner.L05_255_validated_struct_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("validated-struct", "flags-enum", "none", "top-level", typeof(L05_256_validated_struct_flags_enum), () => PFlags.P | PFlags.Q, () => (PFlags)3, () => PFlags.R); - Add("validated-struct", "int", "explicit-interface-member", "nested-in-struct", typeof(L05Os257.L05_257_validated_struct_int), () => 42, () => 42, () => 43); - Add("validated-struct", "int", "explicit-interface-member", "nested-two-levels", typeof(L05Ot258.Inner.L05_258_validated_struct_int), () => 42, () => 42, () => 43); - Add("validated-struct", "int", "none", "nested-two-levels", typeof(L05Ot259.Inner.L05_259_validated_struct_int), () => 42, () => 42, () => 43); - Add("validated-struct", "int", "none", "top-level", typeof(L05_260_validated_struct_int), () => 42, () => 42, () => 43); - Add("validated-struct", "long", "explicit-interface-member", "nested-in-struct", typeof(L05Os261.L05_261_validated_struct_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("validated-struct", "long", "explicit-interface-member", "top-level", typeof(L05_262_validated_struct_long), () => 4200000000L, () => 4200000000L, () => 4200000001L); - Add("validated-struct", "own-record-class", "explicit-interface-member", "top-level", typeof(L05_263_validated_struct_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-struct", "own-record-class", "none", "nested-in-struct", typeof(L05Os264.L05_264_validated_struct_own_record_class), () => new PRec { A = "x", B = 1 }, () => new PRec { A = new string("x".ToCharArray()), B = 1 }, () => new PRec { A = "y", B = 1 }); - Add("validated-struct", "string", "explicit-interface-member", "nested-two-levels", typeof(L05Ot265.Inner.L05_265_validated_struct_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("validated-struct", "string", "none", "nested-in-class", typeof(L05Oc266.L05_266_validated_struct_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("validated-struct", "string", "none", "top-level", typeof(L05_267_validated_struct_string), () => "alpha", () => new string("alpha".ToCharArray()), () => "beta"); - Add("validated-struct", "ulong", "explicit-interface-member", "top-level", typeof(L05_268_validated_struct_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - Add("validated-struct", "ulong", "none", "top-level", typeof(L05_269_validated_struct_ulong), () => 4200000000UL, () => 4200000000UL, () => 4200000001UL); - } -} diff --git a/tools/probe/corpus/harness/OffTheWire.cs b/tools/probe/corpus/harness/OffTheWire.cs deleted file mode 100644 index eae52bbf..00000000 --- a/tools/probe/corpus/harness/OffTheWire.cs +++ /dev/null @@ -1,221 +0,0 @@ -// Claim L06-off-the-wire (README.md#what-typekit-guarantees): "Option and Result stay off the wire. System.Text.Json throws -// NotSupportedException for either, in both directions, instead of writing {} that reads back as None. [...] If you want a wire format of -// your own, a converter you register on the options takes precedence." The message names the type (src/CodoMetis.TypeKit/README.md -// "Option or Result in JSON"); a source-generated context refuses an Option? or Result<...>? property with the serializer's own -// InvalidOperationException when it builds that contract (ibid., "Good to know"). -// Oracle: exception, in every position, with the row's options and input, for an Option or Result produced by the row's operation. A -// converter of the application's for the same type in the same row is the baseline: it must win in both directions, and where the options -// leave the member out (it writes and reads nothing there), no converter runs, which is intended ("A missing property reaches no converter"). -#pragma warning disable CMTK0001 // default-instance: the default Option and Result are the shapes under test -using System.Reflection; -using System.Text.Json; -using System.Text.Json.Serialization; -using CodoMetis.TypeKit; -using Probes.Shapes; -namespace Probes.Json; - -/// An application's own converter for an Option or Result: writes "own-or" and reads it back as the value it was made with, counting reads. -public sealed class OwnOrConverter(T value) : JsonConverter -{ - public int Reads; - public override T Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) - { - Reads++; - if (reader.TokenType is not (JsonTokenType.String or JsonTokenType.PropertyName) || reader.GetString() != "own-or") throw new JsonException("not own-or"); - return value; - } - public override void Write(Utf8JsonWriter writer, T v, JsonSerializerOptions options) => writer.WriteStringValue("own-or"); - public override T ReadAsPropertyName(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) => Read(ref reader, typeToConvert, options); - public override void WriteAsPropertyName(Utf8JsonWriter writer, T v, JsonSerializerOptions options) => writer.WritePropertyName("own-or"); -} - -public static class OffTheWire -{ - public static async Task Check(Row r) - { - var kind = r["shape.kind"]; - var e = kind == "closed-set" ? Registry.Get(kind, "-") : Registry.Get(kind, r["shape.wrapped"]); - var position = r["shape.position"]; - var containing = position == "root" ? "class" : r["shape.containing"]; - var shape = r["or.shape"]; var op = r["or.operation"]; - _ = r["input.json"]; // the row's input, which every check of the row runs under; the read applies it - if (shape == "Result-E" && op is "query-syntax" or "FirstOrNone") - { r.Substituted("or.operation", "Match", "Result has no " + op + " (claims.json excludes the pair)"); op = "Match"; } - var voValue = Make.Vo(e, e.Valid()); - var v = Make.Held(e.Vo); - var produce = typeof(OffTheWire).GetMethod(nameof(Produce), BindingFlags.NonPublic | BindingFlags.Static)!.MakeGenericMethod(v); - var (orType, orValue) = await (Task<(Type, object)>)produce.Invoke(null, [shape, op, voValue])!; - var key = shape == "Option" ? "O" : shape == "Result-E" ? "RE" : "RTE"; - var pb = typeof(OffTheWire).Assembly.GetType($"Probes.Json.L06PB_{v.Name}_{key}")!; var pd = typeof(OffTheWire).Assembly.GetType($"Probes.Json.L06PD_{v.Name}_{key}")!; - var eo = e with { Vo = orType, Plain = orType, PolyBase = pb, PolyDerived = pd, PolyBasePlain = pb, PolyDerivedPlain = pd }; - var hv = Pos.Build(position, containing, e, true, voValue); // the value object's shape: its document is the read's input - var ho = Pos.Build(position, containing, eo, true, orValue); - var o = JCtx.Apply(r, e, hv.Declared, ho.Declared); - var own = (JsonConverter)Activator.CreateInstance(typeof(OwnOrConverter<>).MakeGenericType(orType), orValue)!; - var oOwn = new JsonSerializerOptions(o); oOwn.Converters.Insert(0, own); - int OwnReads() => (int)own.GetType().GetField("Reads")!.GetValue(own)!; - var name = Jx.Name(orType); - // not-promised a-source-generated-context-refuses-a-nullable - var nullableUnderContext = r.Is("json.TypeInfoResolver", "source-generated-context") && position == "nullable-property"; - string? Refused(Exception? x, string dir) - { - if (nullableUnderContext) - return x is InvalidOperationException ? null : $"{dir}: a source-generated context refuses an {name}? property with the serializer's InvalidOperationException; got {(x is null ? "no exception" : Jx.Ex(x))}"; - if (x is not NotSupportedException) return $"{dir}: expected NotSupportedException naming {name}, got {(x is null ? "no exception" : Jx.Ex(x))}"; - // TypeKit's refusal, told by its type and never by its words: the serializer passes a converter's NotSupportedException on as the - // inner exception of its own, so one in the chain is thrown by NotWireTypeJsonConverterFactory's converter, by nameof - if (!ThrownBy(x, typeof(NotWireTypeJsonConverterFactory))) return $"{dir}: the NotSupportedException is not {nameof(NotWireTypeJsonConverterFactory)}'s: {Jx.Ex(x)}"; - return null; - } - - // the baseline: the application's own converter for the type wins, in both directions - var (jOwn, xOwn) = Jx.Ser(ho, oOwn); - var (jv, xv) = Jx.Ser(hv, o); - var refused = Baseline.Refusal; - var fw = r.Check("never-written", refused, c => - { - if (xOwn is not null) - { - if (xv is not null && xv.GetType() == xOwn.GetType()) return c.Inconclusive("write: the options refuse the shape itself, with the application's converter alike: " + Jx.Ex(xv)); - return $"write: a converter registered on the options for {name} does not take precedence: {Jx.Ex(xOwn)}"; - } - var ownWritten = Jx.Tokens(jOwn!).Any(t => t.Text == "own-or"); - var (jo, xo) = Jx.Ser(ho, o); - if (xo is null) - { - if (ownWritten || jo != jOwn) return $"write: {name} was written: {Jx.Cut(jo)}"; - // the options leave the member out, the application's converter's document alike: no converter ran - c.Note(NoteKeys.JsonMemberLeftOutByOptions, "write: " + Jx.Cut(jo)); - return c.Inconclusive("write: the options leave the member out, so no converter runs: " + Jx.Cut(jo)); - } - if (Refused(xo, "write") is { } f) return f; - c.Note(NoteKeys.Compared, $"{name} by {op}, {position}: write refused"); - return null; - }); - // reading: the value object's document with the row's input, where the Option or Result stands; measured before the check, so that - // a document the serializer refuses before the member (side (1), Sides) decides the check's baseline - var full = Jx.Full(o); - var (jf, xf) = Jx.Ser(hv, full); - // does the application's converter get called there? Its own document, read under the same options, says so - var (jOwnFull, _) = Jx.Ser(ho, Jx.Full(oOwn)); - string input = r["input.json"], text = ""; - if (xf is null) text = JsonParity.InputFor(input, e, full, jf!, position); - else if (jOwnFull is not null) { input = "valid"; text = jOwnFull; } - var reads = xOwn is null && (xf is null || jOwnFull is not null); - var (_, xOwnRead) = reads ? Jx.De(jOwnFull ?? "null", ho.Declared, oOwn) : (null, null); - var memberRead = reads && OwnReads() > 0; - var (vr, xr) = reads ? Jx.De(text, ho.Declared, o) : (null, null); - // side (1): the same document with the value object where the Option or Result stands, read under the same options; refusing alike - // (the same exception type at the same path, no message compared), the serializer refused the document before the member - var three = new Sides.Answer("read", null, xr); - var one = new Sides.Answer("read", null, reads && xr is not null && !ThrownBy(xr, typeof(NotWireTypeJsonConverterFactory)) ? Jx.De(text, hv.Declared, o).Ex : null); - var framework = Sides.Alike(one, three) ? Sides.Measured(one, "the document with the value object where the Option or Result stands") : null; - var fr = await Sides.Check(r, "never-read", refused, framework, three, null, null, async c => - { - await Task.CompletedTask; - if (xOwn is not null) return c.Inconclusive("read: the options refuse the shape itself, so no document has the member: " + Jx.Ex(xOwn)); - _ = c["input.json"]; - if (xf is not null) - { - // the value object's own shape cannot be written here (a value object over a class under Preserve, a class as a key): the - // document with the application's converter has a token where the Option or Result stands, and is read as it is - if (jOwnFull is null) return c.Inconclusive("read: no document has the member: " + Jx.Ex(xf)); - c.Substituted("input.json", "valid", "the value object's document cannot be written: " + xf.GetType().Name); - } - if (xOwnRead is not null) return $"read: a converter registered on the options for {name} does not take precedence: {Jx.Ex(xOwnRead)}"; - if (xr is null && input == "null" && position == "in-patch" && Jx.Member(ho.Declared, vr) is { } patch && patch.GetType().GetProperty("State")!.GetValue(patch)!.ToString() == "Null") - // a JSON null is the patch's own null (docs/merge-patches.md): it holds no Option or Result, and none was read - return c.Inapplicable(Cite.Docs("docs/merge-patches.md#tell-absent-null-and-a-value-apart"), $"read null: the Patch<{name}> is null, nothing of {name} read"); - if (xr is null) - { - if (memberRead) return $"read {input}: {name} was read without an exception\n text={Jx.Cut(text)}"; - c.Note(NoteKeys.JsonMemberLeftOutByOptions, "read " + input); - return c.Inapplicable(Cite.NotPromised("a-missing-property-reaches-no-converter"), "read " + input); - } - if (Refused(xr, "read " + input) is { } f) return f + $"\n text={Jx.Cut(text)}"; - c.Note(NoteKeys.Compared, $"{name} by {op}, {position}: read {input} refused"); - return null; - }); - return fw ?? fr; - } - - /// - /// Whether a refusal, or one in its chain of inner exceptions, was thrown by a converter that declares: the - /// method that threw it (its TargetSite) belongs to the factory's type or to a type nested in it. Told by types, never by a message. - /// - static bool ThrownBy(Exception? x, Type factory) - { - for (; x is not null; x = x.InnerException) - for (var t = x.TargetSite?.DeclaringType; t is not null; t = t.DeclaringType) - if (t == factory) return true; - return false; - } - - static async Task<(Type, object)> Produce(string shape, string op, V v) where V : notnull - { - switch (shape) - { - case "Option": - { - Option x = op switch - { - "Match" => Option.Some(v).Match(s => Option.Some(s), () => Option.None()), - "Bind-Map-chain" => Option.Some(v).Bind(s => Option.Some(s)).Map(s => s), - "query-syntax" => from s in Option.Some(v) select s, - "Sequence-Transpose" => Option.Some(Result.Success(v)).Transpose().Match(s => s, _ => Option.None()), - "Result.Catch" => Result.Catch(() => v).ToOption(), - "FirstOrNone" => new[] { v }.FirstOrNone(), - "default-instance" => default, - "after-await-elsewhere" => await Task.Run(() => Option.Some(v)).ConfigureAwait(false), - _ => throw new ArgumentException(op), - }; - return (typeof(Option), x); - } - case "Result-E": - { - Result x = op switch - { - "Match" => Result.Error(v).Match(() => Result.Success(), er => Result.Error(er)), - "Bind-Map-chain" => Result.Success().Bind(() => Result.Error(v)), - "Sequence-Transpose" => new[] { Result.Error(v) }.Sequence(), - "Result.Catch" => Result.Catch(() => { }).MapError(_ => v), - "default-instance" => default, - "after-await-elsewhere" => await Task.Run(() => Result.Error(v)).ConfigureAwait(false), - _ => throw new ArgumentException(op), - }; - return (typeof(Result), x); - } - default: - { - Result x; - if (shape == "Task-of-Result") - { - var ok = Task.FromResult(Result.Success(v)); - x = op switch - { - "Bind-Map-chain" => await ok.BindAsync(s => Result.Success(s)).MapAsync(s => s), - "FirstOrNone" => await Task.FromResult(new[] { v }.FirstOrNone()).ToResultAsync(ShapeFault.Refused), - "Result.Catch" => await Result.CatchAsync(() => Task.FromResult(v)).MapErrorAsync(_ => ShapeFault.Refused), - _ => await Task.FromResult(await Sync(op, v)).ConfigureAwait(false), - }; - } - else if (shape == "ValueTask-of-Result") x = await new ValueTask>(await Sync(op, v)); - else x = await Sync(op, v); - return (typeof(Result), x); - } - } - } - - static async Task> Sync(string op, V v) where V : notnull => op switch - { - "Match" => Result.Success(v).Match(s => Result.Success(s), er => Result.Error(er)), - "Bind-Map-chain" => Result.Success(v).Bind(s => Result.Success(s)).Map(s => s), - "query-syntax" => from s in Result.Success(v) select s, - "Sequence-Transpose" => new[] { Result.Success(v) }.Sequence().Map(l => l[0]), - "Result.Catch" => Result.Catch(() => v).MapError(_ => ShapeFault.Refused), - "FirstOrNone" => new[] { v }.FirstOrNone().ToResult(ShapeFault.Refused), - "default-instance" => default, - "after-await-elsewhere" => await Task.Run(() => Result.Success(v)).ConfigureAwait(false), - _ => throw new ArgumentException(op), - }; -} diff --git a/tools/probe/corpus/harness/Positions.cs b/tools/probe/corpus/harness/Positions.cs deleted file mode 100644 index 800e60ba..00000000 --- a/tools/probe/corpus/harness/Positions.cs +++ /dev/null @@ -1,328 +0,0 @@ -// Builds the value a probe serializes for a row's shape.position x shape.containing (VO side and plain side), and the JSON parity oracle. -using System.Collections; -using System.Collections.Immutable; -using System.Globalization; -using System.Reflection; -using System.Text.Json; -using CodoMetis.TypeKit; -namespace Probes.Shapes; - -public sealed record Held(Type Declared, object Value); - -public static class Pos -{ - static Type Family(string containing, string style, Type m) - { - var n = containing.Replace('-', '_'); - var arity = containing == "generic-type" ? 2 : 1; - var open = typeof(PSet).Assembly.GetType($"Probes.Shapes.Cn_{n}_{style}`{arity}") ?? throw new InvalidOperationException($"no container Cn_{n}_{style}"); - return arity == 2 ? open.MakeGenericType(m, typeof(int)) : open.MakeGenericType(m); - } - - public static Held Build(string position, string containing, Entry e, bool vo, object sample) - { - var t = vo ? e.Vo : e.Plain; - if (position == "root") return new(t, sample); - Type m; object member; string style = "P"; - switch (position) - { - case "property": case "required-init": case "field": case "record-ctor-parameter": - m = t; member = sample; - style = position switch { "field" => "F", "record-ctor-parameter" => "C", "required-init" => "R", _ => "P" }; - break; - case "nullable-property": m = t.IsValueType && Nullable.GetUnderlyingType(t) is null ? typeof(Nullable<>).MakeGenericType(t) : t; member = sample; break; - case "list-element": case "read-only-list-element": - { - var l = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(t))!; l.Add(sample); - member = l; m = position == "list-element" ? l.GetType() : typeof(IReadOnlyList<>).MakeGenericType(t); break; - } - case "array-element": { var a = Array.CreateInstance(t, 1); a.SetValue(sample, 0); member = a; m = a.GetType(); break; } - case "immutable-array-element": - { - var l = (IList)Activator.CreateInstance(typeof(List<>).MakeGenericType(t))!; l.Add(sample); - var create = typeof(ImmutableArray).GetMethods().First(x => x.Name == "CreateRange" && x.GetParameters().Length == 1).MakeGenericMethod(t); - member = create.Invoke(null, [l])!; m = member.GetType(); break; - } - // a dictionary key is never null: a nullable twin (or, under http.nullability=nullable, a nullable value object) is a key of the type it holds - case "dictionary-key": { var d = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(Make.Held(t), typeof(int)))!; d.Add(sample, 1); member = d; m = d.GetType(); break; } - case "dictionary-value": { var d = (IDictionary)Activator.CreateInstance(typeof(Dictionary<,>).MakeGenericType(typeof(string), t))!; d.Add("k", sample); member = d; m = d.GetType(); break; } - // a Patch holds no null (T : notnull): a nullable twin is patched as the type it holds, as a dictionary key is - case "in-patch": - member = typeof(Patch).GetMethod("Value")!.MakeGenericMethod(Make.Held(t)).Invoke(null, [sample])!; m = member.GetType(); break; - case "polymorphic-derived-property": - { - var derived = vo ? e.PolyDerived : e.PolyDerivedPlain; - var inst = Activator.CreateInstance(derived)!; derived.GetProperty("Value")!.SetValue(inst, sample); - member = inst; m = vo ? e.PolyBase : e.PolyBasePlain; break; - } - default: throw new ArgumentException("unknown position " + position); - } - var ct = Family(containing, style, m); - object c; - if (style == "C") c = Activator.CreateInstance(ct, member)!; - else - { - c = Activator.CreateInstance(ct)!; - if (style == "F") ct.GetField("F")!.SetValue(c, member); else ct.GetProperty("P")!.SetValue(c, member); - } - return new(ct, c); - } -} - -/// -/// Input equivalence for the differential oracles: the twin and the value object must hold the same shape under the same options, the -/// twin standing where the value object stands, at its nullability: both admit null or neither (shapes.cs makes the twin of a class value -/// object over a struct the struct made nullable). The one difference no plain type can mirror is a struct value object over a class, -/// which refuses the null its twin reads (not-promised a-struct-refuses-a-json-null), and a dictionary key and what a patch holds, which -/// are never null on either side. A mismatch is a harness error, never a pass or a product violation (Sides.Twins). -/// -public static class Equivalence -{ - public static string? Shapes(Type plain, Type vo, Entry e) => Shapes(plain, vo, e, key: false); - - static string? Shapes(Type plain, Type vo, Entry e, bool key) - { - if (plain == vo) return null; - if (Make.Held(vo) == Make.Held(e.Vo)) return Member(plain, vo, e, key); - if (plain.IsArray && vo.IsArray) return Shapes(plain.GetElementType()!, vo.GetElementType()!, e, false); - if (plain.IsGenericType && vo.IsGenericType && plain.GetGenericTypeDefinition() == vo.GetGenericTypeDefinition()) - { - var pa = plain.GetGenericArguments(); var va = vo.GetGenericArguments(); - // a dictionary's key and what a patch holds are never null, on either side - var notNull = plain.GetGenericTypeDefinition() == typeof(Dictionary<,>) || plain.GetGenericTypeDefinition() == typeof(Patch<>); - for (var i = 0; i < pa.Length; i++) if (Shapes(pa[i], va[i], e, notNull && i == 0) is { } why) return why; - return null; - } - // The generated stand-ins (the polymorphic bases and derived types): one per side by design, equal in - // structure: the same properties by name, each of an equivalent type, and the same polymorphism. - if (plain.IsClass && vo.IsClass && !plain.IsGenericType && !vo.IsGenericType) - { - var pp = plain.GetProperties().ToDictionary(x => x.Name); var vp = vo.GetProperties().ToDictionary(x => x.Name); - if (!pp.Keys.Order().SequenceEqual(vp.Keys.Order())) return $"{Name(plain)} and {Name(vo)} have different properties"; - foreach (var (name, prop) in pp) if (Shapes(prop.PropertyType, vp[name].PropertyType, e, false) is { } why) return $"{name}: {why}"; - var pd = plain.GetCustomAttributes(typeof(System.Text.Json.Serialization.JsonDerivedTypeAttribute), false).Length; - var vd = vo.GetCustomAttributes(typeof(System.Text.Json.Serialization.JsonDerivedTypeAttribute), false).Length; - if (pd != vd) return $"{Name(plain)} and {Name(vo)} declare {pd} and {vd} derived types"; - if ((plain.BaseType is { } pb && pb != typeof(object)) || (vo.BaseType is { } vb && vb != typeof(object))) - return Shapes(plain.BaseType!, vo.BaseType!, e, false); - return null; - } - return $"{Name(plain)} and {Name(vo)} are different shapes"; - } - - /// Where the value object stands: its twin, at its nullability. - static string? Member(Type plain, Type vo, Entry e, bool key) - { - if (Make.Held(plain) != Make.Held(e.Plain)) return $"{Name(plain)} stands where {Name(vo)} should face {Name(e.Plain)}"; - if (key || Make.AdmitsNull(plain) == Make.AdmitsNull(vo)) return null; - // a struct value object over a class refuses the JSON null its twin reads: no plain type mirrors it, the claims cite it - if (!Make.AdmitsNull(vo) && !Make.Held(e.Plain).IsValueType) return null; - return $"{Name(plain)} and {Name(vo)} differ in nullability: the twin {(Make.AdmitsNull(plain) ? "admits" : "refuses")} null, the value object {(Make.AdmitsNull(vo) ? "admits" : "refuses")} it"; - } - - public static string? Options(JsonSerializerOptions plain, JsonSerializerOptions vo, bool allowConverters) - { - if (ReferenceEquals(plain, vo)) return null; - foreach (var p in typeof(JsonSerializerOptions).GetProperties()) - { - if (p.GetIndexParameters().Length > 0 || (allowConverters && p.Name == "Converters")) continue; - var a = Print(p.GetValue(plain)); var b = Print(p.GetValue(vo)); - if (a != b) return $"options differ in {p.Name}: {a} vs {b}"; - } - return null; - } - - static string Print(object? v) => v switch - { - null => "null", - string or bool or char or int or Enum => v.ToString()!, - System.Collections.IEnumerable list => "[" + string.Join(",", list.Cast().Select(x => x?.GetType().FullName)) + "]", - _ => v.GetType().FullName!, - }; - - static string Name(Type t) => t.IsGenericType ? t.Name[..t.Name.IndexOf('`')] + "<" + string.Join(",", t.GetGenericArguments().Select(Name)) + ">" : t.Name; -} - -public static class JsonParity -{ - static (string? Json, Exception? Ex) Ser(Held h, JsonSerializerOptions o) - { try { return (JsonSerializer.Serialize(h.Value, h.Declared, o), null); } catch (Exception ex) { return (null, ex); } } - static (string? Json, Exception? Ex) De(string text, Held h, JsonSerializerOptions o) - { - try { var v = JsonSerializer.Deserialize(text, h.Declared, o); return (v is null ? "null" : JsonSerializer.Serialize(v, h.Declared, o), null); } - catch (Exception ex) { return (null, ex); } - } - - static string Limit(Entry e) => e.Wrapped switch - { - "int" => "2147483647", "long" => "9223372036854775807", "byte" => "255", "ulong" => "18446744073709551615", - "decimal" => "79228162514264337593543950335", "double" => "1.7976931348623157E+308", "bool" => "true", - "string" => "\"" + new string('x', 70000) + "\"", "char" => "\"\\uffff\"", "enum" => "2147483647", "flags-enum" => "7", - "DateTime" => "\"9999-12-31T23:59:59.9999999Z\"", "DateTimeOffset" => "\"9999-12-31T23:59:59.9999999+00:00\"", - "DateOnly" => "\"9999-12-31\"", "TimeOnly" => "\"23:59:59.9999999\"", "TimeSpan" => "\"10675199.02:48:05.4775807\"", - "Uri" => "\"https://example.com/" + new string('p', 3000) + "\"", "NodaTime.Instant" => "\"9999-12-31T23:59:59.999999999Z\"", - "own-record-class" => "{\"A\":\"x\",\"B\":2147483647}", "Guid" => "\"ffffffff-ffff-ffff-ffff-ffffffffffff\"", - _ => "\"Beta\"" - }; - - /// Text a position's JSON takes for an input.json value, built from the plain side's JSON by replacing the sample's own text. - public static string InputFor(string input, Entry e, JsonSerializerOptions o, string jp, string position) => Input(input, e, o, jp, position); - static string Input(string input, Entry e, JsonSerializerOptions o, string jp, string position) - { - string sample; - try { sample = JsonSerializer.Serialize(e.Valid(), e.Plain, o); } catch { return jp; } - var at = jp.IndexOf(sample, StringComparison.Ordinal); - string Swap(string with) => at < 0 ? jp : jp[..at] + with + jp[(at + sample.Length)..]; - switch (input) - { - case "valid": return jp; - case "refused-by-create": - if (e.Refused() is not { } rf) return jp; - try { return Swap(JsonSerializer.Serialize(rf, e.Plain, o)); } catch { return jp; } - case "wrong-token": return Swap(sample.StartsWith('"') || sample.StartsWith('{') ? "[]" : "\"zz\""); - case "null": return Swap("null"); - case "empty-string": return Swap("\"\""); - case "at-type-limit": return Swap(Limit(e)); - case "quoted-number": return Swap(sample.StartsWith('"') ? sample : "\"" + sample + "\""); - case "escape-sequence": - return sample.StartsWith('"') && sample.Length > 3 ? Swap(sample[..1] + "\\u00" + ((int)sample[1]).ToString("x2") + sample[2..]) : jp; - case "duplicate-property": return jp.StartsWith('{') && jp.Length > 2 ? "{" + jp[1..^1] + "," + jp[1..^1] + "}" : jp; - case "metadata-property": return jp.StartsWith('{') ? (jp.Length > 2 ? "{\"$id\":\"1\"," + jp[1..] : "{\"$id\":\"1\"}") : jp; - default: throw new ArgumentException("input " + input); - } - } - - static bool Validated(Entry e) => e.Kind.StartsWith("validated"); - - /// - /// The oracle "JSON parity" (differential) as L01's checks, three-sided (Sides): the value object's write (3) against the wrapped twin in - /// the same shape under the same options (2), held to the twin under the application's options less TypeKit's converters (1), as - /// the-underlying-types-json, or as-a-dictionary-key-too where the value object is a dictionary key; and the read of the twin's document - /// with the row's input.json, read-as-the-underlying-type-reads, alike. L01 promises the underlying type's JSON, so side (1) runs on every - /// row. Under an application's converter for the value object, which writes it instead of the generated one, the read is that - /// converter's own document read back. Every check runs; returns the first failure. - /// - public static string? Check(Row r, Entry e, JsonSerializerOptions o, string position, string containing, string input, JsonSerializerOptions? oPlain = null) - { - oPlain ??= o; - var plain = e.Valid(); - var voV = Make.Vo(e, plain); - var (hp, hv) = Sides.Twins(position, containing, e, plain, voV); - // Input equivalence of the options: the two sides may differ only by the substituted type, not in options (the shapes: Sides.Twins). - if (Equivalence.Options(oPlain, o, allowConverters: e.Kind == "closed-set") is { } whyOptions) { r.Outcome("harness-error", "inputs not equivalent: " + whyOptions); return null; } - var one = Sides.WithoutTypeKit(oPlain); - var w1 = Sides.Write(hp, one); var w2 = Sides.Write(hp, oPlain); var w3 = Sides.Write(hv, o); - var write = position == "dictionary-key" ? "as-a-dictionary-key-too" : "the-underlying-types-json"; - const string read = "read-as-the-underlying-type-reads"; - string? fw, fr; - if (r.Get("json.Converters") == "own-for-value-object") - { - // "Your own converter wins" (AspNetCore README#what-the-document-says): the application's converter writes the value object - // instead of the generated one, so the twin's bytes are none of its. claims.json declares the write inapplicable there. - fw = r.Check(write, Baseline.Twin, c => c.Inapplicable(Cite.Docs("src/CodoMetis.TypeKit.AspNetCore/README.md#what-the-document-says"), "write")); - fr = r.Check(read, Baseline.RoundTrip, c => Sides.Interferes(w1, w2) ?? OwnVoRead(c, e, hp, hv, oPlain, o, w3.Value, w3.Thrown, w2.Thrown)); - return fw ?? fr; - } - fw = Sides.Check(r, write, Baseline.Twin, Sides.Framework(r, e, hp, one, w1, w3), w3, w1, w2, c => Write(c, e, position, w2, w3)); - if (e.Wrapped == "byte" && position == "array-element") - fr = r.Check(read, Baseline.Twin, c => c.Inapplicable(Cite.NotPromised("a-byte-array-is-base64"), "read")); - else if (e.Kind == "closed-set" && position == "dictionary-key" && r.Get("json.DictionaryKeyPolicy") is not (null or "default")) - fr = r.Check(read, Baseline.Twin, c => c.Inapplicable(Cite.NotPromised("a-closed-set-key-is-its-name"), "read")); - else if (w2.Value is not { } jp) - fr = r.Check(read, Baseline.Twin, c => c.Inconclusive("read: the wrapped type is not written under these options, so there is no document to read")); - else - { - var text = Input(input, e, oPlain, jp, position); - var r1 = Sides.Read(text, hp, one); var r2 = Sides.Read(text, hp, oPlain); var r3 = Sides.Read(text, hv, o); - fr = Sides.Check(r, read, Baseline.Twin, Sides.Framework(r, e, hp, one, r1, r3), r3, r1, r2, c => Read(c, e, position, input, text, jp, r2, r3)); - } - return fw ?? fr; - } - - /// The value object's document (3) is the wrapped twin's (2), byte for byte, in the same shape under the same options; (1) and (3) refusing alike is decided before (Sides). - static string? Write(Check c, Entry e, string position, Sides.Answer p, Sides.Answer v) - { - // System.Text.Json writes byte[] as base64 and an array of value objects over byte as a JSON array (KB-038); claims.json declares it - if (e.Wrapped == "byte" && position == "array-element" && p.Value != v.Value) return c.Inapplicable(Cite.NotPromised("a-byte-array-is-base64"), "write"); - if (p.Refused && v.Refused) - // Both refused, not alike (Sides.Alike): evidence about refusing, not about serializing, and nothing to compare. - return c.Inconclusive($"write: both refuse, not alike: the wrapped type {p}, the value object {v}"); - if (!p.Refused && v.Refused) - { - if (c.Is("json.ReferenceHandler", "Preserve") && e.Wrapped == "own-record-class" && v.Thrown is NotSupportedException) return c.Refused(Cite.NotPromised("preserved-references-over-a-walked-class"), "write"); - return $"wrapped serialized ({p.Value}) but the value object threw {v.Thrown!.GetType().Name}: {v.Thrown.Message}"; - } - if (p.Refused) return $"value object serialized ({v.Value}) where the wrapped type threw {p.Thrown!.GetType().Name}: {p.Thrown.Message}"; - // a closed set travels as its name, which no key policy renames; claims.json declares it - if (p.Value != v.Value && e.Kind == "closed-set" && position == "dictionary-key" && c.Get("json.DictionaryKeyPolicy") is not (null or "default")) return c.Inapplicable(Cite.NotPromised("a-closed-set-key-is-its-name"), "write"); - if (p.Value != v.Value) return $"JSON differs: wrapped {Cut(p.Value)} vs value object {Cut(v.Value)}"; - c.Note(NoteKeys.Compared, "write"); - return null; - } - - /// - /// The wrapped twin's document, with the row's input.json, read into both shapes (and what each read written again): each reads it as the - /// other does, or refuses as a document says; (1) and (3) refusing alike is decided before (Sides). - /// - static string? Read(Check c, Entry e, string position, string input, string text, string jp, Sides.Answer p, Sides.Answer v) - { - var stored = c.Get("json.Converters") == "StoredJsonConverterFactory"; - if (p.Refused && v.Refused) return c.Inconclusive($"read {input}: both refuse, not alike: the wrapped type {p}, the value object {v}"); - if (!p.Refused && v.Refused) - { - var xv = v.Thrown!; - if (c.Is("json.ReferenceHandler", "Preserve") && e.Wrapped == "own-record-class" && xv is NotSupportedException) return c.Refused(Cite.NotPromised("preserved-references-over-a-walked-class"), "read"); - // The documented refusals, each the JsonException the generated converter throws. - if (!(xv is JsonException) && ((Validated(e) && input == "refused-by-create" && !stored) || (input == "null" && e.Vo.IsValueType) || (e.Kind == "closed-set" && input != "valid"))) - return $"input {input}: the refusal is {xv.GetType().Name}, not JsonException: {Cut(xv.Message)}"; - if (Validated(e) && input == "refused-by-create" && !stored) return c.Refused(Cite.NotPromised("a-validated-value-object-refuses-what-create-refuses"), $"read {input}"); - if (input == "null" && e.Vo.IsValueType) return c.Refused(Cite.NotPromised("a-struct-refuses-a-json-null"), $"read {input}"); - if (e.Kind == "closed-set" && input != "valid") return c.Refused(Cite.Docs("src/CodoMetis.TypeKit.Generators/README.md#matching-parsing-and-binding"), $"read {input}"); // "Unknown names are rejected." - return $"input {input}: wrapped read it, value object threw {xv.GetType().Name}: {Cut(xv.Message)}\n text={Cut(text)}"; - } - // a reference-type value object takes JSON null as null, as any class does - if (p.Refused && input == "null" && Make.AdmitsNull(e.Vo) && !Make.AdmitsNull(e.Plain)) return c.Inapplicable(Cite.Docs("docs/json.md#null-and-missing"), "read null"); - if (p.Refused) return $"input {input}: value object read what the wrapped type refused ({p.Thrown?.GetType().Name}: {Cut(p.Thrown?.Message)})\n text={Cut(text)}"; - if (Validated(e) && input == "refused-by-create" && text != jp && !stored) - return $"validated value object accepted the refused value{(position == "dictionary-key" ? " as a dictionary key" : "")}: {Cut(text)}"; - if (p.Value != v.Value) return $"input {input}: read-back differs: wrapped {Cut(p.Value)} vs value object {Cut(v.Value)}"; - c.Note(NoteKeys.Compared, "read " + input); - return null; - } - - /// - /// Under an application's converter for the value object, the generated converter does not run: the value object's document is that - /// converter's, and it reads back as written under the row's options. Where the value object's document cannot be written, a converter - /// the application registers for the wrapped type at the same position is the baseline. - /// - static string? OwnVoRead(Check c, Entry e, Held hp, Held hv, JsonSerializerOptions oPlain, JsonSerializerOptions o, string? jv, Exception? ev, Exception? ep) - { - var oOwn = new JsonSerializerOptions(oPlain); - oOwn.Converters.Insert(0, (System.Text.Json.Serialization.JsonConverter)Activator.CreateInstance(typeof(OwnWrappedConverter<>).MakeGenericType(Make.Held(e.Plain)), JsonRow.Inner(e))!); - var (jo, eo) = Ser(hp, oOwn); - if (ev is not null) - { - // System.Text.Json refuses any custom converter there (MaxDepth counts its value one level deeper, KB-040), the wrapped - // type's under a converter of the application's alike - if (ep is not null || (eo is not null && eo.GetType() == ev.GetType())) return c.Inconclusive($"the application's converter cannot write here, for the wrapped type too: {ev.GetType().Name}"); - return $"own VO converter row: VO serialization threw {ev.GetType().Name}: {ev.Message}"; - } - c.Note(NoteKeys.OwnVo); - var (back, eb) = De(jv!, hv, o); - if (eb is not null) - { - // the same converter for the wrapped type, at the same position: its own document does not read back under these options either - var ebOwn = jo is null ? eo : De(jo, hp, oOwn).Ex; - if (ebOwn is not null && ebOwn.GetType() == eb.GetType()) return c.Inconclusive($"the application's converter's document does not read back under these options, for the wrapped type too: {eb.GetType().Name}"); - return "own VO converter: read-back threw " + eb.GetType().Name + ": " + eb.Message; - } - if (back != jv) return $"own VO converter round trip differs: {jv} vs {back}"; - return null; - } - static string Cut(string? s) => s is null ? "(null)" : s.Length > 200 ? s[..200] + "..." : s; -} - -public static class EntryExt -{ - public static object? Refused(this Entry e) => e.Kind.StartsWith("validated") ? e.RefusedFn() : null; -} diff --git a/tools/probe/corpus/harness/PreserveGuard.cs b/tools/probe/corpus/harness/PreserveGuard.cs deleted file mode 100644 index 2f046f3d..00000000 --- a/tools/probe/corpus/harness/PreserveGuard.cs +++ /dev/null @@ -1,252 +0,0 @@ -// Claim C10-preserve-guard (src/CodoMetis.TypeKit.Generators/README.md#json): "Preserved references (ReferenceHandler.Preserve) throw a -// NotSupportedException where the serializer could write $id or $ref inside the underlying value. That's a class it walks as an object [...] -// These are unaffected: a value the serializer writes with a value converter (a number, a string, a Guid, a Uri), anything a converter of -// your own writes, on the options or on one property, a struct of such values, and ReferenceHandler.IgnoreCycles." -// Oracle: claim for the guarded rows (a value object over a class the serializer walks, under Preserve, with no converter of the -// application's for it: NotSupportedException naming the type and Preserve, both directions, never a document); differential for the -// unaffected rows (the value object's document is the wrapped type's under the same options, never an $id twice, and every read agrees -// with the wrapped type's, a refusal being the section's: a JsonException naming the value object, with no inner exception). -using System.Text.Json; -using Probes.Shapes; -namespace Probes.Json; - -public static class PreserveGuard -{ - public static string? Check(Row r) - { - var handler = r["handler"]; - var want = handler == "none" ? "default" : handler; - var row = r; - if (r.Get("json.ReferenceHandler") is { } jr && jr != want) - { - // one setting under two names: the claim's handler decides (claims.json excludes the differing pairs) - r.Substituted("json.ReferenceHandler", want, "handler is the same setting"); - row = r.With("json.ReferenceHandler", want); - } - var kind = r["shape.kind"]; - var e = kind == "closed-set" ? Registry.Get(kind, "-") : Registry.Get(kind, r["shape.wrapped"]); - var position = r["shape.position"]; - var containing = position == "root" ? "class" : r["shape.containing"]; - _ = r["input.json"]; // the row's input, which every check of the row runs under; the reads apply it - var plain = e.Valid(); var voValue = Make.Vo(e, plain); - var (hp, hv) = Sides.Twins(position, containing, e, plain, voValue); - var o = JCtx.Apply(row, e, hp.Declared, hv.Declared); - // A closed set travels as its name, which no naming policy renames and no converter for strings writes (docs/closed-sets.md). - var op = kind == "closed-set" && (row.Get("json.DictionaryKeyPolicy") is not (null or "default") || row.Is("json.Converters", "own-for-wrapped")) - ? JCtx.Apply(row.With("json.DictionaryKeyPolicy", "default").With("json.Converters", row.Is("json.Converters", "own-for-wrapped") ? "default" : row["json.Converters"]), e, hp.Declared, hv.Declared) - : o; - var ownWrapped = row.Is("json.Converters", "own-for-wrapped"); - var ownVo = row.Is("json.Converters", "own-for-value-object"); - var guarded = want == "Preserve" && e.Wrapped == "own-record-class" && !ownWrapped && !ownVo; - // The obligation the row's handler and converters select: a converter of the application's, the guarded class under Preserve, - // IgnoreCycles, and otherwise a value the serializer writes with a value converter, under Preserve or with no handler at all. - var obligation = ownWrapped || ownVo ? "own-converters-unaffected" : guarded ? "refused-where-references-could-be-written" - : want == "IgnoreCycles" ? "ignore-cycles-unaffected" : "value-converters-unaffected"; - return guarded ? Guarded(r, obligation, e, hp, hv, o) : Unaffected(r, obligation, e, hp, hv, op, o, ownVo); - } - - /// - /// The guarded rows: NotSupportedException naming the wrapped type and Preserve, in both directions, never a document. The read of the - /// wrapped type's document is three-sided (Sides): where the twin without TypeKit refuses it alike, the serializer refused the document - /// before any converter ran. - /// - static string? Guarded(Row r, string obligation, Entry e, Held hp, Held hv, JsonSerializerOptions o) - { - var wrapped = Make.Held(e.Plain); - string? Refusal(Exception? x, string what) - { - if (x is not NotSupportedException) return $"{what}: expected NotSupportedException naming {wrapped.Name} under ReferenceHandler.Preserve, got {(x is null ? "no exception" : Jx.Ex(x))}"; - if (!x.Message.Contains(wrapped.FullName!) || !x.Message.Contains("ReferenceHandler.Preserve")) return $"{what}: the NotSupportedException does not name {wrapped.FullName} and ReferenceHandler.Preserve: {x.Message}"; - return null; - } - var (jv, xv) = Jx.Ser(hv, o); - var (jp, xp) = Jx.Ser(hp, o); - var fw = r.Check(obligation, Baseline.Refusal, c => - { - if (jv is not null) - { - // the options may leave the member out of the document, for the wrapped type too: then no converter ran, and nothing was refused - if (jp is not null && !Jx.Tokens(jp).Any(t => t.Text == "x") && !Jx.Tokens(jv).Any(t => t.Text == "x")) - { c.Note(NoteKeys.JsonMemberLeftOutByOptions, "write: " + Jx.Cut(jv)); return c.Inconclusive("write: the options leave the member out, for the wrapped type too: " + Jx.Cut(jv)); } - return Refusal(null, "write") + $"; it wrote {Jx.Cut(jv)}" + (DuplicateId(jv) is { } dup ? $" with {dup} twice" : ""); - } - if (Refusal(xv, "write") is { } f) return f; - c.Note(NoteKeys.Compared, "write refused"); - return null; - }); - if (xp is not null) - return fw ?? r.Check(obligation, Baseline.Refusal, c => c.Inconclusive("read: the wrapped type's document is refused under these options, so there is none to read: " + Jx.Ex(xp))); - // reading: the wrapped type's own document, with the row's input applied - var input = r["input.json"]; - var text = JsonParity.InputFor(input, e, Jx.Full(o), Jx.Ser(hp, Jx.Full(o)).Json ?? jp!, r["shape.position"]); - var (vp, ep) = Jx.De(text, hp.Declared, o); - var (vv, ev) = Jx.De(text, hv.Declared, o); - var one = Sides.WithoutTypeKit(o); - var r1 = new Sides.Answer("read", null, Jx.De(text, hp.Declared, one).Ex); - var r3 = new Sides.Answer("read", null, ev); - var fr = Sides.Check(r, obligation, Baseline.Refusal, r1.Refused && Sides.Alike(r1, r3) ? Sides.Measured(r1, "the twin's read under the application's options, less TypeKit's converters") : null, r3, null, null, c => - { - if (ev is null) - { - // no converter ran: the read holds none of the wrapped value (a JSON null for a class, a member the options do not read) - if (vv is not null && Jx.Tokens(JsonSerializer.Serialize(vv, hv.Declared, Jx.Plain)).Any(t => t.Text == "x")) - return $"read {input}: the value object over {Make.Held(e.Plain).Name} read a document under Preserve: {Jx.Cut(text)}"; - c.Note(NoteKeys.JsonMemberLeftOutByOptions, "read " + input); - return c.Inconclusive($"read {input}: no converter ran, the read holds none of the wrapped value"); - } - if (ev is NotSupportedException) { if (Refusal(ev, "read " + input) is { } f) return f; c.Note(NoteKeys.Compared, "read " + input + " refused"); return null; } - // refused before the guard: a JsonException naming the value object, with no inner exception (Generators README#json) - if (ev is not JsonException || ev.InnerException is not null) return Refusal(ev, "read " + input) + $"\n text={Jx.Cut(text)}"; - if (!ev.Message.Contains(Make.Held(e.Vo).Name)) return Refusal(ev, "read " + input) + $"\n text={Jx.Cut(text)}"; - c.Note(NoteKeys.Compared, "read " + input + ": refused naming the value object before the guard"); - return null; - }); - return fw ?? fr; - } - - /// - /// The unaffected rows, three-sided (Sides; C10 promises these are unaffected, so side (1) runs on every row): the value object's - /// document (3) is the wrapped type's under the same options (2), which is the twin's without TypeKit's converters (1), never an $id - /// twice, and its read agrees with the wrapped type's (ReadAlike); under a converter of the application's for the value object, that - /// converter's document reads back and reads the row's input alike with and without the reference handler. - /// - static string? Unaffected(Row r, string obligation, Entry e, Held hp, Held hv, JsonSerializerOptions op, JsonSerializerOptions o, bool ownVo) - { - var one = Sides.WithoutTypeKit(op); - var w1 = Sides.Write(hp, one); var w2 = Sides.Write(hp, op); var w3 = Sides.Write(hv, o); - var (jp, xp) = (w2.Value, w2.Thrown); var (jv, xv) = (w3.Value, w3.Thrown); - // byte[] is base64 by the serializer's own rule (KB-038), an array of value objects a JSON array: unaffected means the value - // object's document reads back as written - var byteArray = e.Wrapped == "byte" && r.Is("shape.position", "array-element") && jv is not null && jp is not null && jv != jp; - var written = Sides.Framework(r, e, hp, one, w1, w3); - var fw = Sides.Check(r, obligation, ownVo || byteArray ? Baseline.RoundTrip : Baseline.Twin, written, w3, w1, w2, c => - { - if (xp is not null && xv is not null) return $"write: both threw, not alike: the wrapped type {w2}, the value object {w3}"; - if (xv is not null) return $"write: the value object threw {Jx.Ex(xv)} where the wrapped type wrote {Jx.Cut(jp)}"; - // the application's converter for the value object writes its own document: its baseline is that document read back, whatever - // the twin's writes ("anything a converter of your own writes" is unaffected) - if (xp is not null && !ownVo) return $"write: the value object wrote {Jx.Cut(jv)} where the wrapped type threw {Jx.Ex(xp)}"; - if (DuplicateId(jv!) is { } dup) return $"write: $id {dup} twice in {Jx.Cut(jv)}"; - if (ownVo) - { - c.Note(NoteKeys.OwnVo); // the application's converter for the value object writes its own document; it reads it back, unaffected - var (jf, _) = Jx.Ser(hv, Jx.Full(o)); - var (_, xr) = Jx.De(jf ?? jv!, hv.Declared, o); - if (xr is not null) return "own converter for the value object: its document does not read back: " + Jx.Ex(xr); - return null; - } - if (byteArray) - { - var (back, xb) = Jx.De(jv!, hv.Declared, o); - if (xb is not null) return "write: the array of byte value objects does not read back: " + Jx.Ex(xb); - var again = JsonSerializer.Serialize(back, hv.Declared, o); - if (again != jv) return $"round trip: {Jx.Cut(jv)} read back as {Jx.Cut(again)}"; - c.Note(NoteKeys.Compared, "an array of byte value objects round-trips as a JSON array"); - return null; - } - if (jv != jp) return $"write: the value object wrote {Jx.Cut(jv)}, the wrapped type {Jx.Cut(jp)}"; - c.Note(NoteKeys.Compared, "write"); - return null; - }); - if (ownVo) return fw ?? OwnReadsInput(r, obligation, e, hp, hv, o); - // the wrapped type's base64 document is no array of value objects (KB-038): the round trip above holds the value object to its own - if (byteArray) return fw ?? r.Check(obligation, Baseline.Twin, c => c.Inapplicable(Cite.Fact("KB-038"), "read: the wrapped type's document is a base64 string")); - // the value object's document is refused as the framework refuses it, so is its read: the write's refusal, as it is - if (written is not null && Sides.Alike(written.Answer, w3)) return fw ?? Sides.Check(r, obligation, Baseline.Twin, written, w3, null, null, _ => (string?)null); - var full = Jx.Ser(hp, Jx.Full(op)).Json ?? jp; - if (full is null) return fw ?? r.Check(obligation, Baseline.Twin, c => c.Inconclusive("read: the wrapped type is not written under these options, so there is no document to read")); - var input = r["input.json"]; - var text = JsonParity.InputFor(input, e, Jx.Full(op), full, r["shape.position"]); - var r1 = Sides.Read(text, hp, one); var r2 = Sides.Read(text, hp, op); var r3 = Sides.Read(text, hv, o); - var fr = Sides.Check(r, obligation, Baseline.Twin, Sides.Framework(r, e, hp, one, r1, r3), r3, r1, r2, c => ReadAlike.Check(c, e, input, text, r2, r3, hp, hv, op, o)); - return fw ?? fr; - } - - /// - /// The application's converter for the value object reads the row's input inside its own spelling ("vo:" and the wrapped - /// type's JSON) alike under the row's reference handler and under none: "anything a converter of your own writes" is unaffected - /// (Generators README#json). Each side writes its own document, so Preserve's metadata is its own, and only the value is rewritten. - /// Where the handler's document is refused as the framework refuses a converter's value (KB-040), the check is that refusal. - /// - static string? OwnReadsInput(Row r, string obligation, Entry e, Held hp, Held hv, JsonSerializerOptions o) - { - var input = r["input.json"]; - var inner = JsonRow.Inner(e); - var wrapped = JsonSerializer.Serialize(Make.Unwrap(e, Make.Vo(e, e.Valid())), Make.Held(e.Plain), inner); - var value = "vo:" + JsonParity.InputFor(input, e, inner, wrapped, "root"); - var bare = new JsonSerializerOptions(o) { ReferenceHandler = null }; - (string? Read, string? Refused, Sides.Answer? Write) Through(JsonSerializerOptions x) - { - var w = Sides.Write(hv, Jx.Full(x)); - if (w.Refused) return (null, "write: " + w.Thrown!.GetType().Name, w); - var own = Jx.Tokens(w.Value!).Where(t => t.Type is JsonTokenType.String or JsonTokenType.PropertyName && t.Text is { } s && s.StartsWith("vo:", StringComparison.Ordinal)).ToList(); - if (own.Count == 0) return (null, null, null); - var (v, rx) = Jx.De(Jx.Rewrite(w.Value!, own, _ => JsonSerializer.Serialize(value)), hv.Declared, x); - if (rx is not null) return (null, "read: " + rx.GetType().Name, null); - return (JsonSerializer.Serialize(v, hv.Declared, bare), null, null); - } - var handled = Through(o); var unhandled = Through(bare); - // the handler's document, written by the application's converter, refused as the framework refuses any converter's value there - var framework = (handled.Write ?? unhandled.Write) is { } refused ? Sides.WrittenByAConverter(r, e, hp, Sides.WithoutTypeKit(Jx.Full(handled.Write is null ? bare : o)), refused) : null; - return Sides.Check(r, obligation, Baseline.RoundTrip, framework, (handled.Write ?? unhandled.Write) ?? new Sides.Answer("write", null), null, null, c => - { - if (handled.Read is null && handled.Refused is null && unhandled.Read is null && unhandled.Refused is null) - { c.Note(NoteKeys.JsonMemberLeftOutByOptions, "own converter: the options write no member"); return c.Inconclusive("own converter: the options write no member to read"); } - if (handled.Read != unhandled.Read || handled.Refused != unhandled.Refused) - return $"read {input} through the application's converter: {handled.Read ?? handled.Refused ?? "no member"} under ReferenceHandler {c["handler"]}, {unhandled.Read ?? unhandled.Refused ?? "no member"} without one"; - c.Note(NoteKeys.Compared, $"read {input} through the application's converter, alike with and without the reference handler: {Jx.Cut(handled.Read ?? handled.Refused)}"); - return null; - }); - } - - /// The first $id value a document holds twice, or null. - public static string? DuplicateId(string json) - { - var t = Jx.Tokens(json); var seen = new HashSet(); - for (var i = 0; i + 1 < t.Count; i++) - if (t[i].Type == JsonTokenType.PropertyName && t[i].Text == "$id" && !seen.Add(t[i + 1].Text ?? t[i + 1].Raw)) return t[i + 1].Text; - return null; - } -} - -/// -/// One read of the same text into the wrapped type's shape (2) and the value object's (3), each written again to be compared: they agree, or -/// differ only as the documents say (a struct value object refuses a JSON null, a sealed record reads it as null, a validated one refuses what -/// Create refuses, a closed set has only its members); a refusal of the value object's is a JsonException naming it, with no inner exception -/// (Generators README#json). Side (1) refusing alike with (3) is decided before (Sides). -/// -public static class ReadAlike -{ - public static string? Check(Check c, Entry e, string input, string text, Sides.Answer p, Sides.Answer v, Held hp, Held hv, JsonSerializerOptions op, JsonSerializerOptions ov) - { - var voName = Make.Held(e.Vo).Name; - string? Named(Exception x) => x is JsonException && x.Message.Contains(voName) && x.InnerException is null ? null - : $"read {input}: the value object's refusal is {Jx.Ex(x)}{(x.InnerException is null ? "" : " with an inner exception")}, not a JsonException naming {voName}\n text={Jx.Cut(text)}"; - var stored = c.Is("json.Converters", "StoredJsonConverterFactory"); - if (!p.Refused && !v.Refused) - { - if (e.Kind.StartsWith("validated") && input == "refused-by-create" && !stored && e.Refused() is not null && text != Jx.Ser(hp, op).Json && !c.Is("shape.position", "dictionary-key")) - return $"read {input}: the validated value object read what Create refuses\n text={Jx.Cut(text)}"; - if (p.Value != v.Value) return $"read {input}: read back differs: wrapped {Jx.Cut(p.Value)} vs value object {Jx.Cut(v.Value)}\n text={Jx.Cut(text)}"; - c.Note(NoteKeys.Compared, "read " + input); return null; - } - if (p.Refused && v.Refused) - { - if (v.Operation != "read") return $"read {input}: both refuse, not alike: the wrapped type {p}, the value object {v}"; - if (Named(v.Thrown!) is { } f) return f + $"; the wrapped type: {p}"; - c.Note(NoteKeys.Compared, "read " + input + ": both refuse, the value object naming itself"); return null; - } - if (v.Refused) - { - var intended = v.Operation == "read" && ((input == "null" && e.Vo.IsValueType) || (e.Kind.StartsWith("validated") && input == "refused-by-create" && !stored) || (e.Kind == "closed-set" && input != "valid")); - if (!intended) return $"read {input}: the wrapped type read it, the value object refused: {v} {Jx.Ex(v.Thrown!)}\n text={Jx.Cut(text)}"; - if (Named(v.Thrown!) is { } f) return f; - c.Note(NoteKeys.Compared, "read " + input + ": refused as documented"); return null; - } - // the wrapped type refused and the value object read: only a JSON null for a sealed record, which reads it as any class does - if (input == "null" && Make.AdmitsNull(e.Vo) && !Make.AdmitsNull(e.Plain) && Jx.De(text, hv.Declared, ov).Value is { } read - && Jx.Tokens(JsonSerializer.Serialize(read, hv.Declared, Jx.Plain)).Any(t => t.Type == JsonTokenType.Null)) - { c.Note(NoteKeys.Compared, "read null: the sealed record reads null, as any class does"); return null; } - return $"read {input}: the value object read what the wrapped type refused ({p})\n text={Jx.Cut(text)}"; - } -} diff --git a/tools/probe/corpus/harness/Registry.cs b/tools/probe/corpus/harness/Registry.cs deleted file mode 100644 index abe2a1d8..00000000 --- a/tools/probe/corpus/harness/Registry.cs +++ /dev/null @@ -1,27 +0,0 @@ -// Hand half of the shape registry (see gen_shapes.py). Looks an entry up by the row's shape.kind and shape.wrapped. -namespace Probes.Shapes; -public static partial class Registry -{ - public static readonly List All = Build(); - static List Build() { var e = new List(); Fill(e); return e; } - static partial void Fill(List e); - /// The entry a row names. record-class (CMTK1006) and over-value-object (CMTK1005) are refused at build, verified; the row's other axes are - /// still probed on the nearest buildable kind. - public static Entry Of(Row r) - { - var k = r["shape.kind"]; - if (k is "record-class" or "over-value-object") { k = k == "record-class" ? "sealed-record-class" : "record-struct"; r.Substituted("shape.kind", k, "the generators refuse the requested kind"); } - var e = Get(k, r["shape.wrapped"]); - if (r.Is("http.nullability", "nullable")) - { - // The parameter or body declared nullable on both sides (T? against wrapped?): the dimension G1 exposed. - static Type N(Type t) => t.IsValueType && Nullable.GetUnderlyingType(t) is null ? typeof(Nullable<>).MakeGenericType(t) : t; - r.Note(NoteKeys.BindingNullable); - e = e with { Vo = N(e.Vo), Plain = N(e.Plain) }; - } - return e; - } - public static Entry Get(string kind, string wrapped) => - kind == "closed-set" ? All.First(x => x.Kind == "closed-set") - : All.First(x => x.Kind == kind && x.Wrapped == wrapped); -} diff --git a/tools/probe/corpus/harness/ReplayProbe.cs b/tools/probe/corpus/harness/ReplayProbe.cs deleted file mode 100644 index 07249dad..00000000 --- a/tools/probe/corpus/harness/ReplayProbe.cs +++ /dev/null @@ -1,14 +0,0 @@ -// Debug aid (not a claim): PROBE_REPLAY="rowsfile:idx" re-runs the baseline for one row and writes its exchange log to /tmp/replay.txt. -using Probes.Shapes; -namespace Probes; -public sealed class ReplayProbe -{ - [Fact] - public async Task Replay() - { - var spec = Environment.GetEnvironmentVariable("PROBE_REPLAY"); if (spec is null) return; - var parts = spec.Split(':'); var r = Rows.Get(parts[0], int.Parse(parts[1])); - var f = await HttpBaseline.Run(r, Registry.Of(r)); - File.WriteAllText("/tmp/replay.txt", r.ToString() + "\nRESULT: " + (f ?? "pass") + "\nNOTES: " + r.Notes + "\n" + r.Log); - } -} diff --git a/tools/probe/corpus/harness/Row.cs b/tools/probe/corpus/harness/Row.cs deleted file mode 100644 index 79703c65..00000000 --- a/tools/probe/corpus/harness/Row.cs +++ /dev/null @@ -1,592 +0,0 @@ -// Row plumbing shared by every probe: each probe's rows are its own file, written by `prober rows --probe P` (the corpus's -// rows/P.json.gz, unpacked by the replay to Probes/rows/P.json); each probe records one line per row to $PROBE_OUT/