Skip to content

Commit 58c346e

Browse files
pdbethkeclaude
andcommitted
fix: prototype REDUCEDEND-on-SHAPESHIFT -- the clone re-prices; ALL ledgers EMPTY
The last included() cell falls: ReducedEND.included judges o.clone() (ReducedEND.java:130-132), and GenericObject.clone() runs clone.setSelectedOption(op), whose SenseAffectingPower override assigns the power's base cost from the option's. A template prototype was never priced through setSelectedOption -- HD's own echo shows the Shape Shift prototype at baseCost 0 / endUsage 0 -- so the clone this rule judges has endUsage 1 where the original has 0, and HD allows. Probe-proved headlessly (original activeCost 0.0/endUsage 0; clone activeCost 5.0/endUsage 1; verdict allow); the ledgered 'needs a live-HD trace' turned out unnecessary. Port: the default-option Adder now carries its template price (the resolved-option branch always did), and ReducedEND's clone-mirror isolation block re-prices an unpriced object from its selected option for the duration of the check, restored in finally. Scoreboard: prototype ledger 0 of 8,395, stateful 0 of 9,867, sink cost 0 of 60 -- every ledger EMPTY. Suite 1724 passed; corpus parity unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PrVDXgpDfQpzEGErZTLjEs
1 parent b5dd8c2 commit 58c346e

3 files changed

Lines changed: 29 additions & 5 deletions

File tree

kirby_cost/objects/base.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -558,6 +558,16 @@ def apply_template(self, tmpl: "TemplateData", option_id: str = None) -> None:
558558
default_opt._alias = first.alias or first.display
559559
default_opt._selected = True
560560
default_opt._display_in_string = first.display_in_string
561+
# The option carries its own price, exactly as the resolved
562+
# branch above copies it. HD's Template builds every option
563+
# Adder priced, and clone() re-prices a power from it
564+
# (GenericObject.clone -> setSelectedOption ->
565+
# SenseAffectingPower reads ad.getBaseCost()); an unpriced
566+
# default left ReducedEND judging a Shape Shift prototype
567+
# as costing no END.
568+
default_opt._base_cost = first.base_cost
569+
default_opt._levels = first.levels
570+
default_opt._level_value = first.level_value
561571
default_opt.parent = self
562572
self._selected_option = default_opt
563573

kirby_cost/objects/modifiers/reducedend.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,21 @@ def included(self, generic_object: GenericObject) -> str:
146146
orig_main_power = generic_object.main_power
147147
generic_object.parent = None
148148
generic_object.main_power = None
149+
# The clone also RE-PRICES: GenericObject.clone() runs
150+
# clone.setSelectedOption(op) (GenericObject.java, clone(): the
151+
# getSelectedOption() branch), and SenseAffectingPower's override
152+
# assigns the power's base cost from the option's. A template
153+
# PROTOTYPE was never priced through setSelectedOption -- HD's own
154+
# echo shows the Shape Shift prototype at baseCost 0 / endUsage 0 --
155+
# so the clone this rule judges has endUsage 1 where the original
156+
# has 0, and HD allows. Probe-proved 2026-08-31: original
157+
# activeCost=0.0/endUsage=0, clone activeCost=5.0/endUsage=1,
158+
# verdict allow. Mirror it for the duration of the check.
159+
orig_base_cost = generic_object._base_cost
160+
_opt = getattr(generic_object, "_selected_option", None)
161+
if (_opt is not None and generic_object._base_cost == 0
162+
and getattr(_opt, "base_cost", 0)):
163+
generic_object._base_cost = _opt.base_cost
149164
try:
150165
# Cannot be applied with Increased END
151166
if GenericObject.find_object_by_id(
@@ -229,5 +244,6 @@ def included(self, generic_object: GenericObject) -> str:
229244
finally:
230245
generic_object.assigned_modifiers = orig_assigned
231246
finally:
247+
generic_object._base_cost = orig_base_cost
232248
generic_object.parent = orig_parent
233249
generic_object.main_power = orig_main_power
Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
{
22
"_comment": "SHRINK-ONLY. Cells where the engine's included() disagrees with Hero Designer's, with what each side said. Written once as the 2026-08-29 baseline before any fix; every later commit may only remove entries. The target is empty.",
3-
"_baseline": "3770 of 8395 cells disagree (2026-08-29); now 1 remain (2026-08-30)",
4-
"gaps": {
5-
"REDUCEDEND-on-SHAPESHIFT": "HD allows; mechanism unlocated after a full read of ReducedEND.java:124-210 -- the visible path refuses on getEndUsage()==0, HD's own echo for this prototype says end_usage=0/active_cost=0.0, no getEndUsage/getActiveCost override exists in Shapeshift/SenseAffectingPower, and clone() re-selects the same (null) option. Cross-evidence: on the LOADED sink Shape Shift (stateful fixture, object 20260830000064) HD reports end_usage=1/active=8.0 and allows -- consistent with its rule -- and the engine agrees there. The divergence is confined to HD's PROTOTYPE tier. Last prototype cell; needs a live-HD debugger trace (2026-08-31)."
6-
}
7-
}
3+
"_baseline": "3770 of 8395 cells disagree (2026-08-29); now 1 remain (2026-08-30); 0 remain (2026-08-31) -- EMPTY. The last cell was the CLONE: ReducedEND.included judges o.clone(), and GenericObject.clone() re-prices via setSelectedOption (SenseAffectingPower assigns the power's base cost from the option's), so the clone of an unpriced prototype has endUsage 1 where the original has 0. Probe-proved headlessly; no live-HD trace was needed after all.",
4+
"gaps": {}
5+
}

0 commit comments

Comments
 (0)