Skip to content

temporal: what is x at L / state_at of L collapse a loop body to its last execution, so no iteration but the final one is addressable #868

Description

@InauguralPhysicist

Finding

Temporal interrogatives are keyed by source line, and a line executed N times retains only the Nth state. The reversibility layer therefore cannot answer "what was x on iteration 2" — which is the main reason to reach for it when debugging a loop.

Reproduction

v0.38.0, release build at 078e759.

x is 0
for i in range of 5:
    x is i * 10
print of ("what is x at 3 -> " + (str of (what is x at 3)))
print of ("state_at of 3  -> " + (str of (state_at of 3)))
print of ("prev of x      -> " + (str of (prev of x)))
what is x at 3 -> 40
state_at of 3  -> {"x": 40, "i": 4}
prev of x      -> 30

Line 3 executed five times, producing 0, 10, 20, 30, 40. Only the last is reachable. prev of x gives one more step back, and that is the entire depth available.

Two problems with line-keyed addressing

1. Non-injective for anything in a loop or a called function. The address space is source lines; the event space is executions. Every loop body and every function called more than once collapses. Since the interesting temporal questions are almost always about a loop iteration or a particular call, the addressable set excludes most of what one would want to ask.

2. Edit-brittle. A query is written against a line number, so inserting a line anywhere above it silently changes what every downstream query means. This is why tests/test_temporal.eigs is line-number sensitive (flagged in CLAUDE.md) — the test suite is already paying this cost, which is the signal that the addressing model rather than the test is the awkward part.

Suggested direction

An occurrence index alongside the line would make the loop case addressable without changing the existing surface:

  • what is x at 3 — keep current meaning (most recent execution of line 3).
  • what is x at [3, 2] — the 2nd execution of line 3.
  • Or an event-counter form keyed off when is x (which already counts assignments): what is x when 2.

Either is additive. The when counter already exists and is per-binding, so the assignment index is the cheaper of the two to wire up.

Worth weighing against #827, which found the assignment history is already unbounded and OOMs long-running programs — any per-occurrence retention needs a bound decided at the same time. That interaction is probably the main design constraint here.

Related: #827, docs/TRACE.md.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions