Fork FancyScribe as BrevyScribe with the datasheet transforms built in
CI / check (push) Has been cancelled

Print generic datasheets - every option a unit could take, the way the
official cards read - instead of a record of one particular list.

The three Python scripts that used to rewrite the .ros file before upload
are now transforms in src/transforms/, running in the browser between
DOMParser and the roster parser. Most of each script was machinery for
preserving the file byte for byte on the way back to disk; in the browser
the document is never serialised, so only the domain logic came across.

Because they run on the parsed document rather than the uploaded file,
flipping a transform off rebuilds from the original XML with no
re-upload. Saved rosters therefore hold the raw roster XML rather than
the parsed object, under new localStorage keys.

The scripts' output over four 11th-edition rosters is checked in as test
fixtures, so the port is measured against an independent implementation
that was verified by printing the cards; scripts/update-fixtures.mjs
refuses to overwrite those without --force.

Two parser call sites now use :scope> rather than repeating the scope
element's own name, which means the same thing in a browser and also
works under jsdom, where the old form matched nothing.

Also: served at the root rather than a GitHub Pages subpath, PostHog
analytics removed, and deploy/ carries an nginx site for
scribe.luxick.de plus an rsync deploy script.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
2026-07-25 15:11:10 +02:00
parent 3a0fdc2c26
commit 62d6bd211b
45 changed files with 3317 additions and 191 deletions
@@ -0,0 +1,41 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<roster id="7rmu2zl" name="TEST" battleScribeVersion="2.03" generatedBy="https://newrecruit.eu" gameSystemId="sys-352e-adc2-7639-d610" gameSystemName="Warhammer 40,000 11th Edition" gameSystemRevision="6" xmlns="http://www.battlescribe.net/schema/rosterSchema"><costs><cost name="pts" typeId="51b2-306e-1021-d207" value="220" /></costs><forces><force id="7wfvr6o" name="Army Roster" entryId="bb9d-299a-ed60-2d8a" catalogueId="77b9-2f66-3f9b-5cf3" catalogueRevision="5" catalogueName="Imperium - Adeptus Mechanicus"><selections><selection id="bky5c11" name="Battle Size" entryId="7380-3e40-6ed6-b7cc::564e-fbc6-5266-3ea4" number="1" type="upgrade" from="entry"><categories><category id="4ac9-fd30-1e3d-b249" entryId="4ac9-fd30-1e3d-b249" name="Configuration" primary="true" /></categories></selection><selection id="bg2lcx" name="Detachment" entryId="2874-c86-3152-393::c82f-1b42-946-9c9a" number="1" type="upgrade" from="entry"><categories><category id="4ac9-fd30-1e3d-b249" entryId="4ac9-fd30-1e3d-b249" name="Configuration" primary="true" /></categories></selection><selection id="bdcmhqi" name="Force Disposition" entryId="8bc8-6bfe-78bd-2480::2f69-9148-45b4-86a8" number="1" type="upgrade" from="entry"><categories><category id="4ac9-fd30-1e3d-b249" entryId="4ac9-fd30-1e3d-b249" name="Configuration" primary="true" /></categories></selection><selection id="bnoi9vo" name="Show/Hide Options" entryId="3458-3cff-ef5f-a7c5::e8ef-836a-a9d1-901d" number="1" type="upgrade" from="entry"><categories><category id="4ac9-fd30-1e3d-b249" entryId="4ac9-fd30-1e3d-b249" name="Configuration" primary="true" /></categories></selection><selection id="ladocm" name="Belisarius Cawl" entryId="cb05-4e43-6776-3c51::1f1e-2989-4762-cf88" number="1" type="model" from="entry"><rules><rule id="7a21-a958-e47d-5c0d" name="Doctrina Imperatives" hidden="false"><description>At the start of the battle round, you can select one of the Doctrina Imperatives below. Until the end of the battle round, that Doctrina Imperative is active for your army, and all units from your army that have the Doctrina Imperatives ability gain the relevant abilities shown below.
PROTECTOR IMPERATIVE
■ Ranged weapons equipped by models in this unit have the [HEAVY] ability.
■ Improve the Ballistic Skill characteristic of ranged weapons equipped by models in this unit by 1.
■ Each time a melee attack targets this unit, if this unit has the ^^**Battleline**^^ keyword and/or it is within 6&quot; of one or more friendly **^^Adeptus Mechanicus Battleline**^^ units, subtract 1 from the Hit roll.
CONQUEROR IMPERATIVE
■ Ranged weapons equipped by models in this unit have the [ASSAULT] ability.
■ Improve the Weapon Skill characteristic of melee weapons equipped by models in this unit by 1.
■ Each time a model in this unit makes an attack, if this unit has the **^^Battleline**^^ keyword and/or it is within 6&quot; of one or more friendly **^^Adeptus Mechanicus Battleline**^^ units, improve the Armour Penetration characteristic of that attack by 1.</description></rule></rules><profiles><profile id="1f5d-20a5-f37c-7d5a" name="Belisarius Cawl" hidden="false" typeId="c547-1836-d8a-ff4f" typeName="Unit" from="entry"><characteristics><characteristic name="M" typeId="e703-ecb6-5ce7-aec1">8&quot;</characteristic><characteristic name="T" typeId="d29d-cf75-fc2d-34a4">8</characteristic><characteristic name="Sv" typeId="450-a17e-9d5e-29da">2+</characteristic><characteristic name="W" typeId="750a-a2ec-90d3-21fe">10</characteristic><characteristic name="LD" typeId="58d2-b879-49c7-43bc">6+</characteristic><characteristic name="OC" typeId="bef7-942a-1a23-59f8">3</characteristic><characteristic name="InSv" typeId="55a7-5b54-c60d-11dc">4+</characteristic></characteristics></profile><profile id="9e99-0aa1-2c11-33ca" name="Canticles of the Omnissiah" hidden="false" typeId="9cc3-6d83-4dd3-9b64" typeName="Abilities" from="entry">
<characteristics>
<characteristic name="Description" typeId="9b8f-694b-e5e-b573">At the start of your Command phase, select one of the abilities in the Canticles of the Omnissiah section. Until the start of your next Command phase, this model has that ability.</characteristic>
</characteristics>
</profile>
<profile id="481f-052d-6273-e87d" name="Invocation of Machine Vengeance (Aura)" hidden="false" typeId="206d-0762-de03-4bcd" typeName="CANTICLES OF THE OMNISSIAH" from="entry">
<characteristics>
<characteristic name="Description" typeId="f61f-4a40-c8b5-338e">At the start of your Command phase, select one unit from your opponents army. Until the start of your next Command phase, that enemy unit is your Machine Vengeance target. Each time a model in a friendly ADEPTUS MECHANICUS unit makes an attack that targets your Machine Vengeance target, you can reroll the Hit roll.</characteristic>
</characteristics>
</profile>
<profile id="29f0-41e7-f2f6-adf3" name="Mantra of Discipline" hidden="false" typeId="206d-0762-de03-4bcd" typeName="CANTICLES OF THE OMNISSIAH" from="entry">
<characteristics>
<characteristic name="Description" typeId="f61f-4a40-c8b5-338e">This model has the BATTLELINE keyword and has the following ability:
Binharic Courage (Aura): While a friendly ADEPTUS MECHANICUS unit is within 6&quot; of this model, add 1 to the Objective Control characteristic of models in that unit and each time you take a Battle-shock or Leadership test for that unit, add 1 to that test.</characteristic>
</characteristics>
</profile>
<profile id="df91-6e15-f507-1807" name="Shroudpsalm (Aura)" hidden="false" typeId="206d-0762-de03-4bcd" typeName="CANTICLES OF THE OMNISSIAH" from="entry">
<characteristics>
<characteristic name="Description" typeId="f61f-4a40-c8b5-338e">While a friendly ADEPTUS MECHANICUS unit is within 6&quot; of this model, that unit has the Stealth ability.</characteristic>
</characteristics>
</profile><profile id="f172-384f-e1fa-d656" name="Mechanicus Bodyguard" hidden="false" typeId="9cc3-6d83-4dd3-9b64" typeName="Abilities" from="entry"><characteristics><characteristic name="Description" typeId="9b8f-694b-e5e-b573">While this model is within 3&quot; of one or more other friendly ^^**Adeptus Mechanicus**^^ units, this model has the Lone Operative ability.</characteristic></characteristics></profile><profile id="26a7-711f-28f6-7044" name="Self-repair Mechanisms" hidden="false" typeId="9cc3-6d83-4dd3-9b64" typeName="Abilities" from="entry"><characteristics><characteristic name="Description" typeId="9b8f-694b-e5e-b573">At the start of your Command phase, this model regains up to D3 lost wounds.</characteristic></characteristics></profile><profile id="784a-55c7-c7b9-db88" name="Supreme Commander" hidden="false" typeId="9cc3-6d83-4dd3-9b64" typeName="Abilities" from="entry"><characteristics><characteristic name="Description" typeId="9b8f-694b-e5e-b573">If this model is in your army, it must be your **^^Warlord^^**.</characteristic></characteristics></profile></profiles><selections><selection id="la280rb" name="Arc scourge" entryId="cb05-4e43-6776-3c51::2f59-b41c-83be-4efe" number="1" type="upgrade" from="entry"><rules><rule id="4111-82e3-9444-e942" name="Anti" hidden="false" page="28"><description>This ability always takes the form **[ANTI-X Y+]**. Each time an attack is made with an **[ANTI]** weapon, if the target unit has the keyword denoted by **X**, an unmodified **wound roll** of **Y+** is a **critical wound**.
***Example:** An attack made with an **[ANTI-VEHICLE 4+]** weapon against a ^^**Vehicle**^^ unit will result in a **critical wound** on an unmodified **wound roll** of 4+, while an attack made with an **[ANTI-PSYKER 2+]** weapon against a ^^**Psyker**^^ unit will result in a **critical wound** on an unmodified **wound roll** of 2+.*</description></rule><rule id="be1e-ac8e-1e2c-3528" name="Devastating Wounds" hidden="false" page="28"><description>Each time an attack made with a **[DEVASTATING WOUNDS]** weapon results in a **critical wound**, the attack sequence for that attack ends and the target unit suffers a number of **mortal wounds** equal to the **D** characteristic of that weapon. These are inflicted after resolving any normal damage inflicted by those attacks. 
**Mortal wounds** inflicted by **[DEVASTATING WOUNDS]** weapons can damage a maximum of one model for each **critical wound**; any remaining **mortal wounds** inflicted by that attack are lost. 
**Example: An attack made with a **[DEVASTATING WOUNDS]** weapon with a **D** characteristic of 3 results in a **critical wound** against an Intercessor Squad, so inflicts 3 **mortal wounds**. The first 2 **mortal wounds** are sufficient to **destroy** 1 Intercessor model, so the remaining **mortal wound** is lost.*</description></rule><rule id="115b-79dc-f723-d761" name="Extra Attacks" hidden="false" page="28"><description>Each time a unit containing one or more models with an **[EXTRA ATTACKS]** weapon fights, those models will make attacks with those weapons in addition to any others. In the Select Weapons step (04.01), for each of those models, you must select: 
- All of that models **[EXTRA ATTACKS]** weapons. 
- One of that models other melee weapons, if possible.</description></rule></rules><profiles><profile id="e4be-19e1-54b-d219" name="Arc Scourge" hidden="false" typeId="8a40-4aaa-c780-9046" typeName="Melee Weapons" from="entry"><characteristics><characteristic name="Range" typeId="914c-b413-91e3-a132">Melee</characteristic><characteristic name="A" typeId="2337-daa1-6682-b110">4</characteristic><characteristic name="WS" typeId="95d1-95f-45b4-11d6">2+</characteristic><characteristic name="S" typeId="ab33-d393-96ce-ccba">5</characteristic><characteristic name="AP" typeId="41a0-1301-112a-e2f2">-1</characteristic><characteristic name="D" typeId="3254-9fe6-d824-513e">1</characteristic><characteristic name="Keywords" typeId="893f-9000-ccf7-648e">Anti-Vehicle 4+, Devastating Wounds, Extra Attacks</characteristic></characteristics></profile></profiles><categories><category id="84c4-6d1e-e724-bd6e" name="Extra Attacks Weapon" entryId="84c4-6d1e-e724-bd6e" primary="false" /></categories></selection><selection id="ladirbc" name="Cawl's Omnissian axe" entryId="cb05-4e43-6776-3c51::4858-7bde-a8d4-e7da" number="1" type="upgrade" from="entry"><profiles><profile id="f04a-c6ee-6971-f7da" name="Cawl's Omnissian axe" hidden="false" typeId="8a40-4aaa-c780-9046" typeName="Melee Weapons" from="entry"><characteristics><characteristic name="Range" typeId="914c-b413-91e3-a132">Melee</characteristic><characteristic name="A" typeId="2337-daa1-6682-b110">4</characteristic><characteristic name="WS" typeId="95d1-95f-45b4-11d6">2+</characteristic><characteristic name="S" typeId="ab33-d393-96ce-ccba">8</characteristic><characteristic name="AP" typeId="41a0-1301-112a-e2f2">-2</characteristic><characteristic name="D" typeId="3254-9fe6-d824-513e">2</characteristic><characteristic name="Keywords" typeId="893f-9000-ccf7-648e">-</characteristic></characteristics></profile></profiles></selection><selection id="lafgy0s" name="Mechadendrite hive" entryId="cb05-4e43-6776-3c51::665-8af3-4b52-8ac6" number="1" type="upgrade" from="entry"><rules><rule id="115b-79dc-f723-d761" name="Extra Attacks" hidden="false" page="28"><description>Each time a unit containing one or more models with an **[EXTRA ATTACKS]** weapon fights, those models will make attacks with those weapons in addition to any others. In the Select Weapons step (04.01), for each of those models, you must select: 
- All of that models **[EXTRA ATTACKS]** weapons. 
- One of that models other melee weapons, if possible.</description></rule></rules><profiles><profile id="7a81-a490-ee0-ffdd" name="Mechadendrite hive" hidden="false" typeId="8a40-4aaa-c780-9046" typeName="Melee Weapons" from="entry"><characteristics><characteristic name="Range" typeId="914c-b413-91e3-a132">Melee</characteristic><characteristic name="A" typeId="2337-daa1-6682-b110">2D6</characteristic><characteristic name="WS" typeId="95d1-95f-45b4-11d6">3+</characteristic><characteristic name="S" typeId="ab33-d393-96ce-ccba">4</characteristic><characteristic name="AP" typeId="41a0-1301-112a-e2f2">0</characteristic><characteristic name="D" typeId="3254-9fe6-d824-513e">1</characteristic><characteristic name="Keywords" typeId="893f-9000-ccf7-648e">Extra Attacks</characteristic></characteristics></profile></profiles><categories><category id="84c4-6d1e-e724-bd6e" name="Extra Attacks Weapon" entryId="84c4-6d1e-e724-bd6e" primary="false" /></categories></selection><selection id="lac4b2c" name="Solar atomiser" entryId="cb05-4e43-6776-3c51::238d-d584-47ee-856d" number="1" type="upgrade" from="entry"><rules><rule id="6c1f-1cf7-ff25-c99e" name="Blast" hidden="false" page="26"><description>Each time you gather attack dice for a **[BLAST]** weapon, add one additional **attack dice** for every five models that were in the target unit in the Select Targets step (rounding down). If this ability takes the form **[BLAST X]**, each time you gather **attack dice** for such a weapon, add **X** additional **attack dice** for every five models that were in the target unit in the Select Targets step (rounding down) instead. 
***Example:** If a **[BLAST 2]** weapon with an **A** characteristic of 3 targets a unit containing 12 models, you would gather four additional **attack dice** for that weapon (for a total of seven for that weapon).*</description></rule><rule id="7cdb-fb99-44a9-8849" name="Melta" hidden="false" page="26"><description>This ability always takes the form **[MELTA X]**. Each time a model makes an attack with a **[MELTA]** weapon, if the target unit was within half range of that weapon in the Select Targets step, until the attacking units attacks have been resolved, add **X** to that weapons **D** characteristic. 
***Example:** A model targets a unit that is within half range of a **[MELTA 2]** weapon with a **D** characteristic of D6. While resolving those attacks, that weapon has a **D** characteristic of **D6+2**.</description></rule></rules><profiles><profile id="2fd0-1925-e091-75ef" name="Solar atomiser" hidden="false" typeId="f77d-b953-8fa4-b762" typeName="Ranged Weapons" from="entry"><characteristics><characteristic name="Range" typeId="9896-9419-16a1-92fc">18&quot;</characteristic><characteristic name="A" typeId="3bb-c35f-f54-fb08">3</characteristic><characteristic name="BS" typeId="94d-8a98-cf90-183e">2+</characteristic><characteristic name="S" typeId="2229-f494-25db-c5d3">14</characteristic><characteristic name="AP" typeId="9ead-8a10-520-de15">-4</characteristic><characteristic name="D" typeId="a354-c1c8-a745-f9e3">D6</characteristic><characteristic name="Keywords" typeId="7f1b-8591-2fcf-d01c">Melta 3</characteristic></characteristics></profile></profiles></selection><selection id="lanv51g" name="Warlord" entryId="cb05-4e43-6776-3c51::c097-c2fe-be86-de31::0580-79ca-da98-77c3" number="1" type="upgrade" from="entry"><categories><category id="5c0e-4c31-d51b-e470" name="Warlord" entryId="5c0e-4c31-d51b-e470" primary="false" /><category id="75fe-c657-43c6-7c09" name="Supreme Commander" entryId="75fe-c657-43c6-7c09" primary="false" /></categories></selection></selections><costs><cost name="pts" typeId="51b2-306e-1021-d207" value="220" /></costs><categories><category id="9106-4e44-7994-8859" name="Belisarius Cawl" entryId="9106-4e44-7994-8859" primary="false" /><category id="4f3a-f0f7-6647-348d" entryId="4f3a-f0f7-6647-348d" name="Epic Hero" primary="true" /><category id="9693-cf84-fe69-37a9" name="Monster" entryId="9693-cf84-fe69-37a9" primary="false" /><category id="9cfd-1c32-585f-7d5c" name="Character" entryId="9cfd-1c32-585f-7d5c" primary="false" /><category id="59a9-b5cc-7c11-aaad" name="Tech-Priest" entryId="59a9-b5cc-7c11-aaad" primary="false" /><category id="5418-f86b-6e76-c5a" name="Faction: Adeptus Mechanicus" entryId="5418-f86b-6e76-c5a" primary="false" /><category id="aff3-d6a3-2a95-9dc" name="Imperium" entryId="aff3-d6a3-2a95-9dc" primary="false" /><category id="6d46-7883-e5d1-45ee" name="Cult Mechanicus" entryId="6d46-7883-e5d1-45ee" primary="false" /></categories></selection></selections><categories><category name="Uncategorized" id="o5b8aw" primary="false" entryId="(No Category)" /><category name="Configuration" id="7z2e0hj" primary="false" entryId="4ac9-fd30-1e3d-b249" /><category name="Epic Hero" id="o5e736q" primary="false" entryId="4f3a-f0f7-6647-348d" /><category name="Character" id="o6rksa8" primary="false" entryId="9cfd-1c32-585f-7d5c" /><category name="Battleline" id="o6ay1he" primary="false" entryId="e338-111e-d0c6-b687" /><category name="Infantry" id="o6kppe6" primary="false" entryId="cf47-a0d7-7207-29dc" /><category name="Swarm" id="o6tx7a" primary="false" entryId="b00b-5bae-444f-964e" /><category name="Mounted" id="o6cf37m" primary="false" entryId="14a0-40c9-2748-ae6e" /><category name="Beast" id="o6cnr9" primary="false" entryId="4c3e-9310-a516-3590" /><category name="Monster" id="o60v22i" primary="false" entryId="9693-cf84-fe69-37a9" /><category name="Vehicle" id="o6d7b7r" primary="false" entryId="dbd4-63-af05-998" /><category name="Drone" id="o6reyz" primary="false" entryId="2471-e2e0-3f55-d6cb" /><category name="Dedicated Transport" id="o6t0vy" primary="false" entryId="ba07-411c-2832-1f79" /><category name="Fortification" id="o63ey3d" primary="false" entryId="19d7-9c74-2140-5851" /><category name="Unit" id="o6f99k" primary="false" entryId="1160-70ae-a862-b1a8" /><category name="Allies: Astra Militarum" id="o73fsgv" primary="false" entryId="8247-35f6-ec2a-7caa" /><category name="Allies: Chaos Knights" id="o7wqmgi" primary="false" entryId="8dd6-af11-abd3-55f1" /><category name="Allies: Heretic Astartes" id="o7fadd" primary="false" entryId="92e6-f82e-0a48-2276" /><category name="Allies: Imperial Agents" id="o7a694k" primary="false" entryId="742f-5727-1bc9-6da5" /><category name="Allies: Imperial Knights" id="o7xkohf" primary="false" entryId="5e03-9044-273d-e818" /><category name="Allies: Legiones Daemonica" id="o7h1wqa" primary="false" entryId="e1ea-71b9-f0ef-d788" /><category name="Allies: Titanicus Traitoris" id="o7131zt" primary="false" entryId="24fa-8caf-cd68-31fb" /><category name="Allies: Unaligned Forces" id="o7u4bvf" primary="false" entryId="07d9-ab1c-cf28-0939" /><category name="Reference" id="o7g3qsb" primary="false" entryId="eef1-be80-500a-edfc" /><category name="Allies: Adeptus Titanicus" id="o8ica9m" primary="false" entryId="9988-6b5e-660e-d973" /><category name="Illegal Units" id="o881cch" primary="false" entryId="(Illegal Units)" /></categories></force></forces></roster>