Give Perkeo its consumable wants; make theme fragility a decision

Perkeo duplicates a RANDOM held consumable (card.lua:2788), so Spectral
and Planet supply feed it exactly like Tarot supply always did - and the
spectral_gen theme gains the third member it needed (the Seance +
Perkeo negative-spectral archetype, now discoverable three ways).

The other five two-member themes (diamonds, hearts, four_kind, gold_gen,
stone_gen) are vanilla reality: no third joker exists. They get
FRAGILE_OK verdicts in audit.lua, rendered in AUDIT.md, and the audit
now fails on a fragile theme with no verdict - or a stale verdict for a
theme that grew - the same discipline as the silent-joker list.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
funman300
2026-07-15 19:46:41 -07:00
parent 048ab40f85
commit 4a86a3e1ff
3 changed files with 62 additions and 4 deletions
+44
View File
@@ -244,6 +244,18 @@ end
w('')
--------------------------------------------------------------------------------
-- Two-member themes, reviewed and accepted: vanilla simply has no third joker.
-- A fragile theme NOT in this table fails the audit -- either find a member or
-- record why none exists. A stale entry (theme grew past two) also fails, so
-- the table never drifts from the data.
local FRAGILE_OK = {
diamonds = 'Greedy Joker and Rough Gem are the only Diamond payoffs in vanilla.',
hearts = 'Lusty Joker and Bloodstone are the only Heart payoffs in vanilla.',
four_kind = 'DNA enables it, The Family pays it; no other vanilla joker cares.',
gold_gen = 'Midas Mask makes Gold, Golden Ticket cashes it - the whole economy.',
stone_gen = 'Marble adds Stones, Stone Joker counts them; Vampire eats them rather than wanting them.',
}
w('## Tag health')
w('')
w('A tag with one member can never fire (it takes two jokers to make a synergy).')
@@ -251,15 +263,34 @@ w('A tag with two is fragile: drop either joker and the theme dies.')
w('')
w('| tag | givers | wanters | jokers | theme? |')
w('|---|---|---|---|---|')
local fragile_unreviewed, fragile_stale = {}, {}
local fragile_counts = {}
for _, t in ipairs(sorted(members)) do
if not ENGINE[t] then
local n = 0
for _ in pairs(members[t]) do n = n + 1 end
fragile_counts[t] = n
if data.theme_info[t] and n <= 2 and not FRAGILE_OK[t] then
fragile_unreviewed[#fragile_unreviewed + 1] = t
end
w('| `%s` | %d | %d | %s | %s |', t, givers[t] or 0, wanters[t] or 0,
n <= 2 and ('**' .. n .. '**') or tostring(n),
data.theme_info[t] and 'yes' or '-')
end
end
for t in pairs(FRAGILE_OK) do
if (fragile_counts[t] or 0) > 2 then fragile_stale[#fragile_stale + 1] = t end
end
w('')
w('### Fragile by design')
w('')
w('Two-member themes reviewed and accepted: vanilla has no third joker to add.')
w('')
for _, t in ipairs(sorted(FRAGILE_OK)) do
if (fragile_counts[t] or 0) <= 2 then
w('- **`%s`** — %s', t, FRAGILE_OK[t])
end
end
w('')
--------------------------------------------------------------------------------
@@ -312,3 +343,16 @@ if #buckets.unreviewed > 0 then
:format(#buckets.unreviewed, n_jokers, table.concat(buckets.unreviewed, '\n ')))
os.exit(1)
end
-- A theme hanging on two jokers is a discovery achievement one rebalance away
-- from impossible; that has to be a decision too.
if #fragile_unreviewed > 0 then
io.stderr:write(('\n%d fragile theme(s) (two members) have no FRAGILE_OK verdict:\n %s\n')
:format(#fragile_unreviewed, table.concat(fragile_unreviewed, '\n ')))
os.exit(1)
end
if #fragile_stale > 0 then
io.stderr:write(('\nFRAGILE_OK verdict(s) for themes that are no longer fragile -- '
.. 'delete them:\n %s\n'):format(table.concat(fragile_stale, '\n ')))
os.exit(1)
end