Slot tolerance: a record with unread slots is held read-only, not shed #124

Merged
libby merged 20 commits from hands/slot-tolerance into main 2026-11-12 02:45:08 +00:00

2026-11-12

Libby
union-parity: outcomes.msgpack stays main's nine rows; the unit arm is pinned only by its store record
All checks were successful
CultMesh portability and performance / ubuntu-latest reactive semantics and leases (pull_request) Successful in 21m18s
The bare extra-slot-unit-arm.msgpack and its outcomes row are deleted: the TS, Python and Rust
walkers count outcomes.msgpack exactly, so a shared vector does not grow before their leaf cuts.
The unit arm's outcome is the unit-arm row of slot-outcomes.msgpack (record unit-arm of
extra-slot-store.cc, Denied2 1/0 at depth 1). The README says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:20:07 +00:00
Libby
CHANGELOG: an unwritable type is not checked either (reads as a separate clause)
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
A resolver that hands out MessagePack's own [Key] formatter exempts nothing: contract, CHANGELOG, README, test
Soul s3 exemption-bullet-overstates-resolver. AnExtraSlotPastTheWrittenCountStillMarks pins it:
RelayResolver hands out StandardResolver's formatter for Relayed, and its extra slot marks 2/1.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
GeneratorConsumer: check the generated formatter behind the guard's wrapper (CultUnionGuard.Inner)
The check that each document type runs MessagePack's source-generated formatter saw the guard's
SlotFormatter and exited 1 (Soul s3 generator-consumer-check-red). The guard owns the one unwrap,
internal, visible to the consumer through InternalsVisibleTo; that also exposes the library's
internal GeneratedMessagePackResolver, so the consumer suppresses CS0436 (its own is the type used).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
SlotReadOnlyTests: restore the Explained assertion lost with a debug line; an unheld key a store lists foreign is not writable
The assertion went out with 0dfc437b. The new test kills mutant M13 (the unheld branch answering
true), which survived without it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
Contracts, CHANGELOG, vector README: which types are exempt from measuring, the ruled gap, the user code the measure runs
Exempt: any type not written by MessagePack's [Key]-layout formatter (own [MessagePackFormatter],
a consumer resolver, a [MessagePackObject] collection) and a type no instance of which can be
written (cultlib-gaps:ruling:slot-unmeasurable-type). The measure runs [Key] getters and a
parameterless constructor once per type per process; a failure leaves the type unchecked. The
README states the leaf exemptions and the flattenRefusedBy column.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
CultCache.IsWritable: an unheld key is answered by the store's own removal check, not a copy of its foreign clause
Soul s2 iswritable-unheld-copies-foreign. The copy was induced by r5's negative grep forbidding
Store.Refusal(key.Value, null) anywhere in IsWritable; the held branch still asks WriteRefusal.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
CultUnionGuard: measure a type only when the chain hands out MessagePack's own [Key]-layout formatter
Guarded decided from attributes, then measured whatever formatter the composite handed out, so a
consumer resolver's [count, items...] type and a [MessagePackObject] collection were falsely
marked (Soul s2 guard-asks-attribute-not-formatter). KeyLayout decides from the formatter itself,
by identity with StandardResolver's (or StandardResolverAllowPrivate's), excluding the attribute
and generic-collection resolvers' instances, as ResolverClaim does. The [MessagePackFormatter]
attribute check is deleted: that door is now the AttributeFormatterResolver identity.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
Slot tests, red: a resolver-formatted type and a [MessagePackObject] collection are not measured; flatten-chain vector row
ResolvedBag (an assembly CultCacheFormatterResolver writes [count, items...]) and TagList
(List<string> with [MessagePackObject]) are falsely marked by the attribute-based guard (Soul s2,
guard-asks-attribute-not-formatter). extra-slot-store.cc gains marked-variant, a variant of
marked; slot-outcomes.msgpack rows gain a ninth column, the key whose refusal refuses a Flatten.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
Contracts, CHANGELOG, vector README: written-count slots, consent cap, flatten refusal, IsWritable is the write's answer
CHANGELOG Unreleased (org.gamecult.cultlib): Breaking gains the flatten-through-an-unowned-base
refusal, MintElementIds refusing a store holding a marked memory-only record, and the
written-count rule with the [MessagePackFormatter] and unmeasurable-type exclusions; Added
corrects IsWritable. The vector README says what a runtime walks for extra-slot-consent.cc,
the unit-arm record and slot-outcomes.msgpack.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
CultCache: one owner decides whether a record is writable; a flatten through an unowned base is refused
WriteRefusal holds Claim's whole decision (no home store, read-only home, the home store's
Refusal) and the flatten-chain walk: a flatten is refused with the refusal of the first record on
its base chain its home store does not own. Claim throws its answer; IsWritable asks it of the
held record's removal form, and for an unheld key whether no store lists it foreign. IsWritable's
per-store Refusal sweep is deleted (findings iswritable-readonly-store, flatten-marked-base-loss).
MintElementIds' doc names the mark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
CultCache: a mark is explained only up to the highest slot the catalog comparison reports
Explained required only that some slot was reported, so a depth-0 slot the comparison never
reported was consented to and dropped on write (finding unreported-slot-over-consent).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
CultUnionGuard: declared slots are the array the type's own formatter writes; [MessagePackFormatter] types are not guarded
DeclaredSlots (the reflected public [Key] count) is deleted: it falsely marked an AllowPrivate
type's own write. SlotFormatter measures one instance (uninitialized, else a parameterless
constructor) on its first read inside a scope and keeps the count; a type neither instance can be
written for is not guarded (question slot-unmeasurable-type, recommended answer unguarded).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
Slot tests, red: written-count marks, flatten through an unowned base, consent capped, IsWritable is the write's answer
Vectors: extra-slot-store.cc gains the unit-arm record (tests.slot_unit); extra-slot-consent.cc
(records under tests.union_surface.wide) and slot-outcomes.msgpack are new. The consent reader
(SurfaceDeclaring, listing the wide id compatible) and UnitDoc move into UnionParityVectorTests;
the in-test consent store construction, Relabel and AppendRootSlot go. Red against 115bc0a2:
9 tests (spec cultlib-gaps:cut_spec:cut-slot-tolerance.r5).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
CHANGELOG (org.gamecult.cultlib, Unreleased): extra slots are read-only (Breaking); IsWritable, CultUnreadSlots (Added)
Behavioural break per docs/semver-policy.md (tightens the soft-drift classification; writes that
succeeded now refuse). ApiCompat against cultlib-unity-v3.0.0 measures no signature break.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
union-parity README: what every runtime walks for a read-only payload
Per vector: the object carrying the extra slot, its depth and the mark (type, slots carried, slots
declared); the counting rule (highest key + 1, short arrays and retired gaps do not mark, a union adds no
depth); the store-record assertions, the consent rule and the extra-slot-store.cc carry check that
cut-slot-readonly-ts/-py/-rs assert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
Libby
Slot tests read vectors through VectorFiles; the variant read-past test is named for what it pins
SlotReadOnlyTests reads the union-parity vectors through VectorFiles.Committed, the shared reader, so
UnionParityVectorTests.Committed stays private. The variant test that only pins the read no longer says
the extra slot is ignored: under the slot rule it is read past and the record is read-only.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
soul
SlotReadOnlyTests: a compatible id consents only to the depth-0 slot the catalog reports
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
soul
Contracts: extra slots are read-only, not ignored
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00
soul
CultCache: a record whose payload carries slots past its reader's, at any depth, is held read-only
The guard formatter counts each int-keyed object's slots into a thread-bound scope opened around the one payload decode;
ToStoredDocument turns the finding into CultStoredDocument.UnreadSlots (explained only for depth-0 slots the catalog comparison
reports ignored); HeldOwned refuses an unexplained mark whatever is declared; RefuseUndeclared throws the one refusal with
UnreadSlots; CultCache.IsWritable asks the same predicate. Vectors: extra-slot-unit-arm, extra-slot-store.cc.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-11-12 02:19:28 +00:00