Repository navigation
The temple charges the party - #116
Merged
Merged
Conversation
HealingPurchasedEvent gains payers and payments_gp with empty defaults. Acceptance tests marked expected-to-fail: raise_dead draws on the purses in marching order, a patient who can pay pays alone, and a wiped party funds its first raising from its dead members' purses. Two unmarked guards hold today: a party one coin short is refused and keeps every coin, and the price list and the command name the same services. Claude-Session: https://claude.ai/code/session_01NmCezTw8hKKujkaEZ3YGAs
PurchaseHealing draws the fee from the treated member's purse first, then from the other members in marching order, dead members included, emptying each purse in whole gold pieces before it touches the next. A party whose purses together fall short is refused with items.purchase.insufficient_funds and keeps every coin: planning the payment is separate from charging it, so the funds check stays the pure pre-phase it was. The old rule made raise_dead unbuyable in practice. The patient is a corpse, nothing hands a corpse coin, and a party that splits its treasure never holds 1,500 gp in one purse, so the documented salvage flow — game over, PlaceParty to town, raise dead — could not be funded by a party whose pooled wealth cleared the fee several times over. HealingPurchasedEvent fills the payers and payments_gp the contract added. The default English line names the other payers only when the patient could not cover the fee alone, which leaves the common one-payer line as it was. The service names are now one exported type, HealingService, used by both the command field and the HEALING_SERVICES price list, so the offered list and the accepted list cannot drift apart; the table's docstring drops the warning that they might. No draw sequence changed. phase5_milestone.json is regenerated through tests/generate_phase5_goldens.py and its diff is the two added event fields and nothing else — the milestone's patient pays alone, so the transcript line is unchanged, and the golden keeps its 1.7.0 engine stamp. Claude-Session: https://claude.ai/code/session_01NmCezTw8hKKujkaEZ3YGAs
Review corrections to the prose, no behavior change. The whole-gold rule was a refusal case no public text stated: each purse pays in whole gold pieces, so the coin it is worth below a gold piece can never go toward the fee, and two members holding 12 gp and 5 sp each are worth the 25 gp price between them and are still refused. The PurchaseHealing class docstring, its insufficient_funds bullet, and the adaptations register now state it and carry that example. "Each purse is emptied before the next is touched" was false and is gone from the command docstring, the _healing_payers docstring, and the register. A purse puts in as much of what is still owed as its gold covers, keeps what it is worth below a gold piece, and the last purse charged pays the remainder alone. Decision history is out: the register no longer describes the superseded rule, and the payers field says it is empty on a log written before the field existed rather than naming an old rule. No em dash is left in text this branch added. _healing_payers takes a Character and returns list[tuple[Character, int]] | None, which surfaced that Character.id is optional, so the payers tuple now uses the id-or-name fallback that core/classes.py already uses for event ids; every party member in a session has an id, so nothing changes. _healing_purchased takes a HealingPurchasedEvent, imported at module scope beside the core event types. Claude-Session: https://claude.ai/code/session_01NmCezTw8hKKujkaEZ3YGAs
…efusal The rejection-code description for items.purchase.insufficient_funds names the party's pooled purses for a temple service. Claude-Session: https://claude.ai/code/session_01NmCezTw8hKKujkaEZ3YGAs
This was referenced Sep 15, 2026
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.
PurchaseHealingcharged the treated member's own purse, and nothing in the rules can put coin into a dead member's purse. That maderaise_deadpayable only when the corpse happened to be carrying 1,500 gp, so the documented salvage flow — game over,PlacePartyto town,raise_dead— could not be funded even by a party whose pooled wealth cleared the fee several times over. Playtests of authored adventures never once bought a raising.The temple now charges the party. The fee comes out of the treated member's purse first and then out of the other members' purses in marching order, dead members included, and each purse is emptied in whole gold pieces through
CoinPurse.spendbefore the next one is touched. A party whose purses together fall short is refused withitems.purchase.insufficient_fundsand keeps every coin: the payment is planned before any purse is opened, so the funds check stays the pure validation pre-phase it was.HealingPurchasedEventfills thepayersandpayments_gpthe contract commit added, naming the purses charged and what each one put in. The default English line names the other payers only when the patient could not cover the fee alone, so the common one-payer line reads as it always did.Second change, the first item of #108: the closed set of service names was stated twice, once as the
LiteralonPurchaseHealing.serviceand once as the keys ofHEALING_SERVICES. It now has one home, the exportedHealingServicetype incrawl/commands.py, which both use, and the price table's docstring drops the warning that the two could drift.No draw sequence changed.
tests/goldens/phase5_milestone.jsonis regenerated throughtests/generate_phase5_goldens.py, and its diff is the two added event fields and nothing else — the milestone's patient pays alone, so the transcript line is unchanged, and the golden keeps the engine stamp of the version that produced it.One gap this leaves:
tools/docs/rejection_codes.jsonstill describesitems.purchase.insufficient_fundsas more coin than "the character's purse currently holds", which is now only true forPurchaseEquipment. That file is outside this chunk's file list, so it wants a separate edit.Verified in the worktree:
pytest --runxfail tests/test_award.py tests/test_terminal_modes.py tests/test_commands.pygives 188 passed, and the full gate —ruff format --check,ruff check,pyright,pytest,mkdocs build --strict— passes with 2534 passed, 73 skipped, 3 xpassed, pyright reporting 0 errors, and a clean strict docs build. The three xpasses are this chunk's acceptance tests, whosexfailmarkers are still in place for the lead to remove at merge.Closes #73
https://claude.ai/code/session_01NmCezTw8hKKujkaEZ3YGAs