docs(skills): bring contributor-facing skills up to the merged campaign invariants - #5816
Conversation
…gn invariants
The add-* skills lagged five invariants that have landed on main, and one of
them (add-static-ability) stated the opposite of what the engine now does.
- add-static-ability: continuous effects apply to OBJECTS (CR 613.1), not only
battlefield permanents. New "Zone Boundary" section: the layer pass
materializes keywords ONLY into the zones it also resets
({Battlefield, Hand, Stack} — layer_pass_materializes_keywords); library /
graveyard / exile are owned by off_zone_characteristics and computed on
demand. A grant into an unowned zone is a silent no-op that still reports as
supported (the PR #5803 defect class).
- add-keyword: document the strict keyword router (PR #5813 / task #123).
Router slots and routing classifiers must parse through
parse_router_keyword_line / _list / _fragment (all-consuming); the permissive
parse_granted_keyword_fragment / extract_granted_keyword_list are for
EMBEDDED GRANT contexts only. A line with an unconsumed semantic tail must
DECLINE, not commit. Gate G in scripts/check-parser-combinators.sh is the
whole-file invariant. New router-registry checklist item
(is_keyword_cost_line + ROUTER_KEYWORD_CASES set-equality test).
- add-trigger: new "Purged Source" section (CR 608.2h). LKI look-back via
subject_filter_matches_with_lki / source_controller_or_lki; attachments do
NOT survive on the live object (CR 704.5m/n sever them — only
LKISnapshot::attachments holds the pre-sever set) while attack/block history
DOES (durable id-keyed ledgers on GameState). Names the non-vacuity vector:
a ceased-to-exist token (CR 111.7), not a printed creature.
- casting-stack-conditions + add-engine-effect: the three X carriers
(GameObject::cost_x_paid = CR 107.3m cast-X, GameState::announced_source_x =
CR 107.3a/d announce-X, ResolvedAbility::chosen_x = the published channel,
CR 107.3i) and publish_announced_x as the single publish authority.
Re-measuring X at resolution is rules-wrong (CR 107.3c), not merely late.
- add-replacement-effect: cross-line "instead" lowers to a BRANCH
(ClauseDisposition::ReplaceMeaning → else_ability), never two independent
effects — CR 614.1a + CR 614.6, the #44/#79 double-execution class.
- project-reference: card-export measurement hazards — export-cards rewrites
the tracked parser input oracle-subtypes.json (non-idempotent),
gen-card-data dirties known-tokens.toml, and the export is nondeterministic
on ~20 faces (the noise floor of any whole-pool ledger).
- card-test: gen-test-fixture.py --check verifies COVERAGE only (a key-set
comparison), never the stored parse VALUES — a parser change can leave the
integration fixture stale while the gate stays green.
Also fixes stale anchors found while verifying the self-check blocks (all five
now pass; two were red before this change):
- add-static-ability / add-keyword cited crates/engine/src/parser/oracle_static.rs,
which is a directory now (oracle_static/mod.rs, oracle_static/grammar.rs,
oracle_static/keyword_grant.rs).
- casting-stack-conditions cited pay_and_push and pay_ability_cost in casting.rs;
they live in casting_costs.rs and costs.rs.
- add-replacement-effect cited "MTG Rule 614.16" for as-enters choices; 614.16
is the token/counter-creation replacement rule. The correct citations are
CR 614.1c + CR 614.12a.
Every symbol grep-verified against the tree at 5f4fbcd; every CR number
grep-verified against docs/MagicCompRules.txt. scripts/check-skill-doc.sh and
Gate G both pass.
There was a problem hiding this comment.
Code Review
This pull request updates several developer guide markdown files (skills) under the .claude/skills/ directory. The updates add detailed architectural instructions and Comprehensive Rules (CR) references regarding handling X values, keyword parsing surfaces, replacement effects lowering to branches, zone boundaries for the layer pass, Last Known Information (LKI) for purged sources, regenerating integration fixtures, and measurement hazards during card exports. There are no review comments on this pull request, and I have no additional feedback to provide as these documentation updates are highly detailed and align perfectly with the repository's architectural guidelines.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
…ces (phase-rs#5818) CR 614.16 is the token/counter-creation replacement rule. As-enters effects are CR 614.1c; the choice-before-entering timing is CR 614.12a (both grep-verified against docs/MagicCompRules.txt). PR phase-rs#5816 corrected one instance of this miscite but these two predated it in a different format. Co-authored-by: matthewevans <matthewevans@users.noreply.github.com>
The add-* skills lagged five invariants that have landed on main, and one of
them (add-static-ability) stated the opposite of what the engine now does.
battlefield permanents. New "Zone Boundary" section: the layer pass
materializes keywords ONLY into the zones it also resets
({Battlefield, Hand, Stack} — layer_pass_materializes_keywords); library /
graveyard / exile are owned by off_zone_characteristics and computed on
demand. A grant into an unowned zone is a silent no-op that still reports as
supported (the PR ship/t125 stack grants #5803 defect class).
Router slots and routing classifiers must parse through
parse_router_keyword_line / _list / _fragment (all-consuming); the permissive
parse_granted_keyword_fragment / extract_granted_keyword_list are for
EMBEDDED GRANT contexts only. A line with an unconsumed semantic tail must
DECLINE, not commit. Gate G in scripts/check-parser-combinators.sh is the
whole-file invariant. New router-registry checklist item
(is_keyword_cost_line + ROUTER_KEYWORD_CASES set-equality test).
subject_filter_matches_with_lki / source_controller_or_lki; attachments do
NOT survive on the live object (CR 704.5m/n sever them — only
LKISnapshot::attachments holds the pre-sever set) while attack/block history
DOES (durable id-keyed ledgers on GameState). Names the non-vacuity vector:
a ceased-to-exist token (CR 111.7), not a printed creature.
(GameObject::cost_x_paid = CR 107.3m cast-X, GameState::announced_source_x =
CR 107.3a/d announce-X, ResolvedAbility::chosen_x = the published channel,
CR 107.3i) and publish_announced_x as the single publish authority.
Re-measuring X at resolution is rules-wrong (CR 107.3c), not merely late.
(ClauseDisposition::ReplaceMeaning → else_ability), never two independent
effects — CR 614.1a + CR 614.6, the chore: update coverage stats and badges #44/Fix delayed triggers that trigger at the beginning of "your next end step" #79 double-execution class.
the tracked parser input oracle-subtypes.json (non-idempotent),
gen-card-data dirties known-tokens.toml, and the export is nondeterministic
on ~20 faces (the noise floor of any whole-pool ledger).
comparison), never the stored parse VALUES — a parser change can leave the
integration fixture stale while the gate stays green.
Also fixes stale anchors found while verifying the self-check blocks (all five
now pass; two were red before this change):
which is a directory now (oracle_static/mod.rs, oracle_static/grammar.rs,
oracle_static/keyword_grant.rs).
they live in casting_costs.rs and costs.rs.
is the token/counter-creation replacement rule. The correct citations are
CR 614.1c + CR 614.12a.
Every symbol grep-verified against the tree at 5f4fbcd; every CR number
grep-verified against docs/MagicCompRules.txt. scripts/check-skill-doc.sh and
Gate G both pass.