docs(custom-format): design pass for CombatDamageTiming::OnStack (combat damage on the stack) - #8898
Conversation
Dedicated design required by PLAN.md §6-§8 before the last LegacyRuleSet axis is built. Sources the pre-M10 rule from archived 1999/2009/M10 Comprehensive Rules, corrects the charter's "pre-6th-edition" framing (damage on the stack spans Sixth Edition through M10), and specifies a StackEntryKind::CombatDamage object, a spell/ability/combat-damage classification, 310.4a-c dealing semantics via incarnation + LKI, the CR 609.7a source-choice widening, risks, and a three-phase plan ending in the Middle School / Classic Magic registration. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ules review Architecture review (REVISE): typed lifelink batch origin so the resume cannot skip the post-resolution priority window; CR 500.6 step-start triggers at push; one ResolvedCombatDamage input to apply_combat_damage on both paths; incarnation-keyed LKI (lki_by_incarnation) for sources, D6 and shields; no-controller object (CR 800.4a hazard); CR 724.2; journaled push; R1 probe; observer triggers on LKI sources; grep-driven match-site audit. Rules fact-check: Classic Magic is framed as Sixth Edition rules, so D0 is a stated project decision with a departures table (triggered deathtouch/lifelink, first-strike literal reading) and a Classic card-text gate; 6ED release date; Q1 rewritten; over-cited CR 616.1/117.4/405.5 narrowed; new Q4/Q5. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Architecture round 2 (REVISE): push the damage object before step-start
triggers so they land above it, surface trigger prompts first; D10
incarnation-aware damage-source identity through protection, replacement
source filters, events, matchers and the chosen-source shield; controller
becomes Option<PlayerId> (compiler-enumerated readers); per-phase protocol
bumps; R9 LKI on player leave; split into four phases (3a stack object,
3b source identity, 3c behavior, 3d release).
Rules round 2: Q5 decided by 2009 600.4a ("A player leaving the game doesn't
affect combat damage on the stack"); empty first-strike sub-step keeps its
CR 510.3 window; departures table gains triggered lifelink/deathtouch effects
and banding 502.10h; verbatim Classic quotes; owned-vs-controlled wording.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… (APPROVE WITH CHANGES) Architecture: mark an empty sub-step dealt before step-start triggers/prompts; protection predicates over DamageSourceView; post-replacement source slots and LKISnapshot.is_token in D10; explicit noncombat stamping rule with a pinning test; controller carried in StackObjectClass behind a private field; shared include_phase_event helper; legacy-payload compat deserializer. Rules: three-case leave-game table (owned / control-changed / still controlled); drop the incorrect banding row; protection cites 702.16a/16e; step-start ordering cites 2009 408.1f; no lifelink for a departed controller. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…k design Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…eferred Classic Magic's operative ruleset is its enumerated "Differences from Modern" list (mana burn, damage on the stack, pre-M10 Wish, Time Vault / Illusionary Mask text), so D0 is faithful to it. The only gap is format-scoped text for those two cards, which is outside this axis. Phase 3d now registers middle_school() only; classic_magic() follows in its own PR with the overrides. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Warning Review limit reachedNext included review available in 16 minutes. View limit detailsLimit details: You’ve used all 2 included reviews currently available. You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Review configuration: ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (1)
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (2)
Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthroughThis PR adds a design specification for pre-M10 combat damage on the stack. It defines the proposed engine changes, compatibility surfaces, phased implementation plan, review decisions, and README reference. No implementation code changes are included. ChangesCombat Damage on Stack
Priority: ⬇️ Low Estimated code review effort: 1 (Trivial) | ~5 minutes Change: Other Merge Risk: ⚪ Minimal · up to The proposal and index entry can be merged without introducing runtime behavior changes. 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
matthewevans
left a comment
There was a problem hiding this comment.
Changes requested for current head 46a8cbb9e2fd0c77f633f8a8f03aef16d4d4e6e4.
MED — empty combat-damage assignment still needs the historical stack object. The proposal quotes 310.1 at COMBAT_DAMAGE_ON_STACK.md:93-110: all combat-damage assignments go on the stack as one object, and 310.4 resolves/removes that object. But :427-434 and :1052-1063 deliberately push no object for zero/no assignments. A 0- or negative-power creature still assigns combat damage under 310.2a, so preserving only the priority window is not rules-faithful and omits an observable stack state. Require an inert CombatDamage entry for every assignment step, with a zero/negative-power regression proving it appears and resolves, or explicitly narrow/prevent exposure of this axis.
…eview on phase-rs#8898) Maintainer review: a 0- or negative-power creature still assigns combat damage under 2009 310.2a, so the historical stack object exists and is observable. Every combat damage step now pushes exactly one object, including an inert one; OnStack records zero-amount assignments (Modern keeps CR 510.1a's skip); 0 damage deals nothing (CR 120.8). Removes the empty-sub-step branch and with it round 3's C1 hazard. Q1 resolved; tests 3c-10/12 updated, 3c-19 added. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
Thanks. Agreed: a 0- or negative-power creature still assigns under 2009 310.2a, so the object exists and players could see it. Addressed in
|
|
The current-head design review at This is not approved or enqueued yet because the required Rust and Frontend CI aggregates are still pending. Once those checks are terminal and green on this head, we will replace the stale requested-changes review and proceed with final approval/enqueue. |
|
CI is terminal and green on the current head The outstanding No further pushes are planned on this branch, so the head should stay put for the review swap. |
matthewevans
left a comment
There was a problem hiding this comment.
Approved on current head 8808e9d: the prior zero/no-assignment stack-object finding is resolved, this docs-only design is internally consistent, and required CI is green.
Summary
This is the design pass for
CombatDamageTiming::OnStack, the lastLegacyRuleSetaxis.PLAN.md§6–§8 andIMPLEMENTATION_PLAN.mdrequire a design before this axis is built. The PR is docs only, with no code: it addsdocs/proposals/custom-format-engine/COMBAT_DAMAGE_ON_STACK.mdand a README entry.Two findings correct the merged charter:
Design in brief
Model. Each combat damage sub-step puts one
StackEntryKind::CombatDamageobject on the stack. It holds the frozen assignments, keyed by source and recipient incarnation. It resolves through the existingapply_combat_damagebatch; nothing in that path is duplicated.Classification and control. A new
StackObjectClass { Spell, Ability, CombatDamage }makes the object untargetable by counters and Stifle. The class also carries the controller, and combat damage has none: 2009 600.4a says "A player leaving the game doesn't affect combat damage on the stack", and current CR 800.4a would otherwise erase pending damage.Dealing. Rules 310.4a–c are applied at resolution:
Source identity. An incarnation-aware damage-source identity (D10) runs through protection, prevention filters, damage events, trigger matchers and the Circle of Protection "choose a source" shield.
Correctness fixes found in review:
Phases (§9), each its own PR:
OnStackbehavior, with 18 paired tests (eachOnStackresult checked against aModerncontrol).middle_school().Decision recorded (Q4). Classic Magic's operative ruleset is its enumerated "Differences from Modern" list, so the design fits it. Its one gap, format-scoped text for Time Vault and Illusionary Mask, is outside this axis. Middle School registers first. Classic Magic is deferred to its own PR that adds those overrides.
Review
The design went through three rounds, each pairing an architecture review (
review-engine-plan, checked against source) with an adversarial fact-check of the rules premises against the archived 1999/2009 CR and the Eternal Central pages:Optioncontroller, a fourth phase§11 maps every finding to the change it produced.
Test plan
docs/MagicCompRules.txt.file:linecitation is pinned to05c27e0d5.🤖 Generated with Claude Code
Summary by CodeRabbit