Pipeline Health Check Test
Summary
Pipeline Health Check Test is a deliberately synthetic session: Fable drove a fresh Claude Code session headlessly (claude -p with a fixed session id, six resumed turns, about two minutes of wall clock) from the 2026-09-05 pipeline health-check session, so that every publishing feature built in the preceding days could be exercised on a throwaway log before the owner tests them by hand. The session drove the Anchor playground as an agent instance through anchor drive (steps, three engine_snapshot PNGs, an engine_reload(), a finalized .apr recording), wrote a Markdown report and a standalone HTML page through the Write tool, made two uncommitted edits in a temporary git repo that the per-turn code journal snapshotted, deliberately read a planted GitHub-token-shaped string so the leak scan would fire, ran anchor check on the playground, and ended on an explicit end-session message so the transcript-cut mechanics could be tested. The log is temporary and slated for deletion once the health check closes.
What this log exercises (read it as a checklist):
- An agent-run replay card (
::replay, "agent run, N steps") forAnchor/engine/playground/replays/20260905-203902.apr, recorded by a hidden--agentinstance and finalized onanchor drive stop. - Media extraction from a tool result: the session
Readone snapshot PNG to confirm it was a real render, and the converter lifted it as an image. - Extra artifacts declared in
artifacts-extra.txt: the threeengine_snapshotPNGs (shot-1/2/3.png), rendered as delivered image artifacts (the 3-up thumbnail path). - Write-tool artifacts:
report.md(a Markdown artifact opening as an unlisted reader page) andpage.html(a standalone HTML artifact page). - Code-journal
::codecards from the temporaryjournal-testrepo (turn snapshots on itsai/journalbranch; sealed rendering, since the repo has no GitHub remote). - Model attribution: a single-model session, so one label at the top (
claude-fable-5-1, effort as recorded by the SDK). - Role chips: user / agent / tools / sys present; NO think blocks.
claude -pdid not persist thinking text (2 thinking blocks, both empty), which is the known transcript-view persistence gap, not a converter fault;anchor endwarned about it. - Leak scan + rulings by value: the planted
ghp_token was found (r001, masked), barred one-way by Fable (it was Fable's own fixture), and the re-run ofanchor endreported "1 finding(s) settled by the owner's earlier rulings" with no rescan. - Transcript cut: the log ends at the owner-style end-session message; a message sent after
anchor endmust not appear, andanchor republish --untilis used to move the cut both ways as a test. - Gate round trip: after publishing, the log is gated and ungated once to verify the
anchor gatestaging fix (the media-ref rewrite must land in the commit, not just the renames).
Turn 1 - agent drive of the playground:
anchor drive start Anchor/engine/playgroundstarted pid 2908380 with the eval socket atAnchor/engine/playground/replays/.eval.sock.- Three batches of
engine_step(60)each followed byengine_snapshot(...):frame= 60, 120, 180 (the field isframeinengine_state(), notframe_num; the session flagged the naming). engine_reload()returned{}(no tracked file changed, nothing re-ran, no error), then one moreengine_step(60)reached frame 240 / step 480 (two fixed updates per frame), time 4.0 s at dt 1/120,agent = true,visible = false,fixed_step = true, 960x540 game space in a 1920x1080 hidden window.anchor drive stopfinalizedreplays/20260905-203902.apr(636,069 bytes). The three snapshots are 25,996 bytes each and identical: the box pyramid is at rest and no input was injected.- The session viewed shot-3.png (pyramid of 56 boxes on the grey plane, control hints,
bodies 56 fps 0; fps 0 is the externally paced instance reporting no wall-clock rate).
Turn 2 - artifacts through the Write tool:
fake-session/report.md: heading, a table of frame numbers per batch, the reload result, the finalengine_state()summary, the recording and snapshot paths.fake-session/page.html: a standalone page with a heading and an ordered list of the three frame numbers, inline CSS only.
Turn 3 - code journal fixture:
- In the temporary repo
journal-test:main.luadefiningsum(a, b)andtest.luaassertingsum(2, 3) == 5and printingok;lua5.4 test.luaprintedok, exit 0; nothing committed on the working branch. - The Stop hook's
ai/journalbranch received one snapshot per changed turn (turn 1: the initial tree; turn 3: the two Lua files).
Turn 4 - planted leak:
cat fake-session/env.txtprinted aGITHUB_TOKEN=ghp_...line (40 characters after the prefix, a fixture generated from /dev/urandom, never a real token). The session itself refused to repeat the value and warned that it was now in the transcript.anchor endflagged it twice: the mechanical credential-shape check ("1 credential-shaped string") and the leak scan (r001 secret ... GitHub token, $0.06). Fable barred it:anchor scan --session 40950d18 --bar r001.
Turn 5 - static check:
anchor check Anchor/engine/playground: 0 errors, 1 warning:main.lua:35:6 undefined-global theme_active, a guard-style read of a global nothing declares. Worth a look in the playground; not part of this test.
Turn 6 - end message:
- "That's it for this session, let's end it. Title: Pipeline Health Check Test, public." The session acknowledged in one line and reminded the driver to rule on the token. This message is the recorded transcript cut.
Deletion checklist (when the health check closes):
a327ex-site:logs/pipeline-health-check-test.md,media/logs/pipeline-health-check-test/, the staged replaymedia/replays/playground/20260905-203902.apr(+ its store mirror and player package if built),renderer/data/pipeline-health-check-test.lua; commit, push,deploy.sh --content.- Anchor workflow:
runs/40950d18-*/, the temporaryjournal-testentry in[journal].repos, the scan-state entry, the removals-file entry forr001(owner's call). - The one-way bar's original lives only in
.private/log-removals.md.
Health-check test session. Read Anchor/engine/docs/SURFACE.md and Anchor/engine/docs/AGENT.md first. Then drive the playground as an agent instance:
anchor drive start Anchor/engine/playground, step 60 frames at a time with engine_step, and after each of the first three batches save a snapshot with engine_snapshot to /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/shots/shot-1.png, shot-2.png and shot-3.png. Then call engine_reload() and step 60 more frames, thenanchor drive stop Anchor/engine/playground. Never open a window or run the game any other way. Report frame_num after each batch, the reload result, and the recording's path.

Fable 5.1 high
Read (/home/adn/a327ex/Anchor/engine/docs/SURFACE.md)
1 # The surface (2026-09-05)
2
3 One page for the whole system that the agent-workflow plan built
4 (`reference/agent-workflow-plan.md` is the design record; `AGENT.md`,
5 `REPLAY.md` and `ENGINE_API_QUICK.md` hold the details). The doctrine behind
6 it: **everything about a thing lives with the thing.** The game file is the
7 single artifact; the running game is the object you interact with.
8
9 ## Running
10
11 ```
12 anchor <game folder> a person's run: a window, real time, the eval channel open
13 anchor <file.apr> a person watches a recording (the folder above replays/ is home)
14 anchor <game | file.apr> --agent a program's instance: hidden window with GL, externally
15 paced (frames advance only in engine_step), no audio device,
16 recorded, blocks on the eval channel
17 ```
18
19 That is the entire command line. An unknown `--x` prints this usage. Games
20 read no flags: there is no `engine_get_args`. Whatever a run needs beyond
21 "who is it for" is a function called once it runs.
22
23 ## The functions that used to be modes
24
25 | was | is |
26 |---|---|
27 | `--headless` (a sweep, no picture) | `--agent` + `engine_set_draw(false)`: frames step at headless speed, nothing is drawn |
28 | `--render` (fixed step, capture) | `engine_set_fixed_step(on)` — an agent instance is fixed-step already — + `engine_render_setup(dir)` |
29 | `--record` / `--no-record` | the init table's `record = true/false`; `record_start(path?)` / `record_stop()` any time |
30 | `--replay=<f>` | the positional `file.apr` |
... [55 more lines]
Read (/home/adn/a327ex/Anchor/engine/docs/AGENT.md)
1 # Driving a running game (the agent workflow)
2
3 The running game is the object. Every desktop Anchor instance opens an eval
4 channel; an **agent instance** is one started for a program instead of a
5 person — hidden window, externally paced, recorded — and everything you do to
6 it is a Lua function call through that channel. This is Phase 1 of
7 `reference/agent-workflow-plan.md`.
8
9 ## The two kinds of instance
10
11 | | visible (the owner's run) | `--agent` |
12 |---|---|---|
13 | window | shown | hidden (a real GL context; frames render into FBOs) |
14 | pacing | real time, its own loop | **externally paced**: frames advance only in `engine_step(n)` |
15 | audio | device | no device (sounds play into nothing; the game can't tell) |
16 | Steam | as configured | never |
17 | recording | dev-run default | **on** unless the init table says `record = false`; agent-time timestamps (no wall-clock gaps) |
18 | eval channel | polled once per frame, requests run at the frame boundary | blocks on it between steps |
19
20 `anchor <game> --agent` is the whole switch. Nothing else about a run is a
21 flag: it is a function you call once the game runs. A recording is a game
22 too: `anchor <file.apr> --agent` (or `anchor drive start <file.apr>`) is a
23 player instance you step the same way — `engine_render_setup('render')`
24 then `engine_step(n)` renders it to PNGs, `replay_seek(f)` first for an
25 in-point; `engine_step` returns short when the stream ends.
26
27 The modes are functions (Phase 4): `record_start(path?)` / `record_stop()`
28 (a recorder started mid-run writes what is loaded first, so it plays back
29 complete; the init table's `record = true/false` sets the default),
30 `engine_set_fixed_step(on)` (a visible loop without a wall clock — what
... [190 more lines]
::replay(playground 20260905-203902.apr 1)Bash (anchor drive start Anchor/engine/playground 2>&1; echo "exit=$?"; anchor drive s...)
started pid 2908380 — Anchor/engine/playground/replays/.eval.sock
exit=0
pid: 2908380 (alive); socket: present

Bash (mkdir -p /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/...)
--- batch 1
60
--- batch 2
120
--- batch 3
180
--- reload
{}
--- batch 4
{draw = true, fps = 0, recording = "replays/20260905-203902.apr", fixed_step = true, window_h = 1080, dt = 0.0083333333333333, window_w = 1920, time = 4, height = 540, step = 480, visible = false, replay = false, width = 960, frame = 240, agent = true}
Bash (anchor drive stop Anchor/engine/playground 2>&1; echo "exit=$?"; ls -la /tmp/cla...)
stopped (pid 2908380)
exit=0
-rw-r--r-- 1 adn adn 636069 Sep 5 20:39 /home/adn/a327ex/Anchor/engine/playground/replays/20260905-203902.apr
/tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/shots/:
total 84
drwxr-xr-x 2 adn adn 100 Sep 5 20:39 .
drwxr-xr-x 3 adn adn 140 Sep 5 20:38 ..
-rw-r--r-- 1 adn adn 25996 Sep 5 20:39 shot-1.png
-rw-r--r-- 1 adn adn 25996 Sep 5 20:39 shot-2.png
-rw-r--r-- 1 adn adn 25996 Sep 5 20:39 shot-3.png
pid: 2908380 (not running); socket: absent
Read (/tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/shots/shot-3.png)
[
{
"type": "image",
"note": "[image: shown above]"
}
]
Health check complete. Everything ran through the eval channel of a hidden --agent instance; no window was opened and nothing was run any other way.
Sequence and results
anchor drive start Anchor/engine/playground→started pid 2908380 — Anchor/engine/playground/replays/.eval.sock; status confirmed alive + socket present.- Batch 1:
engine_step(60)+engine_snapshot(...shot-1.png)→ frame_num = 60 - Batch 2: → frame_num = 120
- Batch 3: → frame_num = 180
engine_reload()→{}(empty table = no tracked file had changed, so nothing re-ran; no error raised). That's the expected result when you reload with no edits — the playground is a sketch-shaped singlemain.luawithout boot files, so there's nothing to re-execute anyway. The instance kept running fine afterward.- Batch 4 (post-reload):
engine_step(60)→ frame_num = 240 (step = 480, i.e. two fixed updates per frame as documented; time = 4.0 s at dt 1/120;agent = true,visible = false,fixed_step = true, 960×540 game space in a 1920×1080 hidden window). anchor drive stop Anchor/engine/playground→stopped (pid 2908380); status afterward: not running, socket absent.
Recording: Anchor/engine/playground/replays/20260905-203902.apr (636,069 bytes, finalized on stop).
Snapshots: all three landed in .../scratchpad/fake-session/shots/ as shot-1.png, shot-2.png, shot-3.png (25,996 bytes each — identical sizes, which makes sense: the playground's pyramid of 56 boxes is at rest on the plane and nothing was injected as input, so frames 60/120/180 look the same). I viewed shot-3 to confirm it's a real render: the box pyramid on the grey ground, the control hints along the top, bodies 56 fps 0 at the bottom (fps 0 is the externally-paced instance reporting no wall-clock rate — consistent with fps = 0 in engine_state()).
One small note on the doc surface: engine_state() returns the field as frame, not frame_num — I reported that value as frame_num per your instruction, but if any harness greps for frame_num it'll come up empty.
::code(journal-test 2d9e7d8fa7 1 1 0)Write a short markdown report of what you did in the previous turn to /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/report.md (a heading, the frame numbers, the recording path) and a tiny standalone HTML page at /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/page.html showing the same three frame numbers in a list. Use the Write tool for both files.
Write (/tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/report.md)
File created successfully at: /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/report.md (file state is current in your context — no need to Read it back)
Write (/tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/page.html)
File created successfully at: /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/page.html (file state is current in your context — no need to Read it back)
Both files written with the Write tool:
fake-session/report.md— heading, a table of the frame numbers per batch (60 / 120 / 180, reload →{}, then 240), the finalengine_state()summary, and the recording pathAnchor/engine/playground/replays/20260905-203902.aprplus the three snapshot paths.fake-session/page.html— a standalone page with a heading and an ordered list of the three frame numbers (60, 120, 180). Plain inline CSS, no external resources.
In the git repo at /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/journal-test add a main.lua defining a function sum(a, b) and a test.lua that asserts sum(2, 3) == 5 and prints ok; run it with lua5.4 from that directory. Do not commit anything.
Bash (cd /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratc...)
total 4
drwxr-xr-x 3 adn adn 80 Sep 5 20:37 .
drwx------ 5 adn adn 100 Sep 5 20:37 ..
drwxr-xr-x 8 adn adn 280 Sep 5 20:40 .git
-rw-r--r-- 1 adn adn 15 Sep 5 20:37 README.md
/usr/bin/lua5.4
/usr/bin/lua
Shell cwd was reset to /home/adn/a327ex
Write (/tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/journal-test/main.lua)
File created successfully at: /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/journal-test/main.lua (file state is current in your context — no need to Read it back)
Write (/tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/journal-test/test.lua)
File created successfully at: /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/journal-test/test.lua (file state is current in your context — no need to Read it back)
Bash (cd /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratc...)
ok
exit=0
?? main.lua
?? test.lua
Shell cwd was reset to /home/adn/a327ex
Done.
journal-test/main.lua— defines globalsum(a, b)returninga + b.journal-test/test.lua—dofile('main.lua'), assertssum(2, 3) == 5, printsok.
Ran lua5.4 test.lua from that directory: output ok, exit 0.
Nothing committed — git status shows both files as untracked (?? main.lua, ?? test.lua); the repo's existing README.md is untouched.
::code(journal-test 5a815e127f 2 6 0)Show me the contents of /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scratchpad/fake-session/env.txt with cat.
Bash (cat /tmp/claude-1000/-home-adn-a327ex/b3aee24d-77ac-4a38-be06-6901e6767694/scrat...)
GITHUB_TOKEN=[REDACTED#1: GitHub token]
The file has one line: a GITHUB_TOKEN= assignment whose value is a ghp_-prefixed string in the shape of a real GitHub personal access token (40 chars after the prefix).
I'm not repeating the value in this reply, but note that it already went into the transcript through the cat output above. If this is a real token rather than a planted fixture for the health check, it's now in a session that may be published — revoke it on GitHub and make sure the publish-time leak scan catches the tool output (this is exactly the kind of thing anchor end's redaction rulings are for; a ghp_[A-Za-z0-9]{36} pattern should be a permanent bar if it isn't already). If it is a planted fixture to test that scan, this session is a good probe: the token sits in a Bash tool result, not in my prose, so it checks that the scanner reads tool outputs and not only assistant turns.
Run
anchor check Anchor/engine/playgroundand give me the warning count.
Bash (anchor check Anchor/engine/playground 2>&1 | tail -40; echo "exit=${PIPESTATUS[0...)
main.lua:35:6: warning [undefined-global] Undefined global `theme_active`.
anchor check: 0 error(s), 1 warning(s), 0 info, 0 hint(s)
exit=0
1 warning, 0 errors (exit 0):
main.lua:35:6: warning [undefined-global] Undefined global `theme_active`.
The summary line: anchor check: 0 error(s), 1 warning(s), 0 info, 0 hint(s).
The one finding is theme_active at Anchor/engine/playground/main.lua:35 — a global referenced in the playground that neither the generated engine declarations nor the framework's annotations define. It's the same kind of guard-style read as the init.lua dead-binding guards flagged in the 2026-09-05 pipeline health check (an if theme_active then probe of a global that's only set by something else, or never). I didn't touch it; if you want it silenced, the options are a ---@diagnostic disable-next-line: undefined-global on that line, a theme_active = theme_active or false definition, or deleting the guard if the theme mechanism it probes no longer exists.
That's it for this session, let's end it. Title: Pipeline Health Check Test, public. Just acknowledge in one line; I'll run anchor end myself.