Skip to content

ci(record-demos): fix the VHS install and render the tapes from this checkout - #1224

Merged
dmitrii-f-t27 merged 1 commit into
mainfrom
fix/record-demos-vhs-install
Oct 1, 2026
Merged

dmitrii-f-t27 merged 1 commit into
mainfrom
fix/record-demos-vhs-install

Conversation

@dmitrii-f-t27

@dmitrii-f-t27 dmitrii-f-t27 commented Oct 1, 2026 •

Copy link
Copy Markdown
Collaborator

Why

Terminal Demo GIFs (.github/workflows/record-demos.yml) has failed at Install VHS on every run since at least 2026-09-20: on main (run 36894521838 today), on feature branches, and on the v10.2.0 and v10.3.0 tags.

  • The URL named vhs_0.7.1_linux_amd64.tar.gz. The v0.7.1 release publishes vhs_0.7.1_Linux_x86_64.tar.gz, so wget exited 8.
  • wget -qO- wrote the archive to stdout, so the tar xzf vhs_0.7.1_linux_amd64.tar.gz after it would have found no file even with the right URL.

No step after that one had ever run, so I dry-ran the whole job before changing it.

What changes

  • Install VHS. Downloads the real asset to $RUNNER_TEMP with curl -f and checks it against the sha256 in the release's checksums.txt (748443e0…96d4). It extracts only vhs, which sits at the top of the archive next to README.md, LICENSE, completions/ and manpages/. It installs ttyd and ffmpeg from apt, because VHS refuses to start without them and needs ttyd 1.7.2 or newer (Ubuntu 24.04 ships 1.7.4; the runner image has neither, only Chrome). Then it prints all three versions.

  • Build tri. Now zig build -Dci=true, as brain-ci.yml does. Without the flag the raylib GUI targets are built too, and they need X11/GL headers. The step ends with zig-out/bin/tri --version.

  • Tapes. cd ~/trinity-w1 (one developer's checkout) is replaced by a hidden block that puts $PWD/zig-out/bin first on PATH and clears the screen. Output now writes recordings/<name>.gif; VHS creates the directory. The typed commands are unchanged.

  • Commit GIFs → Upload GIFs. git push could not land anywhere:

    • on main, ruleset 23148303 ("T27 mandatory work report", no bypass actors) rejects it with GH013 … Required status check "T27 work report" is expected, the same answer signal-health-self.yml gets every day;
    • a tag push has no branch to push to;
    • on any other branch it would put a bot commit under the author.

    The GIFs are uploaded as the terminal-demo-gifs artifact instead (if-no-files-found: error, 30 days). /recordings/ is gitignored; tri-benchmark.gif alone is 2 MB.

  • Triggers. push now runs only for main. A tag push skips path filters, so every release tag ran this job, and so did every branch that touched a .zig file, archived stashes included. pull_request runs when a tape or this workflow changes, and workflow_dispatch stays. The job also gets contents: read, a 30-minute timeout and one run per ref.

  • docs/CLI_DEMOS_README.md. The VHS section now renders from the repository root after zig build -Dci=true and says CI uploads an artifact.

Dry run

I ran the job twice, both times in ubuntu:24.04 amd64 under Docker with Rosetta on macOS, with a non-root runner user, sudo, Google Chrome and Zig 0.15.2. A driver executed the job's steps the way the runner does (bash --noprofile --norc -eo pipefail, GITHUB_PATH, the artifact glob with if-no-files-found).

Step Run 1: clean image Run 2: final files, --security-opt seccomp=unconfined
Install VHS ✅ 336 s: ttyd 1.7.4 and ffmpeg 6.1.1 installed, sha256 OK, vhs version v0.7.1 (537d03a) ✅ 13 s
Build tri ✅ (cold cache 167 s) ✅ 127 s
Render ×5 ⛔ stopped at Chrome's namespace sandbox, which Docker's default seccomp blocks ✅ 24–120 s each
Upload GIFs not reached ✅ five GIFs, 124 KB – 2.0 MB

Run 1 stopped for a reason specific to the container. The hosted runner is a VM, and go-rod v0.114.5 (pinned by VHS v0.7.1) looks for chrome, then google-chrome. On the runner that resolves to Google Chrome; its Chromium is not on PATH under either name. Ubuntu 24.04's apparmor package grants /opt/google/chrome/chrome user namespaces (/etc/apparmor.d/chrome), so the workflow sets no VHS_NO_SANDBOX.

On the hosted runner this PR's own pull_request run (36906462795) passed every step in 5 min 40 s. Install VHS took 25 s (…vhs_0.7.1_Linux_x86_64.tar.gz: OK, vhs version v0.7.1 (537d03a), ttyd version 1.7.4, ffmpeg version 6.1.1-3ubuntu5) and Build tri 65 s (Trinity v5.1.0 (3cf38d6), Zig: 0.15.2). The five renders took 22–66 s each with Chrome's own sandbox, and terminal-demo-gifs was uploaded at 2 539 372 bytes. The GIFs show the same command output as the dry run.

actionlint 1.7.12 with shellcheck 0.11.0 reports nothing.

What the tapes record today (not changed here)

Tape Typed Output on today's tri
tri-math-demo tri math demo, bind, similarity, phi Unknown subcommand: … followed by the 121-line tri math help
tri-benchmark git status, tri benchmark, … vsa, … vm benchmark is not a command, so it falls through to chat and segfaults in src/tvc/tvc_corpus.zig:145. (tri bench exists but prints bench async: TODO - job system not configured.)
tri-test tri test, --summary, vsa, vm Test command not yet implemented …
tri-status tri status, tri git status, tri faculty M .trinity/command_cache.json (every tri call rewrites that tracked file), then the faculty board
tri-fpga-synth ls -la fpga/specs/, /fpga-synth counter, cat fpga/build/counter-report.txt /fpga-synth is a Claude Code skill: No such file or directory. The report file does not exist.

The job is green, but the GIFs show none of what docs/CLI_DEMOS_README.md says they do (74/74 tests, benchmark results, zero DSP). Choosing what the demos should type is up to the owners, so I left the commands alone. The GIFs committed in examples/recordings/ on 2026-04-03 show bash: tri: command not found. In tri-benchmark each crash outlasts the tape's Sleep 4s, on the hosted runner as under emulation, so the next command is typed into the middle of the stack trace.

Not in this PR

  • signal-health-self.yml and discover-callers.yml end in the same GH013 on main every day.
  • docs/CLI_DEMOS_README.md embeds https://gHashTag.github.io/trinity/recordings/*.gif, which return 404: deploy-docs.yml publishes only apps/website/dist and docs/build. Where the GIFs should be published is a separate decision.
  • The tri benchmark segfault.

Work report

{
  "version": 1,
  "head_sha": "aedfc98319de5b730709bd83c52736d4d84456bb",
  "summary": "Terminal Demo GIFs (record-demos.yml) failed at Install VHS on every run because it downloaded an asset name the VHS v0.7.1 release does not publish. This installs VHS from the real asset with a checksum, adds the ttyd and ffmpeg VHS requires, builds tri with -Dci=true, makes the five tapes render from the repository root with the freshly built tri, and replaces the commit-and-push step, which ruleset 23148303 rejects on main, with an uploaded artifact.",
  "changes": [
    ".github/workflows/record-demos.yml, Install VHS: downloads vhs_0.7.1_Linux_x86_64.tar.gz to RUNNER_TEMP with curl -f, checks the sha256 published in the release's checksums.txt, extracts only the vhs binary from the top of the archive, installs ttyd and ffmpeg from apt, and prints the vhs, ttyd and ffmpeg versions.",
    "Build tri runs zig build -Dci=true, as brain-ci.yml does, so the raylib GUI targets that need X11 and GL headers are skipped, and then runs zig-out/bin/tri --version.",
    "The Commit GIFs step (git add, commit, push) is replaced by actions/upload-artifact@v4, which uploads recordings/*.gif as terminal-demo-gifs with if-no-files-found: error and 30 days of retention.",
    "Triggers: push runs for main only, because a tag push ignores path filters and every release tag ran the job; pull_request runs when a tape or the workflow changes; workflow_dispatch stays. The job gets contents: read, a 30 minute timeout and one run per ref.",
    "tapes/*.tape: the visible cd ~/trinity-w1 becomes a hidden block that puts $PWD/zig-out/bin first on PATH and clears the screen, and Output writes recordings/<name>.gif. The typed commands are unchanged.",
    "docs/CLI_DEMOS_README.md renders the tapes from the repository root after zig build -Dci=true and says CI uploads an artifact; .gitignore ignores /recordings/."
  ],
  "tests": [
    {
      "command": "gh run view 36894521838 --repo gHashTag/trinity --log-failed",
      "result": "Before this change Install VHS ends with exit code 8 on main and every later step is skipped",
      "status": "failed",
      "evidence": "Run of 2026-10-01 on main; the same failure on the v10.2.0 and v10.3.0 tag runs and on every branch run listed since 2026-09-20"
    },
    {
      "command": "gh api repos/charmbracelet/vhs/releases/tags/v0.7.1, then curl the asset, shasum -a 256 and tar tzvf",
      "result": "The release lists vhs_0.7.1_Linux_x86_64.tar.gz and no linux_amd64 asset; the archive matches the published sha256 and holds the vhs binary at its top level",
      "status": "passed",
      "evidence": "sha256 748443e0b5df89475499330b8943bf650cf0627250290cd5a11d38d3859e96d4, identical to checksums.txt of v0.7.1"
    },
    {
      "command": "gha_dryrun.py .github/workflows/record-demos.yml render, a driver that runs the job's steps as the runner does (bash -eo pipefail, GITHUB_PATH), in a clean ubuntu:24.04 amd64 container with a non-root runner user, sudo, Google Chrome and Zig 0.15.2",
      "result": "Install VHS installed ttyd 1.7.4 and ffmpeg 6.1.1 on an image that had neither, passed the sha256 check and printed vhs v0.7.1 (537d03a); Build tri passed, and a cold-cache zig build -Dci=true took 167 s before it",
      "status": "passed",
      "evidence": "Docker with Rosetta emulation on macOS, 2026-10-01; the first render then stopped at Chrome's namespace sandbox, which Docker's default seccomp profile blocks and the hosted runner VM does not"
    },
    {
      "command": "The same driver and image with --security-opt seccomp=unconfined, on a fresh copy of the final branch files, so Chrome starts with its own sandbox and no VHS_NO_SANDBOX",
      "result": "All ten steps succeeded in 411 s under emulation; VHS created recordings/ itself and the upload step found five GIFs: tri-math-demo 275180, tri-benchmark 1986054, tri-test 126812, tri-status 124035 and tri-fpga-synth 143485 bytes",
      "status": "passed",
      "evidence": "First frames open at the prompt with the hidden PATH line absent, and last frames show the real output of each typed command, as listed under limitations"
    },
    {
      "command": "actionlint 1.7.12 -shellcheck shellcheck 0.11.0 .github/workflows/record-demos.yml",
      "result": "No findings for the workflow, including its run scripts",
      "status": "passed",
      "evidence": "Local run on 2026-10-01 with release binaries of both tools, checksum checked for actionlint"
    },
    {
      "command": "GitHub Actions: Terminal Demo GIFs on this pull request (pull_request event, ubuntu-latest)",
      "result": "All steps succeeded on ubuntu-latest in 5 min 40 s: Install VHS 25 s with the sha256 check OK and vhs v0.7.1, ttyd 1.7.4 and ffmpeg 6.1.1 printed, Build tri 65 s, five renders of 22 to 66 s each, and terminal-demo-gifs uploaded at 2539372 bytes",
      "status": "passed",
      "evidence": "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/gHashTag/trinity/actions/runs/36906462795"
    }
  ],
  "limitations": [
    "The tapes still type the commands they always typed, and on this tri none of them shows a working feature: tri math demo, bind, similarity and phi print Unknown subcommand and the 121 line help, tri benchmark crashes with a segmentation fault in src/tvc/tvc_corpus.zig:145, tri test prints not yet implemented, tri status lists only .trinity/command_cache.json, and /fpga-synth is a Claude Code skill rather than a shell command. What the demos should show is for the owners to decide.",
    "Every tri invocation rewrites the tracked .trinity/command_cache.json and tri faculty rewrites two more files under .trinity, so the git status typed in later tapes shows those changes; the runner's checkout is discarded after the job.",
    "The GIFs are no longer committed anywhere. docs/CLI_DEMOS_README.md still embeds https://gHashTag.github.io/trinity/recordings/*.gif, which answer 404 because deploy-docs.yml publishes only apps/website/dist and docs/build; publishing them is a separate decision.",
    "examples/recordings/*.gif, committed on 2026-04-03, were rendered where tri was not on PATH and show bash: tri: command not found; this change does not replace them.",
    "The local dry runs used Docker with Rosetta amd64 emulation on macOS, where Chrome's namespace sandbox needs seccomp unconfined; the hosted runner is a virtual machine whose Ubuntu 24.04 AppArmor profile grants /opt/google/chrome/chrome user namespaces, and its run rendered all five tapes with no VHS_NO_SANDBOX set.",
    "In tri-benchmark each crash outlasts the tape's Sleep 4s on the hosted runner too, so the next command is typed into the middle of the stack trace; the tape's timing belongs with the decision about its commands.",
    "signal-health-self.yml and discover-callers.yml end in the same GH013 rejection on main every day; they are not touched here."
  ],
  "tags": [
    "ci",
    "vhs",
    "demos",
    "workflows"
  ],
  "blog": {
    "title": "The demo GIF workflow ran for the first time past its install step",
    "summary": "Why Terminal Demo GIFs failed in under a second on every run, what the five steps behind the broken download had been hiding, and how a dry run of the whole job shaped the fix.",
    "outline": [
      "Terminal Demo GIFs failed on every run, on main, on feature branches and on release tags, at the step that installs VHS: the download named an asset the v0.7.1 release does not publish, and even the right name would have been written to standard output instead of the file the next command extracted.",
      "Because that step failed first, nothing after it had ever executed, so the fix began with a dry run of the whole job in a runner-like Ubuntu 24.04 container to see what the build, the five tapes and the final commit step would actually do.",
      "The dry run showed a build that pulled in GUI targets, tapes that changed into one developer's home directory and wrote their GIFs to the wrong place, VHS dependencies the runner does not carry, and a final git push that the repository's own ruleset rejects on main.",
      "The job now installs VHS from a checksummed release asset together with ttyd and ffmpeg, builds tri the way brain-ci.yml does, renders each tape from the repository root, and uploads the GIFs as an artifact instead of pushing them, running only for main, for pull requests that touch the tapes, and on demand.",
      "What the GIFs show is a separate question the change deliberately leaves open: the commands the tapes type print help screens, not-yet-implemented notices or a crash on today's tri, and choosing what the demos should demonstrate belongs to the people who own them."
    ]
  }
}

Do not merge without the owner's go-ahead.

🤖 Generated with Claude Code

…checkout

"Install VHS" failed on every run since at least 2026-09-20 -- main, feature
branches and the v10.2.0 and v10.3.0 tags -- with wget exit 8. The v0.7.1
release publishes vhs_0.7.1_Linux_x86_64.tar.gz, not
vhs_0.7.1_linux_amd64.tar.gz, and `wget -qO-` wrote the archive to stdout, so
the `tar xzf` after it would have opened a file that was never written even
with the right URL. The step now downloads the real asset to a file, checks it
against the sha256 in the release's checksums.txt, extracts only the binary
(it sits at the top of the archive), installs ttyd 1.7.4 and ffmpeg from apt
(VHS refuses to start without them and requires ttyd 1.7.2 or newer) and
prints all three versions.

No later step had ever run. A dry run of the whole job on ubuntu:24.04 amd64
showed what each one needed:

- Build: zig build -Dci=true, as brain-ci.yml does, so the raylib GUI
  targets that need X11 and GL headers stay out.
- Tapes: they typed `cd ~/trinity-w1`, one developer's checkout, and wrote
  their GIFs into the working directory. They now run from the repository
  root, put zig-out/bin first on PATH in a hidden line and write
  recordings/<name>.gif. The commands they type are unchanged.
- Commit GIFs: `git push` cannot land. Ruleset 23148303 rejects a direct
  push to main with GH013 (signal-health-self.yml gets that answer daily),
  a tag push has no branch, and on any other branch it would commit under
  the author. The GIFs are uploaded as the terminal-demo-gifs artifact;
  recordings/ is gitignored and the demos README says how to render locally.
- Triggers: push now runs for main only, because a tag push skips path
  filters; pull requests that touch a tape or this workflow render too.
  Read-only token, 30-minute timeout, one run per ref.

---

ci(record-demos): исправлена установка VHS, тейпы рендерятся из этого checkout

Шаг «Install VHS» падал на каждом запуске как минимум с 2026-09-20 — на main,
на рабочих ветках и на тегах v10.2.0 и v10.3.0 — с кодом wget 8. В релизе
v0.7.1 файл называется vhs_0.7.1_Linux_x86_64.tar.gz, а не
vhs_0.7.1_linux_amd64.tar.gz, а `wget -qO-` выводил архив в stdout, так что
следующий `tar xzf` открыл бы файл, которого нет, даже при верном URL. Теперь
шаг скачивает настоящий архив в файл, сверяет его sha256 с checksums.txt
релиза, извлекает только бинарник (он лежит в корне архива), ставит из apt
ttyd 1.7.4 и ffmpeg (без них VHS не запускается, ttyd нужен не старше 1.7.2)
и печатает версии всех трёх.

Ни один следующий шаг ни разу не выполнялся. Прогон всего job в ubuntu:24.04
amd64 показал, что нужно каждому:

- Сборка: zig build -Dci=true, как в brain-ci.yml, чтобы не собирать
  GUI-цели raylib, которым нужны заголовки X11 и GL.
- Тейпы делали `cd ~/trinity-w1` (checkout одного разработчика) и писали
  GIF в рабочий каталог. Теперь они запускаются из корня репозитория,
  скрытой строкой ставят zig-out/bin первым в PATH и пишут
  recordings/<имя>.gif. Набираемые команды не изменены.
- Коммит GIF: `git push` не проходит. Ruleset 23148303 отклоняет прямой
  push в main с GH013 (signal-health-self.yml получает этот ответ каждый
  день), на теге нет ветки, а на любой другой ветке это был бы коммит
  поверх автора. GIF загружаются артефактом terminal-demo-gifs;
  recordings/ добавлен в .gitignore, README с демо объясняет локальный рендер.
- Триггеры: push теперь только для main, потому что push тега игнорирует
  фильтры путей; pull request, меняющий тейп или этот workflow, тоже
  рендерит. Токен только на чтение, таймаут 30 минут, один запуск на ref.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the status:in-progress 🔵 Agent working label Oct 1, 2026
@dmitrii-f-t27
dmitrii-f-t27 merged commit c31677a into main Oct 1, 2026
41 of 50 checks passed
@github-actions github-actions Bot added status:completed Done and removed status:in-progress 🔵 Agent working labels Oct 1, 2026
github-actions Bot added a commit that referenced this pull request Oct 1, 2026
ci(record-demos): fix the VHS install and render the tapes from this checkout (#1224)

"Install VHS" failed on every run since at least 2026-09-20 -- main, feature
branches and the v10.2.0 and v10.3.0 tags -- with wget exit 8. The v0.7.1
release publishes vhs_0.7.1_Linux_x86_64.tar.gz, not
vhs_0.7.1_linux_amd64.tar.gz, and `wget -qO-` wrote the archive to stdout, so
the `tar xzf` after it would have opened a file that was never written even
with the right URL. The step now downloads the real asset to a file, checks it
against the sha256 in the release's checksums.txt, extracts only the binary
(it sits at the top of the archive), installs ttyd 1.7.4 and ffmpeg from apt
(VHS refuses to start without them and requires ttyd 1.7.2 or newer) and
prints all three versions.

No later step had ever run. A dry run of the whole job on ubuntu:24.04 amd64
showed what each one needed:

- Build: zig build -Dci=true, as brain-ci.yml does, so the raylib GUI
  targets that need X11 and GL headers stay out.
- Tapes: they typed `cd ~/trinity-w1`, one developer's checkout, and wrote
  their GIFs into the working directory. They now run from the repository
  root, put zig-out/bin first on PATH in a hidden line and write
  recordings/<name>.gif. The commands they type are unchanged.
- Commit GIFs: `git push` cannot land. Ruleset 23148303 rejects a direct
  push to main with GH013 (signal-health-self.yml gets that answer daily),
  a tag push has no branch, and on any other branch it would commit under
  the author. The GIFs are uploaded as the terminal-demo-gifs artifact;
  recordings/ is gitignored and the demos README says how to render locally.
- Triggers: push now runs for main only, because a tag push skips path
  filters; pull requests that touch a tape or this workflow render too.
  Read-only token, 30-minute timeout, one run per ref.

---

ci(record-demos): исправлена установка VHS, тейпы рендерятся из этого checkout

Шаг «Install VHS» падал на каждом запуске как минимум с 2026-09-20 — на main,
на рабочих ветках и на тегах v10.2.0 и v10.3.0 — с кодом wget 8. В релизе
v0.7.1 файл называется vhs_0.7.1_Linux_x86_64.tar.gz, а не
vhs_0.7.1_linux_amd64.tar.gz, а `wget -qO-` выводил архив в stdout, так что
следующий `tar xzf` открыл бы файл, которого нет, даже при верном URL. Теперь
шаг скачивает настоящий архив в файл, сверяет его sha256 с checksums.txt
релиза, извлекает только бинарник (он лежит в корне архива), ставит из apt
ttyd 1.7.4 и ffmpeg (без них VHS не запускается, ttyd нужен не старше 1.7.2)
и печатает версии всех трёх.

Ни один следующий шаг ни разу не выполнялся. Прогон всего job в ubuntu:24.04
amd64 показал, что нужно каждому:

- Сборка: zig build -Dci=true, как в brain-ci.yml, чтобы не собирать
  GUI-цели raylib, которым нужны заголовки X11 и GL.
- Тейпы делали `cd ~/trinity-w1` (checkout одного разработчика) и писали
  GIF в рабочий каталог. Теперь они запускаются из корня репозитория,
  скрытой строкой ставят zig-out/bin первым в PATH и пишут
  recordings/<имя>.gif. Набираемые команды не изменены.
- Коммит GIF: `git push` не проходит. Ruleset 23148303 отклоняет прямой
  push в main с GH013 (signal-health-self.yml получает этот ответ каждый
  день), на теге нет ветки, а на любой другой ветке это был бы коммит
  поверх автора. GIF загружаются артефактом terminal-demo-gifs;
  recordings/ добавлен в .gitignore, README с демо объясняет локальный рендер.
- Триггеры: push теперь только для main, потому что push тега игнорирует
  фильтры путей; pull request, меняющий тейп или этот workflow, тоже
  рендерит. Токен только на чтение, таймаут 30 минут, один запуск на ref.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant