Skip to content

Time-travel debugger: correctness, lifecycle & architecture issues before merge to master #685

Description

@andreahlert

Tracking issue for the time-travel debugger (src/ext/debugger.js) on the debugger branch. Collects the correctness, lifecycle, and architecture problems found while reviewing the implementation against the Discord discussion, so we can land them as focused PRs before this hits master.

Grouped by severity. Checkboxes track what's fixed.

Conceptual / time-travel correctness

  • 1. Time-travel is lossy by design. The whole rewind rides on MutationObserver, which only sees childList, attributes, and characterData. Anything outside those three is never recorded, so there is nothing to roll back.
  • 2. Form state is not restored. value / checked / selectedIndex are properties, not attributes, so the observer never sees them. Step back over a field change and it doesn't come back, and that's the Fix syntax error in example code #1 thing people do in hyperscript. Same story for dialogs, popovers, iframes, scroll, focus, canvas, media. These are the "trap doors" called out back in March, not fixable with the current mechanism.
  • 3. State capture is too shallow. Snapshots are one level deep, so before/after share the same nested objects and the variable diff isn't trustworthy. This is the deep clone wanted back in April, still shallow.
  • 4. No real call stack. Step-over fakes depth from the AST (command.parent) and breaks the moment execution crosses into a call, function, or behavior.
  • 5. Honesty of scope. Until per-step capture of the invisible state lands, this has to ship as experimental with a loud, visible note on what it does NOT restore. A debugger that quietly lies is worse than no time travel: people trust it and chase ghosts.

Proposed path for 1/2

Keep the mutation deltas we already have for structure, but also explicitly snapshot per step the state the observer can't see (input values, checked boxes, selected options, scroll, focus, active element) and replay those alongside the mutations on rewind. Serializing the whole DOM per step is the other option but it's heavy, still misses live form values, and loses node identity. Hybrid is the cheaper, correct path and covers the form-state case that hurts most.

Concrete bugs

  • 6. Breakpoint line-mismatch (the "breakpoints sometimes work" bug). The editor reformats the script for display, but breakpoints and the paused-line highlight match against the tokenizer's original line numbers. A leading blank line shifts the display, so gutter clicks and the highlight land on the wrong line. Setting breakpoints from code works, which matches what was observed. (Fixed by the first PR linked below; also fixes the paused line not highlighting on the first break.)
  • 7. DOM-XSS in the element list. Element id and class are written into the panel without escaping. The timeline already escapes; this spot was missed. Debugging a page with user content in those attributes runs it inside the panel.

Performance / lifecycle

  • 8. The recorder never stops. Closing the panel doesn't pause it and debugMode never turns back off. After one open, every command keeps firing events and taking snapshots forever, even with the panel closed. The off switch is fake.
  • 9. Leak + event storm. Removed DOM nodes are held for 10k steps so they never get freed (slow leak on busy pages), and an event is dispatched up the whole tree on every command, so a tight loop turns into tens of thousands of dispatches.

Architecture

  • 10. Two debuggers that don't talk. The browser extension still polls the old hdb API every 250ms, but that's gone and the core now exposes a different API. The extension is dead against the current core. We should pick one.
  • 11. Shadow DOM was dropped in favor of global styles + prefixes. On a real page styles can leak both ways. Shadow DOM or properly scoped styles removes the whole class of problem.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions