Skip to content

Commit 41f2d3a

Browse files
committed
chore(validation): fix ledger bookkeeping and a dead regex
- Re-point the 14 template/defense-precedence cells in included_stateful_known_gaps.json from anatomy-note Follow-ups (1) to the new Follow-ups (7); TIMELIMIT and the two COSTSENDTOMAINTAIN cells from (2)/(3) to the correct (4); restate the two assigned:...135 cells honestly now that HD answers allowed=True for both DOUBLEENDCOST and ENDRESERVEOREND. Key set unchanged (137 before, 137 after — verified). - Drop the false 'MULTIPLESFX is Main6E's only REQUIRES' docstring claim in test_included_generic.py; it is 5E-fallback-sourced with no oracle backing, kept pending Follow-up (2). - included_ledger.py: the non-seed path's _baseline regex only ever matched the prototype ledger's trailing clause, never the stateful ledger's narrated one, so the stateful _baseline silently stopped updating. Make it append the 'now N remain (date)' clause when absent instead of assuming it is there, for both ledgers; clarify the docstring on when --seed is used again and that curated values are hand-written reasons the tool preserves. No engine logic changes.
1 parent 132be8b commit 41f2d3a

3 files changed

Lines changed: 34 additions & 29 deletions

File tree

tests/fixtures/included_stateful_known_gaps.json

Lines changed: 19 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,36 +5,36 @@
55
"assigned:20260830000017:PERSISTENT": "loader: active_cost HD=0.0 engine=11; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
66
"assigned:20260830000020:LOS": "loader: duration HD=INSTANT engine=CONSTANT; orig_duration HD=INSTANT engine=CONSTANT; range_value HD=200 engine=-1; follow-up 2026-08-30 anatomy note Follow-ups (1)",
77
"assigned:20260830000085:PHYSICALMANIFESTATION": "loader: active_cost HD=0.0 engine=13; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
8-
"assigned:20260830000135:DOUBLEENDCOST": "5E-fallback EXCLUDES, investigated: DOUBLEENDCOST/ENDRESERVEOREND are not Main6E.hdt modifiers at all (0 hits; only the 5E Main.hdt sibling declares them), so their assigned _excludes come from HDTTemplateProvider's fallback pass (hdt_provider.py:405-427) naming each other. HD's real assigned-tier ask (detached per Task 6 item 0) is allowed=True for both; the fix (clear excludes/requires/types on a fallback-sourced TemplateData in apply_template) was built and verified to close exactly this pair, but broke test_included_generic.py::test_requires_any_of_lists_the_options_and_is_met_by_one -- MULTIPLESFX is ALSO fallback-only (5E Main.hdt has it, Main6E does not) and its REQUIRES is a currently-asserted, real 6E rule. The brief's own gate (\"ONLY if it moves no cost -- full suite\") was not met, so left for the loader follow-up rather than shipped broad. HD state={\"active_cost\": 50.0, \"defense\": \"NORMAL\", \"does_body\": true, \"does_damage\": true, \"does_kb\": true, \"duration\": \"INSTANT\", \"end_usage\": 5, \"orig_duration\": \"INSTANT\", \"range_value\": 400, \"target\": \"DCV\"}",
9-
"assigned:20260830000135:ENDRESERVEOREND": "5E-fallback EXCLUDES, investigated: see the DOUBLEENDCOST cell at this same object -- identical cause and identical deferral (fix built, reverted: broke MULTIPLESFX's REQUIRES, also fallback-sourced but a real rule).",
8+
"assigned:20260830000135:DOUBLEENDCOST": "5E-fallback EXCLUDES/REQUIRES precedence, restated 2026-08-30 (anatomy note Follow-ups (2)): DOUBLEENDCOST/ENDRESERVEOREND are not Main6E.hdt modifiers at all (0 hits; only the 5E Main.hdt sibling declares them), so their assigned _excludes come from HDTTemplateProvider's fallback pass (hdt_provider.py:405-427) naming each other. HD's real assigned-tier ask (detached per Task 6 item 0) is allowed=True for both. A definitions-only fallback fix (clear excludes/requires/types on a fallback-sourced TemplateData in apply_template) was built and verified to close exactly this pair, but was reverted because it broke test_included_generic.py::test_requires_any_of_lists_the_options_and_is_met_by_one, whose docstring claimed MULTIPLESFX's REQUIRES was \"Main6E's only REQUIRES\" -- that claim was false: MULTIPLESFX's REQUIRES is ALSO fallback-only (5E Main.hdt has it, Main6E does not), with 0 Main6E entries and 0 HD rows in either fixture backing it, so the fix was blocked by an untested assertion, not a genuine 6E rule. The open question -- whether MULTIPLESFX's REQUIRES is real -- is settled by adding a MULTIPLESFX state to the sink and asking HD directly, not by more reading. HD state={\"active_cost\": 50.0, \"defense\": \"NORMAL\", \"does_body\": true, \"does_damage\": true, \"does_kb\": true, \"duration\": \"INSTANT\", \"end_usage\": 5, \"orig_duration\": \"INSTANT\", \"range_value\": 400, \"target\": \"DCV\"}",
9+
"assigned:20260830000135:ENDRESERVEOREND": "5E-fallback EXCLUDES/REQUIRES precedence, restated 2026-08-30: see the DOUBLEENDCOST cell at this same object -- identical cause and identical deferral (fix built, reverted only because it broke an untested REQUIRES assertion on MULTIPLESFX, not because MULTIPLESFX's REQUIRES is a genuine rule).",
1010
"template:1674528649027:CONTINUOUS": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
1111
"template:1674528649027:NONPERSISTENT": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
1212
"template:1674528649027:PERSISTENT": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
13-
"template:1674528649148:ACV:MENTALOCV": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
14-
"template:1674528649148:AFFECTSPHYSICALWORLD": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
15-
"template:1674528649148:AOE:RADIUS": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
16-
"template:1674528649148:CANBEMISSILEDEFLECTED": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
17-
"template:1674528649148:DAMAGEOVERTIME": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
18-
"template:1674528649148:INDIRECT:SAMEDIRECT": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
19-
"template:1674528649148:PERSONALIMMUNITY": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
20-
"template:1674528649148:RANGED:RANGED": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
21-
"template:1674528649148:STICKY:STANDARD": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
22-
"template:1674528649148:TRANSDIMENSIONAL:SINGLE": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (1)",
13+
"template:1674528649148:ACV:MENTALOCV": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
14+
"template:1674528649148:AFFECTSPHYSICALWORLD": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
15+
"template:1674528649148:AOE:RADIUS": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
16+
"template:1674528649148:CANBEMISSILEDEFLECTED": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
17+
"template:1674528649148:DAMAGEOVERTIME": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
18+
"template:1674528649148:INDIRECT:SAMEDIRECT": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
19+
"template:1674528649148:PERSONALIMMUNITY": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
20+
"template:1674528649148:RANGED:RANGED": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
21+
"template:1674528649148:STICKY:STANDARD": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
22+
"template:1674528649148:TRANSDIMENSIONAL:SINGLE": "loader (non-duration field): target HD=DCV engine=SELFONLY; follow-up 2026-08-30 anatomy note Follow-ups (7)",
2323
"template:1674528649234:CONTINUOUS": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
2424
"template:1674528649234:NONPERSISTENT": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
2525
"template:1674528649234:PERSISTENT": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
2626
"template:1674528649464:CONTINUOUS": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
2727
"template:1674528649464:NONPERSISTENT": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
2828
"template:1674528649464:PERSISTENT": "loader: orig_duration HD=CONSTANT engine=PERSISTENT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
29-
"template:20260830000009:TIMELIMIT": "follow-up: uses_end is a field (True) independent of end_usage (0 here) -- HD state end_usage=0 satisfies TimeLimit's END check directly; the engine's uses_end==True check (a stale/unrelated flag) trips the refusal instead. 2026-08-30 anatomy note Follow-ups (2).",
29+
"template:20260830000009:TIMELIMIT": "follow-up: uses_end is a field (True) independent of end_usage (0 here) -- HD state end_usage=0 satisfies TimeLimit's END check directly; the engine's uses_end==True check (a stale/unrelated flag) trips the refusal instead. 2026-08-30 anatomy note Follow-ups (4).",
3030
"template:20260830000011:CONTINUOUS": "loader: active_cost HD=0.0 engine=9.0; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
3131
"template:20260830000011:COSTSEND:ACTIVATE": "loader: active_cost HD=0.0 engine=9.0; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
3232
"template:20260830000011:INCREASEDEND:2X": "loader: active_cost HD=0.0 engine=9.0; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
3333
"template:20260830000011:INHERENT": "loader: active_cost HD=0.0 engine=9.0; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
3434
"template:20260830000011:NONPERSISTENT": "loader: active_cost HD=0.0 engine=9.0; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
3535
"template:20260830000011:PERSISTENT": "loader: active_cost HD=0.0 engine=9.0; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
3636
"template:20260830000011:TIMELIMIT": "loader: active_cost HD=0.0 engine=9.0; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
37-
"template:20260830000015:COSTSENDTOMAINTAIN:FULL": "follow-up: continuing_effect is inferred, not the HD field -- engine continuing_effect=True (inferred from duration modifiers) where HD's own field is False, which is what lets \"...do not already cost END to maintain\" apply on HD's side and not the engine's (orig_duration=INSTANT on both). 2026-08-30 anatomy note Follow-ups (3).",
37+
"template:20260830000015:COSTSENDTOMAINTAIN:FULL": "follow-up: continuing_effect is inferred, not the HD field -- engine continuing_effect=True (inferred from duration modifiers) where HD's own field is False, which is what lets \"...do not already cost END to maintain\" apply on HD's side and not the engine's (orig_duration=INSTANT on both). 2026-08-30 anatomy note Follow-ups (4).",
3838
"template:20260830000017:CONTINUOUS": "loader: active_cost HD=0.0 engine=11; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
3939
"template:20260830000017:NONPERSISTENT": "loader: active_cost HD=0.0 engine=11; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
4040
"template:20260830000020:CONTINUOUS": "loader: duration HD=INSTANT engine=CONSTANT; orig_duration HD=INSTANT engine=CONSTANT; range_value HD=200 engine=-1; follow-up 2026-08-30 anatomy note Follow-ups (1)",
@@ -89,8 +89,8 @@
8989
"template:20260830000045:NONPERSISTENT": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
9090
"template:20260830000045:PERSISTENT": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
9191
"template:20260830000045:PHYSICALMANIFESTATION": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
92-
"template:20260830000046:ARMORPIERCING": "loader (non-duration field): defense HD=SPECIAL engine=NONE; follow-up 2026-08-30 anatomy note Follow-ups (1)",
93-
"template:20260830000046:CUMULATIVE": "loader (non-duration field): defense HD=SPECIAL engine=NONE; follow-up 2026-08-30 anatomy note Follow-ups (1)",
92+
"template:20260830000046:ARMORPIERCING": "loader (non-duration field): defense HD=SPECIAL engine=NONE; follow-up 2026-08-30 anatomy note Follow-ups (7)",
93+
"template:20260830000046:CUMULATIVE": "loader (non-duration field): defense HD=SPECIAL engine=NONE; follow-up 2026-08-30 anatomy note Follow-ups (7)",
9494
"template:20260830000052:ALWAYSON": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
9595
"template:20260830000052:CONTINUOUS": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
9696
"template:20260830000052:INHERENT": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
@@ -129,9 +129,9 @@
129129
"template:20260830000085:NONPERSISTENT": "loader: active_cost HD=0.0 engine=13; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
130130
"template:20260830000085:PERSISTENT": "loader: active_cost HD=0.0 engine=13; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
131131
"template:20260830000085:PHYSICALMANIFESTATION": "loader: active_cost HD=0.0 engine=13; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
132-
"template:20260830000091:CANBEMISSILEDEFLECTED": "loader (non-duration field): defense HD=SPECIAL engine=NORMAL; follow-up 2026-08-30 anatomy note Follow-ups (1)",
133-
"template:20260830000101:COSTSENDTOMAINTAIN:FULL": "follow-up: continuing_effect is inferred, not the HD field -- engine continuing_effect=False where HD's own field is True, which is what lets HD allow COSTSENDTOMAINTAIN on an INSTANT-duration Drain (both sides read duration=INSTANT) and not the engine's \"...must be Constant in duration\" refusal. 2026-08-30 anatomy note Follow-ups (3).",
134-
"template:20260830000111:CANBEMISSILEDEFLECTED": "loader (non-duration field): defense HD=POWER engine=NORMAL; follow-up 2026-08-30 anatomy note Follow-ups (1)",
132+
"template:20260830000091:CANBEMISSILEDEFLECTED": "loader (non-duration field): defense HD=SPECIAL engine=NORMAL; follow-up 2026-08-30 anatomy note Follow-ups (7)",
133+
"template:20260830000101:COSTSENDTOMAINTAIN:FULL": "follow-up: continuing_effect is inferred, not the HD field -- engine continuing_effect=False where HD's own field is True, which is what lets HD allow COSTSENDTOMAINTAIN on an INSTANT-duration Drain (both sides read duration=INSTANT) and not the engine's \"...must be Constant in duration\" refusal. 2026-08-30 anatomy note Follow-ups (4).",
134+
"template:20260830000111:CANBEMISSILEDEFLECTED": "loader (non-duration field): defense HD=POWER engine=NORMAL; follow-up 2026-08-30 anatomy note Follow-ups (7)",
135135
"template:20260830000118:ALWAYSON": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
136136
"template:20260830000118:CONTINUOUS": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",
137137
"template:20260830000118:INHERENT": "loader: active_cost HD=0.0 engine=9.0; duration HD=PERSISTENT engine=CONSTANT; orig_duration HD=PERSISTENT engine=CONSTANT; follow-up 2026-08-30 anatomy note Follow-ups (1)",

tests/included_ledger.py

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,12 @@
2626
``--seed`` writes the ledger from scratch: every currently-disagreeing cell,
2727
as a raw survey line, with no comparison against an existing file (there may
2828
not be one, or a fixture re-key may have moved every key at once -- neither is
29-
a regression of the engine). This is the one-time baseline write the task's
30-
Global Constraints permit; every write after it must go through the normal
31-
(non-seed) path, which can only shrink the file.
29+
a regression of the engine). This is the baseline write: used once to start a
30+
ledger, and used again only when the fixture itself is regenerated (a re-key,
31+
a new state added to the sink) -- never as a shortcut around the shrink-only
32+
path. Every other write goes through the normal (non-seed) path, which can
33+
only shrink the file; curated values in that file are hand-written reasons a
34+
person typed in, and the tool preserves them verbatim (see below).
3235
3336
Prints what fell (or was seeded), what remains, and the remaining cells by
3437
kind and by modifier.
@@ -111,11 +114,11 @@ def main(argv: list[str]) -> int:
111114
for key in old
112115
if key in survey
113116
}
114-
doc["_baseline"] = re.sub(
115-
r"now \d+ remain \([^)]*\)",
116-
f"now {len(survey)} remain ({stamp})",
117-
doc["_baseline"],
118-
)
117+
clause = f"now {len(survey)} remain ({stamp})"
118+
if re.search(r"now \d+ remain \([^)]*\)", doc["_baseline"]):
119+
doc["_baseline"] = re.sub(r"now \d+ remain \([^)]*\)", clause, doc["_baseline"])
120+
else:
121+
doc["_baseline"] = f"{doc['_baseline']}; {clause}"
119122
ledger_path.write_text(json.dumps(doc, indent=2) + "\n")
120123

121124
print(f"fixed {len(fixed)}; {len(survey)} remain")

tests/test_included_generic.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -115,8 +115,10 @@ def test_excludes_refuses_the_power_itself_by_xmlid():
115115

116116

117117
def test_requires_any_of_lists_the_options_and_is_met_by_one():
118-
"""Modifier.java:900-960 -- MULTIPLESFX is Main6E's only REQUIRES, and it
119-
narrows on an option (XMLID.OPTIONID)."""
118+
"""Modifier.java:900-960 -- MULTIPLESFX's REQUIRES is 5E-fallback-sourced
119+
(Main6E.hdt has no REQUIRES of its own; 0 HD rows in either fixture back
120+
this one as real), kept as the engine's current behaviour pending anatomy
121+
note Follow-up (2). It narrows on an option (XMLID.OPTIONID)."""
120122
mod = template_modifier("MULTIPLESFX")
121123
mod._types = [] # TYPE=ADJUSTMENT, not the point here
122124
assert mod._requires == ("VARIABLEEFFECT.TWO", "VARIABLEEFFECT.FOUR",

0 commit comments

Comments
 (0)