Skip to content

LaserLlama: class resource definitions — Alternate Ranger loses 5 of 6 resources, 11 keys missing from the registry #26

Description

@Geph

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions