Bottom line
A focus in the main tree that carries a joint_trigger block loses it on export. The
extra-tree exporter keeps it; the main-tree one does not.
Why
focus_tree/parse.py:186-188 captures the block off any focus block, not just a
joint_focus:
jt = blocks.get("joint_trigger")
if jt:
raw_rewards[(fid, "_joint_extra")] = f"joint_trigger = {{\n{jt}\n}}"
focus_tree/build.py:229 attaches it to every built focus regardless of tree.
focus_tree/codec.py:182-184 only emits it when include_joint_extra is true, and that
parameter defaults to False (codec.py:140).
export_focus_tree passes include_joint_extra=True (focus_tree/export.py:69-80).
export_main_tree does not pass it at all (focus_tree/export.py:219-228), so the block
is dropped with no error or warning.
AGENTS.md:48 states the rule this breaks: "Parse→export of an existing file must not
drop fields." No test covers it — tests/test_focus_tree_export_main.py has no
joint_trigger case, and tests/test_focus_tree_roundtrip.py only exercises
export_focus_tree.
Acceptance
- A main-tree focus with a
joint_trigger block round-trips through export_main_tree
unchanged.
tests/test_focus_tree_export_main.py covers it.
BLUF
Bottom line
A focus in the main tree that carries a
joint_triggerblock loses it on export. Theextra-tree exporter keeps it; the main-tree one does not.
Why
focus_tree/parse.py:186-188captures the block off any focus block, not just ajoint_focus:focus_tree/build.py:229attaches it to every built focus regardless of tree.focus_tree/codec.py:182-184only emits it wheninclude_joint_extrais true, and thatparameter defaults to
False(codec.py:140).export_focus_treepassesinclude_joint_extra=True(focus_tree/export.py:69-80).export_main_treedoes not pass it at all (focus_tree/export.py:219-228), so the blockis dropped with no error or warning.
AGENTS.md:48states the rule this breaks: "Parse→export of an existing file must notdrop fields." No test covers it —
tests/test_focus_tree_export_main.pyhas nojoint_triggercase, andtests/test_focus_tree_roundtrip.pyonly exercisesexport_focus_tree.Acceptance
joint_triggerblock round-trips throughexport_main_treeunchanged.
tests/test_focus_tree_export_main.pycovers it.BLUF