Skip to content

feat(combat): inject optional allowed-set into clean_damage_types/combatant_defenses - #8

Merged
sligara7 merged 1 commit into
mainfrom
l3-allowed-param
Jul 29, 2026
Merged

feat(combat): inject optional allowed-set into clean_damage_types/combatant_defenses#8
sligara7 merged 1 commit into
mainfrom
l3-allowed-param

Conversation

@sligara7

@sligara7 sligara7 commented Jun 28, 2026

Copy link
Copy Markdown
Owner

What

Adds a keyword-only allowed: frozenset[str] = DAMAGE_TYPES parameter to clean_damage_types() and combatant_defenses() in combat/combat.py.

  • Default unchanged — intersects with the SRD-13 DAMAGE_TYPES exactly as before (backward-compatible; all existing call sites compile + behave identically).
  • A caller that governs its own damage-type vocabulary passes a wider set (SRD-13 ∪ custom) so registered types survive the filter at component assembly, the computed-stats read, and live combat damage application.
  • dndwright stays domain-agnostic (it accepts a set; it doesn't know where allowed comes from). Members must be lower-cased (caller owns canonicalisation).
  • Public API (__all__) unchanged.

Why

Downstream StoryFlow v1.1.0 adds a per-universe damage-type registry. The hardcoded SRD-13 frozenset was the single chokepoint that silently dropped any custom type — and it is applied by two consumers (the character-sheet compute path AND the live-combat combatant_defenses call). Dependency-injecting the allowed-set is the single-source-of-truth fix that avoids a split-brain (registry honored on the sheet but not in combat) and avoids duplicating the filter across consumers.

Tests

  • New real-path tests in tests/test_defenses_and_serialization.py: widened-vocab survival, default-unchanged back-compat, per-channel threading, and a registered type actually halving damage in apply_damage (governed at the point damage is applied, not just on the sheet).
  • Full suite re-run after the rebase: 1658 passed, ruff check src tests examples clean, api-contract green (__all__ unchanged).

Rebased onto main (2026-07-28)

This branch was cut from v0.25.2, before 0.26.0 was released. It has been rebased onto current main (358eb3e, = the v0.26.0 tag):

  • Dropped the 0.25.2 -> 0.26.0 version bump. 0.26.0 has since been released to PyPI with different content (evaluate_character(components=...), feat(rules): evaluate_character(components=...) — compose item modifiers onto the sheet #9), so this branch must not claim that number. pyproject.toml and __version__ are left at main's 0.26.0, in lockstep.
  • Re-titled the CHANGELOG section ## 0.26.0 -> ## [Unreleased] and removed the now-duplicate empty [Unreleased] heading further down. Versioning this work is a release-time decision (next would be 0.27.0 — minor, since it is an additive API change).
  • The feature itself is byte-for-byte unchanged by the rebase (combat/combat.py and the new tests verified content-identical to the pre-rebase commit).

Release/PyPI publish remains a separate gated step per RELEASING.md.

Note (separate from this PR): 0.26.0 is released but undocumented in CHANGELOG.md — there is no ## 0.26.0 section on main. Worth backfilling.

@sligara7 sligara7 changed the title feat(combat): inject optional allowed-set into clean_damage_types/combatant_defenses (0.26.0) feat(combat): inject optional allowed-set into clean_damage_types/combatant_defenses Jul 29, 2026
sligara7 added a commit that referenced this pull request Jul 29, 2026
…10)

main is currently RED on CI, and no commit caused it. The lint gate runs
`ruff check src tests examples` after `pip install -e ".[dev]"`, where the
extra declared an open-ended `ruff>=0.1`. CI therefore installs whatever ruff
shipped most recently, and `[tool.ruff]` set only line-length/target-version —
so the ENFORCED RULE SET was whatever that version happened to default to.

ruff 0.16.0 widened those defaults (I/RUF/SIM/PL/UP). The result: 40 findings
appeared at once — 17 import-sort (I001), 7 RUF022, plus SIM/PL/UP — in files
no open PR had touched, retroactively failing work that was green when written.
Verified by running 0.16.0 against unmodified `main`: 40 errors, the same
failure seen on PR #8, which is therefore blocked by this and not by its own diff.

Two changes, because there are two independent causes:
- `ruff>=0.5,<0.17` — bound the tool on both sides so the gate is reproducible.
  Floor 0.5 because the rule set below uses the `[tool.ruff.lint]` table.
- `[tool.ruff.lint] select = ["E4","E7","E9","F"]` — state the enforced set
  explicitly so it is a property of this repo, not of the installed ruff. This
  is the load-bearing half: without it, the next default-widening release moves
  the gate again regardless of any version pin.

These four are what the project has actually been enforcing (ruff's historical
default). Adopting import sorting or the RUF/SIM/UP families is a deliberate
choice for its own commit, with its ~40 fixes reviewed — not a side effect of a
dependency resolving forward. No source file is touched here.

Verified: ruff 0.16.0 (what CI installs) and local ruff 0.15.15 BOTH report
"All checks passed" against this commit — the point is that they now agree.
pytest 1654 passed; the one failure (test_version_matches_package_metadata) is a
stale editable install on the dev host, reproduces on unmodified main, and CI
installs fresh.

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
…batant_defenses

Add a keyword-only allowed: frozenset[str] = DAMAGE_TYPES to clean_damage_types
and combatant_defenses. Default preserves SRD-13 behavior (backward-compatible);
a caller governing its own damage-type vocabulary passes SRD-13 ∪ custom so
registered types survive the filter at assembly, computed-stats read, AND live
combat damage application — no split-brain. dndwright stays domain-agnostic.
Bump 0.25.2 -> 0.26.0. Public API (__all__) unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01JXPSRe6rg8jtdAAvqZrxvf
@sligara7
sligara7 merged commit f15fe71 into main Jul 29, 2026
4 checks passed
@sligara7
sligara7 deleted the l3-allowed-param branch July 29, 2026 02:12
sligara7 added a commit that referenced this pull request Jul 29, 2026
Adds the caller-governed damage-type vocabulary: clean_damage_types() and
combatant_defenses() take an optional keyword-only `allowed` frozenset, so a host
application that registers custom damage types can pass SRD-13 ∪ custom and have
those types survive the filter at component assembly, the computed-stats read AND
live combat damage application — instead of being silently dropped on one side and
not the other. Default is DAMAGE_TYPES, so every existing caller is unchanged, and
the public API (__all__) is untouched. (#8)

Also backfills the CHANGELOG section for 0.26.0, which shipped to PyPI on
2026-07-01 with no entry — evaluate_character(components=...), recorded from #9 —
and repairs the compare links, which had drifted four releases behind (they ended
at v0.23.2, leaving 0.24.0 through 0.26.0 unlinked).

Verified per RELEASING.md against a fresh `pip install -e ".[dev]"`, which is what
makes the installed-metadata guard actually run rather than skip:
  - pytest: 1659 passed, 0 failed — including
    test_version_matches_package_metadata, which now has both version sources
    agreeing at 0.27.0
  - ruff check src tests examples: All checks passed
  - python -m build: dndwright-0.27.0.{whl,tar.gz}
  - twine check: both PASSED
  - dist/ and build/ removed; CI rebuilds them

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant