Skip to content

Keep the expression pass off fork_pointer - #65

Merged
DavidSagan merged 1 commit into
mainfrom
fork-pointer-format
Jul 22, 2026
Merged

Keep the expression pass off fork_pointer#65
DavidSagan merged 1 commit into
mainfrom
fork-pointer-format

Conversation

@DavidSagan

Copy link
Copy Markdown
Member

A fork_pointer holds a node id, and a node id is a number, so substitute_values — the pass that replaces every evaluable scalar with its value — "evaluated" it too and wrote the result back through format_double, the shortest text that round-trips as a double:

110  →  "%.1g" = 1e+02   → 100 ≠ 110
        "%.2g" = 1.1e+02 → 110 ✓   so the pointer became "1.1e+02"
291  →  "%.3g" = 291                stayed "291"

which is why only some pointers looked wrong: an id has to be a multiple of ten from 100 up to reach exponent form. The value is still numerically right — but every reader parses it with std::stoull, which stops at the . and returns 1.

Three readers, three failures, one message

Reader Effect of stoull("1.1e+02") == 1
run_element_bookkeeper fork_seed[1] = def — the destination branch inherits no reference or floor from the Fork
link_fork_from node 1 is not a map, so continue — the destination silently loses its ForkFromP entry
remap_fork_pointers 1 is not in the provenance map — the one visible symptom, fork_pointer target is outside the expanded lattice

The fix

Add fork_pointer to non_expr_keys(). Its siblings to_line, destination_element, new_branch and propagate_reference were already there; this one was missed because expansion creates it rather than the user writing it, so it never looked like a value the expression pass would see. A node id is not a physics parameter and has no business going through the evaluator — a better place to fix this than making three call sites parse defensively.

The regression test

It reproduces a chained-and-cross-linked fork lattice and asserts no problems, five all-digit pointers, and five ForkFromP groups.

The first version of it passed with the fix reverted: every id happened to land outside the exponent-form range. It now carries three unused pad Drifts that shift the work-tree ids until two of the five destinations land in it. Verified both ways — fails with the fix reverted (lat.problems.count == 0 reports 1), passes with it in. The assertions hold whatever the ids turn out to be; they just stop exercising this particular bug if an unrelated change shifts them again, which the comment says.

153/153.

🤖 Generated with Claude Code

A fork_pointer holds a node id, and a node id is a number, so the pass that
replaces every evaluable scalar with its value "evaluated" it too and wrote
the result back through format_double -- the shortest text that round-trips
as a double. For most ids that is the digits themselves, but an id of 110
comes back as `1.1e+02`.

Every reader of the pointer parses it with std::stoull, which stops at the
`.` and yields 1, so one such id broke three things at once and only the
last of them said anything:

  - run_element_bookkeeper seeded reference/floor propagation on node 1, so
    the destination branch inherited nothing from the Fork.
  - link_fork_from found node 1 was not a map and moved on, so the
    destination silently lost its ForkFromP entry.
  - remap_fork_pointers reported the target as outside the expanded lattice.

The fix is to leave the pointer alone: non_expr_keys() already skips the
Fork's other bookkeeping keys (to_line, destination_element, new_branch,
propagate_reference), and fork_pointer was missed only because expansion
creates it rather than the user writing it. A node id is not a physics
parameter and has no business going through the evaluator, which is a better
place to fix this than making three call sites parse defensively.

The regression test needs an id in the range that formats as an exponent --
a multiple of ten from 100 up -- so it carries three unused padding elements
that shift the work-tree ids until two of its five destinations land there.
Without them every pointer comes out in plain digits and the bug hides.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@DavidSagan
DavidSagan merged commit ea3cbeb into main Jul 22, 2026
2 checks passed
@DavidSagan
DavidSagan deleted the fork-pointer-format branch July 22, 2026 21:16
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant