Skip to content

test: the EF family's probes run one check per obligation - #56

Merged
CaffeinatedCoder merged 4 commits into
release/2.2.0from
test/ws2-ef-checks
Oct 17, 2026
Merged

CaffeinatedCoder merged 4 commits into
release/2.2.0from
test/ws2-ef-checks

Conversation

@CaffeinatedCoder

@CaffeinatedCoder CaffeinatedCoder commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

WS2 step 4, the EF family: every probe whose claim's first surface is ef (E01 to E16) runs one Row.Check per obligation its row's axes select, named by the claim's obligation id and given its baseline. No EF probe or harness file calls an outcome note key any more. prober status shows 0 uncredited for all 16 EF claims, where every one of their 44 listed obligations was uncredited before, and 48 of 48 seeds are caught, each by the check of its own obligation. No row that passed before fails now.

What changed

  • One check per obligation. Each probe picks the obligations from the row's axes as claims.json lists them. Checked mechanically over the replay's evidence: for all 852 rows, the checks that ran are exactly the obligations the row's axes select (0 mismatches).
  • Baselines.
    • Baseline.Twin (the wrapped twin's context under plain EF against the value object's under UseTypeKit()): E01, E02, E03, E04, E12's value-object-type-reaches, E14, E15, E16.
    • Baseline.Refusal: E05's comparison, ordering, Min/Max and query filter.
    • Baseline.Model (memory): E05's equality and .Value order, E06's value-object equality, E13.
    • Baseline.Docs where the sentence is the only baseline: E04's char and Guid defaults (README#keys), E06's "could not be translated" (README#queries), E07 (#closed-sets), E08 (docs/ef-core.md#option-and-result, with RefusalNamesTheType), E09 (#an-option-for-nothing-found), E10 (docs/ef-core.md#reading-back-without-validation, with StoredFactoryReadUnvalidated), E11 (#setup), E12's new obligation.
  • Each check judges its own share where a row has several.
    • E01: column-type/nullable compare the model and the script with the configured facet's own fields masked; facet-kept compares those fields (and the script for a column type).
    • E02: the accessor's obligation compares the rows the query answers; column-not-cast compares the SQL.
    • E07: stored-as-name covers a table's stored text and the member read back; in-collections-and-json covers the stored JSON of a collection or JSON column.
    • E08: model-fails checks the refusal and its type; error-says-so checks that the message names the type.
    • E10: the read-back value goes to reads-without-create, Revalidate() to revalidate-finds.
  • EfSides (harness/EfHarness.cs) decides how a two-sided check ends when a side threw. Neither threw: compare. Only the value object threw: violation. Both threw with one exception type: inconclusive (uncited). Both threw with different types: violation. Only the twin threw: inconclusive.
  • E01 applies its facet as configured on every wrapped type. A length on a non-string or a precision on a non-decimal used to be substituted to none, so facet-kept compared nothing on 82 rows and 42 pairs could never be covered. All 82 now compare equal to the twin. E01's pairs went from 372/414 to 414/414.
  • NoteKeys is untouched. The pass keys the EF probes used stay as c.Note, and Compared notes say what each check compared.

Commits:

Obligation decisions: four obligations added

Some rows selected no listed obligation. As #54 did for C11 and C17, they get an obligation quoted from the claim's own source, each with its seed:

Claim Obligation Sentence (quoted) Axes Rows Seed
E04 not-on-efs-list "That's EF's own list: int, long, short, sbyte, uint, ulong and ushort, and not byte." shape.wrapped string, ef.key-config default 1 a string key declared generated on add
E04 own-configuration-wins "A configuration you write yourself always wins over these defaults." shape.wrapped char, Guid, string; ef.key-config ValueGeneratedOnAdd, ValueGeneratedNever 9 E16's on-add defect (a char or Guid key forced never generated as an explicit configuration)
E12 underlying-type-does-not-reach "So configurationBuilder.Properties<decimal>().HavePrecision(18, 2) reaches decimal properties, and not an Amount that wraps one." ef.convention-type underlying 7 a property-added convention applying the wrapped type's model-wide configuration (EF's ModelConfiguration) to a value-object property
E13 contains "db.Orders.Where(o => o.Tags.Contains(tag));" ef.query contains 5 the query interceptor rewrites Contains of a value object as false

Before and after, per probe

  • Before: release/2.2.0 at f8993e3, replayed with footprints from an empty state in a detached checkout of its own (16 passed).
  • After: this branch at 5e7884a, replay --family ef (16 passed).
  • A row that runs no Row.Check counts as one check. "Rows passing" is what Rec.Run passes.
  • Before, every row ran no check, so the checks before equal the rows before.
Probe rows before rows after checks after checks after, by obligation rows passing, before → after regained (before) lost
E01ColumnParityProbe pass 175 pass 175 pass 307 column-type pass 139; facet-kept pass 132; nullable pass 36 175 → 175 0 0
E02ValueTranslatesProbe expected-refusal 12, pass 203 inapplicable 12, pass 203 inapplicable 24, pass 406 column-not-cast inapplicable 12, pass 203; get-value inapplicable 2, pass 75; value inapplicable 6, pass 68; value-or-null inapplicable 4, pass 60 203 → 203 0 0
E03ColumnRoundTripProbe pass 177 pass 177 pass 177 keys pass 35; nullable-properties pass 35; stored-as-underlying pass 107 177 → 177 0 0
E04IntegerKeyProbe pass 28 pass 28 pass 28 byte-not-generated pass 4; char-key-yours pass 1; generated-on-add pass 12; guid-key-yours pass 1; not-on-efs-list pass 1; own-configuration-wins pass 9 28 → 28 0 0
E05OwnCompareToProbe pass 90 pass 90 pass 90 comparison pass 30; equality-allowed pass 6; min-max pass 12; ordering pass 24; query-filter pass 12; value-order-allowed pass 6 90 → 90 0 0
E06OwnConverterNoValueProbe pass 12 pass 12 pass 12 value-not-translated pass 5; value-object-comparison pass 7 12 → 12 0 0
E07ClosedSetNameProbe pass 9 pass 9 pass 11 in-collections-and-json pass 2; non-member-throws pass 3; stored-as-name pass 3; unknown-name-throws pass 3 9 → 9 0 0
E08OptionResultNotColumnsProbe pass 20 pass 20 pass 40 error-says-so pass 20; model-fails pass 20 20 → 20 0 0
E09OrNoneInDbProbe pass 18 pass 18 pass 18 element-at-skips pass 3; first-reads-one-row pass 9; single-asks-for-two pass 6 18 → 18 0 0
E10ReadBackSkipsValidationProbe pass 30 pass 30 pass 60 reads-without-create pass 30; revalidate-finds pass 30 30 → 30 0 0
E11UseTypeKitOrderProbe pass 14 pass 14 pass 14 order-does-not-matter pass 14 14 → 14 0 0
E12ConventionByTypeProbe pass 12 pass 12 pass 12 underlying-type-does-not-reach pass 7; value-object-type-reaches pass 5 12 → 12 0 0
E13PrimitiveCollectionProbe pass 10 pass 10 pass 10 contains pass 5; element-value pass 5 10 → 10 0 0
E14InheritanceKeyProbe pass 9 pass 9 pass 9 tpc-assign-on-sqlite pass 3; tph-tpt-root pass 6 9 → 9 0 0
E15CompositeKeyProbe pass 8 pass 8 pass 8 as-for-primitives pass 8 8 → 8 0 0
E16OwnConfigurationWinsProbe pass 25 pass 25 pass 25 column-type pass 5; conversion pass 5; never pass 5; on-add pass 5; optional pass 5 25 → 25 0 0
16 probes 852 852 1251 (1227 pass) 840 → 840 0 0

No row regained and none lost. Before, every row but E02's 12 already passed as a row, and those 12 still earn nothing: they were expected-refusal (uncited both-threw) and are now inapplicable, declared from KB-039. The conversion changes what each passing row credits: the obligations it serves, and E01's facet pairs.

Claim before (f8993e3) after (5e7884a)
E01-column-parity 372/414, 3 obligations, 3 uncredited 414/414, 3 obligations, 0 uncredited
E02-value-translates-to-column 400/412, 4 obligations, 4 uncredited 400/412, 6 inapplicable, 4 obligations, 0 uncredited
E03-column-round-trip 290/290, 3 obligations, 3 uncredited 290/290, 3 obligations, 0 uncredited
E04-integer-key-generated 61/61, 4 obligations, 4 uncredited 61/61, 6 obligations, 0 uncredited
E05-own-compareto-rejected 90/90, 6 obligations, 6 uncredited 90/90, 6 obligations, 0 uncredited
E06-own-converter-no-value 26/26, 2 obligations, 2 uncredited 26/26, 2 obligations, 0 uncredited
E07-closed-set-stored-as-name 9/9, 4 obligations, 4 uncredited 9/9, 4 obligations, 0 uncredited
E08-option-result-not-columns 47/47, 2 obligations, 2 uncredited 47/47, 2 obligations, 0 uncredited
E09-or-none-in-database 69/69, 3 obligations, 3 uncredited 69/69, 3 obligations, 0 uncredited
E10-read-back-skips-validation 52/52, 2 obligations, 2 uncredited 52/52, 2 obligations, 0 uncredited
E11-usetypekit-order 14/14, 1 obligations, 1 uncredited 14/14, 1 obligations, 0 uncredited
E12-convention-by-vo-type 26/26, 1 obligations, 1 uncredited 26/26, 2 obligations, 0 uncredited
E13-primitive-collections 10/10, 1 obligations, 1 uncredited 10/10, 2 obligations, 0 uncredited
E14-inheritance-key-generation 9/9, 2 obligations, 2 uncredited 9/9, 2 obligations, 0 uncredited
E15-composite-key 8/8, 1 obligations, 1 uncredited 8/8, 1 obligations, 0 uncredited
E16-own-configuration-wins 25/25, 5 obligations, 5 uncredited 25/25, 5 obligations, 0 uncredited

E02's 6 uncovered pairs were uncovered before too (Noticed, below).

Seeds

prober seeds --probe P for each of the 16 probes, one at a time:

  • the 15 other than E02 at 66dc2d7;
  • E02 at 5e7884a, after its declaration and the merge.

The merge changed nothing under src/ or tools/probe, so the 15 earlier runs stand.

48 seeds, 48 caught, each by the check of its own obligation (the detail's example names it), the four new ones included.

seeds output
  E01ColumnParityProbe/column-type (E01-column-parity): caught, 12 of 175 rows violate; e.g. #0 column-type: violation:model differs: VO: t.P: type=TEXT nullable=False max= prec= scale= unicode= pk=False gen=Never | plain: t.P: ty
  E01ColumnParityProbe/nullable (E01-column-parity): caught, 36 of 175 rows violate; e.g. #1 nullable: violation:model differs: VO: t.P: type=TEXT nullable=False max= prec=* scale=* unicode= pk=False gen=Never | plain: t.P: type=T
  E01ColumnParityProbe/facet-kept (E01-column-parity): caught, 108 of 175 rows violate; e.g. #1 facet-kept: violation:HavePrecision not kept: the value object's column has prec=, the wrapped type's prec=18 (t.P: type=TEXT nullable
  E02ValueTranslatesProbe/value (E02-value-translates-to-column): caught, 58 of 215 rows violate; e.g. #0 value: violation:the query with the value object threw InvalidOperationException: The LINQ expression 'DbSet<EntP<V>>(); the
  E02ValueTranslatesProbe/get-value (E02-value-translates-to-column): caught, 58 of 215 rows violate; e.g. #1 get-value: violation:the query with the value object threw InvalidOperationException: The LINQ expression 'DbSet<EntP<X_
  E02ValueTranslatesProbe/value-or-null (E02-value-translates-to-column): caught, 51 of 215 rows violate; e.g. #2 value-or-null: violation:the query with the value object threw InvalidOperationException: The LINQ expression 'DbSet
  E02ValueTranslatesProbe/column-not-cast (E02-value-translates-to-column): caught, 203 of 215 rows violate; e.g. #0 column-not-cast: violation:SQL differs. VO: SELECT "t0"."Key" FROM ( SELECT CAST("t"."P" AS INTEGER) AS "Key" FRO
  E03ColumnRoundTripProbe/stored-as-underlying (E03-column-round-trip): caught, 5 of 177 rows violate; e.g. #13 stored-as-underlying: violation:stored column differs: value object [real:7.8] vs wrapped type [text:'7.8']
  E03ColumnRoundTripProbe/keys (E03-column-round-trip): caught, 35 of 177 rows violate; e.g. #0 keys: violation:saving and reading a key with the value object threw InvalidOperationException: The 'V' property 'EntK<V>.Id' could no
  E03ColumnRoundTripProbe/nullable-properties (E03-column-round-trip): caught, 35 of 177 rows violate; e.g. #1 nullable-properties: violation:saving and reading a nullable with the value object threw DbUpdateException: An error oc
  E04IntegerKeyProbe/generated-on-add (E04-integer-key-generated): caught, 5 of 28 rows violate; e.g. #8 generated-on-add: violation:VO key: threw; plain key: keys=[1,2]
  E04IntegerKeyProbe/byte-not-generated (E04-integer-key-generated): caught, 1 of 28 rows violate; e.g. #9 byte-not-generated: violation:VO key: keys=[2,1]; plain key: threw
  E04IntegerKeyProbe/char-key-yours (E04-integer-key-generated): caught, 1 of 28 rows violate; e.g. #16 char-key-yours: violation:char key is declared OnAdd, not never generated (plain: threw)
  E04IntegerKeyProbe/guid-key-yours (E04-integer-key-generated): caught, 1 of 28 rows violate; e.g. #12 guid-key-yours: violation:Guid key is declared OnAdd, not never generated (plain: keys=[guid,guid])
  E04IntegerKeyProbe/not-on-efs-list (E04-integer-key-generated): caught, 1 of 28 rows violate; e.g. #7 not-on-efs-list: violation:VO key: keys=[guid,guid]; plain key: threw
  E04IntegerKeyProbe/own-configuration-wins (E04-integer-key-generated): caught, 2 of 28 rows violate; e.g. #18 own-configuration-wins: violation:VO key: threw; plain key: keys=[guid,guid]
  E05OwnCompareToProbe/comparison (E05-own-compareto-rejected): caught, 30 of 90 rows violate; e.g. #0 comparison: violation:le on HO_int_class returned [1,2] instead of throwing
  E05OwnCompareToProbe/ordering (E05-own-compareto-rejected): caught, 24 of 90 rows violate; e.g. #7 ordering: violation:OrderByDescending on HO_decimal_struct returned [3.5,2.5,1.5] instead of throwing
  E05OwnCompareToProbe/min-max (E05-own-compareto-rejected): caught, 12 of 90 rows violate; e.g. #4 min-max: violation:Max on HO_string_struct returned [c] instead of throwing
  E05OwnCompareToProbe/equality-allowed (E05-own-compareto-rejected): caught, 6 of 90 rows violate; e.g. #1 equality-allowed: violation:equals (allowed) threw InvalidOperationException: HO_int_class is compared with >= in a query,
  E05OwnCompareToProbe/value-order-allowed (E05-own-compareto-rejected): caught, 6 of 90 rows violate; e.g. #11 value-order-allowed: violation:value-order (allowed) threw InvalidOperationException: HO_decimal_struct is ordered by 
  E05OwnCompareToProbe/query-filter (E05-own-compareto-rejected): caught, 12 of 90 rows violate; e.g. #2 query-filter: violation:HasQueryFilter on HO_int_struct returned [3] instead of throwing
  E06OwnConverterNoValueProbe/value-not-translated (E06-own-converter-no-value): caught, 5 of 12 rows violate; e.g. #1 value-not-translated: violation:converter-type: a query on .Value of a property the application converts itself
  E06OwnConverterNoValueProbe/value-object-comparison (E06-own-converter-no-value): caught, 7 of 12 rows violate; e.g. #0 value-object-comparison: violation:converter-type: comparing the value object failed with InvalidOperationEx
  E07ClosedSetNameProbe/stored-as-name (E07-closed-set-stored-as-name): caught, 3 of 9 rows violate; e.g. #2 stored-as-name: violation:reading the stored ["BETA","ALPHA"] back threw InvalidOperationException: PSet has no member of
  E07ClosedSetNameProbe/in-collections-and-json (E07-closed-set-stored-as-name): caught, 2 of 9 rows violate; e.g. #2 in-collections-and-json: violation:stored text is ["Beta!","Alpha!"], expected ["Beta","Alpha"]
  E07ClosedSetNameProbe/unknown-name-throws (E07-closed-set-stored-as-name): caught, 3 of 9 rows violate; e.g. #0 unknown-name-throws: violation:an unknown stored name read without throwing: null
  E07ClosedSetNameProbe/non-member-throws (E07-closed-set-stored-as-name): caught, 3 of 9 rows violate; e.g. #3 non-member-throws: violation:a non-member instance was written without throwing; stored: {"P":"Unknown"}
  E08OptionResultNotColumnsProbe/model-fails (E08-option-result-not-columns): caught, 10 of 20 rows violate; e.g. #4 model-fails: violation:the model with Result`2 as owned-property built and created the table
  E08OptionResultNotColumnsProbe/error-says-so (E08-option-result-not-columns): caught, 20 of 20 rows violate; e.g. #0 error-says-so: violation:model build failed but the message does not name the type: This property cannot be map
  E09OrNoneInDbProbe/first-reads-one-row (E09-or-none-in-database): caught, 3 of 18 rows violate; e.g. #2 first-reads-one-row: violation:FirstOrNoneAsync: the SELECT has no LIMIT (reads more than the rows asked for): SELECT "t"."I
  E09OrNoneInDbProbe/single-asks-for-two (E09-or-none-in-database): caught, 1 of 18 rows violate; e.g. #9 single-asks-for-two: violation:SingleOrNoneAsync over many-match: got some:2, expected throws
  E09OrNoneInDbProbe/element-at-skips (E09-or-none-in-database): caught, 3 of 18 rows violate; e.g. #4 element-at-skips: violation:ElementAtOrNone: the SELECT has no OFFSET: SELECT "t"."Id", "t"."Key" FROM "t" AS "t" WHERE "t"."Ke
  E10ReadBackSkipsValidationProbe/reads-without-create (E10-read-back-skips-validation): caught, 30 of 30 rows violate; e.g. #0 reads-without-create: violation:reading the row back threw InvalidOperationException: The column holds
  E10ReadBackSkipsValidationProbe/revalidate-finds (E10-read-back-skips-validation): caught, 30 of 30 rows violate; e.g. #0 revalidate-finds: violation:Revalidate() of the stored refused value is Success, expected Error
  E11UseTypeKitOrderProbe/order-does-not-matter (E11-usetypekit-order): caught, 14 of 14 rows violate; e.g. #0 order-does-not-matter: violation:closed set not stored as its name text:
  E12ConventionByTypeProbe/value-object-type-reaches (E12-convention-by-vo-type): caught, 4 of 12 rows violate; e.g. #1 value-object-type-reaches: violation:a convention on the value object type E_record_struct_decimal did not rea
  E12ConventionByTypeProbe/underlying-type-does-not-reach (E12-convention-by-vo-type): caught, 7 of 12 rows violate; e.g. #0 underlying-type-does-not-reach: violation:a convention on Uri reached the value object's property: t.P: t
  E13PrimitiveCollectionProbe/element-value (E13-primitive-collections): caught, 5 of 10 rows violate; e.g. #0 element-value: violation:any-value-equals over List<decimal value object> threw (InvalidOperationException: The LINQ ex
  E13PrimitiveCollectionProbe/contains (E13-primitive-collections): caught, 5 of 10 rows violate; e.g. #1 contains: violation:contains returned [0: 1: 2: 3:], the collections hold [0:1 1:1,2 2:2,3 3:]
  E14InheritanceKeyProbe/tph-tpt-root (E14-inheritance-key-generation): caught, 4 of 9 rows violate; e.g. #0 tph-tpt-root: violation:default keys differ from the plain hierarchy under TPT: VO threw:InvalidOperationException | plai
  E14InheritanceKeyProbe/tpc-assign-on-sqlite (E14-inheritance-key-generation): caught, 2 of 9 rows violate; e.g. #4 tpc-assign-on-sqlite: violation:default keys differ from the plain hierarchy under TPC: VO threw:DbUpdateExceptio
  E15CompositeKeyProbe/as-for-primitives (E15-composite-key): caught, 6 of 8 rows violate; e.g. #0 as-for-primitives: violation:model/results differ from the plain composite key: VO: t.A: type=INTEGER nullable=False max= prec= sca
  E16OwnConfigurationWinsProbe/never (E16-own-configuration-wins): caught, 2 of 25 rows violate; e.g. #17 never: violation:ValueGeneratedNever not kept: t.Id: type=INTEGER nullable=False max= prec= scale= unicode= pk=True gen=OnAd
  E16OwnConfigurationWinsProbe/on-add (E16-own-configuration-wins): caught, 2 of 25 rows violate; e.g. #9 on-add: violation:ValueGeneratedOnAdd not kept: t.Id: type=TEXT nullable=False max= prec= scale= unicode= pk=True gen=Never 
  E16OwnConfigurationWinsProbe/column-type (E16-own-configuration-wins): caught, 5 of 25 rows violate; e.g. #0 column-type: violation:HasColumnType(VARCHAR(40)) not kept: t.Id: type=TEXT nullable=False max= prec= scale= unicode= p
  E16OwnConfigurationWinsProbe/conversion (E16-own-configuration-wins): caught, 5 of 25 rows violate; e.g. #1 conversion: violation:stored values are not the application's converter output: 4000000000,4000000001, expected C:400000
  E16OwnConfigurationWinsProbe/optional (E16-own-configuration-wins): caught, 5 of 25 rows violate; e.g. #2 optional: violation:IsRequired(false) not kept on a non-key value object property: t.Id: type=INTEGER nullable=False max= 

Findings of this step, triaged

  • E02, 12 rows: a value object over DateTimeOffset, TimeSpan or ulong under where-value-compare or order-by. Before, these were uncited both-threw; after the conversion's first replay, inconclusive: both sides refuse the query alike.
    • Triage 1: no. The plain twin under plain EF fails the same way: >= raises InvalidOperationException "could not be translated", and ORDER BY raises NotSupportedException "SQLite does not support expressions of type 'X' in ORDER BY clauses".
    • Triage 2: a framework fact, pinned as KB-039 (design/knowledge/KB-039-…, test test/CodoMetis.TypeKit.Facts/KB039.cs, 10 cases). EF Core 10's SQLite provider neither compares nor orders those three types, and translates their equality.
      • Source: EF's "SQLite Database Provider - Limitations", Query limitations, plus measured.
      • The page also lists decimal, which EF 10 compares and orders; the test holds that as the control.
    • Cited by declaration (build: declared applicability: an obligation says where it does not apply, read by the universe, the covering generator, the gate and Row.Check #50), since the excuse depends on the axes alone. Each of E02's four obligations declares shape.wrapped ∈ {DateTimeOffset, TimeSpan, ulong} × ef.query ∈ {where-value-compare, order-by} inapplicable, cite: fact:KB-039. universe counts the 6 pairs as wanted by no obligation.
  • E01, rows PR hygiene: issue forms, labels and a CI check, as in ValueRanges #68 and #134 (char key with HasColumnType), in the first try: triage 1, my harness. facet-kept compared the create script without the AUTOINCREMENT strip that column-type applies for the documented char/Guid generation. Fixed before the commit: both checks share the normalised script.
  • No step-4 finding, nothing skipped.

Uncited excuses

Each outcome note the EF family called, and what it is now:

  • both-threw (E01, E02, E03, E13; E10's model step; E16's expected-refusal "both refuse the model") is now EfSides: compared by exception type, inconclusive when alike, a violation when not.
    • E02's 12 are cited by KB-039 (above).
    • Alike, on any other row: inconclusive, uncited. No row reached it at 5e7884a.
  • plain-refused (E01, E02, E03, E10's twin write) is now inconclusive, uncited: no twin to compare with. No row reached it.
  • no-operator (E05) is now inconclusive, uncited. Decision 57's value objects all have the generated operators. No row reached it.
  • E12's Outcome("inapplicable"), a facet that changes nothing on the plain type even after the fallback facet, is now inconclusive, uncited. No row reached it.

Entries added

  • Fact KB-039, with its Facts test, its row in design/knowledge/README.md, and test: pointing at KB039.cs. docs-query check: 0 problems in 726 links, 512 ids, 8 fact citations and 39 facts.
  • test/CodoMetis.TypeKit.Facts/CodoMetis.TypeKit.Facts.csproj gained one line, <PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />, for KB039. It merged cleanly beside WS2b: a cited fact is executable, fixtures beside fact tests, seeds by footprint #55's fixture item. Nothing else under that project changed.
  • No not-promised entry and no docs sentence. No difference here needed one: the KB-039 excuse is a framework fact, and the char/Guid generation difference already cites the existing char-key-not-generated and guid-key-not-generated.
  • Four obligations and four seeds (above).

Declared applicability (#50)

  • Declared: E02, the KB-039 pairs, on all four obligations.
  • Kept in code, because what a side did decides it, not the axes:
    • EfSides' outcomes;
    • E12's "no effect on a plain property";
    • E04/E01's char and Guid generation compared apart.

Verdicts

All through build-verdict test --project P at 5e7884a:

  • Facts: TESTS OK, 79 passed, 9 skipped. The 9 skipped are the PostgreSQL facts, which need TYPEKIT_PG.
  • Prober: TESTS OK, 257 passed, 0 skipped.
  • Conventions: TESTS OK, 1485 passed, 0 skipped. ProbeCiteTests and ProbeNoteKeyTests are among them; the Note floor of 100 holds.

Noticed, not changed

  • E02's 6 uncovered pairs (400/412), the same before and after:
    • ef.query=where-value-compare × shape.wrapped ∈ {string, Uri, enum, flags-enum, bool}. The probe substitutes where-equals where the wrapped type has no >=, so no row can credit them: an exclusion candidate.
    • shape.site=nested-two-levels × DateTimeOffset, covered only by declared rows. rows --claim prints a group-by row that would cover it.
  • No PostgreSQL side (WS6), as asked.

🤖 Generated with Claude Code

CaffeinatedCoder and others added 4 commits October 17, 2026 11:02
E01 to E16 ran as one check named `claim`, so no listed obligation of an EF claim
earned credit and every per-obligation seed of theirs could only be missed. Each
probe now runs one Row.Check per obligation its row's axes select, named by the
claim's obligation id and given its baseline: the wrapped twin under plain EF
(Twin), the documented refusal (E05), memory (E05, E06, E13), or the docs sentence
where it is the only baseline (E04's char and Guid defaults, E06, E07, E08, E09,
E10, E11, E12).

No outcome note key is called any more. Where a side throws, EfSides decides:
both alike is inconclusive (uncited), the twin alone is inconclusive, the value
object alone or both differently is a violation. NoOperator (E05) and E12's
uncited inapplicable became inconclusive; E16's both-refuse expected refusal too.

E01 applies a length or a precision to every wrapped type, as configured, where it
substituted `none` and so compared nothing for facet-kept.

Four obligations for rows no obligation selected, each with its seed:
E04 not-on-efs-list and own-configuration-wins, E12 underlying-type-does-not-reach,
E13 contains.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…meOffset, TimeSpan or ulong

E02's 12 rows over those types under where-value-compare and order-by failed on both
sides, the wrapped twin under plain EF included: ">=" is "could not be translated"
(InvalidOperationException) and ORDER BY a NotSupportedException. Triage 2: a framework
behaviour, pinned. EF's "Query limitations" page lists decimal beside them, which EF
Core 10 compares and orders; the test holds that as the control. The Facts project
gains the Microsoft.EntityFrameworkCore.Sqlite reference for it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…able, citing KB-039

Each of E02's four obligations declares shape.wrapped DateTimeOffset, TimeSpan or ulong
with ef.query where-value-compare or order-by inapplicable (fact:KB-039). The excuse
depends on the axes alone, so it is a declaration (#50), not code: the six pairs are
wanted by no obligation.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant