Audit of the six LaserLlama Alternate classes (Barbarian, Fighter, Monk, Ranger, Rogue, Sorcerer), run by importing the Drive JSON through the real prepareImportedContent path — not just applyImportEnrichmentPresets, which is where the existing laserllama-*-import.test.ts files stop.
1. Alternate Ranger loses five of its six class resources (P0, data loss)
lib/import/enrich-import-classes.ts calls the preset merger once per row with a single-element array, then keeps the last element:
const withRanger = mergeAlternateRangerClassResources(row.class_name, features, [row])
const mergedRow = withRanger[withRanger.length - 1] ?? row
mergeClassResourcesWithPresets (aliased as mergeAlternateRangerClassResources) appends its seed to the end of the array when the key is not already present:
if (next.some((row) => row.resource_key === spec.resourceKey)) continue
next = [...next, spec.build(className)]
So for every Alternate Ranger row that is not already quarry, the result is [realRow, quarrySeed] and [length - 1] throws the real row away. Six resources go in and six identical quarry rows come out:
input=quarry -> [quarry] ; takes LAST = quarry
input=knacks_known -> [knacks_known, quarry] ; takes LAST = quarry
input=quarry_die -> [quarry_die, quarry] ; takes LAST = quarry
input=exploit_dice -> [exploit_dice, quarry] ; takes LAST = quarry
input=exploits_known -> [exploits_known, quarry] ; takes LAST = quarry
input=exploit_degree -> [exploit_degree, quarry] ; takes LAST = quarry
auditImportWiring already catches the damage and reports two errors for the Ranger (Missing class_resources.quarry_die, Missing class_resources.knacks_known) while every other Alternate class reports zero.
Downstream effects: 0 of 41 Ranger knacks resolve a spend, only 6 of them produce a sheet card at all, and quarry_die never renders.
Fix: pass the full array (as apply.ts itself does) or hoist the merge out of the per-row loop. The regression test has to assert after mergeTableParsedClassResources, since asserting before it is exactly why the current Ranger test passes.
2. Eleven LaserLlama resource keys are missing from third-party-resources.ts
The registry only knows exploit_dice, exploits_known, sorcery_points, and spell_limit. These are entirely dependent on hand-authored import JSON being correct, with no fallback pattern matching:
rage, quarry, quarry_die, knacks_known, exploit_degree, martial_arts_die, techniques_known, alternate_monk_ki_points, divine_favor, divine_limit, metamagics_known
3. Sorcerous Regeneration silently loses its short-rest recharge
The Alternate Sorcerer import JSON tags resources class_name: "Sorcerer" while the class itself is named "Alternate Sorcerer", so enrichPointPoolClassResources bails on the mismatch:
as imported (class_name="Sorcerer"): recharges=[{"rest":"long_rest"}]
with class_name corrected: recharges=[{"rest":"long_rest"},
{"rest":"short_rest","amountFormula":"half_class_level_round_up","maxPerLongRest":1}]
4. Promote auditImportWiring errors to a hard gate
It correctly detected the Ranger breakage above and nothing acted on it.
Confirmed working, for the record
Worth stating so nobody re-does this work:
- Exploit Dice spends deduct correctly: Fighter 79/79 exploits, Rogue 57/58.
- Exploit die size scaling is wired as a real resource die and renders on the sheet (
6 (d12) for a level 17+ Fighter, via dieSidesByLevel + resolveStaticResourceLabel).
- Alternate Monk
alternate_monk_ki_points (27 at L20) and martial_arts_die (d6 to d12) both resolve; Flurry of Blows spends ki.
- Alternate Sorcerer point-pool casting works end to end, including the cost table, spell-limit gating, and the Innate Arcanum bypass.
- Alternate Rogue Avenger
divine_favor / divine_limit both resolve and Divine Step spends 2.
- Alternate Barbarian
rage correctly renders as unlimited at level 20 via freeUseAfterLevel.
Audit of the six LaserLlama Alternate classes (Barbarian, Fighter, Monk, Ranger, Rogue, Sorcerer), run by importing the Drive JSON through the real
prepareImportedContentpath — not justapplyImportEnrichmentPresets, which is where the existinglaserllama-*-import.test.tsfiles stop.1. Alternate Ranger loses five of its six class resources (P0, data loss)
lib/import/enrich-import-classes.tscalls the preset merger once per row with a single-element array, then keeps the last element:mergeClassResourcesWithPresets(aliased asmergeAlternateRangerClassResources) appends its seed to the end of the array when the key is not already present:So for every Alternate Ranger row that is not already
quarry, the result is[realRow, quarrySeed]and[length - 1]throws the real row away. Six resources go in and six identicalquarryrows come out:auditImportWiringalready catches the damage and reports two errors for the Ranger (Missing class_resources.quarry_die,Missing class_resources.knacks_known) while every other Alternate class reports zero.Downstream effects: 0 of 41 Ranger knacks resolve a spend, only 6 of them produce a sheet card at all, and
quarry_dienever renders.Fix: pass the full array (as
apply.tsitself does) or hoist the merge out of the per-row loop. The regression test has to assert aftermergeTableParsedClassResources, since asserting before it is exactly why the current Ranger test passes.2. Eleven LaserLlama resource keys are missing from
third-party-resources.tsThe registry only knows
exploit_dice,exploits_known,sorcery_points, andspell_limit. These are entirely dependent on hand-authored import JSON being correct, with no fallback pattern matching:rage,quarry,quarry_die,knacks_known,exploit_degree,martial_arts_die,techniques_known,alternate_monk_ki_points,divine_favor,divine_limit,metamagics_known3. Sorcerous Regeneration silently loses its short-rest recharge
The Alternate Sorcerer import JSON tags resources
class_name: "Sorcerer"while the class itself is named"Alternate Sorcerer", soenrichPointPoolClassResourcesbails on the mismatch:4. Promote
auditImportWiringerrors to a hard gateIt correctly detected the Ranger breakage above and nothing acted on it.
Confirmed working, for the record
Worth stating so nobody re-does this work:
6 (d12)for a level 17+ Fighter, viadieSidesByLevel+resolveStaticResourceLabel).alternate_monk_ki_points(27 at L20) andmartial_arts_die(d6 to d12) both resolve; Flurry of Blows spends ki.divine_favor/divine_limitboth resolve and Divine Step spends 2.ragecorrectly renders as unlimited at level 20 viafreeUseAfterLevel.