Imported from cbw29512/D20-ironpit (
AGENTS.md). Install upstream withnpx skills add cbw29512/D20-ironpit. Copyright stays with the author.
D20 Iron Pit repository instructions
Authority order
Before changing combat code, read:
docs/IRON_PIT_RULES_CONTRACT.md— authoritative product/combat rules.docs/VTT_CARD_BATTLEFIELD_CONTRACT.md— specific battlefield/card-token/grid architecture; it supersedes older fixed-formation/deity-closing assumptions wherever they conflict.docs/UNIVERSAL_COMBATANT_ARCHITECTURE.md— durable universal-engine and data-binding architecture.docs/ABILITY_HOOK_ENGINE_PROPOSAL.md— authoritative sequencing-phase extension to the universal architecture.docs/MAIN_ACTION_SELECTION_CONTRACT.md— authoritative separation of legal Action candidates from Arena tactical selection.docs/IRON_PIT_AUDIT_EVENT_SCHEMA.md— audit/event evidence contract.docs/CANONICAL_COMBAT_BUILD_POLICY.md— canonical pregen construction.- current source/runtime code and permanent tests.
- generated certification state in
data/hero_certification_manifest.jsonanddata/monster_certification_manifest.json.
Repository truth beats chat summaries, historical counts, old milestone prose, uploaded registry dumps, and stale file-library references. External/user-provided files are evidence only until reconciled against the exact current commit.
Non-negotiable engineering contract
- Act as a systems/rules architect, not a mock-up generator.
- Never claim code, validation, refactoring, certification, or a mechanic is complete unless the literal full code/diff is produced and the required verification has passed.
- State first: define or identify the shared data schema, immutable source data, mutable combat state, lifecycle/timing, and reset behavior before execution functions.
- Error first: every new Python function/state mutation/rules calculation must use explicit
try/exceptwith meaningful contextual logging. Browser mutations/resolvers must expose explicit failure handling and must not silently recover by changing rules. - No shortcuts: no mock combat paths, no monster-name/class-name/hero-name special-case resolvers, no hand-authored readiness flags, no hardcoded bypasses around the universal engine.
- Unsupported outcome-changing mechanics fail closed.
- Prefer universal capabilities plus declarative data. A monster/pregen supplies parameters; the engine supplies mechanics.
- Source cards/templates are immutable. All fight mutation belongs to temporary combat state and resets after every match.
- Python is the rules-reference/certification oracle; browser JavaScript is the production fight engine. Supported behavior requires parity.
- Step, Watch, Replay, and Turbo consume the same canonical resolver/event stream. Presentation mode never changes rules.
- Audit/logging is evidence-only. It never rolls dice, chooses actions/targets, or mutates combat results.
- Production combat is browser-only/backend-free.
- Do not weaken a valid test to make CI green. Replace an obsolete assertion only when an explicit contract change supersedes it, with equally strong coverage for the new rule.
- Keep production source modules at or below the repository source-size limit. Split modules rather than growing monoliths.
- Before creating any new combat primitive, resolver, helper module, or rules hook, inspect both the Python rules-reference engine and browser production engine for an existing equivalent or composable primitive. Reuse or extend the shared primitive first; never add a second implementation merely because one side is easier to change.
- New mechanics require an explicit parity map: identify the Python resolution point, browser resolution point, shared/declarative data, lifecycle/reset behavior, and permanent tests before implementation. If one engine already supports the behavior, port/reuse that model rather than redesigning it independently.
- Before certification/merge, perform a technical-debt pass over the tranche: remove dead helpers/imports, collapse duplicated construction or calculations, reject speculative abstractions with no current consumer, keep generated data generated, and verify that independent certification fingerprints remain independent rather than being "deduplicated" into the runtime they audit.
Mandatory uncertainty and clarification gate
- If there is any uncertainty about RAW wording, source interpretation, timing, architecture, data mapping, user intent, or whether an existing shared mechanic already covers the behavior, stop before changing code.
- Do not guess, infer around the uncertainty, create a temporary special case, or keep coding merely to preserve momentum.
- Ask Chris one precise clarification question that isolates the unresolved decision.
- After Chris answers, write the decision into the repository before implementation continues. Rules/mechanics decisions belong in
docs/IRON_PIT_RULES_CONTRACT.md; durable universal architecture decisions belong indocs/UNIVERSAL_COMBATANT_ARCHITECTURE.md; battlefield/card-token/grid decisions belong indocs/VTT_CARD_BATTLEFIELD_CONTRACT.md; operating/process decisions belong inAGENTS.md. - If the correct authority file is itself unclear, ask before writing.
- Re-read the written decision and implement against that repository authority. Do not rely on chat memory alone for a decision that can affect future combat work.
- If a new clarification conflicts with an existing authoritative rule, stop and reconcile the conflict explicitly in the repository before changing runtime behavior.
- This gate overrides speed, convenience, and perceived momentum. Asking one targeted question is preferred to implementing the wrong abstraction.
Arena/environment invariants
- The Iron Pit magically makes the environment survivable/hospitable for every creature. Breathing, atmosphere, aquatic biology, flight requirements, and similar survival constraints never exclude a combatant.
- Movement modes must never be used as roster eligibility filters.
- Preserve printed movement modes and printed base speeds exactly as source data; never convert Swim/Fly/Climb/Burrow speed into a generic land/base speed just to make a creature runnable.
- The battlefield is one authoritative 5-foot square grid with real combatant x/y positions.
- Voluntary movement consumes actual effective movement speed. The old free-closing/fixed-formation path is migration scaffolding only and must not remain the final combat authority.
- Creature footprint derives from immutable printed size data: Tiny/Small/Medium 1x1, Large 2x2, Huge 3x3, Gargantuan 4x4 unless a more specific supported rule changes occupied space.
- Movement, reach, range, collision, Opportunity Attacks, forced movement, auras, line of sight, and area geometry consume the same authoritative grid state.
- Arena design and AI policy prevent degenerate fleeing/kiting; do not bypass printed movement to force engagement.
- The card artwork is the moving battlefield token. Current HP, Temporary HP, conditions, buffs/debuffs, concentration, recharge/resource state, and similar live symbols are presentation overlays derived from runtime state and never rule inputs.
- Reach/range still determine legal attack geometry. Forced movement, Opportunity Attacks, speed-changing effects, Grappled/Prone interactions, and any feature that explicitly depends on movement remain real mechanics and must follow the selected ruleset.
- The arena must not create a hidden combat buff/debuff from a creature's locomotion type.
Universal mechanic workflow
When a card exposes a missing combat mechanic:
- identify the smallest reusable mechanic/capability;
- define/extend schema, immutable parameters, mutable state, timing, expiry, and reset semantics;
- implement the Python oracle;
- implement browser-runtime parity;
- add permanent regression/parity tests and audit evidence;
- regenerate native generated artifacts;
- rerun capability/source analysis across the full 330-monster roster and canonical hero progressions;
- allow generated certification to promote every newly unblocked card.
Specific source wording beats generic behavior. Resolve each subevent fully and update state before resolving the next.
Monsters
- Canonical 2024 SRD 5.2.1 roster: exactly 330 monsters.
- Promotion path: source -> detected mechanics -> universal capability data -> runtime -> Python/browser behavior -> generated assets -> certification -> exact-head CI.
- A monster is runnable only when every outcome-changing printed mechanic is supported or explicitly proven irrelevant under the permanent arena contract.
- Never implement a mechanic by checking a monster name when a reusable schema/capability can represent it.
- After a shared capability changes, re-audit all 330; never hand-pick only the motivating monster.
Paired-edition monster certification
- Keep 2014 and 2024 source data, rule text, parameters, and certification independent. A shared universal primitive may serve both editions, but one edition's values or behavior must never be copied into the other without matching source evidence.
- Current operating priority is to catch the 2014 certified roster up to the 2024 certified count before pushing the 2024 count materially farther ahead.
- During catch-up, prioritize monsters that exist in both editions where the 2024 version is already RAW READY and the 2014 version is blocked, starting with the fewest/highest-yield 2014 blocker families.
- Once the 2014 certified count reaches the 2024 certified count, process same-named monsters as an edition pair whenever practical: inspect 2014 and 2024 source side by side, identify the common reusable mechanic, then bind each edition's own parameters and timing to that primitive.
- When a 2014 monster is certified, immediately inspect the same 2024 monster before moving to an unrelated monster. When a 2024 monster is certified first, immediately inspect the 2014 counterpart.
- If the two editions differ materially, preserve the difference through ruleset-specific declarative data or explicitly ruleset-scoped behavior; do not force artificial parity merely because the monster name matches.
- A paired tranche may certify both editions only when they truly share the same mechanic family and both have full Python/browser/source evidence. If one edition still has unrelated blockers, certify the completed edition and record the remaining counterpart blockers without stalling the queue.
- Re-run the paired-edition progress report plus both normal roster audits after every shared primitive. Use the report as the next-work queue rather than choosing monsters from memory.
- Track the edition totals independently: 2014 has 327 source monsters; 2024 has 330. Edition-only or renamed monsters remain valid independent work and must not be force-matched.
Generated artifacts: never hand-edit
Generated files are outputs, not authoring surfaces. Change authoritative schema/data/runtime first, then run the repository exporter.
Runtime monster capability registry:
python scripts/export_runtime_monster_capabilities.py
python scripts/export_runtime_monster_capabilities.py --check
Certification manifests:
python scripts/verify_certification_manifests.py --write
git diff --exit-code -- data/hero_certification_manifest.json data/monster_certification_manifest.json
python scripts/report_certification_progress.py
Static/browser artifacts:
python scripts/export_browser_heroes.py
python scripts/prepare_static_site.py
git diff --exit-code -- frontend/browser-heroes.js frontend/figure-profiles.js frontend/browser-monsters-generated.js frontend/browser-spell-effects.js frontend/data/srd_5_2_1_monsters.json
Capability/source reports:
python scripts/check_combat_engine_coverage.py
python scripts/export_roster_mechanic_checklist.py --check
python scripts/report_zero_engine_monsters.py
python scripts/report_capability_yield.py
Never splice generated JSON/JS manually to satisfy parity.
Required verification
From repository root unless a command says otherwise:
python scripts/check_source_limits.py
python scripts/check_combat_engine_coverage.py
python scripts/export_roster_mechanic_checklist.py --check
python scripts/export_runtime_monster_capabilities.py --check
python scripts/export_browser_heroes.py
python scripts/verify_certification_manifests.py --write
python scripts/prepare_static_site.py
Python rules reference:
cd backend
pip install -e '.[dev]'
pytest -q
cd ..
Browser syntax:
for file in frontend/*.js; do node --check "$file"; done
Run the complete permanent browser regression command list and production wiring/backend-free checks exactly as defined by .github/workflows/ci.yml; that workflow is the canonical complete CI command set. Final certification requires the exact intended commit to pass GitHub Actions CI.
Task isolation and source hygiene
- One tranche/PR should address one coherent universal mechanic or contract change. Do not mix environment refactoring, unrelated monster traits, pregens, UI polish, and deployment work in one tranche.
- Finish or explicitly park the current tranche before opening an unrelated one.
- Do not use stale generated counts from memory. Recompute/report from the exact current commit.
- Do not copy outdated uploaded specs or registry dumps into the repo. If external material conflicts with current repository authority, stop and reconcile the conflict explicitly.
- Keep Netlify for deliberate production checkpoints; routine verification belongs in repository CI/local-static checks.