@alpaca-software/40kdc-data 0.1.2 → 0.1.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -0
- package/dist/abilities-resolver/resolver.d.ts +13 -4
- package/dist/abilities-resolver/resolver.d.ts.map +1 -1
- package/dist/abilities-resolver/resolver.js +22 -15
- package/dist/abilities-resolver/resolver.js.map +1 -1
- package/dist/audit-coverage.d.ts +78 -0
- package/dist/audit-coverage.d.ts.map +1 -0
- package/dist/audit-coverage.js +341 -0
- package/dist/audit-coverage.js.map +1 -0
- package/dist/author-batch.d.ts +147 -0
- package/dist/author-batch.d.ts.map +1 -0
- package/dist/author-batch.js +675 -0
- package/dist/author-batch.js.map +1 -0
- package/dist/author-input.d.ts +37 -0
- package/dist/author-input.d.ts.map +1 -0
- package/dist/author-input.js +162 -0
- package/dist/author-input.js.map +1 -0
- package/dist/cli.js +7 -0
- package/dist/cli.js.map +1 -1
- package/dist/commands/translate.d.ts.map +1 -1
- package/dist/commands/translate.js +9 -4
- package/dist/commands/translate.js.map +1 -1
- package/dist/cruncher/attribution.d.ts +66 -0
- package/dist/cruncher/attribution.d.ts.map +1 -0
- package/dist/cruncher/attribution.js +88 -0
- package/dist/cruncher/attribution.js.map +1 -0
- package/dist/cruncher/buffs.d.ts +23 -1
- package/dist/cruncher/buffs.d.ts.map +1 -1
- package/dist/cruncher/buffs.js +1 -1
- package/dist/cruncher/buffs.js.map +1 -1
- package/dist/cruncher/from-dsl.d.ts +32 -0
- package/dist/cruncher/from-dsl.d.ts.map +1 -1
- package/dist/cruncher/from-dsl.js +485 -40
- package/dist/cruncher/from-dsl.js.map +1 -1
- package/dist/cruncher/index.d.ts +1 -0
- package/dist/cruncher/index.d.ts.map +1 -1
- package/dist/cruncher/index.js +1 -0
- package/dist/cruncher/index.js.map +1 -1
- package/dist/data/bundle.generated.js +1 -1
- package/dist/data/bundle.generated.js.map +1 -1
- package/dist/data/collection.d.ts +9 -0
- package/dist/data/collection.d.ts.map +1 -1
- package/dist/data/collection.js +14 -0
- package/dist/data/collection.js.map +1 -1
- package/dist/data/dataset.d.ts +80 -2
- package/dist/data/dataset.d.ts.map +1 -1
- package/dist/data/dataset.js +143 -6
- package/dist/data/dataset.js.map +1 -1
- package/dist/data/entities.d.ts +2 -5
- package/dist/data/entities.d.ts.map +1 -1
- package/dist/data/entities.js.map +1 -1
- package/dist/data/index.d.ts +3 -2
- package/dist/data/index.d.ts.map +1 -1
- package/dist/data/index.js +1 -1
- package/dist/data/index.js.map +1 -1
- package/dist/data/roster-resolve.d.ts +26 -1
- package/dist/data/roster-resolve.d.ts.map +1 -1
- package/dist/data/roster-resolve.js +46 -0
- package/dist/data/roster-resolve.js.map +1 -1
- package/dist/export/index.d.ts +1 -0
- package/dist/export/index.d.ts.map +1 -1
- package/dist/export/index.js +3 -0
- package/dist/export/index.js.map +1 -1
- package/dist/export/rosterizer.d.ts +3 -0
- package/dist/export/rosterizer.d.ts.map +1 -0
- package/dist/export/rosterizer.js +144 -0
- package/dist/export/rosterizer.js.map +1 -0
- package/dist/export/serializer.d.ts +1 -1
- package/dist/export/serializer.d.ts.map +1 -1
- package/dist/export/serializer.js.map +1 -1
- package/dist/gen-conformance.js +212 -11
- package/dist/gen-conformance.js.map +1 -1
- package/dist/import/gw.d.ts +69 -0
- package/dist/import/gw.d.ts.map +1 -0
- package/dist/import/gw.js +245 -0
- package/dist/import/gw.js.map +1 -0
- package/dist/import/import-roster.d.ts +52 -3
- package/dist/import/import-roster.d.ts.map +1 -1
- package/dist/import/import-roster.js +114 -4
- package/dist/import/import-roster.js.map +1 -1
- package/dist/import/index.d.ts +2 -2
- package/dist/import/index.d.ts.map +1 -1
- package/dist/import/index.js +1 -1
- package/dist/import/index.js.map +1 -1
- package/dist/import/listforge.d.ts.map +1 -1
- package/dist/import/listforge.js +15 -1
- package/dist/import/listforge.js.map +1 -1
- package/dist/import/newrecruit-text.d.ts +3 -0
- package/dist/import/newrecruit-text.d.ts.map +1 -1
- package/dist/import/newrecruit-text.js +6 -0
- package/dist/import/newrecruit-text.js.map +1 -1
- package/dist/import/newrecruit-wtc.d.ts.map +1 -1
- package/dist/import/newrecruit-wtc.js +10 -7
- package/dist/import/newrecruit-wtc.js.map +1 -1
- package/dist/import/rosterizer.d.ts +70 -0
- package/dist/import/rosterizer.d.ts.map +1 -0
- package/dist/import/rosterizer.js +348 -0
- package/dist/import/rosterizer.js.map +1 -0
- package/dist/import/types.d.ts +1 -1
- package/dist/import/types.d.ts.map +1 -1
- package/dist/import/types.js.map +1 -1
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/migrations/2026-weapon-keywords.js +4 -0
- package/dist/migrations/2026-weapon-keywords.js.map +1 -1
- package/dist/runner.d.ts +38 -0
- package/dist/runner.d.ts.map +1 -0
- package/dist/runner.js +492 -0
- package/dist/runner.js.map +1 -0
- package/dist/scrub-ip.d.ts +14 -0
- package/dist/scrub-ip.d.ts.map +1 -0
- package/dist/scrub-ip.js +88 -0
- package/dist/scrub-ip.js.map +1 -0
- package/package.json +9 -2
- package/schemas/core/roster.schema.json +3 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"author-batch.js","sourceRoot":"","sources":["../src/author-batch.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * DSL stub authoring engine (#21) — batched, subscription-billed, two-phase.\n *\n * Empty-modifier ability stubs (`{type:\"stat-modifier\", modifier:{}}` and kin)\n * are authored into real DSL from their 10e-archive source rule. The work is a\n * pile of discrete, stateless, structured LLM calls, so we run them as batched\n * `claude -p --json-schema` invocations on the Claude subscription rather than\n * spawning a full agent per ability (the agent fan-out's per-call system-prompt\n * + tool-loop overhead is ~50-100x the useful work). Assembly and schema\n * validation are pure TS — the model only classifies and judges.\n *\n * author-input/<faction>.json (datasheet-resolved rules, from author-input.ts)\n * ── classify ──▶ flat slot-forms (batched claude -p)\n * ── assemble ──▶ full ability entries (TS: effect + scope, no LLM)\n * ── validate ──▶ AJV against the schema (TS — rejects invented enums)\n * ── verify ──▶ fidelity verdict (batched claude -p, scope-aware)\n * ─────────────▶ data/_audit/proposed/<faction>.json\n *\n * Two modes:\n * propose (default) — write proposals; never touch live data.\n * apply — splice gated proposals into live abilities.json. Only\n * rewrites entries that are STILL empty-modifier stubs,\n * so re-running is safe and authored work is never\n * clobbered. Gate defaults: schema-valid + verifier-\n * faithful + confidence≠low + not complex-flagged.\n *\n * Usage:\n * npx tsx tools/src/author-batch.ts propose <faction|--all> [--batch N] [--model M]\n * npx tsx tools/src/author-batch.ts apply <faction|--all> [--min-confidence high|medium]\n * [--include-complex] [--dry-run]\n */\nimport { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { execFile } from \"node:child_process\";\nimport { fileURLToPath } from \"node:url\";\nimport { createValidator } from \"./schema-loader.js\";\nimport { hasEmptyModifier } from \"./audit-coverage.js\";\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\nconst DATA_ROOT = resolve(__dirname, \"../../data\");\nconst INPUT_DIR = resolve(DATA_ROOT, \"_audit\", \"author-input\");\nconst ENRICHMENT_ROOT = resolve(DATA_ROOT, \"enrichment\");\nconst OUT_DIR = resolve(DATA_ROOT, \"_audit\", \"proposed\");\nconst ABILITY_SCHEMA_ID = \"https://40kdc.dev/schemas/enrichment/ability-dsl/ability.schema.json\";\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Json = any;\nconst readJSON = (p: string): Json => JSON.parse(readFileSync(p, \"utf-8\"));\nconst writeJSON = (p: string, v: Json): void => writeFileSync(p, JSON.stringify(v, null, 2) + \"\\n\");\n\nconst PARAMETERLESS = new Set([\"deep-strike\", \"fallback-and-act\", \"fight-first\", \"fight-last\", \"shoot-on-death\", \"fight-on-death\"]);\n\nexport interface Proposal {\n ability_id: string;\n name: string;\n faction: string;\n effect_type?: string;\n complex?: boolean;\n confidence?: \"high\" | \"medium\" | \"low\";\n schema_valid: boolean;\n proposed_effect?: Json;\n proposed_scope?: Json;\n /** Ability-level behavior the repair pass inferred (passive/activated/reactive/aura). */\n proposed_behavior?: string;\n verdict?: { severity: string; faithful: boolean; issue: string } | null;\n final_faithful: boolean;\n error?: string;\n /** Set by the full-tree repair pass — the effect is a nested tree, not flat-form. */\n repaired?: boolean;\n /** Repair pass flagged this rule as genuinely unencodable (needs hand-authoring). */\n unencodable?: boolean;\n /** Canonical-key lint result (repair pass only). false = invented/out-of-vocab modifier keys. */\n canonical?: boolean;\n}\n\n// ─── claude CLI bridge (subscription, structured output) ─────────────\n\n/** One batched, structured `claude -p` call. Resolves to the validated object. */\nexport function callClaude(system: string, user: string, schema: Json, model: string): Promise<Json> {\n return new Promise((res, rej) => {\n execFile(\n \"claude\",\n [\"-p\", user, \"--system-prompt\", system, \"--exclude-dynamic-system-prompt-sections\",\n \"--json-schema\", JSON.stringify(schema), \"--output-format\", \"json\", \"--model\", model],\n { maxBuffer: 64 * 1024 * 1024, timeout: 300_000 },\n (err, stdout) => {\n if (err && !stdout) return rej(err);\n try {\n const env = JSON.parse(stdout);\n if (env.is_error) return rej(new Error(env.result ?? \"claude error\"));\n if (!env.structured_output) return rej(new Error(\"no structured_output in response\"));\n res(env.structured_output);\n } catch (e) {\n rej(new Error(`parse failed: ${(e as Error).message}; head=${String(stdout).slice(0, 200)}`));\n }\n },\n );\n });\n}\n\n// ─── prompts + schemas ───────────────────────────────────────────────\n\nconst CLASSIFY_SYSTEM =\n `You translate Warhammer 40k ability rules into a structured DSL. For each ability return one slot-form.\\n\\n` +\n `effect_type — pick the SINGLE best of:\\n` +\n ` stat-modifier {operation:\"add\"|\"subtract\"|\"set\", stat:\"A\"|\"S\"|\"T\"|\"Sv\"|\"AP\"|\"OC\"|\"Ld\", value:int}\\n` +\n ` roll-modifier {operation:\"add\"|\"subtract\", roll:\"hit\"|\"wound\"|\"save\"|\"charge\", value:int}\\n` +\n ` re-roll {roll:\"hit\"|\"wound\"|\"save\"|\"damage\"|\"charge\", subset:\"ones\"|\"all-failures\"} — ONLY combat dice, NOT Battle-shock/Leadership\\n` +\n ` leadership-modifier {test:\"battle-shock\", operation:\"re-roll\"} or {operation:\"add\"|\"subtract\", value:int} — USE for Battle-shock/Leadership rerolls or Ld changes\\n` +\n ` mortal-wounds {count:int|\"D3\"|\"D6\"} ; feel-no-pain {threshold:int} ; invulnerable-save {invuln_sv:int}\\n` +\n ` keyword-grant {keywords:[ \"lethal-hits\"|\"sustained-hits\"|\"devastating-wounds\"|\"twin-linked\"|... ]} (ARRAY)\\n` +\n ` damage-reduction {reduction:int} ; objective-control-modifier {operation,value}|{sticky:true}\\n` +\n ` ability-grant {ability_id:\"kebab\"}|{grant_type:\"...\"} ; attack-restriction {restriction:\"...\"}\\n` +\n ` cp-gain|cp-refund {amount:int} ; resurrection {count:int|\"D3\"} ; model-destruction {count:int}\\n` +\n ` resource-gain|resource-spend {pool_id:\"...\", amount:int|\"D3\"} — faction resources: Miracle Dice→\"miracle-dice-pool\", Khorne Blessings→\"blessings-of-khorne-pool\", Pain tokens→\"pain-token-pool\"\\n` +\n ` movement-modifier {move_type,value} ; deep-strike/fallback-and-act/fight-first/fight-last/shoot-on-death/fight-on-death → modifier {}\\n\\n` +\n `attack_type — \"melee\"|\"ranged\" if the rule limits to that attack kind, else \"any\". (Do NOT encode this as a condition.)\\n` +\n `condition_kind — DEFAULT \"none\". Only set if the rule EXPLICITLY restricts: \"phase\" (+condition_param = phase name), \"vs-keyword\" (+param=keyword), ` +\n `\"charged\", \"stationary\", \"below-half\", \"below-starting\", \"attached\", \"leading\". Do NOT add a phase condition just because the ability operates in a phase. ` +\n `If the rule needs a compound/event trigger (e.g. a dice roll, an either/or choice, or \"when a friendly VEHICLE is destroyed within 12\\\\\"\") set complex=true.\\n` +\n `scope_range — \"self\"|\"unit\"|\"attached-unit\"|\"aura-6\"|\"aura-9\"|\"aura-12\"|... . scope_duration — \"phase\"|\"turn\"|\"battle-round\"|\"battle\"|\"permanent\".\\n` +\n `target — \"self\"|\"unit\"|\"friendly-within-aura\"|\"enemy-within-aura\"|\"attacker\"|\"defender\"|... (only values from the schema enum).\\n` +\n `Never copy rule text into any field. Give confidence and a one-sentence reasoning.`;\n\nexport const VERIFY_SYSTEM =\n `You judge whether authored DSL faithfully captures a 40k rule. The DSL includes scope {range,duration} — credit the aura/range/duration when it is in scope (do NOT flag \"missing 6\\\\\" aura\" if scope.range is \"aura-6\"). ` +\n `Be strict about the core mechanic: wrong effect type, wrong stat/roll, wrong value, a condition the rule does NOT state (phantom), a stated condition that is missing, or modeling a Leadership/Battle-shock re-roll as a combat re-roll. ` +\n `severity \"ok\" = core mechanic + conditions + scope correct; \"minor\" = core correct but a secondary detail imperfect; \"wrong\" = core mechanic wrong. Return one verdict per ability, echoing its ability_id.`;\n\nconst CLASSIFY_SCHEMA = {\n type: \"object\", additionalProperties: false,\n properties: { results: { type: \"array\", items: {\n type: \"object\", additionalProperties: false,\n properties: {\n ability_id: { type: \"string\" }, effect_type: { type: \"string\" }, target: { type: \"string\" },\n modifier: { type: \"object\", additionalProperties: true }, attack_type: { enum: [\"any\", \"melee\", \"ranged\"] },\n condition_kind: { enum: [\"none\", \"phase\", \"vs-keyword\", \"charged\", \"stationary\", \"below-half\", \"below-starting\", \"attached\", \"leading\"] },\n condition_param: { type: [\"string\", \"null\"] }, scope_range: { type: \"string\" }, scope_duration: { type: \"string\" },\n complex: { type: \"boolean\" }, confidence: { enum: [\"high\", \"medium\", \"low\"] }, reasoning: { type: \"string\" },\n },\n required: [\"ability_id\", \"effect_type\", \"target\", \"modifier\", \"attack_type\", \"condition_kind\", \"scope_range\", \"scope_duration\", \"complex\", \"confidence\", \"reasoning\"],\n } } },\n required: [\"results\"],\n};\n\nexport const VERIFY_SCHEMA = {\n type: \"object\", additionalProperties: false,\n properties: { results: { type: \"array\", items: {\n type: \"object\", additionalProperties: false,\n properties: { ability_id: { type: \"string\" }, severity: { enum: [\"ok\", \"minor\", \"wrong\"] }, faithful: { type: \"boolean\" }, issue: { type: \"string\" } },\n required: [\"ability_id\", \"severity\", \"faithful\", \"issue\"],\n } } },\n required: [\"results\"],\n};\n\n// ─── full-tree repair prompts + schema ───────────────────────────────\n//\n// The flat-form classifier (above) emits a single condition + flat leaf, so it\n// structurally CANNOT express compound conditions, event triggers, or nested\n// effect kinds — every such rule lands in the proposed/ residue with a verifier\n// `issue` naming the gap. The repair pass hands the model the FULL DSL grammar\n// and asks it to emit the complete nested effect tree, seeded with the existing\n// draft + that exact gap. The envelope schema below is intentionally loose (just\n// `effect`/`scope` objects); the real gate is AJV against ability.schema, exactly\n// as the flat-form path validates `buildEntry` output.\n\nexport const REPAIR_SYSTEM =\n `You repair Warhammer 40k ability DSL. You are given a rule, a DRAFT effect that an earlier flat-form pass produced, and the EXACT gap a verifier found (usually a missing trigger or compound condition). Emit the COMPLETE nested effect tree that fixes the gap. Never copy rule text into any field.\\n\\n` +\n `An effect node is ONE of:\\n` +\n ` • a leaf: {type, target, modifier} — type ∈ [stat-modifier, roll-modifier, re-roll, mortal-wounds, feel-no-pain, invulnerable-save, ward, keyword-grant, movement-modifier, deep-strike, fallback-and-act, fight-first, fight-last, shoot-on-death, fight-on-death, objective-control-modifier, leadership-modifier, damage-reduction, attack-restriction, ability-grant, cp-gain, cp-refund, model-destruction, resurrection, resource-gain, resource-spend, charge-roll-modifier, terrain-area-tag, bs-modifier, engagement-passthrough]; target ∈ [self, bearer, unit, attached-unit, attacker, defender, friendly-within-aura, enemy-within-aura, all-friendly, all-enemy]\\n` +\n ` • conditional: {type:\"conditional\", condition, effect}\\n` +\n ` • sequence: {type:\"sequence\", steps:[effect, ...]} — multiple effects that all apply\\n` +\n ` • choice: {type:\"choice\", options:[effect, ...], choice_label?} — pick exactly one\\n` +\n ` • dice-gated: {type:\"dice-gated\", dice:\"D6\"|..., threshold:int, comparison?:\"greater-or-equal\"|..., on_success:effect, on_fail?:effect}\\n` +\n ` • dice-pool-allocation: {type:\"dice-pool-allocation\", pool:{count,die}, max_activations:int, options:[{name, requirement, effect}, ...]}\\n\\n` +\n `A condition is ONE of:\\n` +\n ` • simple: {type, parameters:{...}} — ALL params go UNDER \"parameters\", never as top-level keys (e.g. {\"type\":\"unit-has-keyword\",\"parameters\":{\"keyword\":\"VEHICLE\"}}, NOT {\"type\":\"unit-has-keyword\",\"keyword\":\"VEHICLE\"}). type ∈ [phase-is{phase}, timing-is{timing}, player-turn-is{turn}, unit-below-starting-strength, unit-below-half-strength, unit-has-keyword{keyword}, unit-within-range-of{target_type}, model-is-leader, target-has-keyword{keyword}, charged-this-turn, advanced-this-turn, remained-stationary, is-battle-shocked, has-lost-wounds, opponent-unit-within-range, within-range-of-objective, attack-is-type{attack_type}, has-fought-this-phase, destroyed-by-attack-type{attack_type}, controls-objective, is-attached, terrain-area-control, engagement-state, territory-control, fights-first, disposition-matches, units-destroyed{side,window,count_min}, units-destroyed-comparison, objective-majority]\\n` +\n ` • compound: {operator:\"and\"|\"or\"|\"not\", operands:[condition, ...]} — use \"not\" with ONE operand to negate (e.g. \"while not Battle-shocked\" → {operator:\"not\", operands:[{type:\"is-battle-shocked\"}]}). Nest compounds freely.\\n\\n` +\n `Encode reactive/event triggers as a conditional whose condition is the trigger (e.g. an enemy destroyed a model nearby → destroyed-by-attack-type / opponent-unit-within-range). Encode \"first time per turn\"/\"once per game\" by choosing the correct timing condition; do not invent fields.\\n` +\n `scope = {range, duration}: range ∈ [self, unit, attached-unit, aura-6, aura-9, aura-12, ...]; duration ∈ [phase, turn, battle-round, battle, permanent]. Credit the aura in scope.range, NOT as a condition.\\n` +\n `behavior ∈ [passive, activated, reactive, aura].\\n\\n` +\n `CANONICAL MODIFIER KEYS — use ONLY the keys listed per type; never invent a key (an unknown key is silently ignored by consumers and corrupts the data):\\n` +\n ` stat-modifier.modifier: {stat, operation:\"add\"|\"subtract\"|\"set\", value:int}. stat ∈ [A,S,T,Sv,AP,OC,Ld,M,W,D] ONLY (use \"M\" for Move, never \"Move\"/\"range\"; weapon range is NOT a unit stat). operation:\"set\" IS allowed for \"characteristic of N\" rules (e.g. OC of 9). Optional narrowing: attack_type:\"melee\"|\"ranged\", weapon_type:\"melee\"|\"ranged\", or weapon_name:\"<weapon>\" for a single named weapon. Do NOT use weapon_keyword/weapon_filter/model_filter.\\n` +\n ` roll-modifier.modifier: {roll:\"hit\"|\"wound\"|\"save\"|\"charge\"|\"damage\", operation, value}. re-roll.modifier: {roll, subset:\"ones\"|\"all-failures\"}. Optional attack_type/weapon_type/weapon_name as above.\\n` +\n ` keyword-grant.modifier: {keywords:[...]} (array) — combat keywords as written (\"Lethal Hits\",\"Sustained Hits 1\",\"Twin-linked\"). Optional weapon_type:\"melee\"|\"ranged\", weapon_name.\\n` +\n ` feel-no-pain.modifier:{threshold:int}; damage-reduction.modifier:{reduction:int}; bs-modifier.modifier:{operation,value}; ability-grant.modifier:{grant_type:\"kebab-label\"}; objective-control-modifier.modifier:{operation:\"add\"|\"set\",value} or {sticky:true}; movement-modifier.modifier:{move_type:\"kebab\",value}; deep-strike.modifier:{} (parameterless).\\n\\n` +\n `dice-gated.comparison ∈ [\"gte\",\"lte\",\"gt\",\"lt\",\"eq\"] (use \"gte\" for \"on a 2+\"). dice e.g. \"D6\",\"2D6\"; threshold int. on_success/on_fail are effect nodes.\\n` +\n `ENCODING THE RESIDUE — these ARE expressible, do not punt on them:\\n` +\n ` • \"roll a D6, on 2+ <effect>\" → dice-gated {dice:\"D6\", threshold:2, comparison:\"gte\", on_success:<effect>}.\\n` +\n ` • \"select one of N abilities/effects\" → choice {options:[<effect>,...]}.\\n` +\n ` • \"re-roll Battle-shock/Leadership tests\" → leadership-modifier {test:\"battle-shock\", operation:\"re-roll\"} (NOT a combat re-roll).\\n` +\n ` • deployment/redeploy (\"set up in Strategic Reserves\", \"set up anywhere >9\\\\\"\", \"redeploy after deployment\") → deep-strike, or ability-grant {grant_type:\"<descriptive-kebab>\"} for a named deployment rule.\\n` +\n ` • \"move through terrain\" → movement-modifier {move_type:\"through-terrain\"}.\\n` +\n ` • \"characteristic of N\" → the matching stat/OC modifier with operation:\"set\".\\n\\n` +\n `Set unencodable:true ONLY when the rule is NOT an in-battle ability effect at all — army-construction (\"you can include one X per Y\", \"cannot include A with B\"), roster selection (\"cannot be your Warlord\"), model geometry/transport-capacity declarations, or roll-off/meta procedures. For those, return your best partial effect and explain. Everything that IS an in-battle effect must be encoded with the grammar above. Give confidence + one-sentence reasoning.`;\n\nexport const REPAIR_SCHEMA = {\n type: \"object\", additionalProperties: false,\n properties: { results: { type: \"array\", items: {\n type: \"object\", additionalProperties: false,\n properties: {\n ability_id: { type: \"string\" },\n effect: { type: \"object\", additionalProperties: true },\n scope: { type: \"object\", additionalProperties: true },\n behavior: { type: \"string\" },\n unencodable: { type: \"boolean\" },\n confidence: { enum: [\"high\", \"medium\", \"low\"] },\n reasoning: { type: \"string\" },\n },\n required: [\"ability_id\", \"effect\", \"scope\", \"behavior\", \"unencodable\", \"confidence\", \"reasoning\"],\n } } },\n required: [\"results\"],\n};\n\n// ─── assembly (pure TS, no LLM) ──────────────────────────────────────\n\nexport function conditionNode(kind: string, param: string | null | undefined): Json | null {\n switch (kind) {\n case \"phase\": return { type: \"phase-is\", parameters: { phase: param } };\n case \"vs-keyword\": return { type: \"target-has-keyword\", parameters: { keyword: param } };\n case \"charged\": return { type: \"charged-this-turn\" };\n case \"stationary\": return { type: \"remained-stationary\" };\n case \"below-half\": return { type: \"unit-below-half-strength\" };\n case \"below-starting\": return { type: \"unit-below-starting-strength\" };\n case \"attached\": return { type: \"is-attached\" };\n case \"leading\": return { type: \"model-is-leader\" };\n default: return null;\n }\n}\n\n/** Build the effect node + scope from a flat-form. */\nexport function assembleEffect(form: Json): { effect: Json; scope: Json } {\n const modifier = PARAMETERLESS.has(form.effect_type) ? {} : { ...(form.modifier ?? {}) };\n if (form.attack_type && form.attack_type !== \"any\" && [\"stat-modifier\", \"roll-modifier\", \"re-roll\"].includes(form.effect_type)) {\n modifier.attack_type = form.attack_type;\n }\n let effect: Json = { type: form.effect_type, target: form.target, modifier };\n const cond = conditionNode(form.condition_kind, form.condition_param);\n if (cond) effect = { type: \"conditional\", condition: cond, effect };\n return { effect, scope: { range: form.scope_range, duration: form.scope_duration } };\n}\n\n/** Splice the authored effect+scope onto the original entry, preserving metadata. */\nexport function buildEntry(original: Json, form: Json): Json {\n const { effect, scope } = assembleEffect(form);\n return { ...original, effect, scope, community_notes: \"community-authored from 10e source (provisional 11e); see #21\" };\n}\n\nconst BEHAVIOR_VALUES = new Set([\"passive\", \"activated\", \"reactive\", \"aura\"]);\n\n// ─── canonical-key lint ──────────────────────────────────────────────\n//\n// The full-tree repair model emits the whole effect node, including the open\n// `modifier` object. AJV permits any modifier key (additionalProperties:true),\n// so an invented key (`weapon_keyword`, `model_filter`, `critical_threshold`)\n// passes schema validation — but the cruncher reads ONLY the canonical keys, so\n// an ignored filter on an `add` operation silently OVER-APPLIES the buff. The\n// verifier can't catch this: it judges the JSON against the rule as a reader,\n// not against what the engine honors. This lint is the deterministic gate.\n//\n// Vocabulary is calibrated to what EXISTING enrichment data actually uses (not\n// world-eaters alone): `keywords` array is the dominant keyword-grant form,\n// `damage-reduction` uses `reduction`, and `stat` spans the full statline. The\n// lint only runs on NEW repair proposals, so strictness can't regress shipped\n// data — a rejected proposal just stays residue for hand-authoring.\n\n/** Modifier keys the cruncher / canonical conventions recognise, per leaf type. */\nconst CANONICAL_MODIFIER_KEYS: Record<string, Set<string>> = {\n // weapon_type/weapon_name are valid narrowing keys (gold uses weapon_name): the\n // cruncher honors weapon_type as a phase gate and fail-safes (unsupported) on\n // weapon_name, so the data can carry them without risking a silent over-apply.\n \"stat-modifier\": new Set([\"stat\", \"operation\", \"value\", \"attack_type\", \"weapon_type\", \"weapon_name\"]),\n \"roll-modifier\": new Set([\"roll\", \"operation\", \"value\", \"attack_type\", \"weapon_type\", \"weapon_name\", \"critical_on\", \"uses\", \"context\"]),\n \"re-roll\": new Set([\"roll\", \"subset\", \"attack_type\", \"weapon_type\", \"weapon_name\", \"max_rerolls\", \"uses\", \"context\"]),\n \"keyword-grant\": new Set([\"keyword\", \"keywords\", \"weapon_type\", \"weapon_name\"]),\n \"bs-modifier\": new Set([\"operation\", \"value\", \"attack_type\"]),\n \"feel-no-pain\": new Set([\"threshold\"]),\n \"damage-reduction\": new Set([\"reduction\", \"amount\"]),\n};\nconst CANONICAL_STATS = new Set([\"A\", \"S\", \"T\", \"Sv\", \"AP\", \"OC\", \"Ld\", \"M\", \"W\", \"D\", \"Damage\", \"BS\", \"WS\"]);\nconst CANONICAL_ROLLS = new Set([\"hit\", \"wound\", \"save\", \"charge\", \"damage\", \"advance\", \"any\", \"all\"]);\nconst CANONICAL_SUBSETS = new Set([\"ones\", \"all-failures\"]);\nconst CANONICAL_ATTACK_TYPES = new Set([\"melee\", \"ranged\"]);\n\n/**\n * Walk an effect tree and flag any cruncher-interpreted leaf whose modifier\n * carries an unknown key or an out-of-vocabulary stat/roll/subset/attack_type.\n * Non-interpreted leaf types (ability-grant, movement-modifier, …) are left\n * permissive — they don't reach the damage path, so an unknown key there is a\n * consistency nit, not a silent-corruption risk.\n */\nexport function lintCanonical(effect: Json): { canonical: boolean; issues: string[] } {\n const issues: string[] = [];\n // A simple condition is {type, parameters?, negated?}; every param lives UNDER\n // `parameters`. The cruncher reads condition.parameters.* only, and AJV doesn't\n // forbid stray top-level keys, so a param placed top-level (e.g.\n // {type:\"unit-has-keyword\", keyword:\"X\"}) silently makes the condition\n // unevaluatable — the buff never fires. Existing data is 680 nested / 0 top-level.\n const visitCondition = (c: Json): void => {\n if (!c || typeof c !== \"object\") return;\n if (Array.isArray(c.operands)) return c.operands.forEach(visitCondition); // compound {operator, operands}\n if (typeof c.type === \"string\") {\n for (const k of Object.keys(c)) if (k !== \"type\" && k !== \"parameters\" && k !== \"negated\") issues.push(`condition ${c.type}: param \"${k}\" must live under \"parameters\"`);\n }\n };\n const visit = (node: Json): void => {\n if (Array.isArray(node)) return node.forEach(visit);\n if (!node || typeof node !== \"object\") return;\n const type = node.type as string | undefined;\n const allow = type ? CANONICAL_MODIFIER_KEYS[type] : undefined;\n if (allow && node.modifier && typeof node.modifier === \"object\") {\n const m = node.modifier as Record<string, unknown>;\n for (const k of Object.keys(m)) if (!allow.has(k)) issues.push(`${type}: non-canonical modifier key \"${k}\"`);\n if (type === \"stat-modifier\" && m.stat != null && !CANONICAL_STATS.has(String(m.stat))) issues.push(`stat-modifier: unknown stat \"${String(m.stat)}\"`);\n if ((type === \"roll-modifier\" || type === \"re-roll\") && m.roll != null && !CANONICAL_ROLLS.has(String(m.roll))) issues.push(`${type}: unknown roll \"${String(m.roll)}\"`);\n if (m.subset != null && !CANONICAL_SUBSETS.has(String(m.subset))) issues.push(`${type}: unknown subset \"${String(m.subset)}\"`);\n if (m.attack_type != null && !CANONICAL_ATTACK_TYPES.has(String(m.attack_type))) issues.push(`${type}: unknown attack_type \"${String(m.attack_type)}\"`);\n }\n if (node.condition) visitCondition(node.condition);\n // Recurse through the wrapper kinds (conditional/sequence/choice/dice-*).\n for (const key of [\"effect\", \"steps\", \"options\", \"on_success\", \"on_fail\"]) if (node[key]) visit(node[key]);\n };\n visit(effect);\n return { canonical: issues.length === 0, issues };\n}\n\n/**\n * Splice a pre-formed nested effect tree (from the repair pass) onto the original\n * entry. Unlike {@link buildEntry} the LLM owns the whole tree, so we only graft\n * `effect`/`scope`/`behavior` and the citation — never the flat-form assembly.\n * `behavior` is an ability-level field; only set it when the model returned a\n * valid enum value (an invalid one would just fail AJV and lose the whole entry).\n */\nexport function buildRepairedEntry(original: Json, effect: Json, scope: Json, behavior?: string): Json {\n const entry: Json = { ...original, effect, scope, community_notes: \"community-authored from 10e source (provisional 11e); see #21\" };\n if (behavior && BEHAVIOR_VALUES.has(behavior)) entry.behavior = behavior;\n return entry;\n}\n\n// ─── apply gate ──────────────────────────────────────────────────────\n\nexport interface GateOpts { minConfidence: \"high\" | \"medium\"; includeComplex: boolean }\n\n/** Whether a proposal is safe to apply automatically. */\nexport function passesGate(p: Proposal, opts: GateOpts): boolean {\n if (!p.schema_valid || !p.final_faithful) return false;\n if (p.confidence === \"low\") return false;\n if (opts.minConfidence === \"high\" && p.confidence !== \"high\") return false;\n // A repaired proposal IS the full nested tree, so `complex` no longer means\n // \"couldn't express it\" — the AJV + verifier gate already proved it expresses\n // the rule faithfully. It must also clear the canonical-key lint so an invented\n // modifier key can't silently over-apply. The complex-exclusion only applies to\n // flat-form output.\n if (p.repaired) return p.canonical !== false;\n if (p.complex && !opts.includeComplex) return false;\n return true;\n}\n\n// ─── batching helpers ────────────────────────────────────────────────\n\nconst chunk = <T>(arr: T[], n: number): T[][] => {\n const out: T[][] = [];\n for (let i = 0; i < arr.length; i += n) out.push(arr.slice(i, i + n));\n return out;\n};\n\nconst classifyUserPrompt = (items: Json[]): string =>\n `Classify each ability below. Return results[] (one per ability, echo its ability_id):\\n\\n` +\n items.map((it) => `- ability_id: ${it.ability_id}\\n name: ${it.name}\\n rule: ${it.src?.description ?? \"(none)\"}`).join(\"\\n\");\n\nexport const verifyUserPrompt = (entries: { ability_id: string; rule: string; effect: Json; scope: Json }[]): string =>\n `Judge each authored DSL against its rule. Return results[] (one per ability, echo its ability_id):\\n\\n` +\n entries.map((e) => `- ability_id: ${e.ability_id}\\n rule: ${e.rule}\\n authored: ${JSON.stringify({ effect: e.effect, scope: e.scope })}`).join(\"\\n\");\n\nexport const repairUserPrompt = (items: { ability_id: string; rule: string; draft: Json; issue: string }[]): string =>\n `Repair each ability's DSL. Emit the full nested effect tree + scope + behavior that fixes the stated gap. Return results[] (one per ability, echo its ability_id):\\n\\n` +\n items.map((it) =>\n `- ability_id: ${it.ability_id}\\n rule: ${it.rule || \"(none)\"}\\n draft_effect: ${JSON.stringify(it.draft ?? null)}\\n gap_to_fix: ${it.issue || \"(verifier produced no issue — re-author faithfully from the rule)\"}`,\n ).join(\"\\n\\n\");\n\n// ─── propose ─────────────────────────────────────────────────────────\n\ninterface ProposeOpts { batch: number; model: string }\n\nasync function proposeFaction(faction: string, opts: ProposeOpts, validate: (x: unknown) => boolean): Promise<Json> {\n const inputPath = resolve(INPUT_DIR, `${faction}.json`);\n if (!existsSync(inputPath)) return { faction, skipped: \"no author-input\" };\n const input: Json[] = readJSON(inputPath).filter((e: Json) => e.resolved);\n if (input.length === 0) return { faction, skipped: \"no resolved stubs\" };\n\n const original = new Map<string, Json>();\n for (const a of readJSON(resolve(ENRICHMENT_ROOT, faction, \"abilities.json\")) as Json[]) original.set(a.ability_id, a);\n\n const proposals: Proposal[] = [];\n for (const batch of chunk(input, opts.batch)) {\n let forms: Json[];\n try {\n ({ results: forms } = await callClaude(CLASSIFY_SYSTEM, classifyUserPrompt(batch), CLASSIFY_SCHEMA, opts.model));\n } catch (e) {\n // One flaky call shouldn't sink the run — record the batch as errored and move on.\n process.stderr.write(` ${faction}: classify batch failed (${(e as Error).message.slice(0, 80)}) — skipping ${batch.length}\\n`);\n for (const it of batch) proposals.push({ ability_id: it.ability_id, name: it.name, faction, schema_valid: false, final_faithful: false, error: \"classify call failed\" });\n continue;\n }\n const byId = new Map<string, Json>(forms.map((f: Json) => [f.ability_id, f]));\n\n const built: { it: Json; form: Json; entry: Json; schemaValid: boolean }[] = [];\n for (const it of batch) {\n const form = byId.get(it.ability_id);\n const orig = original.get(it.ability_id);\n if (!form || !orig) {\n proposals.push({ ability_id: it.ability_id, name: it.name, faction, schema_valid: false, final_faithful: false, error: !form ? \"no classification\" : \"no original entry\" });\n continue;\n }\n const entry = buildEntry(orig, form);\n built.push({ it, form, entry, schemaValid: validate(entry) });\n }\n\n const toVerify = built.filter((b) => b.schemaValid);\n const verdicts = new Map<string, Json>();\n if (toVerify.length > 0) {\n try {\n const { results } = await callClaude(VERIFY_SYSTEM,\n verifyUserPrompt(toVerify.map((b) => ({ ability_id: b.it.ability_id, rule: b.it.src?.description ?? \"\", effect: b.entry.effect, scope: b.entry.scope }))),\n VERIFY_SCHEMA, opts.model);\n for (const v of results) verdicts.set(v.ability_id, v);\n } catch (e) {\n // Verify failure → leave verdicts null (proposal kept, just not auto-gateable).\n process.stderr.write(` ${faction}: verify batch failed (${(e as Error).message.slice(0, 80)})\\n`);\n }\n }\n\n for (const b of built) {\n const verdict = verdicts.get(b.it.ability_id) ?? null;\n proposals.push({\n ability_id: b.it.ability_id, name: b.it.name, faction,\n effect_type: b.form.effect_type, complex: b.form.complex, confidence: b.form.confidence,\n schema_valid: b.schemaValid, proposed_effect: b.entry.effect, proposed_scope: b.entry.scope,\n verdict, final_faithful: !!verdict?.faithful && b.schemaValid,\n });\n }\n process.stderr.write(` ${faction}: ${proposals.length}/${input.length}\\n`);\n }\n\n mkdirSync(OUT_DIR, { recursive: true });\n writeJSON(resolve(OUT_DIR, `${faction}.json`), proposals);\n return {\n faction, total: proposals.length,\n schema_valid: proposals.filter((p) => p.schema_valid).length,\n faithful: proposals.filter((p) => p.final_faithful).length,\n gateable: proposals.filter((p) => passesGate(p, { minConfidence: \"medium\", includeComplex: false })).length,\n };\n}\n\n// ─── repair (full-tree pass over the residue) ────────────────────────\n\ninterface RepairOpts { batch: number; model: string; types?: Set<string> }\n\n/**\n * Re-author the complex residue in proposed/<faction>.json as full nested DSL.\n * Reuses the propose pipeline shape — classify(→repair)/assemble/AJV/verify —\n * but the model emits the whole effect tree and we seed it with the existing\n * draft + the verifier's gap. Updates the proposals in place (matched by\n * ability_id), tagging each `repaired:true`; the already-gateable and the errored\n * entries are left untouched.\n */\nasync function repairFaction(faction: string, opts: RepairOpts, validate: (x: unknown) => boolean): Promise<Json> {\n const proposalsPath = resolve(OUT_DIR, `${faction}.json`);\n if (!existsSync(proposalsPath)) return { faction, skipped: \"no proposals — run propose first\" };\n const proposals: Proposal[] = readJSON(proposalsPath);\n const gate = { minConfidence: \"medium\" as const, includeComplex: false };\n\n // Residue = not already auto-appliable, not a hard error. Phase A narrows to\n // the cruncher-relevant leaf types via --types.\n const targets = proposals\n .map((p, idx) => ({ p, idx }))\n .filter(({ p }) => !passesGate(p, gate) && !p.error)\n .filter(({ p }) => !opts.types || (p.effect_type != null && opts.types.has(p.effect_type)));\n if (targets.length === 0) return { faction, skipped: \"no matching residue\" };\n\n // Source rules (the gap the draft must close) + originals (metadata + AJV).\n const inputPath = resolve(INPUT_DIR, `${faction}.json`);\n const srcById = new Map<string, string>();\n if (existsSync(inputPath)) for (const e of readJSON(inputPath) as Json[]) if (e.src?.description) srcById.set(e.ability_id, e.src.description);\n const original = new Map<string, Json>();\n for (const a of readJSON(resolve(ENRICHMENT_ROOT, faction, \"abilities.json\")) as Json[]) original.set(a.ability_id, a);\n\n let done = 0;\n for (const batch of chunk(targets, opts.batch)) {\n let results: Json[];\n try {\n ({ results } = await callClaude(REPAIR_SYSTEM,\n repairUserPrompt(batch.map(({ p }) => ({ ability_id: p.ability_id, rule: srcById.get(p.ability_id) ?? \"\", draft: p.proposed_effect, issue: p.verdict?.issue ?? \"\" }))),\n REPAIR_SCHEMA, opts.model));\n } catch (e) {\n process.stderr.write(` ${faction}: repair batch failed (${(e as Error).message.slice(0, 80)}) — skipping ${batch.length}\\n`);\n continue;\n }\n const byId = new Map<string, Json>(results.map((r: Json) => [r.ability_id, r]));\n\n const built: { idx: number; p: Proposal; r: Json; entry: Json; schemaValid: boolean; canonical: boolean }[] = [];\n for (const { p, idx } of batch) {\n const r = byId.get(p.ability_id);\n const orig = original.get(p.ability_id);\n if (!r || !orig) continue; // model dropped it / live entry gone — leave the proposal as-is\n const entry = buildRepairedEntry(orig, r.effect, r.scope, r.behavior);\n built.push({ idx, p, r, entry, schemaValid: validate(entry), canonical: lintCanonical(r.effect).canonical });\n }\n\n // Verify only what can still pass the gate — a non-canonical entry can't, so\n // don't spend a verify call on it.\n const toVerify = built.filter((b) => b.schemaValid && b.canonical);\n const verdicts = new Map<string, Json>();\n if (toVerify.length > 0) {\n try {\n const { results: vs } = await callClaude(VERIFY_SYSTEM,\n verifyUserPrompt(toVerify.map((b) => ({ ability_id: b.p.ability_id, rule: srcById.get(b.p.ability_id) ?? \"\", effect: b.entry.effect, scope: b.entry.scope }))),\n VERIFY_SCHEMA, opts.model);\n for (const v of vs) verdicts.set(v.ability_id, v);\n } catch (e) {\n process.stderr.write(` ${faction}: repair-verify batch failed (${(e as Error).message.slice(0, 80)})\\n`);\n }\n }\n\n for (const b of built) {\n const verdict = verdicts.get(b.p.ability_id) ?? null;\n const behavior = b.r.behavior && BEHAVIOR_VALUES.has(b.r.behavior) ? b.r.behavior : undefined;\n proposals[b.idx] = {\n ...b.p,\n confidence: b.r.confidence ?? b.p.confidence,\n schema_valid: b.schemaValid,\n canonical: b.canonical,\n proposed_effect: b.entry.effect,\n proposed_scope: b.entry.scope,\n proposed_behavior: behavior,\n verdict,\n final_faithful: !!verdict?.faithful && b.schemaValid && b.canonical,\n repaired: true,\n unencodable: !!b.r.unencodable,\n };\n }\n done += batch.length;\n process.stderr.write(` ${faction}: repaired ${done}/${targets.length}\\n`);\n }\n\n writeJSON(proposalsPath, proposals);\n const repaired = proposals.filter((p) => p.repaired);\n return {\n faction, attempted: targets.length,\n now_faithful: repaired.filter((p) => p.final_faithful).length,\n non_canonical: repaired.filter((p) => p.canonical === false).length,\n unencodable: repaired.filter((p) => p.unencodable).length,\n gateable: proposals.filter((p) => passesGate(p, gate)).length,\n };\n}\n\n// ─── apply ───────────────────────────────────────────────────────────\n\ninterface ApplyOpts extends GateOpts { dryRun: boolean }\n\n/** Splice gated proposals into the live abilities.json — only over surviving stubs. */\nfunction applyFaction(faction: string, opts: ApplyOpts): Json {\n const proposalsPath = resolve(OUT_DIR, `${faction}.json`);\n if (!existsSync(proposalsPath)) return { faction, skipped: \"no proposals — run propose first\" };\n const proposals: Proposal[] = readJSON(proposalsPath);\n const abilitiesPath = resolve(ENRICHMENT_ROOT, faction, \"abilities.json\");\n if (!existsSync(abilitiesPath)) return { faction, skipped: \"no live abilities.json\" };\n const abilities: Json[] = readJSON(abilitiesPath);\n const byId = new Map<string, Json>(abilities.map((a) => [a.ability_id, a]));\n\n let applied = 0;\n const skipped: { id: string; why: string }[] = [];\n for (const p of proposals) {\n if (!passesGate(p, opts)) { skipped.push({ id: p.ability_id, why: \"gate\" }); continue; }\n const entry = byId.get(p.ability_id);\n if (!entry) { skipped.push({ id: p.ability_id, why: \"gone\" }); continue; }\n // Never clobber work that's no longer a stub (idempotent + safe to re-run).\n if (!hasEmptyModifier(entry.effect)) { skipped.push({ id: p.ability_id, why: \"not-a-stub\" }); continue; }\n entry.effect = p.proposed_effect;\n entry.scope = p.proposed_scope;\n if (p.proposed_behavior && BEHAVIOR_VALUES.has(p.proposed_behavior)) entry.behavior = p.proposed_behavior;\n entry.community_notes = \"community-authored from 10e source (provisional 11e); see #21\";\n applied++;\n }\n if (!opts.dryRun && applied > 0) writeJSON(abilitiesPath, abilities);\n return { faction, applied, skipped_gate: skipped.filter((s) => s.why === \"gate\").length, skipped_other: skipped.filter((s) => s.why !== \"gate\").length, dry_run: opts.dryRun };\n}\n\n// ─── review (cluster proposals into shape-families) ──────────────────\n\n/** Load every faction's proposals (skips the ad-hoc damage-batch scratch file). */\nfunction loadAllProposals(): Proposal[] {\n if (!existsSync(OUT_DIR)) return [];\n return readdirSync(OUT_DIR)\n .filter((f) => f.endsWith(\".json\") && f !== \"damage-batch.json\")\n .flatMap((f) => readJSON(resolve(OUT_DIR, f)) as Proposal[]);\n}\n\n/** Write a shape-family clustered REVIEW.md — gateable vs the complex residue, grouped for templating. */\nfunction review(): Json {\n const all = loadAllProposals();\n const gate = { minConfidence: \"medium\" as const, includeComplex: false };\n const gateable = all.filter((p) => passesGate(p, gate));\n const residue = all.filter((p) => !passesGate(p, gate) && !p.error);\n\n const byType = (ps: Proposal[]): [string, number][] =>\n Object.entries(ps.reduce<Record<string, number>>((m, p) => ((m[p.effect_type ?? \"?\"] = (m[p.effect_type ?? \"?\"] ?? 0) + 1), m), {})).sort((a, b) => b[1] - a[1]);\n\n // Cross-faction name dupes among the residue — author once, fan to all members.\n const byName = new Map<string, Proposal[]>();\n for (const p of residue) (byName.get(p.name) ?? byName.set(p.name, []).get(p.name)!).push(p);\n const shared = [...byName.entries()].filter(([, ps]) => new Set(ps.map((p) => p.faction)).size > 1)\n .map(([name, ps]) => ({ name, type: ps[0].effect_type, factions: [...new Set(ps.map((p) => p.faction))] }))\n .sort((a, b) => b.factions.length - a.factions.length);\n\n const L: string[] = [\n \"# DSL stub authoring — review\",\n \"\",\n `Generated by \\`author-batch review\\`. ${all.length} proposals across ${new Set(all.map((p) => p.faction)).size} factions.`,\n \"\",\n `- **schema-valid:** ${all.filter((p) => p.schema_valid).length}`,\n `- **verifier-faithful:** ${all.filter((p) => p.final_faithful).length}`,\n `- **complex-flagged:** ${all.filter((p) => p.complex).length}`,\n `- **auto-appliable (gate: valid+faithful+conf≠low+not-complex):** ${gateable.length}`,\n \"\",\n \"## Auto-appliable now — by faction\",\n \"\",\n ...Object.entries(gateable.reduce<Record<string, number>>((m, p) => ((m[p.faction] = (m[p.faction] ?? 0) + 1), m), {})).sort().map(([f, n]) => `- \\`${f}\\`: ${n}`),\n \"\",\n \"Apply with: `npm run author:apply -- <faction|--all> --dry-run` then drop `--dry-run`.\",\n \"\",\n \"## Complex residue — by shape family (author a template per family)\",\n \"\",\n ...byType(residue).map(([t, n]) => `- **${t}** — ${n}`),\n \"\",\n \"## Cross-faction shared shapes (author once, fan to all members)\",\n \"\",\n ...(shared.length ? shared.map((s) => `- **${s.name}** (\\`${s.type}\\`) → ${s.factions.join(\", \")}`) : [\"_(none found)_\"]),\n \"\",\n ];\n writeFileSync(resolve(OUT_DIR, \"REVIEW.md\"), L.join(\"\\n\") + \"\\n\");\n return { total: all.length, gateable: gateable.length, residue: residue.length, shared_shapes: shared.length };\n}\n\n// ─── main ────────────────────────────────────────────────────────────\n\nfunction factionList(arg: string, dir: string): string[] {\n return arg === \"--all\"\n ? readdirSync(dir).filter((f) => f.endsWith(\".json\") && f !== \"damage-batch.json\").map((f) => f.replace(/\\.json$/, \"\")).sort()\n : [arg];\n}\nconst flag = (argv: string[], name: string): string | undefined => (argv.includes(name) ? argv[argv.indexOf(name) + 1] : undefined);\n\nasync function main(): Promise<void> {\n const argv = process.argv.slice(2);\n const mode = argv[0];\n const target = argv[1];\n if (mode === \"review\") {\n console.log(JSON.stringify(review(), null, 2));\n console.error(`\\nWrote ${resolve(OUT_DIR, \"REVIEW.md\")}`);\n return;\n }\n if (![\"propose\", \"repair\", \"apply\"].includes(mode) || !target) {\n console.error(\"Usage:\\n author-batch propose <faction|--all> [--batch N] [--model M]\\n author-batch repair <faction|--all> [--types t1,t2] [--batch N] [--model M]\\n author-batch apply <faction|--all> [--min-confidence high|medium] [--include-complex] [--dry-run]\\n author-batch review\");\n process.exit(1);\n }\n\n if (mode === \"propose\") {\n const ajv = createValidator();\n const validateFn = ajv.getSchema(ABILITY_SCHEMA_ID);\n if (!validateFn) throw new Error(`ability schema not loaded: ${ABILITY_SCHEMA_ID}`);\n const validate = (x: unknown): boolean => !!validateFn(x);\n const opts: ProposeOpts = { batch: Number(flag(argv, \"--batch\")) || 15, model: flag(argv, \"--model\") ?? \"claude-haiku-4-5\" };\n const summary: Json[] = [];\n for (const f of factionList(target, INPUT_DIR)) {\n try {\n summary.push(await proposeFaction(f, opts, validate));\n } catch (e) {\n process.stderr.write(` ${f}: FAILED (${(e as Error).message.slice(0, 100)})\\n`);\n summary.push({ faction: f, error: (e as Error).message });\n }\n }\n console.log(JSON.stringify(summary, null, 2));\n return;\n }\n\n if (mode === \"repair\") {\n const ajv = createValidator();\n const validateFn = ajv.getSchema(ABILITY_SCHEMA_ID);\n if (!validateFn) throw new Error(`ability schema not loaded: ${ABILITY_SCHEMA_ID}`);\n const validate = (x: unknown): boolean => !!validateFn(x);\n const typesArg = flag(argv, \"--types\");\n const opts: RepairOpts = {\n batch: Number(flag(argv, \"--batch\")) || 8,\n model: flag(argv, \"--model\") ?? \"claude-sonnet-4-6\",\n types: typesArg ? new Set(typesArg.split(\",\").map((t) => t.trim()).filter(Boolean)) : undefined,\n };\n const summary: Json[] = [];\n for (const f of factionList(target, OUT_DIR)) {\n try {\n summary.push(await repairFaction(f, opts, validate));\n } catch (e) {\n process.stderr.write(` ${f}: FAILED (${(e as Error).message.slice(0, 100)})\\n`);\n summary.push({ faction: f, error: (e as Error).message });\n }\n }\n console.log(JSON.stringify(summary, null, 2));\n const unenc = summary.reduce((n, s) => n + (s.unencodable ?? 0), 0);\n const faithful = summary.reduce((n, s) => n + (s.now_faithful ?? 0), 0);\n const noncanon = summary.reduce((n, s) => n + (s.non_canonical ?? 0), 0);\n console.error(`\\nrepair: ${faithful} now faithful, ${noncanon} non-canonical (gate-blocked), ${unenc} flagged unencodable (need hand-authoring).`);\n return;\n }\n\n // apply\n const opts: ApplyOpts = {\n minConfidence: flag(argv, \"--min-confidence\") === \"high\" ? \"high\" : \"medium\",\n includeComplex: argv.includes(\"--include-complex\"),\n dryRun: argv.includes(\"--dry-run\"),\n };\n const summary = factionList(target, OUT_DIR).map((f) => applyFaction(f, opts));\n console.log(JSON.stringify(summary, null, 2));\n const total = summary.reduce((n, s) => n + (s.applied ?? 0), 0);\n console.error(`\\n${opts.dryRun ? \"[dry-run] would apply\" : \"applied\"} ${total} entr${total === 1 ? \"y\" : \"ies\"}.`);\n}\n\nconst isMain =\n process.argv[1] &&\n resolve(process.argv[1]).replace(/\\.\\w+$/, \"\") === fileURLToPath(import.meta.url).replace(/\\.\\w+$/, \"\");\nif (isMain) main().catch((e) => { console.error(e); process.exit(1); });\n"]}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
type Json = any;
|
|
2
|
+
export interface SourceRule {
|
|
3
|
+
datasheet_id: string;
|
|
4
|
+
src_type: string | null;
|
|
5
|
+
parameter: string | null;
|
|
6
|
+
phases: string[] | null;
|
|
7
|
+
description: string;
|
|
8
|
+
}
|
|
9
|
+
export interface AuthorInputEntry {
|
|
10
|
+
faction: string;
|
|
11
|
+
ability_id: string;
|
|
12
|
+
name: string;
|
|
13
|
+
unit_ids: string[];
|
|
14
|
+
target: string | null;
|
|
15
|
+
scope: Json;
|
|
16
|
+
faction_id: string | null;
|
|
17
|
+
ability_type: string | null;
|
|
18
|
+
resolved: boolean;
|
|
19
|
+
/** Why the source rule couldn't be resolved (when `resolved` is false). */
|
|
20
|
+
reason?: string;
|
|
21
|
+
src?: SourceRule;
|
|
22
|
+
}
|
|
23
|
+
interface ArchiveIndex {
|
|
24
|
+
/** kebab faction id → archive faction code (e.g. "adepta-sororitas" → "AS"). */
|
|
25
|
+
factionCode: (kebab: string) => string | undefined;
|
|
26
|
+
/** `${unitName}|${factionCode}` (lower) → datasheet ids. */
|
|
27
|
+
datasheetsFor: (unitName: string, code: string) => string[];
|
|
28
|
+
/** datasheet_id → (abilityName lower → rule). */
|
|
29
|
+
ruleFor: (datasheetId: string, abilityName: string) => Omit<SourceRule, "datasheet_id"> | undefined;
|
|
30
|
+
}
|
|
31
|
+
/** Resolve the source rule for one stub via the unit→datasheet→ability chain. */
|
|
32
|
+
export declare function resolveSource(archive: ArchiveIndex, factionCode: string | undefined, unitNames: string[], abilityName: string): {
|
|
33
|
+
src?: SourceRule;
|
|
34
|
+
reason?: string;
|
|
35
|
+
};
|
|
36
|
+
export {};
|
|
37
|
+
//# sourceMappingURL=author-input.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"author-input.d.ts","sourceRoot":"","sources":["../src/author-input.ts"],"names":[],"mappings":"AAoCA,KAAK,IAAI,GAAG,GAAG,CAAC;AAKhB,MAAM,WAAW,UAAU;IACzB,YAAY,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,MAAM,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;IACxB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,IAAI,CAAC;IACZ,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,2EAA2E;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,GAAG,CAAC,EAAE,UAAU,CAAC;CAClB;AAED,UAAU,YAAY;IACpB,gFAAgF;IAChF,WAAW,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IACnD,4DAA4D;IAC5D,aAAa,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,KAAK,MAAM,EAAE,CAAC;IAC5D,iDAAiD;IACjD,OAAO,EAAE,CAAC,WAAW,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,UAAU,EAAE,cAAc,CAAC,GAAG,SAAS,CAAC;CACrG;AA+CD,iFAAiF;AACjF,wBAAgB,aAAa,CAC3B,OAAO,EAAE,YAAY,EACrB,WAAW,EAAE,MAAM,GAAG,SAAS,EAC/B,SAAS,EAAE,MAAM,EAAE,EACnB,WAAW,EAAE,MAAM,GAClB;IAAE,GAAG,CAAC,EAAE,UAAU,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAA;CAAE,CAavC"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Build the authoring input for the DSL stub fan-out (#21): join each
|
|
3
|
+
* empty-modifier stub to its *correct* source rule in the 10e archive.
|
|
4
|
+
*
|
|
5
|
+
* The naive `ability.name → Datasheets_abilities.name` join is unsafe — ability
|
|
6
|
+
* names collide across datasheets and factions (e.g. "Simulacrum Imperialis"
|
|
7
|
+
* exists on a Sororitas *and* an Agents-of-the-Imperium "Sanctifiers" datasheet
|
|
8
|
+
* with different rules). We disambiguate by chaining through the unit that
|
|
9
|
+
* carries the ability and the faction it belongs to:
|
|
10
|
+
*
|
|
11
|
+
* ability.unit_ids → core unit.name → archive Datasheet (name + faction code)
|
|
12
|
+
* → datasheet_id → Datasheets_abilities (datasheet_id + ability name)
|
|
13
|
+
*
|
|
14
|
+
* Output: data/_audit/author-input/<faction>.json — one entry per stub with the
|
|
15
|
+
* resolved source rule (or `resolved:false` + a reason when the chain breaks),
|
|
16
|
+
* ready to feed the classify→assemble→verify workflow.
|
|
17
|
+
*
|
|
18
|
+
* The archive lives outside the repo; point `ARMY_ASSIST_JSON` at it or rely on
|
|
19
|
+
* the default `~/army-assist/src/assets/json`.
|
|
20
|
+
*
|
|
21
|
+
* Usage: npx tsx tools/src/author-input.ts [faction|--all]
|
|
22
|
+
*/
|
|
23
|
+
import { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync } from "node:fs";
|
|
24
|
+
import { resolve } from "node:path";
|
|
25
|
+
import { homedir } from "node:os";
|
|
26
|
+
import { fileURLToPath } from "node:url";
|
|
27
|
+
import { hasEmptyModifier } from "./audit-coverage.js";
|
|
28
|
+
const __dirname = fileURLToPath(new URL(".", import.meta.url));
|
|
29
|
+
const DATA_ROOT = resolve(__dirname, "../../data");
|
|
30
|
+
const ENRICHMENT_ROOT = resolve(DATA_ROOT, "enrichment");
|
|
31
|
+
const CORE_ROOT = resolve(DATA_ROOT, "core");
|
|
32
|
+
const OUT_DIR = resolve(DATA_ROOT, "_audit", "author-input");
|
|
33
|
+
const ARCHIVE = process.env.ARMY_ASSIST_JSON ?? resolve(homedir(), "army-assist/src/assets/json");
|
|
34
|
+
const readJSON = (p) => JSON.parse(readFileSync(p, "utf-8"));
|
|
35
|
+
const norm = (s) => s.toLowerCase().trim();
|
|
36
|
+
/**
|
|
37
|
+
* Kebab faction id → archive code, for the cases name-normalization can't bridge
|
|
38
|
+
* (apostrophes the kebab drops, or an archive name that uses different words).
|
|
39
|
+
*/
|
|
40
|
+
const FACTION_CODE_ALIAS = {
|
|
41
|
+
"emperors-children": "EC", // archive "Emperor's Children"
|
|
42
|
+
"tau-empire": "TAU", // archive "T'au Empire"
|
|
43
|
+
"leagues-of-votann": "LoV", // archive "Votann"
|
|
44
|
+
};
|
|
45
|
+
function loadArchive() {
|
|
46
|
+
const factions = readJSON(resolve(ARCHIVE, "Factions.json"));
|
|
47
|
+
const codeByName = new Map();
|
|
48
|
+
for (const f of factions)
|
|
49
|
+
codeByName.set(norm(f.name), f.id);
|
|
50
|
+
const datasheets = readJSON(resolve(ARCHIVE, "Datasheets.json"));
|
|
51
|
+
const dsByNameCode = new Map();
|
|
52
|
+
for (const d of datasheets) {
|
|
53
|
+
const key = `${norm(d.name)}|${d.faction_id}`;
|
|
54
|
+
(dsByNameCode.get(key) ?? dsByNameCode.set(key, []).get(key)).push(d.id);
|
|
55
|
+
}
|
|
56
|
+
const dsAbilities = readJSON(resolve(ARCHIVE, "Datasheets_abilities.json"));
|
|
57
|
+
const byDatasheet = new Map();
|
|
58
|
+
for (const a of dsAbilities) {
|
|
59
|
+
if (!a.datasheet_id || !a.name)
|
|
60
|
+
continue;
|
|
61
|
+
let m = byDatasheet.get(a.datasheet_id);
|
|
62
|
+
if (!m)
|
|
63
|
+
byDatasheet.set(a.datasheet_id, (m = new Map()));
|
|
64
|
+
if (!m.has(norm(a.name))) {
|
|
65
|
+
m.set(norm(a.name), {
|
|
66
|
+
src_type: a.type ?? null,
|
|
67
|
+
parameter: a.parameter ?? null,
|
|
68
|
+
phases: a.phases ?? null,
|
|
69
|
+
description: a.description ?? "",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
return {
|
|
74
|
+
factionCode: (kebab) => FACTION_CODE_ALIAS[kebab] ?? codeByName.get(kebab.replace(/-/g, " ")),
|
|
75
|
+
datasheetsFor: (unitName, code) => dsByNameCode.get(`${norm(unitName)}|${code}`) ?? [],
|
|
76
|
+
ruleFor: (dsId, abilityName) => byDatasheet.get(dsId)?.get(norm(abilityName)),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/** Resolve the source rule for one stub via the unit→datasheet→ability chain. */
|
|
80
|
+
export function resolveSource(archive, factionCode, unitNames, abilityName) {
|
|
81
|
+
if (!factionCode)
|
|
82
|
+
return { reason: "no archive faction code for this faction" };
|
|
83
|
+
if (unitNames.length === 0)
|
|
84
|
+
return { reason: "ability has no unit_ids (faction/detachment scope) — datasheet join needs a unit" };
|
|
85
|
+
const tried = [];
|
|
86
|
+
for (const unitName of unitNames) {
|
|
87
|
+
const dsIds = archive.datasheetsFor(unitName, factionCode);
|
|
88
|
+
if (dsIds.length === 0)
|
|
89
|
+
tried.push(`${unitName}:no-datasheet`);
|
|
90
|
+
for (const dsId of dsIds) {
|
|
91
|
+
const rule = archive.ruleFor(dsId, abilityName);
|
|
92
|
+
if (rule)
|
|
93
|
+
return { src: { datasheet_id: dsId, ...rule } };
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return { reason: `no matching ability on faction datasheets (tried: ${tried.join(", ") || unitNames.join(", ")})` };
|
|
97
|
+
}
|
|
98
|
+
function buildFaction(faction, archive) {
|
|
99
|
+
const code = archive.factionCode(faction);
|
|
100
|
+
const unitsPath = resolve(CORE_ROOT, faction, "units.json");
|
|
101
|
+
const unitName = new Map();
|
|
102
|
+
if (existsSync(unitsPath))
|
|
103
|
+
for (const u of readJSON(unitsPath))
|
|
104
|
+
unitName.set(u.id, u.name);
|
|
105
|
+
const abilitiesPath = resolve(ENRICHMENT_ROOT, faction, "abilities.json");
|
|
106
|
+
if (!existsSync(abilitiesPath))
|
|
107
|
+
return [];
|
|
108
|
+
const abilities = readJSON(abilitiesPath);
|
|
109
|
+
const out = [];
|
|
110
|
+
for (const a of abilities) {
|
|
111
|
+
if (!hasEmptyModifier(a.effect))
|
|
112
|
+
continue;
|
|
113
|
+
const unitIds = a.unit_ids ?? [];
|
|
114
|
+
const unitNames = unitIds.map((id) => unitName.get(id)).filter((n) => !!n);
|
|
115
|
+
const { src, reason } = resolveSource(archive, code, unitNames, a.name);
|
|
116
|
+
out.push({
|
|
117
|
+
faction,
|
|
118
|
+
ability_id: a.ability_id,
|
|
119
|
+
name: a.name,
|
|
120
|
+
unit_ids: unitIds,
|
|
121
|
+
target: (a.effect && typeof a.effect === "object" && a.effect.target) || null,
|
|
122
|
+
scope: a.scope ?? null,
|
|
123
|
+
faction_id: a.faction_id ?? null,
|
|
124
|
+
ability_type: a.ability_type ?? null,
|
|
125
|
+
resolved: !!src,
|
|
126
|
+
...(src ? { src } : { reason }),
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
return out;
|
|
130
|
+
}
|
|
131
|
+
function main() {
|
|
132
|
+
const arg = process.argv[2];
|
|
133
|
+
if (!arg) {
|
|
134
|
+
console.error("Usage: npx tsx tools/src/author-input.ts <faction|--all>");
|
|
135
|
+
process.exit(1);
|
|
136
|
+
}
|
|
137
|
+
const archive = loadArchive();
|
|
138
|
+
const factions = arg === "--all"
|
|
139
|
+
? readdirSync(ENRICHMENT_ROOT, { withFileTypes: true })
|
|
140
|
+
.filter((e) => e.isDirectory() && e.name !== "_example")
|
|
141
|
+
.map((e) => e.name)
|
|
142
|
+
: [arg];
|
|
143
|
+
mkdirSync(OUT_DIR, { recursive: true });
|
|
144
|
+
let totalStubs = 0;
|
|
145
|
+
let totalResolved = 0;
|
|
146
|
+
for (const faction of factions) {
|
|
147
|
+
const entries = buildFaction(faction, archive);
|
|
148
|
+
if (entries.length === 0)
|
|
149
|
+
continue;
|
|
150
|
+
const resolved = entries.filter((e) => e.resolved).length;
|
|
151
|
+
totalStubs += entries.length;
|
|
152
|
+
totalResolved += resolved;
|
|
153
|
+
writeFileSync(resolve(OUT_DIR, `${faction}.json`), JSON.stringify(entries, null, 2) + "\n");
|
|
154
|
+
console.log(` ${faction}: ${entries.length} stubs, ${resolved} source-resolved`);
|
|
155
|
+
}
|
|
156
|
+
console.log(`\n${totalStubs} stubs total, ${totalResolved} resolved (${Math.round((100 * totalResolved) / Math.max(1, totalStubs))}%). → ${OUT_DIR}`);
|
|
157
|
+
}
|
|
158
|
+
const isMain = process.argv[1] &&
|
|
159
|
+
resolve(process.argv[1]).replace(/\.\w+$/, "") === fileURLToPath(import.meta.url).replace(/\.\w+$/, "");
|
|
160
|
+
if (isMain)
|
|
161
|
+
main();
|
|
162
|
+
//# sourceMappingURL=author-input.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"author-input.js","sourceRoot":"","sources":["../src/author-input.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Build the authoring input for the DSL stub fan-out (#21): join each\n * empty-modifier stub to its *correct* source rule in the 10e archive.\n *\n * The naive `ability.name → Datasheets_abilities.name` join is unsafe — ability\n * names collide across datasheets and factions (e.g. \"Simulacrum Imperialis\"\n * exists on a Sororitas *and* an Agents-of-the-Imperium \"Sanctifiers\" datasheet\n * with different rules). We disambiguate by chaining through the unit that\n * carries the ability and the faction it belongs to:\n *\n * ability.unit_ids → core unit.name → archive Datasheet (name + faction code)\n * → datasheet_id → Datasheets_abilities (datasheet_id + ability name)\n *\n * Output: data/_audit/author-input/<faction>.json — one entry per stub with the\n * resolved source rule (or `resolved:false` + a reason when the chain breaks),\n * ready to feed the classify→assemble→verify workflow.\n *\n * The archive lives outside the repo; point `ARMY_ASSIST_JSON` at it or rely on\n * the default `~/army-assist/src/assets/json`.\n *\n * Usage: npx tsx tools/src/author-input.ts [faction|--all]\n */\nimport { readdirSync, readFileSync, writeFileSync, mkdirSync, existsSync } from \"node:fs\";\nimport { resolve } from \"node:path\";\nimport { homedir } from \"node:os\";\nimport { fileURLToPath } from \"node:url\";\nimport { hasEmptyModifier } from \"./audit-coverage.js\";\n\nconst __dirname = fileURLToPath(new URL(\".\", import.meta.url));\nconst DATA_ROOT = resolve(__dirname, \"../../data\");\nconst ENRICHMENT_ROOT = resolve(DATA_ROOT, \"enrichment\");\nconst CORE_ROOT = resolve(DATA_ROOT, \"core\");\nconst OUT_DIR = resolve(DATA_ROOT, \"_audit\", \"author-input\");\nconst ARCHIVE = process.env.ARMY_ASSIST_JSON ?? resolve(homedir(), \"army-assist/src/assets/json\");\n\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\ntype Json = any;\n\nconst readJSON = (p: string): Json => JSON.parse(readFileSync(p, \"utf-8\"));\nconst norm = (s: string): string => s.toLowerCase().trim();\n\nexport interface SourceRule {\n datasheet_id: string;\n src_type: string | null;\n parameter: string | null;\n phases: string[] | null;\n description: string;\n}\n\nexport interface AuthorInputEntry {\n faction: string;\n ability_id: string;\n name: string;\n unit_ids: string[];\n target: string | null;\n scope: Json;\n faction_id: string | null;\n ability_type: string | null;\n resolved: boolean;\n /** Why the source rule couldn't be resolved (when `resolved` is false). */\n reason?: string;\n src?: SourceRule;\n}\n\ninterface ArchiveIndex {\n /** kebab faction id → archive faction code (e.g. \"adepta-sororitas\" → \"AS\"). */\n factionCode: (kebab: string) => string | undefined;\n /** `${unitName}|${factionCode}` (lower) → datasheet ids. */\n datasheetsFor: (unitName: string, code: string) => string[];\n /** datasheet_id → (abilityName lower → rule). */\n ruleFor: (datasheetId: string, abilityName: string) => Omit<SourceRule, \"datasheet_id\"> | undefined;\n}\n\n/**\n * Kebab faction id → archive code, for the cases name-normalization can't bridge\n * (apostrophes the kebab drops, or an archive name that uses different words).\n */\nconst FACTION_CODE_ALIAS: Record<string, string> = {\n \"emperors-children\": \"EC\", // archive \"Emperor's Children\"\n \"tau-empire\": \"TAU\", // archive \"T'au Empire\"\n \"leagues-of-votann\": \"LoV\", // archive \"Votann\"\n};\n\nfunction loadArchive(): ArchiveIndex {\n const factions: Json[] = readJSON(resolve(ARCHIVE, \"Factions.json\"));\n const codeByName = new Map<string, string>();\n for (const f of factions) codeByName.set(norm(f.name), f.id);\n\n const datasheets: Json[] = readJSON(resolve(ARCHIVE, \"Datasheets.json\"));\n const dsByNameCode = new Map<string, string[]>();\n for (const d of datasheets) {\n const key = `${norm(d.name)}|${d.faction_id}`;\n (dsByNameCode.get(key) ?? dsByNameCode.set(key, []).get(key)!).push(d.id);\n }\n\n const dsAbilities: Json[] = readJSON(resolve(ARCHIVE, \"Datasheets_abilities.json\"));\n const byDatasheet = new Map<string, Map<string, Omit<SourceRule, \"datasheet_id\">>>();\n for (const a of dsAbilities) {\n if (!a.datasheet_id || !a.name) continue;\n let m = byDatasheet.get(a.datasheet_id);\n if (!m) byDatasheet.set(a.datasheet_id, (m = new Map()));\n if (!m.has(norm(a.name))) {\n m.set(norm(a.name), {\n src_type: a.type ?? null,\n parameter: a.parameter ?? null,\n phases: a.phases ?? null,\n description: a.description ?? \"\",\n });\n }\n }\n\n return {\n factionCode: (kebab) => FACTION_CODE_ALIAS[kebab] ?? codeByName.get(kebab.replace(/-/g, \" \")),\n datasheetsFor: (unitName, code) => dsByNameCode.get(`${norm(unitName)}|${code}`) ?? [],\n ruleFor: (dsId, abilityName) => byDatasheet.get(dsId)?.get(norm(abilityName)),\n };\n}\n\n/** Resolve the source rule for one stub via the unit→datasheet→ability chain. */\nexport function resolveSource(\n archive: ArchiveIndex,\n factionCode: string | undefined,\n unitNames: string[],\n abilityName: string,\n): { src?: SourceRule; reason?: string } {\n if (!factionCode) return { reason: \"no archive faction code for this faction\" };\n if (unitNames.length === 0) return { reason: \"ability has no unit_ids (faction/detachment scope) — datasheet join needs a unit\" };\n const tried: string[] = [];\n for (const unitName of unitNames) {\n const dsIds = archive.datasheetsFor(unitName, factionCode);\n if (dsIds.length === 0) tried.push(`${unitName}:no-datasheet`);\n for (const dsId of dsIds) {\n const rule = archive.ruleFor(dsId, abilityName);\n if (rule) return { src: { datasheet_id: dsId, ...rule } };\n }\n }\n return { reason: `no matching ability on faction datasheets (tried: ${tried.join(\", \") || unitNames.join(\", \")})` };\n}\n\nfunction buildFaction(faction: string, archive: ArchiveIndex): AuthorInputEntry[] {\n const code = archive.factionCode(faction);\n const unitsPath = resolve(CORE_ROOT, faction, \"units.json\");\n const unitName = new Map<string, string>();\n if (existsSync(unitsPath)) for (const u of readJSON(unitsPath) as Json[]) unitName.set(u.id, u.name);\n\n const abilitiesPath = resolve(ENRICHMENT_ROOT, faction, \"abilities.json\");\n if (!existsSync(abilitiesPath)) return [];\n const abilities: Json[] = readJSON(abilitiesPath);\n\n const out: AuthorInputEntry[] = [];\n for (const a of abilities) {\n if (!hasEmptyModifier(a.effect)) continue;\n const unitIds: string[] = a.unit_ids ?? [];\n const unitNames = unitIds.map((id) => unitName.get(id)).filter((n): n is string => !!n);\n const { src, reason } = resolveSource(archive, code, unitNames, a.name);\n out.push({\n faction,\n ability_id: a.ability_id,\n name: a.name,\n unit_ids: unitIds,\n target: (a.effect && typeof a.effect === \"object\" && a.effect.target) || null,\n scope: a.scope ?? null,\n faction_id: a.faction_id ?? null,\n ability_type: a.ability_type ?? null,\n resolved: !!src,\n ...(src ? { src } : { reason }),\n });\n }\n return out;\n}\n\nfunction main(): void {\n const arg = process.argv[2];\n if (!arg) {\n console.error(\"Usage: npx tsx tools/src/author-input.ts <faction|--all>\");\n process.exit(1);\n }\n const archive = loadArchive();\n const factions =\n arg === \"--all\"\n ? readdirSync(ENRICHMENT_ROOT, { withFileTypes: true })\n .filter((e) => e.isDirectory() && e.name !== \"_example\")\n .map((e) => e.name)\n : [arg];\n\n mkdirSync(OUT_DIR, { recursive: true });\n let totalStubs = 0;\n let totalResolved = 0;\n for (const faction of factions) {\n const entries = buildFaction(faction, archive);\n if (entries.length === 0) continue;\n const resolved = entries.filter((e) => e.resolved).length;\n totalStubs += entries.length;\n totalResolved += resolved;\n writeFileSync(resolve(OUT_DIR, `${faction}.json`), JSON.stringify(entries, null, 2) + \"\\n\");\n console.log(` ${faction}: ${entries.length} stubs, ${resolved} source-resolved`);\n }\n console.log(`\\n${totalStubs} stubs total, ${totalResolved} resolved (${Math.round((100 * totalResolved) / Math.max(1, totalStubs))}%). → ${OUT_DIR}`);\n}\n\nconst isMain =\n process.argv[1] &&\n resolve(process.argv[1]).replace(/\\.\\w+$/, \"\") === fileURLToPath(import.meta.url).replace(/\\.\\w+$/, \"\");\nif (isMain) main();\n"]}
|
package/dist/cli.js
CHANGED
|
@@ -5,6 +5,7 @@ import { validateEnrichmentCommand } from "./commands/validate-enrichment.js";
|
|
|
5
5
|
import { validateAllCommand } from "./commands/validate-all.js";
|
|
6
6
|
import { translateCommand } from "./commands/translate.js";
|
|
7
7
|
import { importCommand } from "./commands/import.js";
|
|
8
|
+
import { auditCoverageCommand } from "./audit-coverage.js";
|
|
8
9
|
const program = new Command();
|
|
9
10
|
program
|
|
10
11
|
.name("40kdc-validate")
|
|
@@ -30,6 +31,12 @@ program
|
|
|
30
31
|
.description("Translate ability DSL to plain English")
|
|
31
32
|
.argument("[path]", "Path to abilities.json file")
|
|
32
33
|
.action(translateCommand);
|
|
34
|
+
program
|
|
35
|
+
.command("audit-coverage")
|
|
36
|
+
.description("Audit how much ability data translates into cruncher buffs, per faction")
|
|
37
|
+
.option("--reporter <mode>", "Output format: pretty or json", "pretty")
|
|
38
|
+
.option("--write", "Also write data/_audit/coverage.json + summary.md", false)
|
|
39
|
+
.action((opts) => auditCoverageCommand({ reporter: opts.reporter, write: opts.write }));
|
|
33
40
|
program
|
|
34
41
|
.command("import")
|
|
35
42
|
.description("Import a ListForge army-list export into a 40kdc roster")
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACpC,OAAO,EAAE,mBAAmB,EAAE,MAAM,6BAA6B,CAAC;AAClE,OAAO,EAAE,yBAAyB,EAAE,MAAM,mCAAmC,CAAC;AAC9E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAE3D,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE,CAAC;AAE9B,OAAO;KACJ,IAAI,CAAC,gBAAgB,CAAC;KACtB,WAAW,CAAC,2CAA2C,CAAC;KACxD,OAAO,CAAC,OAAO,CAAC,CAAC;AAEpB,OAAO;KACJ,OAAO,CAAC,eAAe,CAAC;KACxB,WAAW,CAAC,0BAA0B,CAAC;KACvC,MAAM,CAAC,mBAAmB,EAAE,+BAA+B,EAAE,QAAQ,CAAC;KACtE,MAAM,CAAC,mBAAmB,CAAC,CAAC;AAE/B,OAAO;KACJ,OAAO,CAAC,qBAAqB,CAAC;KAC9B,WAAW,CAAC,gCAAgC,CAAC;KAC7C,MAAM,CAAC,mBAAmB,EAAE,+BAA+B,EAAE,QAAQ,CAAC;KACtE,MAAM,CAAC,yBAAyB,CAAC,CAAC;AAErC,OAAO;KACJ,OAAO,CAAC,cAAc,CAAC;KACvB,WAAW,CAAC,yBAAyB,CAAC;KACtC,MAAM,CAAC,mBAAmB,EAAE,+BAA+B,EAAE,QAAQ,CAAC;KACtE,MAAM,CAAC,kBAAkB,CAAC,CAAC;AAE9B,OAAO;KACJ,OAAO,CAAC,WAAW,CAAC;KACpB,WAAW,CAAC,wCAAwC,CAAC;KACrD,QAAQ,CAAC,QAAQ,EAAE,6BAA6B,CAAC;KACjD,MAAM,CAAC,gBAAgB,CAAC,CAAC;AAE5B,OAAO;KACJ,OAAO,CAAC,gBAAgB,CAAC;KACzB,WAAW,CAAC,yEAAyE,CAAC;KACtF,MAAM,CAAC,mBAAmB,EAAE,+BAA+B,EAAE,QAAQ,CAAC;KACtE,MAAM,CAAC,SAAS,EAAE,mDAAmD,EAAE,KAAK,CAAC;KAC7E,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,oBAAoB,CAAC,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;AAE1F,OAAO;KACJ,OAAO,CAAC,QAAQ,CAAC;KACjB,WAAW,CAAC,yDAAyD,CAAC;KACtE,QAAQ,CAAC,SAAS,EAAE,wEAAwE,CAAC;KAC7F,MAAM,CAAC,mBAAmB,EAAE,+BAA+B,EAAE,MAAM,CAAC;KACpE,MAAM,CAAC,cAAc,EAAE,+CAA+C,CAAC;KACvE,MAAM,CAAC,aAAa,CAAC,CAAC;AAEzB,OAAO,CAAC,KAAK,EAAE,CAAC","sourcesContent":["#!/usr/bin/env node\nimport { Command } from \"commander\";\nimport { validateCoreCommand } from \"./commands/validate-core.js\";\nimport { validateEnrichmentCommand } from \"./commands/validate-enrichment.js\";\nimport { validateAllCommand } from \"./commands/validate-all.js\";\nimport { translateCommand } from \"./commands/translate.js\";\nimport { importCommand } from \"./commands/import.js\";\nimport { auditCoverageCommand } from \"./audit-coverage.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"40kdc-validate\")\n .description(\"Validate 40kdc data files against schemas\")\n .version(\"0.1.0\");\n\nprogram\n .command(\"validate-core\")\n .description(\"Validate core data files\")\n .option(\"--reporter <mode>\", \"Output format: pretty or json\", \"pretty\")\n .action(validateCoreCommand);\n\nprogram\n .command(\"validate-enrichment\")\n .description(\"Validate enrichment data files\")\n .option(\"--reporter <mode>\", \"Output format: pretty or json\", \"pretty\")\n .action(validateEnrichmentCommand);\n\nprogram\n .command(\"validate-all\")\n .description(\"Validate all data files\")\n .option(\"--reporter <mode>\", \"Output format: pretty or json\", \"pretty\")\n .action(validateAllCommand);\n\nprogram\n .command(\"translate\")\n .description(\"Translate ability DSL to plain English\")\n .argument(\"[path]\", \"Path to abilities.json file\")\n .action(translateCommand);\n\nprogram\n .command(\"audit-coverage\")\n .description(\"Audit how much ability data translates into cruncher buffs, per faction\")\n .option(\"--reporter <mode>\", \"Output format: pretty or json\", \"pretty\")\n .option(\"--write\", \"Also write data/_audit/coverage.json + summary.md\", false)\n .action((opts) => auditCoverageCommand({ reporter: opts.reporter, write: opts.write }));\n\nprogram\n .command(\"import\")\n .description(\"Import a ListForge army-list export into a 40kdc roster\")\n .argument(\"[input]\", \"ListForge URL, base64 segment, JSON, or file path (omit/'-' for stdin)\")\n .option(\"--reporter <mode>\", \"Output format: json or pretty\", \"json\")\n .option(\"--out <file>\", \"Write roster JSON to a file instead of stdout\")\n .action(importCommand);\n\nprogram.parse();\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/commands/translate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;
|
|
1
|
+
{"version":3,"file":"translate.d.ts","sourceRoot":"","sources":["../../src/commands/translate.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AA6RH,wBAAsB,gBAAgB,CACpC,IAAI,CAAC,EAAE,MAAM,GACZ,OAAO,CAAC,IAAI,CAAC,CAsBf"}
|
|
@@ -132,10 +132,13 @@ function translateEffectInline(e) {
|
|
|
132
132
|
return `deal ${m.amount ?? m.amount_table ? "variable" : "?"} mortal wounds to ${target}`;
|
|
133
133
|
case "feel-no-pain":
|
|
134
134
|
return `${target} gains Feel No Pain ${m.threshold}+`;
|
|
135
|
-
case "keyword-grant":
|
|
136
|
-
|
|
135
|
+
case "keyword-grant": {
|
|
136
|
+
// Grants come as singular `keyword` or the (dominant) `keywords` array.
|
|
137
|
+
const kw = Array.isArray(m.keywords) ? m.keywords.join(", ") : (m.keyword ?? "keywords");
|
|
138
|
+
return `${target}'s ${m.weapon_type ?? "all"} weapons gain ${kw}`;
|
|
139
|
+
}
|
|
137
140
|
case "ability-grant":
|
|
138
|
-
return `${target} gains ${formatGrantType(m.grant_type)}`;
|
|
141
|
+
return `${target} gains ${formatGrantType((m.grant_type ?? m.ability_id))}`;
|
|
139
142
|
case "movement-modifier":
|
|
140
143
|
return `${target} gains ${m.move_type}${m.value ? ` ${m.value}"` : ""}`;
|
|
141
144
|
case "damage-reduction":
|
|
@@ -191,7 +194,9 @@ function formatTarget(t) {
|
|
|
191
194
|
return t.replace(/-/g, " ");
|
|
192
195
|
}
|
|
193
196
|
function formatGrantType(g) {
|
|
194
|
-
|
|
197
|
+
// ability-grant carries either `grant_type` or `ability_id`; an unauthored
|
|
198
|
+
// stub may carry neither. Don't crash the whole translation on a missing key.
|
|
199
|
+
return g ? g.replace(/-/g, " ") : "an ability";
|
|
195
200
|
}
|
|
196
201
|
function formatComparison(comp, threshold) {
|
|
197
202
|
const thStr = typeof threshold === "string" ? threshold : `${threshold}`;
|