Externalize the card library behind an allowlisted set loader#36
Merged
Conversation
Replace the hardcoded CARD_LIBRARY with an effect-factory vocabulary (EFFECTS) and a JSON-card loader (build_card/build_pool) in engine/cards.py; a card names an effect plus params and cannot define new behaviour. Thread the resolved card pool through apply_action(state, action, cards) and onto Game, keeping it off GameState so state still round-trips through JSON. Add api/set_loader.py to allowlist set URLs (parsed host + path), fetch them with hardening (https-only, timeout, no redirects, size cap, content-type), validate against a vendored copy of the card-set schema, build the pool, reject duplicate composed ids, and snapshot the resolved pool with a sha256 content hash. POST /games accepts an optional set_urls body (default: the core set); the export includes the snapshot so a saved game replays self-contained. Map bad input to 422 and upstream fetch failures to 502, leaving the store untouched on any failure. Update the engine to add a flavor field, the demo and tests to build pools from a fixture set, add httpx + jsonschema as runtime dependencies, and refresh README/AGENTS/ CONTRIBUTING/docs (fixing the stale game-docs links). https://claude.ai/code/session_015NZxntWsQego4NzHjt1Cmx
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.
Context
Third of three coordinated PRs externalizing Mundane's card library. Card content now loads from JSON sets in
mundane-cards(validated against themundaneschema); card behaviour stays here as a fixed effect vocabulary. The engine keeps no HTTP knowledge — all fetching/validation/snapshotting lives inapi/.Engine
engine/cards.py:CARD_LIBRARYremoved. In its place, an effect-factory registryEFFECTS(the only place effect functions live) and a loaderbuild_card/build_pool. A card names an effect +params; the loader composesset_id:id, looks up the effect (raisingUnknownEffectError), and binds params (raisingInvalidEffectParamsError). Permanents use the no-opnone. Old behaviour generalized:damage_composure(readsparams.amount, was a hardcoded −3) andcounter_task(the old Noise Complaint).apply_action(state, action, cards)andGamenow carry the resolved pool — kept offGameStateso state still round-trips through JSON (the<function ...>regression guard test still passes). Added aflavorfield and acanonical_jsonhelper for hashing.API
api/set_loader.py— givenset_urls: allowlist each (parsed host + path, only themundane-cardsraw origin — not a substring test) → fetch with hardening (https-only, hard timeout, no redirects, size cap, content-type check) → validate against the vendored schema (api/card_schema/, never fetched at runtime) → build cards → reject duplicate composed ids → snapshot the resolved pool with asha256content hash.POST /gamesaccepts an optional{"set_urls": [...]}body, defaulting to the core set.GET /games/{id}/exportnow includes the snapshot, so a saved game replays self-contained.Deps & docs
httpxpromoted to a runtime dependency;jsonschemaadded;types-jsonschemaadded to dev.uv.lockregenerated (uv lock --lockedverified in sync).game-docs/SPEC.md&CARDS.mdlinks fixed tospecs/+rulebook/.Verification
Against a Python 3.14 venv with
litestar@main:ruff format --check✅ ·ruff check✅ ·mypy --strict src/ tests/ examples/✅ ·ty check .✅sha256:hash + composed ids; non-allowlisted (incl.raw.githubusercontent.com.evil.comand wrong-path) → 422; schema-invalid → 422; unknown effect → 422; bad params → 422; duplicate ids → 422; fetch failure → 502; loader failure leaves the store empty.python examples/demo.pyruns end to end.https://claude.ai/code/session_015NZxntWsQego4NzHjt1Cmx
Generated by Claude Code