Skip to content

demo: reproducible stills + animation harness, and real 2.0 screenshots - #178

Draft
NikolayS wants to merge 13 commits into
mainfrom
agent/visual-harness
Draft

demo: reproducible stills + animation harness, and real 2.0 screenshots#178
NikolayS wants to merge 13 commits into
mainfrom
agent/visual-harness

Conversation

@NikolayS

Copy link
Copy Markdown
Owner

Replaces the demos/ harness with one that anybody can run, and regenerates every image the docs use from real query output.

Handoff context and the full audit that produced this: #160.

Draft on purpose. Per CLAUDE.md this needs REV, human visual review of the artifacts, and explicit owner approval before it goes anywhere. It has had none of those.

Why

demos/record.sh hard-required a local Docker daemon and a Docker Hub pull, so only a workstation with unrestricted Docker could regenerate anything. That is the root cause of "some of those I recorded on my laptop." It also used blind sleep 3.8-style choreography (a slow query captures a half-drawn frame), took ~9 minutes per run, and still installs from devel/sql/ash-install.sql, which the 2.0 release stamp emptied (#138).

And the two README screenshots showed ash.top_waits / ash.timeline_chartv1.x readers that 2.0 removed. The landing page was advertising an API that no longer exists.

What

Capture and rendering are split, with a different mechanism per job because their constraints differ:

  • Stills → a stdlib-only ANSI→SVG renderer. No terminal, no browser, no timing. Byte-identical output, so images are diffable in review and make check is a real gate.
  • Animation → tmux + asciinema + agg with an OSC-2 prompt sentinel replacing the blind sleeps. The escape rides the same pty byte stream as the output, so completion cannot be observed before every preceding byte is consumed — non-racy by construction.

VHS was evaluated and rejected as an engine (headless Chromium — adopting a browser-dependent engine to fix an "only runs on my laptop" problem reintroduces the defect). Its visual spec was kept.

Both halves share one seeder, one incident window frozen to absolute timestamps, one theme and one vendored font, so stills and GIF show digit-identical numbers while staying independently re-runnable.

make doctor   # dependency tiers + which backends work here
make all      # 161 s from nothing: database, seed, 8 stills, GIF + MP4
make check    # 1 s, no database — renderer determinism against fixtures
make stills   # regenerate every doc image
make down     # drop the database / remove the container

Postgres comes from a backend abstraction — local cluster, Docker, or remote via PG* — so Docker is one option, not a requirement. Verified on this branch from its real location, without the out-of-tree override: make doctor resolves all three tiers, make check re-renders all 8 fixtures byte-identically.

Honesty of the numbers

Every value in every artifact is genuine ash.* output over samples ash.take_sample() wrote from pg_stat_activity while pgbench drove the database. The seeder decides which samples exist and when they count as taken (1 real second = 1 virtual minute, with ash.config.sample_interval set to match so backend_seconds is exactly right). No reader output is edited.

Review notes

  • Conflicts with docs: remove legacy reader screenshots #149, which also deletes the two legacy JPEGs. Land one or the other; this PR additionally replaces the README block rather than only removing it.
  • JetBrains Mono is vendored (OFL, license included). Deliberate: a missing font does not error — VHS and the rasteriser silently fall back to a serif face and render wrong. That already produced one bad artifact during development.
  • assets/ grows to ~2.6 MB (8 SVG + 8 PNG + GIF + MP4). The SVGs are 20–28 KB each and are the primary artifact; PNGs are fallbacks. Drop the PNGs and/or the MP4 if that is too much.
  • demos/out/ and demos/env.local are gitignored.

Not yet exercised — please do not read green CI as coverage

  • Linux/GNU userland has never run this. Written to the portability rules (no sed -i, no date -d, bash 3.2-safe) and clean under BSD userland, but the first Linux run is a genuine risk.
  • .github/workflows/demo.yml has never executed. Two steps are conventional but unproven: sudo systemctl start postgresql on ubuntu-latest, and the agg release URL, which is x86_64-unknown-linux-gnu — wrong on arm64.
  • The pg_cron scheduling path is exercised by nobody here — the harness drives ash.take_sample() itself.
  • Only PG 18. ASH_PG_MAJOR=14 ASH_BACKEND=docker make stills is the whole command for others.
  • make all is 161 s, not the 120 s target; 76 s of that is a real-time recording.
  • Known bug: capture-stills.sh writes only the header comment into out/embed.md — the image list is empty. The README block in this PR was assembled by hand from scenes/captions.tsv.
  • Rendered SQL shows unexpanded $STORM_SINCE / :'UNTIL'. Keeps images deterministic, but is not copy-pasteable; now() - interval '30 minutes' would read better.

🤖 Generated with Claude Code

https://claude.ai/code/session_014HzBGzjFyN8dXZHbdWmYBj

NikolayS and others added 13 commits July 27, 2026 23:43
… pipeline

The old harness could only ever run on one laptop. demos/record.sh hard-required
a local Docker daemon AND a Docker Hub pull, so CI runners and dev containers
could not regenerate anything -- which is why README's screenshots were hand-taken
laptop JPEGs. It also drove the recording with blind fixed sleeps (sleep 3.8,
sleep 4.6), so a slow query captured a half-drawn frame, and it still installed
from devel/sql/ash-install.sql, which the 2.0 release stamp emptied (#138).

Worse, those JPEGs showed ash.top_waits and ash.timeline_chart -- v1.x readers
that 2.0 removed. The landing page was advertising an API that no longer exists.

The replacement splits capture and rendering, and uses a different mechanism for
each job because their constraints genuinely differ:

  * Stills -- a stdlib-only ANSI->SVG renderer. No terminal, no browser, no
    timing. Byte-identical output, so images are diffable in review and
    `make check` is a real gate rather than a smoke test.
  * Animation -- tmux + asciinema + agg, with an OSC-2 prompt sentinel replacing
    the blind sleeps. The escape rides the same pty byte stream as the output, so
    the driver cannot observe completion before consuming every preceding byte.
    Non-racy by construction.

VHS was evaluated and rejected as an engine: it drives headless Chromium, and
adopting a browser-dependent engine to fix an "only runs on my laptop" problem
reintroduces the defect. Its visual spec (chrome, geometry, palette) was kept.

Both halves share one seeder, one incident window frozen to absolute timestamps,
one theme and one vendored font, so the stills and the GIF show digit-identical
numbers while remaining independently re-runnable. Postgres is reached through a
backend abstraction -- local cluster, Docker, or remote via PG* -- so Docker is
one option rather than a requirement.

Every number in every artifact is genuine ash.* output over samples that
ash.take_sample() wrote from pg_stat_activity while pgbench drove the database.
The seeder decides which samples exist and when they count as taken; no reader
output is edited.

  make doctor   dependency tiers + which backends work here
  make all      161 s from nothing: database, seed, 8 stills, GIF + MP4
  make check    1 s, no database -- renderer determinism against fixtures
  make stills   regenerate every doc image
  make down     drop the database / remove the container

README's two dead JPEGs are replaced with ash.chart() and ash.top('wait_event'),
both real 2.0 readers. JetBrains Mono is vendored (OFL) because a missing font
does not error -- it silently falls back to a serif face and renders wrong.

Not yet exercised: Linux/GNU userland and .github/workflows/demo.yml have never
run, the pg_cron scheduling path is not touched by this harness, and only PG 18
has been used. See demos/README.md and #160.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014HzBGzjFyN8dXZHbdWmYBj
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.

2 participants