x4-balance
Ground a balance or stat-tuning discussion in measured values before proposing any change. Opens by naming which instrument answers which question and what a zero from each one means, so the numeric t
Files1
View on GitHubSKILL.md.claude/skills/x4-balance/SKILL.md
GitHub<!-- GENERATED by tools/x4validate/scripts/gen-agent-trees.py from agent/. Do not edit: regenerate. -->
Balance work fails in a specific way: a number gets quoted from a file, from a mod page, or from memory, and everything downstream inherits it. The tools that prevent that exist and are the ones least often reached for — so this skill starts by putting them in front of you.
Run tools via uv from the tool dir:
cd $CLAUDE_PROJECT_DIR/tools/x4validate && uv run --python 3.13 <tool> <args>
Step 0 — state the route BEFORE the sweep
Before the first query, write one line: the question · the instrument · what a zero would mean. A balance investigation is a long chain of calls, and a wrong instrument at step 2 is invisible by step 9 because every later number is consistent with it. Stating the route makes a misroute visible in the conversation instead of buried in a tool result.
The instrument card
| the question you are actually asking | reach for | what a ZERO / empty result means |
|---|---|---|
| what does the game currently use for this value? | x4effective show <kind> <id> · x4effective attr <kind> <prop> (that prop across every entity of the kind) |
the entity is not in the effective tree — check the kind, then whether anything supplies it |
| who set it, and did a mod win? | x4effective who-sets <kind> <id> <prop> |
nothing overrides it; the value is vanilla |
| what does vanilla ship, independent of the modlist? | x4effective built with no overlays, or read the base file |
a claim about vanilla is NOT refuted by the effective tree disagreeing — different tiers |
| how does this value sit against comparable content? | x4stats wares <mod> · x4stats macro <path/to/*_macro.xml> |
⚠ advisory, never a verdict — it grounds the discussion, it does not settle it |
| is this entity a near-duplicate of one already installed? | x4similar --candidate <mod> |
no near-duplicate found within its class+purpose filter — not "nothing similar exists" |
| does the change collide with the installed set? | x4compat check <mod> |
no collision over the effective tree; load order is the engine's MEASURED rule (signature and apply order, in-game probe 2026-09-26; the profile root applies after the game root, and a dependency does not cross between them) -- the Steam Workshop root is unmeasured |
| do the selectors actually resolve? | x4validate <mod> (add --tier b if it touches another mod) |
run it — a sel= that matches nothing is the cheapest and most expensive bug here |
| what values exist across the corpus for this attribute? | BaseX ask.py --db x4eff |
a bare zero is a lead; only ask.py with a coverage denominator makes it a finding |
⚠ x4stats and x4similar are the two instruments most often named and least often run.
They are the balance-specific ones. If a balance discussion finishes without either being
invoked, say so out loud rather than letting it pass.
The three values — mandatory, no bare numbers
For every value you propose changing, state all four:
| vanilla | what base+DLC ship |
| effective | what it is right now, with the winning source NAMED (or "no override, still vanilla") |
| proposed | the new value |
| in-game effect | what the player sees or feels — direction, rough magnitude, what it touches. Not "sets X to N". |
A selector written against the vanilla value silently matches nothing when something else already changed it. That is the single most expensive bug class here, and the three-values rule exists to catch it before the edit, not after the test cycle.
The in-sector / out-of-sector check — mandatory
Name the mechanic that balances the change in sector, then state whether out of sector models it. OOS is arithmetic: a factor absent from the formula does not exist. If a change's cost is paid by a mechanic — interception, dodging, positioning, turret traverse — ask what pays that cost OOS. Often nothing does, and the change is IS-only.
Verify against the actual OOS scripts rather than reasoning from what IS does. An assumed formula is an ASSUMED-tier claim.
Label the regime — every combat claim says IS, OOS, or BOTH
A combat number without a regime label is a half-finding wearing the grammar of a whole one. State the label in the sentence that carries the number.
| label | means |
|---|---|
| IS | in-sector only: physics, projectile travel, turret traverse, interception, RNG. |
| OOS | out-of-sector only: arithmetic in aiscripts/fight.attack.object.*. |
| BOTH | verified separately in each — two measurements, never one generalised. |
| UNKNOWN | say so. It is valid, and common. |
How to tell which you are reading, mechanically: in the attack aiscripts <attention min="visible">
is the IN-SECTOR branch and <attention min="unknown"> is OUT OF SECTOR. A property read inside
one is scoped to that regime and nothing else — check the enclosing block before quoting any
formula from those files.
A stat's VALUE is usually engine-wide; its USE usually is not. Different claims, separate
labels: measuring the number tells you nothing about which regime consumes it. A day of work
once derived a spoof curve to float precision before the question "is this IS and OOS?" found
17 reads, ZERO inside attention min="visible" — the transform is engine-wide, every
formula on it out-of-sector only.
⚠ Scope a corpus claim PACKED-INCLUSIVE before recording it. That measurement first read
"2 places across 399 vanilla documents" — loose-vanilla only, blind to a packed mod reading the
stat in sector. Re-run over 963 documents it found a third reader (combat_tactics_script).
Two more requirements before the change: check who does not have the thing (a gap tactics cover IS can be decisive OOS, where arithmetic does not), and pin OOS first — it is crunchable, IS is not, so treat IS as the variable.
Before proposing
- Check the design charter first — "the game does X" is evidence, never justification.
- Label every claim's tier: MEASURED (with the denominator) · READ (file and line) · INFERRED (hedged out loud) · ASSUMED (say what would confirm it).
- State a confidence level and the assumptions under it. Below 90%, the next step is a measurement, not a change.
- A destructive change — removing content, stripping attributes — needs explicit approval and a higher evidence bar, whatever the confidence.
Output shape
Lead with the table of three values. Then the IS/OOS reading. Then the instruments actually run and what each returned, including the ones that returned nothing. Close with the confidence level and what would raise it.