test(engine): close the t105 ruling riders — soulbond pin, dangling-id pin, save-compat round trip#5794
Merged
Merged
Conversation
…d pin, save-compat round trip Three pins demanded by the ruling on the t105 checkpoint. Test-only; no production change. 1. `purged_token_soulbond_source_must_not_pair_cr_702_95b` — soulbond is the largest slice of the class (50 of 91 defs, 25 of 65 faces) and MUST stay false for a source that has ceased to exist (CR 702.95b: pairing exists only between creatures on the battlefield). It stays false structurally today, but structural immunity is one refactor away from gone, so pin the behavior rather than the mechanism. Soulbond's triggers are synthesized from the keyword during the card-data build, not by `parse_oracle_text`, so they cannot be pulled off an oracle-synthesized object the way Dreampod Druid's are (the inline-keyword harness foot-gun). The filter is therefore constructed — and then asserted BYTE-IDENTICAL to the filter all 50 soulbond defs actually carry in card-data. That premise guard is what keeps a constructed filter from becoming a fabricated one: if the real shape drifts, the premise fails loudly instead of the pin passing against a filter no card has. Carries a non-vacuity leg (a live unpaired creature MUST match the same filter). 2. `lki_attachment_ids_are_identity_only_and_survive_a_purged_aura` — the ruling flagged the `ObjectId` inside `LKISnapshot::attachments` as a possible dangling-reference trap. It is not: `att.object_id` is used in exactly two places in the filter layer, and both are pure identity comparisons (`== source_id` / `!= source.id`, for `exclude_source`); it is never fed to `state.objects.get()`. The `kind` and `controller` the gate reads are stored by value. Pinned by destroying the Aura object outright after the host is purged, so the stored id dangles — and `HasAttachment` must still answer TRUE from the snapshot without a panic. (`AttachmentSnapshot` is also already persisted today via `ZoneChangeRecord::attachments`, so this is a reused shape, not a new surface.) 3. `lki_snapshot_attachments_are_save_compatible_both_directions` — `GameState` is Serialize/Deserialize and owns `lki_cache`, so `LKISnapshot` IS persisted. OLD → NEW: a pre-change save with no `attachments` key must still deserialize and default to the empty set (the pre-change fail-closed answer, so a legacy save cannot start fabricating attachment matches). NEW → NEW: a snapshot carrying attachments must survive a full `GameState` round trip intact.
matthewevans
enabled auto-merge
July 14, 2026 07:39
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Three pins demanded by the ruling on the t105 checkpoint. Test-only; no production
change.
purged_token_soulbond_source_must_not_pair_cr_702_95b— soulbond is the largestslice of the class (50 of 91 defs, 25 of 65 faces) and MUST stay false for a source
that has ceased to exist (CR 702.95b: pairing exists only between creatures on the
battlefield). It stays false structurally today, but structural immunity is one
refactor away from gone, so pin the behavior rather than the mechanism.
Soulbond's triggers are synthesized from the keyword during the card-data build, not
by
parse_oracle_text, so they cannot be pulled off an oracle-synthesized object theway Dreampod Druid's are (the inline-keyword harness foot-gun). The filter is therefore
constructed — and then asserted BYTE-IDENTICAL to the filter all 50 soulbond defs
actually carry in card-data. That premise guard is what keeps a constructed filter from
becoming a fabricated one: if the real shape drifts, the premise fails loudly instead of
the pin passing against a filter no card has. Carries a non-vacuity leg (a live unpaired
creature MUST match the same filter).
lki_attachment_ids_are_identity_only_and_survive_a_purged_aura— the ruling flagged theObjectIdinsideLKISnapshot::attachmentsas a possible dangling-reference trap. It isnot:
att.object_idis used in exactly two places in the filter layer, and both are pureidentity comparisons (
== source_id/!= source.id, forexclude_source); it is neverfed to
state.objects.get(). Thekindandcontrollerthe gate reads are stored byvalue. Pinned by destroying the Aura object outright after the host is purged, so the
stored id dangles — and
HasAttachmentmust still answer TRUE from the snapshot withouta panic. (
AttachmentSnapshotis also already persisted today viaZoneChangeRecord::attachments, so this is a reused shape, not a new surface.)lki_snapshot_attachments_are_save_compatible_both_directions—GameStateisSerialize/Deserialize and owns
lki_cache, soLKISnapshotIS persisted. OLD → NEW: apre-change save with no
attachmentskey must still deserialize and default to the emptyset (the pre-change fail-closed answer, so a legacy save cannot start fabricating
attachment matches). NEW → NEW: a snapshot carrying attachments must survive a full
GameStateround trip intact.