fix(creation-content): four fragments were reading the blurb back to the player
The creation screen shows a race's blurb on its card and the chosen calling's
blurb in the detail panel directly above the DM panel. Blurb and fragment are on
screen at the same moment, inches apart — so a fragment that paraphrases its own
blurb makes the DM sound like it is reciting the card. Four did:
- bonesetter lifted two clauses verbatim ("you set the bone, you say the words")
- reaver re-ran the blurb's door image with the same sentence shape
- hedge_mage re-opened on "what you know" and re-landed the dead teacher
- beastfolk repeated both the claws and the being-judged beat
Each now says the NEXT thing instead: the hour they wake you and which of the
two trades you will not claim credit for; the employer who arranges to be
elsewhere; where you went to fail safely and what the book's gaps cost your
skin; the town that takes your coin and still wants you outside the walls.
beastfolk's rewrite also breaks the race-set monotony — it no longer opens "You
were born" (now unique to human) and drops the shared "X, and you have…" spine,
so clicking the four cards in a row stops reading as one voice.
The parity test guarded presence but could not fail on the three properties that
actually make all 28 race×calling concatenations grammatical. It now asserts
race fragments start uppercase and end with '.', and calling fragments start
lowercase and end with '.' — verified by breaking both and watching all four new
assertions fire.
255 client tests green; content build --check green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -136,11 +136,26 @@ func test_every_race_and_calling_carries_a_fragment():
|
||||
# The DM origin panel (M4-b) composes race.fragment + calling.fragment. A
|
||||
# missing one degrades to the blurb at runtime rather than crashing — but it
|
||||
# must never SHIP missing, so the suite is where it fails.
|
||||
#
|
||||
# The composition is "{race.fragment} Now you carry a {Calling}'s work —
|
||||
# {calling.fragment}", so three shape rules make all 28 pairings legal
|
||||
# sentences: a race fragment opens a sentence (uppercase) and closes it (.);
|
||||
# a calling fragment continues one after the em dash (lowercase) and closes
|
||||
# it (.). Guard them here — a future author adding a calling gets a signal,
|
||||
# not a subtly ungrammatical DM.
|
||||
for id in Races.IDS:
|
||||
assert_true(db.race(id).has("fragment"), "race %s has no fragment" % id)
|
||||
assert_ne(str(db.race(id).get("fragment", "")).strip_edges(), "",
|
||||
"race %s has an empty fragment" % id)
|
||||
var race_fragment := str(db.race(id).get("fragment", "")).strip_edges()
|
||||
assert_ne(race_fragment, "", "race %s has an empty fragment" % id)
|
||||
assert_eq(race_fragment.substr(0, 1), race_fragment.substr(0, 1).to_upper(),
|
||||
"race %s: fragment opens the composition, so it must start uppercase" % id)
|
||||
assert_true(race_fragment.ends_with("."),
|
||||
"race %s: fragment must end with '.' — a sentence precedes 'Now you carry…'" % id)
|
||||
for id in Callings.IDS:
|
||||
assert_true(db.calling(id).has("fragment"), "calling %s has no fragment" % id)
|
||||
assert_ne(str(db.calling(id).get("fragment", "")).strip_edges(), "",
|
||||
"calling %s has an empty fragment" % id)
|
||||
var calling_fragment := str(db.calling(id).get("fragment", "")).strip_edges()
|
||||
assert_ne(calling_fragment, "", "calling %s has an empty fragment" % id)
|
||||
assert_eq(calling_fragment.substr(0, 1), calling_fragment.substr(0, 1).to_lower(),
|
||||
"calling %s: fragment follows an em dash, so it must start lowercase" % id)
|
||||
assert_true(calling_fragment.ends_with("."),
|
||||
"calling %s: fragment must end with '.' — it closes the composition" % id)
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "id": "bonesetter", "name": "Bonesetter", "blurb": "The Warden is distant and does not explain himself. Still — you set the bone, you say the words, and often enough the rot does not take. Often enough.", "fragment": "you set the bone, you say the words over it, and you have stopped claiming to know which one matters." }
|
||||
{ "id": "bonesetter", "name": "Bonesetter", "blurb": "The Warden is distant and does not explain himself. Still — you set the bone, you say the words, and often enough the rot does not take. Often enough.", "fragment": "you are the one they wake at the third hour, and you have stopped claiming to know whether it is your hands or the Warden that does the saving." }
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "id": "hedge_mage", "name": "Hedge-Mage", "blurb": "No tower took you. What you know, you got from a book you should not have had and a teacher who is not alive to confirm it.", "fragment": "what you know came out of a stolen book, and there is no one left to tell you what you got wrong." }
|
||||
{ "id": "hedge_mage", "name": "Hedge-Mage", "blurb": "No tower took you. What you know, you got from a book you should not have had and a teacher who is not alive to confirm it.", "fragment": "you taught yourself the rest where nobody would come looking, and the scars are from the parts the book got wrong." }
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "id": "reaver", "name": "Reaver", "blurb": "They hire you when they want a door opened and do not care about the door. You do not wear plate. You have never needed it.", "fragment": "they point you at a door and pay you not to care what is behind it." }
|
||||
{ "id": "reaver", "name": "Reaver", "blurb": "They hire you when they want a door opened and do not care about the door. You do not wear plate. You have never needed it.", "fragment": "they send for you when the talking is finished, and they make a point of being somewhere else when you arrive." }
|
||||
|
||||
@@ -1 +1 @@
|
||||
{ "id": "beastfolk", "name": "Beastfolk", "blurb": "Claws you did not ask for and a nose you cannot switch off. Every gate you pass, someone decides what you are before you speak.", "fragment": "You were born with claws the Margreave never learned to ignore, and you have spent your life being looked at a beat too long." }
|
||||
{ "id": "beastfolk", "name": "Beastfolk", "blurb": "Claws you did not ask for and a nose you cannot switch off. Every gate you pass, someone decides what you are before you speak.", "fragment": "Most towns will take your coin and still want you outside the walls by dark. You stopped taking that personally a long time ago." }
|
||||
|
||||
Reference in New Issue
Block a user