Skip to content

kxray.replay: step through a recorded Tier 1 session - #61

Merged
tamnd merged 1 commit into
mainfrom
replay-stepped-sessions
Sep 5, 2026
Merged

kxray.replay: step through a recorded Tier 1 session#61
tamnd merged 1 commit into
mainfrom
replay-stepped-sessions

Conversation

@tamnd

@tamnd tamnd commented Sep 5, 2026

Copy link
Copy Markdown
Owner

What this adds

A reader on Tier 0 has a browser and nothing else. A browser cannot run gcc over a hundred and sixty megabytes of kernel source, so every lesson that builds a kernel has had the same hole in it: the build gets described instead of shown, and a description of a build leaves out the one thing a person who has never done it is missing.

So the build gets recorded on a real machine and shipped as something you step through. The command, the output, how long it took, whether it worked.

The recording

The first one is thirteen commands. A Debian container with no compiler in it becomes a container that has built Linux 7.2.2 and booted it.

#   took    status  out   command
--  ------  ------  ----  ---------------------------------------------------------
0   15ms    ok      10    cat /etc/os-release
1   43.0s   ok      413   apt-get update && apt-get install -y --no-install-recom...
2   5ms     ok      1     gcc --version | head -1
3   7ms     ok      1     ld --version | head -1
4   405ms   ok      1     sha256sum linux-7.2.2.tar.xz
5   24.2s   ok      0     tar xf linux-7.2.2.tar.xz
6   0ms     ok      0     cd linux-7.2.2
7   9.7s    ok      17    make ARCH=um defconfig
8   5ms     ok      1     grep -c ^CONFIG_ .config
9   5m 11s  ok      1524  make ARCH=um -j6
10  3ms     ok      1     ls -lh linux
11  4ms     ok      38    cat /root/kxreplay/inside.sh
12  1.5s    ok      99    ./linux rootfstype=hostfs rootflags=/ ro init=... mem=256M

Look at step 9 against everything else. Five minutes and eleven seconds, against a minute and a half for the other twelve put together. That shape is what a written transcript throws away, and it is why the widget has a strip across the top with one segment per step, each as wide a share as that step was of the time.

Step 4 is worth more than it looks. The hash it prints is character for character the one in pin.toml, so the kernel built in step 9 is the kernel this project pins, proved rather than asserted.

Step 12 is the point of the session. The kernel built two commands earlier is started as an ordinary program, with no disk image and no virtual machine anywhere, and it says its own version back:

Linux version 7.2.2 (root@15a922314f08) (gcc (Debian 14.2.0-19) 14.2.0, GNU ld (GNU Binutils for Deb
ian) 2.44) #1 Sat Sep  5 21:42:26 UTC 2026

processors it can see:
1

That is User Mode Linux booted with rootfstype=hostfs, so it uses the container's own filesystem as its root and needs no rootfs image at all.

How a recording is made steppable

A terminal recording is one stream of bytes with nothing in it saying where one command ended and the next began. The structure comes from four escape sequences the shell is asked to print, OSC 133, which is an existing convention that iTerm2, kitty and WezTerm already speak. A prompt starts, the prompt ends, the command runs, the command finishes with a status.

The file is asciinema v2, a documented format rather than one invented here, so asciinema play opens the same file this repository parses.

Both of those choices are the same choice. A format nobody else speaks would fit our parser better and would be worth less.

The cases where it degrades, and what it does about them

Each of these has a test, because each of them is a place where a tool could quietly produce something that looks right.

  • A cast recorded with plain asciinema rec has no marks. It comes back as one step holding the whole session and the widget says so. Looking for a dollar sign to find the prompts would find every dollar sign in a build log too.
  • Bash has only printed the command started mark since 4.4, and the machine this was written on has 3.2. Without it the command is everything up to the first newline, which is what the terminal echoed.
  • A recording cut off mid command is complete = False, the same way a trace the tracer window cut off is.
  • A command that prints nothing still took the time it took. Timing from the first byte of output made sleep 10 read as zero seconds.
  • Output is cut to the head and the tail with a line saying how many lines were taken out. ... 1472 lines not shown ... in the fold for step 9. A reader who cannot tell a short command from a truncated one is being lied to about the size of a build.

The right hand edge of the screen

This one nearly shipped wrong and is worth writing down.

Step 1 is longer than the terminal is wide. A shell echoing a command that does not fit writes the character in the last column and then a carriage return, because the terminal has already moved the cursor down a line on its own. Read those bytes with no idea where the right edge is and the carriage return sends you back to the start of the same line, so the tail of the command lands on top of its own head:

before   dev libelf-dev xz-utilset install -y --no-install-recommends ... libssl-d
after    apt-get update && apt-get install -y --no-install-recommends ... libssl-dev libelf-dev xz-utils

The first of those is a command that does not exist, shown to a reader as though somebody ran it. So the width comes out of the recording, which wrote it down, and a line that fills the screen carries on the row below. The prompt counts too: two characters of prompt means the command wraps two characters earlier.

Files

  • kxray/replay/record.py writes the cast, driving one bash under a pseudo terminal. The pseudo terminal is not an implementation detail: make only draws its progress over the line above when it thinks a person is watching.
  • kxray/replay/cast.py reads asciinema v2 and accounts for every line as read, skipped or unparsed.
  • kxray/replay/terminal.py is deliberately not a terminal emulator. Escapes, carriage return, backspace, tab, and the right edge.
  • kxray/replay/session.py walks the marks and cuts the stream into steps.
  • kxray/replay/notes.py keeps the sentences a person wrote about a session next to it, keyed by step number and by command. The recording is evidence and is never edited. A notes file that has drifted raises rather than attaching last week's sentence to this week's step.
  • kxwidgets/replay.py draws it. No JavaScript, like everything else in that package.
  • corpora/replays/tier1/ has the recording, its metadata and its notes, plus a README.
  • tools/baseline.py pins the number of steps as well as the line counts, because the steps are found from marks in the middle of the byte stream and a change to that walk would leave the line counts alone while quietly halving the walkthrough.

Checks

pytest 41 new tests and the whole suite green, ruff check, ruff format --check, node --test, lintprose, and baseline, claimledger, coverage, bpc, kconfig, refcheck, lintnb, kxmanim, diagrams --check, nbbuild --check, sitebuild --check, kxbox --check, vendor --check.

Closes part of #2.

A browser cannot build a kernel, so the sessions that build one are
recorded on a real machine and shipped as something a reader can walk
through: the command, what came back, how long it took, whether it
worked.

Adds the recorder, the reader, the widget, and the first recording,
which is Linux 7.2.2 built for User Mode Linux and booted, in the
container pin.toml names.
@tamnd
tamnd merged commit a99372f into main Sep 5, 2026
3 checks passed
@tamnd
tamnd deleted the replay-stepped-sessions branch September 5, 2026 21:53
@tamnd tamnd mentioned this pull request Sep 5, 2026
20 tasks
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