Skip to content

Doc: terminal-vs-browser IO seam in 'write once run anywhere' framing #608

@aallan

Description

@aallan

Summary

Vera's "write once, run anywhere" framing has a real seam at the IO boundary that's not currently surfaced in user-facing documentation. A program written for terminal-target ergonomics (using IO.sleep for animation timing and ANSI escape codes for cursor control) compiles cleanly to --target browser but doesn't run meaningfully there: IO.sleep busy-waits and freezes the tab, and ANSI escapes render as literal text in the DOM. The right shape for the browser is "Vera as pure simulation core, JS as timing+rendering driver" — but this isn't documented anywhere, and the runtime gaps that would make the JS-driver pattern ergonomic (#603) aren't filled in either.

Concrete manifestation

An agent writing Conway's Life on current main:

  1. Wrote a perfectly reasonable terminal-target program (IO.sleep(100) between frames + ANSI \u{1B}[2J\u{1B}[H clear+home).
  2. Compiled it cleanly with vera compile --target browser.
  3. Tried to load the bundle and observed: tab frozen for 30 seconds, output appearing as raw escape codes in DOM rather than animated grid.
  4. Wrote a separate browser-shaped variant: pure simulation core in Vera, JS driver using requestAnimationFrame and <canvas>.
  5. Hit #603 (no string-marshalling helpers exposed), worked around with a "compute-everything-upfront, drain stdout once" pattern.

This is a real adoption surface: the message "Vera is write-once-run-anywhere" sets up an expectation that the same source runs identically on both targets. The seam at IO breaks that expectation in a way that isn't a bug — it's a runtime-shape mismatch that no language can fully hide — but the documentation should set the expectation correctly.

Two architecture-level options

Option 1 (recommended): embrace the seam, document it explicitly

  • Add a "Browser target: limitations and patterns" section to SKILL.md and the spec
  • Explicitly call out: terminal animations using IO.sleep + ANSI escapes don't translate; the browser runtime expects "Vera = pure simulation core, JS = timing/rendering" and that the IO surface for the browser is IO.print (writes to the page) only
  • Provide a worked example (Conway's Life is a candidate) showing the JS-driver pattern
  • Pair with #603 to make the marshalling ergonomic

Option 2: bridge the seam in the runtime

  • Extend the browser runtime to interpret ANSI escapes (clear-screen, cursor-home, EOL-erase, plus colour codes) into DOM mutations
  • Replace IO.sleep's busy-wait with a requestAnimationFrame-aware yield that doesn't block the main thread
  • Significantly bigger lift; would let the same source run on both targets

Option 1 is honest about Vera's design point ("pure core + effects at the boundary, the boundary differs between targets"). Option 2 papers over a real distinction with engineering work that may be misplaced — agents writing for a browser target arguably should think differently about timing and rendering than agents writing for a terminal.

Acceptance for option 1

  • SKILL.md has a "Browser target" subsection explaining the IO model split, with concrete examples of:
    • what works (IO.print rendering, pure functions called from JS)
    • what doesn't (IO.sleep blocks main thread, ANSI escapes render as text)
    • the recommended pattern (Vera pure core + JS driver via requestAnimationFrame)
  • The "write once, run anywhere" claim in README.md is qualified to acknowledge the IO boundary

Related

  • #603 — the runtime gap that would make option 1's recommended pattern ergonomic
  • #604 — the prelude-skipped issue, also browser-target-specific

Metadata

Metadata

Assignees

No one assigned

    Labels

    documentationImprovements or additions to documentationenhancementNew feature or request

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions