Files
DumiFlex 934f7dc92d fix(axes): drive constraints by the rolled tag, fix the assembler read, preserve on import
An accepts axis rolls one tag per pick (read as $var.AXIS), but a constraint
sourced from it folded over the option's whole menu instead of that rolled tag,
so $outfit.SHOES and the constrained $shoes could disagree. The source now
sends only the rolled winner; a target accepts axis OR-folds so a multi-tag
option survives when any alternative is allowed, then its own roll is pinned to
the allowed tag -- $source.AXIS == $target.AXIS under a diagonal. Classify
groups are unchanged (multiply).

The assembler also rendered $var.AXIS empty: __wp_axes__ crosses the socket on
ContextPayload.internals but the node only merged the internal-flag map; it now
merges the whole carve-out so the accessor resolves.

Import: the community validators silently dropped tag_group_kinds (and the
constraint target_select) because the lenient payload object never listed them,
degrading a shared accepts axis to classify. Both are now in the v2/v1
validators + the engine-parity fixtures, with preservation tests. Engine math
mirrored in TS via the shared corpus.
2026-08-20 23:58:44 +03:00

33 lines
1.4 KiB
Python

import json
import pathlib
import pytest
from engine.modules._constraint_math import EXCLUDE, combine_constraint_factor
from engine.modules.derivation_handler import branch_carrier_key
_CORPUS_PATH = pathlib.Path(__file__).parents[3] / "tests/fixtures/constraint-corpus.json"
CORPUS = json.loads(_CORPUS_PATH.read_text())
@pytest.mark.parametrize("case", CORPUS["cases"], ids=lambda c: c["name"])
def test_corpus(case):
# `source_axis_kinds` is absent on every pre-axis case, and absence must
# reproduce the old fold exactly — that is what makes the feature additive.
got = combine_constraint_factor(
case["picks"], case["option"], case["matrix"], case["exceptions"],
axis_kinds=case.get("source_axis_kinds"),
target_axes=case.get("target_axes"))
if case["expect"] == "EXCLUDE":
assert got is EXCLUDE
else:
assert got == pytest.approx(case["expect"])
# Py≡TS branch-key parity: the carrier key the engine stamps for a derivation
# branch occurrence MUST be byte-identical to the key TS computes
# (constraint-pairs.ts `branchKey`). The shared corpus is the single source of
# truth so the two formats cannot drift independently.
@pytest.mark.parametrize("case", CORPUS["branch_key_cases"], ids=lambda c: c["name"])
def test_branch_key_corpus(case):
assert branch_carrier_key(case["rule_id"], case["branch"]) == case["key"]