Skip to content

✅ Prove one session view renders from source and a binary (#799) - #805

Open
taras wants to merge 2 commits into
mainfrom
agent/issue-799-session-rendering
Open

✅ Prove one session view renders from source and a binary (#799)#805
taras wants to merge 2 commits into
mainfrom
agent/issue-799-session-rendering

Conversation

@taras

@taras taras commented Sep 11, 2026

Copy link
Copy Markdown
Owner

Why

A terminal session is the one part of this system nothing could test. The
rendering path existed only inside a running agent UI, so a regression in how a
message is selected, replaced, coalesced, scrolled or repainted reached a person
rather than a suite. Closes the evidence gap #799 describes.

What changes

Before:

No test drove a session view at all. Component selection for a message
presentation, streaming replacement, backlog collapsing, scroll and follow
state, resize, and incremental repaint were only ever exercised by a human
watching a real terminal.

After:

One deterministic Deno test runs a finite session journey twice — from
TypeScript source and from a deno compile executable started outside the
checkout — and requires the two evidence records to be equal in whole. Nine
rejecting controls, one per claim, each run in a process of its own.

How it works

entrypoint.ts → runJourney(root, mutation?) → one JSON evidence record → checkJourney()

@bomb.sh/tty 0.9.0 is a terminal renderer and input parser with zero I/O:
it takes an operation tree and returns ANSI bytes, and takes raw bytes and
returns decoded key events. Both are pure computation over bundled WebAssembly,
so a full session view can be driven with no terminal, no PTY and no emulator.

A source task publishes immutable snapshots of a conversation. An ingest task
reduces each one the moment it arrives — fresh structural state, dirty stable
ids, a monotonically increasing view version. A projector renders whatever the
newest state says: a projection that resumes against a stale version is
discarded and restarted, and a commit is keyed by the entry's own id, so a
replacement takes the same place rather than arriving beside what it replaces.

Each entry is projected by executing an ordinary Markdown component. The
DocumentOutput capture takes the Markdown before any terminal formatting,
and a narrow projector lexes the paragraph / emphasis / fenced-code subset with
the existing parser and fails closed on anything else. marked-terminal is not
involved: tty alone decides what a frame's bytes are.

Review guide

Start with: scripts/tests/session-rendering.test.ts

Then review:

  1. scripts/tests/session-rendering/evidence.ts — the invariant checker and the
    record parser. One checker admits the positive journey and rejects every
    control, by category.
  2. scripts/tests/session-rendering/journey.ts — the scope-owned session view:
    private entry shape, ingest/project split, barrier, viewport, tty.
  3. scripts/tests/session-rendering/markdown-projection.ts — the lexer and the
    operation tree.
  4. The seven defaults/Session/Message/*.md and the single
    repository/components/Session/Message/Tool.md override.

Look carefully at:

  • The barrier handshake in journey.ts. The source publishes the blocking
    revision, waits for the barrier to actually engage, then publishes two more
    revisions and the completion. Without that wait the backlog could be ingested
    before the projector reached the barrier, and the coalescing claim would be
    about nothing.
  • Cleanup ordering. ensure() is registered before anything is acquired or
    subscribed. The tty objects hold no host resource and expose no close API, so
    teardown is the scope dropping its references.

What must stay true

  • Seven named presentations resolve to proof Markdown files — enforced by the
    existing include mechanism, checked by presentation-selection and broken by
    the omit-component control.
  • A repository-local component outranks the default beneath it — enforced by
    include ordering, checked by override-precedence and broken by
    default-before-repository.
  • Paragraphs, emphasis and fenced code survive into the operation tree
    checked by structured-markdown in both the raw captures and the operations,
    independently, and broken by plain-markdown.
  • A replacement keeps one id at one position — enforced by keying commits by
    entry id, checked by stable-replacement, broken by unstable-regions.
  • A blocked projection's backlog collapses to its latest completed revision
    checked by coalescing, broken by no-coalescing.
  • The anchor is an entry id, not a row offset — checked by
    resize-preservation, broken by row-offset-viewport.
  • An incremental update costs less than a repaint — checked by
    bounded-diff against a freshly constructed term rendering the same visible
    frame, broken by whole-history-repaint. No ANSI sequence and no byte count
    is written down anywhere.
  • Nothing reaches a forbidden integration — checked by
    forbidden-integration over denied process authorities and a document
    filesystem ledger, broken by forbidden-read.
  • The proof stays out of every shipped package — checked by
    checkProductionBoundary over each workspace manifest and export map.

How to verify it

deno task test scripts/tests/session-rendering.test.ts \
  scripts/tests/runtime-exclusions.test.ts \
  scripts/tests/test-file-discovery.test.ts
  • The source/compiled pair proves the binary carries the components it renders,
    and fails if a --include is dropped — the binary has no checkout to fall
    back on and is deliberately run from a private temporary directory.
  • Each control proves its claim is still being checked, and fails if the
    process exits non-zero (an accident) or is admitted (a claim nobody enforces).
  • compareRecords is shown to reject both a missing member and a mismatched
    dependency version, and to report no difference for a record against itself,
    so the equality assertion is not passing vacuously.
  • The checker is shown to reject a record with its history emptied, so a green
    checkJourney is not green for the wrong reason.

Scope

Included

  • The Deno-only black-box proof and its component fixtures.
  • An exact @bomb.sh/tty 0.9.0 root devDependency and the generated lock
    updates.
  • The runtime-exclusion entry classifying the test as Deno-only tooling.

Intentionally unchanged

  • No production package, export map, architecture or specification file, release
    configuration, or component-resolution behavior.
  • No entry type, component props, Tail, REPL, provider observer, Agent/ACP
    connection, provider-file access, PTY, terminal emulator, Grid provider,
    Workflow behavior, or durable UI state.
  • test-weights.json — measured on a runner after the corpus settles, never by
    hand.

New dependencies

  • Package: @bomb.sh/tty 0.9.0, exact, root devDependencies only.
  • Used for: terminal layout, clipping, input decoding, resize processing and
    ANSI diffing, as pure computation with no terminal attached.
  • Why existing dependencies are insufficient: marked-terminal formats Markdown
    for a terminal but neither lays out a viewport, nor decodes input, nor
    produces a cell-level diff, so it cannot answer whether an update costs less
    than a repaint.

Generated or mechanical changes

  • deno.lock and pnpm-lock.yaml come from
    deno install --frozen=false --dev --save-exact --package-json @bomb.sh/tty@0.9.0
    followed by deno task setup. Both deltas are the single new package.

Risks and limitations

  • Environment authority cannot be denied at the process level. Both journeys
    deny network, subprocess, write, system and FFI authority outright, and read
    only the proof's component roots plus core's own catalog. Environment is the
    exception, and not by choice: the module graph an execution loads probes
    process.env while it is still being imported — which reads OSTYPE, and
    debug enumerates the whole environment — so a process that denied it cannot
    reach this proof's own code at all. The authority is therefore withheld where
    a document can see it instead: no host Env provider is installed, and the
    document filesystem ledger records every path either journey asked for. This
    is a deviation from the frozen matrix's wording, reported rather than worked
    around.
  • Recovery or rollback: the proof is disposable test infrastructure in
    scripts/tests/; reverting the commit removes it and the dependency pin with
    no production consequence.

Scope confirmation

  • Every changed file supports the purpose described above.
  • Unrelated cleanup and formatting changes are excluded.
  • Generated or mechanical changes are clearly identified.
  • The description matches the final diff and test results.

A terminal session was the one part of this system nothing could test. The
rendering path existed only inside a running agent UI, so a regression in how a
message is selected, replaced, coalesced, scrolled or repainted reached a person
rather than a suite.

This drives the whole path headlessly. `@bomb.sh/tty` 0.9.0 does layout, input
decoding and ANSI as pure computation with no terminal attached, so one
deterministic journey can ask the questions a reader would notice: seven named
Markdown presentations render, a repository-local `Tool.md` outranks the default
beside it, a streaming replacement lands in the same place, a backlog collapses
to its latest completed revision, the view stays where the reader put it across
an append and a resize, and an update costs fewer bytes than a repaint.

Every claim carries a control that breaks it. The nine controls are a closed
enum, each run in a process of its own, and the same invariant checker that
admits the positive journey has to reject each one by the category of the claim
it broke — so a claim that quietly stopped being checked fails here instead of
passing.

The journey runs twice: from TypeScript under Deno, and from a `deno compile`
executable started outside the checkout with the proof Markdown explicitly
embedded. A binary that lost a component has nothing to fall back on, so the two
records are required to be equal in whole.

Neither run may reach the network, spawn a process, write, read the host beyond
the proof's own component roots, or use system or FFI authority. Environment is
the exception and cannot be otherwise: the module graph an execution loads
probes `process.env` while it is still being imported, so a process that denied
it could not reach this proof's own code. That authority is withheld where a
document can see it instead, and the document filesystem ledger records every
path either journey asked for.

The proof is disposable test infrastructure. It adds no production type, no
component props, and no export; the `@bomb.sh/tty` pin is an exact root
devDependency, and the test asserts that no shipped package adopted either it or
these modules.
@github-actions

github-actions Bot commented Sep 11, 2026

Copy link
Copy Markdown

PR #805: ✅ Prove one session view renders from source and a binary (#799)

18 files, +3474 / -932

Scope

🔴 PR has 4406 lines changed. Split into focused PRs.

🟡 4406 lines changed. PRs under 400 receive more thorough review.

🟡 PR mixes config and source changes.

Structural

🟡 1 console statements.

Slop

✅ Slop indicators look low.

Static Analysis

Oxlint: 6 diagnostics across 2 files (4 rules)
Density: 0.002 violations/added-line

no-floating-promises (3): scripts/tests/session-rendering/journey.ts
no-shadow (1): scripts/tests/session-rendering/evidence.ts
no-array-sort (1): scripts/tests/session-rendering/evidence.ts
consistent-function-scoping (1): scripts/tests/session-rendering/journey.ts

Correctness

No extraneous code patterns detected.

Downloaded from the Measure test weights run for 9a3cf24, unedited. The
proof measures 15.7s under Deno and is charged to no other runtime, which
is what the exclusion manifest says it should be.
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