Skip to content

DemoTranscriptExecSmokeTest.kt

src/jvmMain/kotlin/afield/examples/DemoTranscriptExecSmokeTest.kt · 1393 lines

package afield.examples

import afield.document.graph.Box
import afield.tools.automation.OperationTranscript
import afield.app.protocol.Rect
import afield.document.persistence.SavingContext
import afield.app.server.SessionInformation
import afield.document.graph.box
import afield.document.graph.isTrue
import afield.runtime.macros.checkAtticAndExecute
import afield.runtime.macros.checkIntroductionAndExecute
import afield.runtime.macros.checkPreambleAndExecute
import afield.lang.remotejs.KotlinJsExecution
import afield.lang.remotejs.kotlinJsTopologyNeedsUpdating
import afield.app.server.thisSessionInfo
import kotlinx.coroutines.runBlocking
import org.yaml.snakeyaml.Yaml
import java.io.File
import java.nio.file.Files
import java.util.concurrent.ConcurrentHashMap
import kotlin.script.experimental.api.ScriptDiagnostic
import kotlin.system.exitProcess

FIELD_TEST id: examples.demo-transcript.exec runner: jvm-main entrypoint: afield.examples.DemoTranscriptExecSmokeTest tags: [smoke, agents, transcript, docs, verification] timeoutSec: 1200 expect: exitCode: 0 stdoutContains: - "DemoTranscriptExecSmokeTest ok"

object DemoTranscriptExecSmokeTest

Verification-integrity harness: executes / compiles the box bodies of every shipped demo transcript under dev_docs/agents/transcripts/ and asserts they carry no diagnostics.

On the timeout

timeoutSec was 300 until 2026-08-12, when the 58th transcript pushed the sweep past it: the run died at 4m46s having reached spatial_geometry_examples, within a handful of transcripts of the end. The cost is concentrated rather than spread, so trimming is the wrong first instinct — morpho_circuit_search alone is 66.5s of GraalJS compile-and-simulate, five times the next largest, and faust_voice is 13.7s of one-off libfaust warm-up. A cap at 1.05× the observed runtime would flake on any machine slower than the one it was measured on, so it is now 600: roughly 2×, with room for the corpus to keep growing.

600 then became marginal on 2026-08-25, with the Kotlin 2.4.20 fork uplift: the Kotlin/JS transcripts (remotear_*, vision_geometry_*, tone_synth_sequence, visionos_start) had been failing in milliseconds against a stale kotlin-stdlib-js klib, and once that was rebuilt they do real Kotlin->JS compiles. The tell was progress, not the error: the run reached 108 execute lines in ~300s before, and only 93 in 600s after. A full green sweep then measured 570s — under the old cap, but by 5%, which is precisely the margin the paragraph above says will flake on a slower machine. Hence 1200: ~2x the measurement, same rule as before.

If it times out again, measure before trimming. AFIELD_DEMO_TRANSCRIPT_VERBOSE=1 echoes what the boxes printed, and the evaluate=<n>s lines in the run's stdout attribute the time to the transcript being replayed when they were emitted — which is how the two numbers above were found. And note the harness caches the catalog: a timeoutSec edit does nothing until run_field_tests.py --refresh-catalog (or a run whose --id is not in build/field-tests/catalog.json), so a stale limit will re-fail at the old number and look like the edit had no effect.

Why this exists: on 2026-07-18 fn_differentiable_fit_demo.yaml shipped with two compile errors in its box body and merged, because nothing in CI ever ran the box Kotlin — OperationTranscriptMacroSmokeTest only applies transcript ops and diffs the code string. This test closes that gap by driving the REAL engine (no toy compiler):

  • EXECUTE demos (pure-compute; no Window/Renderer): replayed end-to-end through the identical path a user/agent uses — OperationTranscript.runMacro -> executeFragmentAndWait -> the real compile+eval pipeline. A box compile error does not throw and does not clear run.success; it surfaces only as an output line with kind == "error" (see dispatchExecutionOutput in execution.kt). So we assert BOTH run.success AND zero error-kind output lines. This is the class of failure the fn-fit demo shipped with.

  • COMPILE-ONLY demos (open a GLFW Window/Renderer or depend on a running renderer's context — unsafe/inappropriate to spin a render loop headless at 2am): the box graph is built with the real create/connect apply path, then each box's Kotlin code is run through the real macro expander (applyMacros), the real preamble machinery (checkPreambleAndExecute — which only evaluates property-delegate declarations, never a Window) and the real K2 compiler (compileSnippetAndWait) with EVAL SKIPPED. GLSL shader properties (vertex/fragment/ compute) are not Kotlin and are not compiled here (they are exercised by the graphics render smokes). This still catches the fn-fit class of error (unresolved refs, ambiguous overloads).

Adding a demo: drop the *.yaml in dev_docs/agents/transcripts/, then add its name to exactly one of EXECUTE or COMPILE_ONLY below. An unclassified transcript fails the test on purpose.

These three sets are also where the public index page's claim about CI comes from: afield.tools.docs.AgentTranscriptIndexGuard reads them and fails the build unless public/docs/agent_transcripts.md has an entry for every transcript, filed under a section whose declared verification level matches the set the transcript is in. That is why they are internal rather than private — classification is decided here and nowhere else.

object DemoTranscriptExecSmokeTest {

internal val EXECUTE

Pure-compute demos that are safe to run to completion headless.

body — 485 lines, from line 98
    internal val EXECUTE = setOf(
        // visualizers.md's presets demo: a preset bar, record button and MIDI learn; the MIDI box feeds stand-in CCs.
        "controls_presets_morph_midi",
        // Loads a committed glTF fixture through Assimp and opens column()/tree() on it; no renderer.
        "assimp_browsing",
        // stacksl_examples.md's fitting demo: fits a StackSL program's literals on the CPU.
        "stacksl_fit_demo",
        // ai4_chat_archive.md's demo: reads the example chat under public/docs/assets by checkout path.
        "ai4_chat_archive",
        // IN_FIELD_AGENT_HARNESS.md's repo-tools demo: a toy checkout in a temporary directory, removed after.
        "ai4_repo_tools",
        "particle_video_reader",
        // contour_trees.md's starting point: contour trees of a computed field and the leaf print, plain Kotlin.
        "contour_trees_explorer",
        // vector_field_topology.md's starting point: the topology of a computed flow and of a photograph's gradient.
        "vector_field_explorer",
        // mapper.md's starting point: Mapper graphs of generated point clouds.
        "mapper_explorer",
        // The staged-project starting place: a level edited through guarded tools, three study-build
        // stages behind a gate, a qualified loot rule and a corpus of variants. CPU only; Manifold is used
        // when installed and the blockout says so when it is not.
        "staged_project",
        // Five CPU boxes on the drawing layer's scene (the root box's `drawing` property): draw from
        // code, read it back, register a stroke reaction, and read two graphic scores by cutting
        // them. No client is needed — the scene is server-side data — so every box executes headless.
        "drawing_notation",
        // Three CPU boxes and their saved notation scene, promoted from the offline-viewer fixture.
        "offline_viewer_demo",
        // drawing_and_notation.md's full tour: every mark kind, a score, the API audit, stroke/arrow/text
        // reactions. CPU only; headless, `hand`'s page script goes to no browser but the box completes.
        "notation_elements",
        // automating_field.md's Macro Recorder starting point: saves a macro, replays it as offset copies and by steps.
        "macro_recorder_stamp",
        // interactive_guides.md's starting point: a four-step slideshow over two CPU boxes. Headless the
        // notes and selections go to no browser, but every box (the guide included) runs to completion.
        "slideshow_guide",
        // Five CPU boxes: draw a score on the drawing, read it with notation.space, turn it into notes
        // twice (as drawn, and snapped to a scale), render with patRenderStereo into audio players,
        // and measure onsets and pitches in the renders. No
        // audio device is opened; the audio is an offline buffer displayed as a data: URI.
        "drawn_score",
        // Five CPU boxes: two strokes drawn from code, read back, resampled and paired with
        // matchPoints, morphed, and the morph played as pitch into audio players. Offline buffers only.
        "drawn_morph",
        // Three CPU boxes: a SourceFramesStudy of a box property with registerStudyTools, then two
        // children that publish a SetNumbers revision and sweep/try variants. Frames come from a CPU
        // painter at 256 px at most; no Window, no Renderer.
        "studies_rings",
        // Lua over LuaJIT: the transcript executes only its Kotlin root, which prints where LuaJIT was
        // found (or how to install it), so it needs nothing the build does not own. The Lua boxes are a
        // guest language; afield.lang.lua.LuaStateTest and LuaBoxTest run their code against a real lua_State.
        "lua_demo",
        // Three bounded CPU boxes: refusal messages, field-clock curves, and a computed ledger.
        // No audio device or renderer; unlike marf_demo, every box is safe to execute headless.
        "marf_field_clocks",
        // Three bounded CPU/allocation workloads, one real recording, then stop and assert samples.
        "profiling_timeline",
        "simulation_clock",
        // The first of the EXECUTE demos that *launch* rather than execute: its box is a `loop { }`,
        // so the transcript launches it, runs 90 real runloop frames (startRunloopDaemon above is
        // what advances them) and stops it. Safe here because the whole scene is CPU physics plus
        // showPlane — Skia SVG -> display — so there is no Window and no Renderer to spin up.
        "cloth_simdverlet_start",
        // The world cloth_simdverlet_start hangs in: colliders, tearing, wind and a rope. Also a
        // launcher rather than an executor, and for the same reason — four frame loops. All four
        // boxes are launched together, which is the part worth holding down: four independent
        // SimdVerlet worlds stepping in one runloop. Same no-Window, no-Renderer argument as the
        // cloth — CPU physics plus showPlane, Skia SVG -> display.
        "simdverlet_world",
        // Inertial easing and the movement model built on it, as three `loop { }` boxes that get
        // longer in that order — launched, 90 frames, stopped, on the same no-Window argument as the
        // cloth: `showSignals` and `showPlane` are Plotly/Skia into `display`. The equality the
        // second box demonstrates is asserted properly in
        // InterpolantsTest.theHandDrivenSolveIsExactlyWhatInertialToPackages; what running it here
        // buys is that `inertialTo`, `MovementState` and the control-group callbacks are exercised
        // through the real box path, which no unit test covers.
        "inertial_movement",
        // The creature GA: two launched loops that evolve on their own thread under a box-written
        // ContactModel (a water surface; a floor under gravity). CreatureEvolutionTest covers the
        // library; running it here covers the box path — a ContactModel and fitness written in a box
        // and handed to the library, and the .end { } that stops the evolution thread.
        "evolving_water_bugs",
        // The keyframe editor: a launched loop playing a curve into a meter, and three executed boxes
        // (typed keys; an onChange overlay; key types of its own with interpolant(...)). Pure CPU — the editor is an iframe in `display` — so what
        // running it buys is keyframes(...), KeyframeEvaluator and the Keyframe-seeded overload
        // through the real box path.
        "keyframe_editor",
        // A gesture take baked into two keyframe lanes: one launched loop that bakes on first run and
        // plays the pad, its playhead and both lanes, stopped after 90 frames. Pure CPU; what running it
        // buys is bakeGesture, toKeyframes and setTake through the real box path.
        "gesture_to_keyframes",
        // A pen gesture's take baked into x/y/pressure lanes and painted from them with a brush of
        // moving hairs, and one take painted four ways to show what the pad keeps and drops. Two
        // still CPU boxes through afield.graphics.paint; without the paint library both say so and stop.
        "gesture_paint_replay",
        // BSP-OT matching: two still boxes executed (one of them a million points a side, a few
        // seconds) and a launched morph loop stopped after 90 frames.
        "point_matching",
        // flowMatch samples graded by a Datalog program and drawn by island; three CPU boxes, all executed.
        "flow_islands",
        // A word cut out of a plate by Manifold, judged by Datalog, bridged; three CPU boxes, all executed.
        "stencil_bridges",
        "fn_differentiable_fit_demo",
        // The two ways to run a `@{...}` Kotlin right-hand side, side by side; all three boxes execute.
        // `fnrw_broken` is the one-box form, which used to re-enter its own box 172 levels deep and
        // never return — it is runnable here only because `FnRewriteSelfBoxReentry` now refuses the
        // second hydration, which breaks the recursion: its body runs exactly twice and the rewrite
        // then completes. Two "one box: body entered" lines in the verbose output are that, and are
        // expected. The refusal goes to the `error,fn-rewrite` log channel and NOT to box output, which
        // is why this stays green under the zero-error-lines assertion. The mechanical pair is
        // functions.rewrite-kotlin.{live-box,two-box}; this transcript is the loadable version, and it
        // is also where `parents.last()` as the host-box idiom is demonstrated.
        // See dev_docs/FN_REWRITE_SELF_BOX_REENTRY.md.
        "fn_rewrite_kotlin_boxes",
        // Blending two code keyframes. Pure CPU: the StackSL goes through the real compiler and then
        // through afield.lang.stacksl.cpu's GLSL interpreter, and the fourth box compiles generated Kotlin
        // through K2 inside its own box — which is the one part of the feature a unit test cannot
        // reach, and the reason this is EXECUTE rather than COMPILE_ONLY.
        "keyframe_code_blend",
        "linalg_thumbnails_demo",
        "hello_display",
        "hello_display_capture",
        "ai4_iframe_hello",
        // Registers two box tools and prints where stalls go; no model and no git call on replay.
        "ai4_cold_reader",
        // Two boxes, no Window: the rates box's variables and the sheet box, whose `sheet { }` seeds
        // its property and displays the editor iframe. Replaying it runs the real seeding path.
        "spreadsheet_tour",
        // Two sheets whose assertions are solved by Ceres on open; replaying them runs the real solve.
        "spreadsheet_constraints",
        "two_box_context",
        // Compiles a data class onto the shared classloader and uses it from three other boxes, one
        // of them through @rootExported. Slower than its neighbours — box 1 shells out to
        // K2JVMCompiler and then recycles the other boxes' engines — but it is pure compute, and the
        // second run of box 1 is what holds the two engine-recycling regressions down.
        "shared_types_demo",
        // Kotlin DataFrame's compiler plugin. All FOUR boxes are executed. This comment used to say
        // three were, with the fourth created and deliberately not executed because it documented a
        // compile error on purpose — which was true of `dataframe_boundary`, the box d7d8dccc2
        // replaced with `dataframe_live`. That commit added the fourth `execute` op and rewrote the
        // public index paragraph, and did not touch this file, so the classification comment outlived
        // what it described. Box 4 is the `typed()` demo: two `//%-` fragments in one execution, which
        // is why running it here exercises the preamble re-entry path as well as the plugin.
        "dataframe_typed_columns",
        "spectral_examples",
        "imaging_cv_examples",
        // Two CPU boxes: one executed stipple(), one launched `loop { }` taking a Lloyd step a frame
        // for 90 real runloop frames. Both draw through stipplesToImg -> showImg (a PNG into display),
        // so no Window and no Renderer.
        "stippling",
        // A photograph to a printable solid, across four families that do not import one another:
        // DepthAnything (ONNX) for monocular depth, the imaging family to normalise it, reliefMesh to
        // close it into something with a volume, and Manifold for the Boolean. EXECUTE rather than
        // COMPILE_ONLY even though it reaches a pretrained model and a native library, because both
        // run on the CPU and the weights are already cached under ~/.afield/onnx; the pictures are
        // TriMesh.renderPng, the AWT-free CPU rasteriser, so there is no Window and no Renderer.
        "photo_to_solid_chain",
        "imaging_vector_kernels",
        "imaging_visualizers_demo",
        "mesh_geometry_examples",
        // Three pure-compute boxes that read geometry off disk: a committed GLB through Assimp, an
        // OBJ/STL round trip via a temp directory, and an ASCII PLY written and read back. Every
        // picture is showMesh — CPU rasterizer -> display — so no Window and no Renderer. The GLB box
        // is deliberately in CI: it is what makes "Field can look at what its back ends produce" a
        // checked claim rather than a page's assertion.
        "geometry_from_disk",
        "vision_metal_cube",
        // JVM Kotlin that compiles a Live Metal box's Kotlin/JS for real, runs it, and rasterises
        // the mesh it builds. No headset and no Metal toolchain, so it runs to completion here.
        "vision_geometry_desk_preview",
        // Compiles a Faust program with the vendored libfaust-wasm and draws it: a block diagram
        // straight out of the compiler, an offline render, and the UI metadata the compiler reports.
        // The playing box and the deliberately-broken box are NOT executed by the transcript — one
        // opens an audio device, the other fails on purpose — so neither can trip this harness.
        "faust_voice",
        // Faust as a polyphonic instrument that a `Pat` pattern plays. Four of its five boxes render
        // offline through the same `FaustVoiceBank` the audio thread uses, in 128-frame blocks, into WAV
        // bytes and a plot — so no audio device and no GPU. The fifth returns a live launch and is not
        // executed by the transcript.
        "faust_polyphony",
        // sound_paths.md's starting point: the declared map printed, then one Dirt-Samples beat through
        // a Signal-graph lowpass and a Faust lowpass, all offline. Box 2 needs the pinned Dirt-Samples
        // subset (./gradlew fetchDocsDirtSamples) and fails loudly without it, by decision: no synth
        // stand-in for this comparison.
        "sound_path_map",
        "pattern_rewrite_worked_example",
        "rewrite_strategies_demo",
        // Three pure-compute boxes over the `pat` Fn graph op: one prints the node, two run a real
        // fitGradient over a rhythm's numeric leaves and plot the result. No Window, no Renderer.
        "pat_fn_graph_fit",
        // Eight pure-compute pattern boxes; showPatRoll is drawToSVG -> display, so no Window, no
        // Renderer, no GPU and no audio device.
        "pattern_language_composition",
        // Five pure-compute boxes closing notation -> sound -> notation. Every buffer is finite and
        // offline (patRenderStereo, doughRenderMono), the figure is drawToSVG -> display, and the
        // analysis is this repo's own YIN. No Window, no Renderer, no GPU, no audio device, and in
        // particular no services/field-audio-ai.
        "pitch_transcription_roundtrip",
        // Six pure-compute Petri boxes. showPetriNet is FLines -> display and showPatAudio renders
        // through the same offline CPU voice, so this plays without an audio device.
        "petri_sound",
        // Five pure-compute boxes over the exhaustive state-space analysis. showPetriStateSpace is
        // FLines -> display like showPetriNet, and nothing here makes a sound.
        "petri_state_space",
        // Three pure-compute boxes drawing one net under FORCE and under TALA. showPetriNet is
        // FLines -> display, and the TALA half falls back to FORCE when native/field-d2-layout is
        // not built, so this runs the same either way.
        "petri_tala_layout",
        // Nine boxes and their connections, created and wired but never executed: the scene is the
        // starting position for the "Lay out selection" commands, which the reader runs.
        "box_layout_demo",
        // Seven pure-compute boxes: prop inheritance through a diamond (nearer wins, then
        // connection order) and fromContext / fromContextWithNameOrNull, each writing one markdown
        // line. The examples of public/docs/why.md, in one document.
        "box_graph_demo",
        // Deterministic synthesised audio and token fixtures through the real CodecVisualization
        // renderer; it deliberately does not reach for EnCodec's GPU service, so it completes here.
        "audio_ai_codec",
        // The first box draws the real typed gesture-board renderer. The service-backed second box
        // remains inert until a user runs it with Field Audio available.
        "audio_ai_gesture_board",
        // The guide and synchronized preview gallery use deterministic embedded data. The third
        // box contains the complete service-backed analysis workflow but remains inert in CI.
        "audio_ai_analysis",
        // The guide and synthetic calibration view execute without weights. The third box owns the
        // real emotion2vec/WavLM submission path and remains an explicit user action.
        "audio_ai_speech_embeddings",
        // The guide and synthetic phase portraits are pure compute; the live third box owns the
        // optional librosa service call and is deliberately left for an interactive run.
        "audio_ai_spectral_embeddings",
        // Guide and synthetic instrument-labelled piano roll are model-free. The third box owns the
        // gated MuScriptor service request and remains an explicit user action.
        "audio_ai_muscriptor",
        // The guide and durable monitor run headless. The second box contains the complete
        // Librosa-backed indexing and retrieval workflow, and remains inert until run by a user.
        "audio_ai_corpus_microscope",
        // Six independent pure-CPU WFC boxes; nothing opens a Window or a Renderer.
        "wfc_worked_example",
        // Five independent pure-CPU plane-geometry boxes; showPlaneGrid is drawToSVG -> display,
        // so no AWT, no GPU and nothing to launch.
        "spatial_geometry_examples",
        // Ten independent pure-CPU spatial-ALife boxes; nothing opens a Window or a Renderer.
        "alife_evolution",
        "alife_worked_example",
        // Five pure-CPU Braitenberg-vehicle boxes (braitenberg_vehicles.md sections 5-9); the
        // moving-lights box is an animateEvolution clip and is launched. No Window, no Renderer.
        "braitenberg_where_next",
        // Two pure-CPU section 10 boxes (equal-budget search comparison), about 11-13 s each.
        "braitenberg_brain_search",
        // Three pure-CPU alife_examples.md "Does search beat random?" boxes (compareSearch), under 20 s each.
        "alife_search_comparison",
        // Five independent pure-CPU statistics boxes; showPlaneGrid is drawToSVG -> display,
        // showSignals is a plotly HTML fragment -> display, histogramToSvg is just a string.
        "statistics_examples",
        // Two pure-CPU FLine boxes; showPlaneGrid is drawToSVG -> display, so no AWT and no GPU.
        "arclength_spacing_demo",
        // Two more of the same shape: the interior-Close demo reverses, subdivides and dashes
        // closed contours and reports the numbers, all through showPlaneGrid.
        "close_node_demo",
        // lp_curves.md's companion: LP methods as curve generators over LpPolygon, drawn through
        // showPlaneGrid / showPlane. The second box ends in `animate { }` and is launched; the third
        // draws the fan map in an editable threeLines viewer. CPU only.
        "lp_curves",
        // lp_curves.md's 3-D section companion: the same methods over LpPolytope, drawn through `three { }`
        // (a Three.js payload sent to display), so no Window and no Renderer. Both boxes execute.
        "lp_curves_3d",
        // The madp geometry toolbox: GeometryBuilder recipes drawn through `three { }`, so no Window and
        // no Renderer. breathing_sheet ends in animateGeometry, a Frames, so executing it draws t = 0.
        "madp_geometry",
        // Six independent pure-CPU dynamic-graph boxes. Four print and draw through showPlaneGrid /
        // showSignals; the other two build a Frames through animateEvolution, so executing them is a
        // compile check and the animation only runs when the box is launched. No Window, no Renderer.
        "dynamic_graph_systems_examples",
        // Five independent pure-CPU automatic-differentiation boxes; plot, showSignals and
        // showPlaneGrid all end in display(), so no AWT, no GPU and nothing to launch.
        "autodiff_worked_example",
        // λ-terms compiled to interaction nets, reduced and read back, against a substitution
        // evaluator that shares nothing with them. Pure CPU: no Window, no Renderer, no service —
        // the only drawing is `showInteractionOverview`, which is FLines into an output panel. Runs
        // the whole 25-term corpus under four schedulers, which is the same work
        // `stdlib.lambda-terms.smoke` does and takes about three seconds.
        "interaction_nets_lambda",
        // Its 3D companion: the same reductions, twenty at once, five terms by four schedulers.
        // EXECUTE for the same reason — the 3D is a Three.js viewer in the box's own output panel
        // (the `three(channel = …)` helper, which `polytope_canonical_gallery` and three other
        // EXECUTE demos already use headlessly), not a Window and not a Renderer. The box is a
        // launched `frames { }` loop the runloop steps once per tick (it used to block the runloop in
        // a sleep loop for over 30 s, which the runaway-box watchdog now interrupts); it ends when all
        // twenty cells have landed or after a 2600-frame cap and then prints `GRID3D DONE`.
        "confluence_grid",
        // 2026-10-26: reads the checked-in agent eval corpus (src/jvmMain/resources/ai4/) and prints it. Pure CPU.
        "agent_eval_corpus",
        // Sixteen saved demo documents under `dev_docs/agents/demos/` were invisible to this
        // harness, to the welcome screen and to the gallery — openable with `./field.sh` and
        // nothing else. These are those, promoted: nine of them already shipped an agent-authored
        // `build.yaml` and went over nearly as they were, the rest were assembled from the saved
        // document's own boxes. All pure CPU; the ones that open a window are in COMPILE_ONLY.
        "alife_script",
        "alife_walls",
        "chart_clip",
        "chart_primitive",
        "completion_rank_at_scale",
        "keyframe_key_discovery",
        "logging_layer",
        "midi_clip",
        // SMPTE timecode (2026-10-08): drop-frame arithmetic, LTC round trip under gain/noise/speed
        // ramp, MTC into a TimecodeClock, a clock following LTC. Pure CPU, no audio or MIDI device.
        "smpte_timecode",
        // Matrix profile (2026-10-15): motifs/discords, mSTAMP across four channels, and a
        // contrast profile. Pure CPU, signals generated in the box.
        "matrix_profile",
        // GP instruments (2026-10-10): GpOptimizer tunes a curve to a target, a GpPreference A/B loop
        // waits on two buttons, and the same loop runs against a simulated judge. Pure CPU, FLines only.
        "gp_instruments",
        // GP drawing (2026-10-10): a closed periodic loop through six pins, 1-D conditioning and a
        // 2-D field, with controls. Pure CPU, FLines and heatmaps only.
        "gp_drawing",
        // Inspector embeds (2026-10-12): tree(), column(), a live list and logs(). Pure CPU.
        "browse_embeds",
        "pattern_condense",
        "read_only_session",
        "solidsl_inverse",
        // Certified envelopes for Fn (2026-09-23): a SignalViewer with and without its certified
        // band, certifiedRange, and the refusal of a lambda channel. Pure CPU, no window.
        "certified_envelope",
        // Own-document tour (2026-10-29): numbers, squares, and a three-step slideShow that
        // selects them by name. Pure CPU.
        "slideshow_own_tour",
        // Controls renewal (2026-10-29): a five-control surface feeding a live figure and a slider
        // that re-renders itself mid-drag. Pure CPU, output-channel controls only.
        "controls_surface",
        // History renewal (2026-10-30): one box edited three times with autosave waits between, so
        // the document holds four versions for the history view's This Box list. Pure CPU.
        "history_box_story",
        // Kolakoski counts crossed with Euclidean placement. Pure CPU: the figures are
        // `showPatRollOf` (SVG into an output channel) and the audio is `showDirtAudio` /
        // `patRenderStereoDirt`, which mixes samples into a buffer offline — no audio device, and
        // no sample library on a build machine just means every token falls back to `patVoice`.
        // The third box writes a `.wav` into the *session's* document folder and a base64 player
        // into its own `customization`, both of which land in the scratch document the harness
        // opens, not in the tree.
        "kolakoski_euclid",
        // Four independent pure-CPU keyframer boxes. A KeyframeScope needs no GPU: a uniform
        // `Structure` is constructible bare and its bindings are plain maps, the camera box does
        // CameraPose arithmetic without ever touching a live `Camera`, and persistence writes to a
        // temp directory. showPlaneGrid is drawToSVG -> display, so no AWT and nothing to launch.
        "keyframer_worked_example",
        // Three pure-compute MorphoHDL circuit-search boxes. Every candidate is compiled by
        // morpho's own JS compiler on GraalJS and simulated by MorphoSim; the figures go through
        // showPlaneGrid (drawToSVG -> display), so no Window and no Renderer. The searches are
        // budgeted in compiler invocations, and they are the most expensive thing in this harness by a
        // factor of five: 66.5s of the sweep's ~300s, measured 2026-08-12 (44.6s + 21.9s of
        // `evaluate=`). Not "a few seconds each", as this comment said while that was still true.
        "morpho_circuit_search",
        // Four independent pure-CPU heatmap boxes; showHeatmap / showImage encode a PNG and end in
        // display(), so no AWT, no GPU and nothing to launch.
        "viz_showheatmap",
        // One pure-CPU polytope box. It derives polygonal FLines and publishes their payload to the
        // on-canvas Three.js viewer; no native Window or Renderer is opened by threeLines().
        "polytope_conway_demo",
        // Four more pure-CPU polytope boxes, one question each — the demo set POLYTOPE_ALGEBRA.md
        // asks for, and the loadable companion to public/docs/polytope_examples.md. Two end in
        // threeLines() as above; the other two rasterise with TriMesh.renderPng, which is the
        // AWT-free PngEncoder path into display(). No Window, no Renderer, no GPU either way.
        "polytope_conway_gallery",
        // A second pure-CPU polytope box on the same path: evaluate one Conway expression,
        // canonicalize it, and draw the realized and canonical forms side by side.
        "polytope_canonical_gallery",
        // Three pure-CPU polytope boxes for faces with holes and for the chained Conway vocabulary.
        // Box 1 ends in threeLines() as above; boxes 2 and 3 rasterise with TriMesh.renderPng. Box 1
        // also *catches* four exceptions on purpose — the operations a holed face refuses — which are
        // caught inside the box body and so are not error-kind output lines.
        "polytope_holes_and_chains",
        // Eight pure-CPU typography boxes. The letters are filled from their own contours by the
        // family's own scanline filler and shown through showImgGrid / showPlaneGrid / showMeshGrid,
        // all of which are PngEncoder or Skia-SVG into `display` — no AWT rasteriser, no GPU. Box 5
        // ends in `animate { }`, which registers an animation rather than blocking, as with the two
        // parameter sweeps in spatial_geometry_examples.
        "typography_worked_example",
        // The companion to animation_time_model.md: four pure-CPU boxes that start, stop, re-run and
        // clean up a frame loop through lines() and markdown(). The three it launches end in
        // `animate { }`, which registers an animation rather than blocking; box 4 is left for the
        // reader to drive.
        "frames_lifecycle",
        // The two fline_examples.md documents, promoted from saved documents in public/docs/assets.
        // threeInspect writes a three.js viewer into an output and inspect() writes an SVG with
        // click handlers; neither needs a Window or a Renderer. three_inspector_wall's code is also
        // that page's captured fence, which ThreeInspectorDemoDriftTest holds character for character.
        "three_inspector_wall",
        // stipple_transport.md's live sheet: a committed JSON model evaluated in plain Kotlin,
        // drawn through lines() and controlGroup sliders. CPU only.
        "stipple_sheet",
        // learned_pen.md's starting point: the Distill handwriting LSTM run in Kotlin from a bundled
        // resource, drawn through lines(); the drawing-panel box is a frames {} loop. CPU only.
        "learned_pen",
        // teaching_a_pen.md's starting point: the six bundled synthetic pens and rounder, loaded from
        // resources; the probe, holds and taste box are plain CPU loops. CPU only.
        "teaching_a_pen",
        // a_pen_that_sees.md's starting point: the bundled sensing pens (PenSense) and their blind
        // twins; two drawing-panel boxes are frames {} loops. CPU only.
        "a_pen_that_sees",
        "fline_hit_records",
        // fline_examples.md's measuring sections: locationAt, trim/splitAt, crossings, fill rules. CPU only.
        "fline_measure",
        // fline_examples.md's exact booleans and offsets: Area vs booleanExact, byOffsetting vs
        // byOffsettingExact, side by side through lines(). CPU only.
        "fline_exact_booleans",
        // The companion to sound.md's real-drums section. showDirtAudio / showPatAudio mix offline
        // into inline players, so no audio device; with no Dirt-Samples on the build machine every
        // token falls back to patVoice, which is box 3's subject anyway.
        "sound_render_path",
        // Two pure-CPU Ceres boxes: a canonical realization whose per-face planes are free variables in
        // the residual expression VM, and a sphere fit whose unknowns are not points at all. Both are
        // FFM calls into the native wrapper plus threeLines(), so no Window and no Renderer. Both
        // detect a wrapper older than ABI v3 — which is exactly what the *committed* dylib is, so that
        // is the branch CI takes — and draw the unsolved seed with an explanation rather than failing.
        "ceres_free_variables",
        // Two pure-CPU Manifold boxes: a machined part built out of Booleans, and a Kotlin lambda
        // marched into a guaranteed-closed solid and then sliced. Both are FFM calls into
        // `libmanifoldc` plus showMesh's CPU rasteriser, so no Window and no Renderer. Both detect an
        // absent Manifold — which on a machine without `brew install manifold` is the branch CI takes
        // — and the first one draws the same shape through Field's own marchingCubes instead, with the
        // difference named, rather than failing.
        "manifold_solids",
        // The optional Rust bridge opens a bundled design and renders both parameter values through
        // Field's CPU mesh preview. With no native artifact the box explains the build command.
        "solidcraft_intro",
        // Three pure-CPU boxes that paint and draw FLines through afield.graphics.paint (FFM into the optional
        // Rust wrapper over claude-paint's `paint` crate) and display a PNG, so no Window and no
        // Renderer. All check PaintCanvas.isAvailable first; without `./gradlew buildPaintNative` — the branch CI
        // takes — they print where they looked and stop, rather than failing.
        "paint_canvas",
        // Two pure-CPU boxes over the same paint canvas: one registers an agentExtension (paint tools,
        // context, commands, the canvas as branch state) and shows the bare canvas, the other adds a
        // scribble aiTool that paints on it. No model is called; without the paint library the Studio
        // says so and stops, like paint_canvas.
        "ai4_paint_studio",
        // One pure-CPU box that keeps a StackSLEvolution on itself and registers an agentExtension over
        // it (evolve_* tools, context, commands, the population as branch state). Executing it renders
        // only the target: the search, LPIPS and the watchmaker start at the first evolve_step, which
        // needs a chat. No model is called.
        "stacksl_evolve_studio",
        // Three CPU boxes: pen strokes with pressure drawn on the drawing layer and read back with
        // notation, then painted (brush swell from the pen) and drawn in pencil through afield.graphics.paint.
        // The paint boxes check PaintCanvas.isAvailable first, like paint_canvas.
        "drawn_paint",
        // Two pure-CPU boxes that lower a RectSL scene to a GLSL distance field and print it.
        // `emitSdf` is a string builder over the compiled SceneProgram — no shaderc, no Window,
        // no Renderer — so both run to completion, which is also the cheapest check that the
        // emitter's own starting point still compiles against it.
        "rectsl_glsl_sdf",
        "solidsl_notation",
        // The solid_notation.md gallery and the geometry_from_disk.md repair demo, as starting points.
        // CPU only; the Manifold halves print an install message and fall back when it is absent.
        "solidsl_gallery",
        "mesh_repair_demo",
        // sorting_networks_examples.md's four fences as boxes, pure CPU. Only the two drawing boxes are
        // launched: the two annealing boxes search for 70 and 90 seconds, and the docs runner executes
        // them for the page's captures.
        "sorting_networks",
        // Two pure-CPU boxes on the third track backend: one prints a track's symbolic IR with its
        // free parameters still unsolved, the other fits those parameters against waypoints and
        // rasterises target/seed/fitted with TriMesh.renderPng into display(). `toFn` and the Adam
        // fitter are plain arithmetic over Fn — no shader, no Window, no Renderer.
        "rectsl_inverse_animation",
        // The same backend one refusal later: `->face-travel` lowers now, so a heading can be drawn
        // beside its own lowering and beside the quadrant-less form it is *not*, and then solved
        // through. CPU raster into display() and an Adam descent over an Fn — no Window, no
        // Renderer, and no atan2 anywhere except the one in the graph under test.
        "rectsl_heading_fit",
        // The three control-surface affordances: slider laws, a momentary button, a meter fed by a
        // `source` lambda. Pure arithmetic and one daemon sampler thread — no Window, no Renderer, no
        // audio device — so the whole transcript runs to completion here.
        "control_surface_laws",
        // Asks `afield.runtime.completion.CommonMemberPrior` and `KotlinSourceIndex` what ranking prior and completion
        // category the popup currently has for a receiver type, and tabulates the answer into
        // display(). Two in-process lookups and some string building — no completion server, no
        // window — so running it here is also the cheapest check that the two entry points
        // public/docs/completion_ranking.md names still exist with the signatures it claims.
        "popup_prior_inspect",
        // The madp GA demo: four boxes that each render nine ColorRpn programs on the CPU into an
        // EyeEvolution grid. No window, no GPU; ColorRpnTest pins the language to the demo's own
        // interpreter and EyeEvolutionTest drives the click path.
        "madp_ga",
    )

internal val COMPILE_ONLY

Graphics demos: box bodies are compiled (real pipeline, eval skipped) but not launched.

body — 359 lines, from line 585
    internal val COMPILE_ONLY = setOf(
        // Interactive playback loops and optional ARDY/CLoSD workers; portable APIs have JVM tests.
        "motion_recording_probe",
        "motion_closd_recording",
        "motion_resistance_replay",
        "motion_tether_score",
        "motion_closd_live",
        "motion_ardy_path",
        "motion_ardy_intentions",
        "motion_ardy_foot",
        "motion_recorded_results",
        "motion_ardy_demo_replay",
        // Spins a box in while (true) on purpose until the watchdog interrupts it (~5 s); the
        // runtime.runaway-box-interrupt.smoke FIELD_TEST runs the same escape.
        "runaway_box_interrupt",
        // The demo document: a renderer, an ElectronRTTView and a compute shader under it, and loops.
        "demo_document",
        // ORCA in a box output, playing through a returned PatSynthBank, so running it opens an audio
        // device; and ORCA itself only plays in a browser. `run_ui_regressions.py --id orca-io` runs
        // the same calls in a real page, and OrcaTest feeds the Kotlin side the page's reports.
        "orca_hello",
        // Two of them and a frames loop between: the same reasons. OrcaHeadlessTest runs its exchange
        // on ORCA's own operators in the JVM, and pins it to this transcript.
        "orca_duet",
        // ORCA with AI4 on the board: the same reasons as orca_hello. Its tools run in the orca-io regression.
        "orca_jam",
        // The canvas as a score: the lead box returns a loop that runs until stopped and its boxes play
        // dough through the audio device. ScoreTest steps the runner frame by frame and DoughLiveTest
        // measures the sound helper's levels, both headless.
        "madp_score",
        // Living strokes: the lead takes the drawing layer's pen and its strokes animate and play dough
        // until cleared, which needs a page to draw on and strokes drawn by hand. StrokeVerbsTest pins
        // the verbs and the frame the page draws, DoughLiveTest the pattern player.
        "madp_line",
        // charGrid: both builders return animations that run until stopped, and their glyphs play dough
        // through the audio device. CharGridTest checks the cells, the panel's diffs and the sound
        // grouping headless.
        "madp_chargrid",
        // A sender and a receiver that loop until stopped over loopback sACN on a fixed UDP port, and a
        // measuring box whose numbers SceneOnARigTest already pins.
        "chain_scene_to_light",
        // Syphon (macOS frame sharing) through the optional native/field-syphon library: two of the
        // three boxes are `loop { }`s that publish and receive frames across Syphon's shared GPU
        // surface. Not evaluated here (a Mac, Metal and `./gradlew buildSyphonNative`);
        // afield.io.syphon.SyphonTest runs the cross-process round trip with exact pixels instead.
        "syphon_bridge",
        // The same over the Vulkan renderer: followOnGpu (GPU copy into an IOSurface) and a Syphon
        // client as a texture. Needs a Window, a Renderer and the library; verified live by hand.
        "syphon_gpu",
        // Editable tiled fluid, grain/strokes and motion RTT; live GPU check is separate.
        "flow_grain",
        "aperture_playground",
        // Persistent optional Kratos worker; exercised separately by the live playground check.
        "kratos_thermal_playground",
        "kratos_flow_playground",
        // Also wants TetGen: it meshes the bracket here before the worker sees it.
        "kratos_structural_playground",
        // Optional native field_morse_smale (./gradlew buildMorseSmaleNative); the live box loops forever.
        "morse_smale_explorer",
        // Optional native field_morse_smale; the live box loops forever.
        "tracking_explorer",
        // vector_field_topology.md's footage starting point: decodes its clip with FFmpeg, and the movie box renders 306 frames.
        "eagle_flow_explorer",
        // Optional native field_principal_curves (./gradlew buildPrincipalCurvesNative); the live box loops forever.
        "principal_curves_explorer",
        // Optional native field_polyvector (OpenCV, Boost, Eigen; ./gradlew buildPolyvectorNative).
        "polyvector_explorer",
        // The same worker, times fifty: a grid sweep is minutes of solver, so CI compiles it and
        // the `kratos.study.parameter-sweep` catalog entry runs the same code path at 3x3.
        "kratos_parameter_study",
        // Every box is compiled through Field's real K2/preamble path, but none is evaluated: four
        // of them intentionally reach an external analysis worker or real-time Sonic Pi recorder.
        "audio_render_analysis",
        // Real optional offline Loris worker: compile complete async workflows without a native download.
        "loris_partial_explorer",
        // Imports a local source and reaches the optional Underfit/Stable Audio worker; compile the
        // complete live path here, while the authenticated GPU request remains an explicit action.
        "audio_ai_underfit_mancini",
        // Reader is portable; the optional third box reaches prepared Torch/CUDA through a cached subprocess.
        "particle_video",
        "streaming_diffusion", // Optional Linux/NVIDIA worker and a Vulkan renderer.
        "graphics_renderer_launch",
        "direct_lighting", // Vulkan runtime is covered separately by four.direct-lighting.smoke.
        "open_dlss_nr", // Optional native network; live transcript and GPU recording verified separately.
        "open_dlss_nr_cubes", // Same native pass, with editable raster geometry and its GPU recorder.
        "neural_reconstruction", // Optional NGX evaluation is covered by ReconstructionSmoke on the NVIDIA machine.
        "ray_reconstruction", // Real NGX/RTXDI evaluation: RayReconstructionSmoke.
        "path_traced_reconstruction", // PathTracedReconstructionSmoke; optional NGX.
        "computed_ray_geometry", // ClusterGeometrySmoke; optional NV cluster AS.
        "gpu_shader_learning", // GpuShaderTrainingSmoke; optional cooperative-vector training.
        "optical_flow", // Dedicated optical-flow queue: OpticalFlowSmoke.
        "optical_flow_pingpong", // Editable source history -> sampled motion: check_flow_pingpong.py.
        "learned_shader", // Frozen weights / cooperative vectors: LearnedShaderSmoke.
        "reconstruction_plumbing", // Live moduleHere MRT -> NGX; GPU checked by check_plumbing.py.
        "rr_plumbing", // Raw RTXDI and editable guide conversion -> RR; same GPU check.
        "learned_from_module", // Real moduleHere capture + asynchronous training: check_shader_learning.py.
        // Box 1 opens a Window because Field 4 graphics has no headless mode — a compute-only dispatch
        // still needs one for the device to exist — so the bench cannot be replayed here. What this
        // entry buys is the thing that was actually missing: all four box bodies are compiled
        // against Field's real K2/preamble import set on every build, which is where box code
        // diverges from `jvmMain` and where no Gradle gate could previously see it. The GPU numbers
        // themselves are asserted by `four.gpu-probe.pressure-chain` and `four.gpu-probe.self-check`.
        "gpu_probe_bench",
        // These open a native Vulkan renderer; real pixels and reloads are checked separately.
        "path_tracing_materials",
        "path_tracing_camera",
        "path_tracing_glass",
        "path_tracing_gltf",
        "path_tracing_hidden_environment",
        "path_tracing_material_shader_check",
        // The cross-subsystem chains that end at the same tracer. Each one's CPU half — the Conway
        // operators, the Manifold Booleans, the cloth solve — is covered by its own tests; what is
        // left here is the window, so they classify with the rest of the tracer group.
        "polytope_path_trace_lineage",
        "polytope_manifold_path_trace",
        "polytope_layer_stack_path_trace",
        "photo_mount_path_trace",
        "physics_manifold_path_trace",
        // Also a window, and additionally a compute dispatch whose push constants are bound to
        // lambdas; the pattern scheduler and the band analysis in front of it are covered by
        // synth.pat.seam, which needs no device at all.
        "pattern_particles",
        // A window and a per-frame burst buffer; the net and the three run queries the sparks box
        // makes each frame (firingsOver, markingAt, countAt) are covered by PetriSparksDocsTest.
        "petri_sparks",
        "life3d_manifold_path_trace",
        "rectsl_spacetime_path_trace",
        "skinned_volume_path_trace",
        "petri_state_space_sculpture",
        // Real Vulkan/OpenXR rendering; binocular depth additionally requires HTC eye hardware.
        "openxr_eye_intersection",
        "afield_four_traditional_triangle",
        // Two ShaderBoids flocks on their own native Window + Renderer; captured by neural_particles.md.
        "shader_boids_separation",
        // Needs a live Vulkan renderer; the column it opens is captured by visualizers.md (graphics context).
        "renderer_browsing",
        // Streams a splat sequence into a native window, and wants a directory of .compressed.ply
        // frames that is deliberately not committed -- so compiled here and no further.
        "splat_sequence_streaming",
        // The madp splat-shader port: per-splat GLSL inserts on a native Window + Renderer, so
        // compile-only here. SplatShaderTest compiles every box's compute tab against the
        // splat/insert + splat/end pair, and four.splat-shader.smoke measures the sort on a GPU.
        "splat_shader",
        // A native window plus a directory of images that is nobody's to commit.
        //
        // Its GLSL is *not* checked here, and the comment that used to say so was wrong in a way
        // worth recording: `{{ mega.* }}` is registered by `megaTextureHere` at run time, and this
        // bucket never runs anything — so `moduleHere`'s expansion cannot resolve the template and
        // logs a TemplateException. That is survivable (the macro falls back to the no-shader
        // `_moduleHere` and the Kotlin still compiles, which is what this bucket is for), but it
        // means the shaders here are compiled by nothing. MegaTextureShaderTest and
        // MegaTextureLayoutTest check that same generated GLSL through shaderc without a GPU or a
        // folder, and they are where that coverage actually lives.
        "mega_texture_wall",
        // Builds its own Window + Renderer and a six-pass chain across three resolutions, so it
        // wants a GLFW window and a Vulkan device the lightweight build does not own. The plan
        // arithmetic it prints is covered without a device by FabricatePassChainTest, and the
        // pixels by the four.pass-chain.smoke jvm-main.
        "four_pass_chain",
        // Two persistent-state pass chains — a stable fluid and a jump flood — on their own Window
        // and Renderer, plus a mouse handler that paints into the fluid. Same classification and for
        // the same reason as four_pass_chain: the plan arithmetic is covered without a device by
        // FabricatePassChainTest, and the pixels (including the fluid actually advancing with no
        // input) by the four.simulation-chain.smoke jvm-main.
        "four_simulation_chain",
        // A raster summary pyramid and a Morton-code point quadtree, both declared as pass chains
        // on their own Window and Renderer. Same classification and for the same reason as the two
        // above: the plan arithmetic is covered without a device by FabricatePassChainTest, every
        // shader by FabricateShaderCompileTest, and the numbers each cell of the tree holds — in
        // both producers, against closed forms — by the four.quadtree-chain.smoke jvm-main.
        // The four demo documents that open their own Window and Renderer, and the one whose
        // cells are Python and need an interpreter with JEP's native library found before Field
        // started — all promoted out of `dev_docs/agents/demos/` with the rest.
        "frame_history_reprojection",
        "path_tracer_instrument",
        "path_tracing_emitters",
        "path_tracing_medium",
        // Clojure box parity: its Kotlin root is compiled; the three Clojure boxes are a guest
        // language and skipped. ClojureFieldCoreTest and ClojureHostTest run the Clojure half.
        "clojure_box_parity",
        // RemoteJS box parity: the Kotlin root opens a browser head, which a headless run lacks;
        // the five Javascript boxes and five Kotlin → head boxes are guest languages and skipped. RemoteJsBoxSurfaceTest runs
        // the JVM half; the head half was driven through the backdoor (REMOTEJS_BOX_PARITY.md).
        "remotejs_box_parity",
        "python_box_parity",
        "python_cells",
        // fieldcv: the Python cells need JEP plus the optional cv group, and a camera; the Kotlin
        // graphics boxes are compiled.
        "fieldcv_webcam",
        // Pure Data: the Kotlin boxes need libpd built to run, which the build does not own, so their
        // Kotlin is compiled; the three Pd boxes are a guest language and skipped, as Python's are.
        "pure_data_demo",
        // Csound: the Kotlin root and five Kotlin boxes need libcsound built (./gradlew
        // buildCsoundNative), which the build does not own, so they are compiled; the three Csound
        // boxes are a guest language and skipped, as Pd's are. afield.audio.csound.* tests run the calls
        // against a real Csound when one is built. Every box was run by hand on 2026-10-02.
        "csound_demo",
        "four_summary_pyramid",
        "four_morton_quadtree",
        // Opens its own Window + Renderer and then renders a still and a clip per blend for a dozen
        // keyframe blends, so it wants a device CI does not own. The sweeper's arithmetic is covered
        // without one by KeyframeSweepTest, and its pixels by the keyframe.sweep.smoke jvm-main.
        "keyframe_autosweep",
        "recstl_playground",
        "gpu_state",
        "threebox",
        "rectsl_dataviz",
        "rectsl_plot_axes",
        // Opens its own Window + Renderer to run a RectSL scene on a musical clock.
        "rectsl_musicality",
        // Lowers a RectSL scene to a GLSL distance field and draws it through a generated
        // fullscreen-triangle pipeline. Opens its own Window + Renderer, so compile-only here;
        // its pixels are checked by the `rectsl-sdf` docs capture instead.
        "rectsl_sdf_live",
        "spin_glass",
        // Each opens its own Window + Renderer and runs a GPU simulation (a lattice Boltzmann wind
        // tunnel, a forest fire), so compile-only here; their pixels are the
        // `four-lattice-boltzmann` and `four-forest-fire` docs captures in afield_four_graphics.md.
        "lattice_boltzmann_cylinder",
        "forest_fire",
        // A million-agent ShaderPhysarumSystem on its own Window + Renderer; its pixels are the
        // `alife-physarum-gpu` docs capture and alife.physarum-gpu.smoke measures it.
        "gpu_physarum",
        // The madp Fern port: compute-shader iterated systems plotted into a PointAccumulation on
        // a native Window + Renderer, so compile-only here. IteratedSystemShaderTest compiles every
        // system's compute tab against the points/system harness without a GPU.
        "fern_iterated_systems",
        // The madp Particles port: madp's hooks as compute shaders over a GPU neighbour grid, drawn
        // into a PointAccumulation on a native Window + Renderer, so compile-only here.
        // ParticleSketchShaderTest compiles every sketch's compute tab against the
        // particles/sketch harness without a GPU, and four.particle-sketch.smoke grades the verbs
        // against a transcription of madp's JavaScript on one.
        "particles_sketchbook",
        // The boids page: a declarative flock spec stepped on the GPU over a neighbour grid and drawn
        // into a PointAccumulation on a native Window + Renderer, so compile-only here.
        // BoidSketchTest compiles every flock's compute tab against the boids/flock harness and
        // checks its tables without a GPU; four.boid-sketch.smoke grades a step against the page's
        // JavaScript on one.
        "madp_boids",
        "imgui_vr_panel",
        // Desktop Vulkan stereo/FXAA; OpenXR is opt-in. Live capture is on its public page.
        "stereo_rtt_composite",
        "field_vr_ide",
        // A hidden Electron window renders Field itself into Chromium shared textures; the live
        // macOS replay crosses IOSurface Mach rights and samples them in Vulkan. CI compiles the
        // self-contained starting scene without starting Electron or opening a native window.
        "field_ui_browser_texture",
        // Desktop puppet-browser scene: an aspect-matched YouTube texture receives the native
        // Window's mouse, wheel, physical keyboard and text-input stream directly through Chromium.
        "youtube_mouse_keyboard_browser",
        // A live YouTube tab sampled across 224 independently animated Vulkan triangles. The live
        // replay opens Electron, reaches the network and starts media; CI compiles the whole graph.
        "youtube_kaleidoscope",
        // Product-facing puppet-browser scene: a YouTube watch page on an aspect-correct world quad.
        // It opens Electron and a native Vulkan stereo window, so CI compiles without launching it.
        "youtube_vr_browser",
        // Publishes a shader over WebRTC from an Electron output-area view and samples a stream
        // through a hidden Electron page; both need the Field Electron app and a peer, so CI
        // compiles the graph. The live checks are in dev_docs/WEBRTC_SOURCES_SINKS.md.
        "webrtc_shader_jam",
        // Exercises IOSurface/Mach-port import, a Vulkan copy, and a graphics-fence callback on
        // macOS. CI still compiles both boxes everywhere; the tracked backdoor replay runs it live.
        "external_browser_surface_macos_smoke",
        // The Linux twin: a DRM-modifier RTT, a dma-buf export, and a foreign-queue import. Same
        // deal — compiled everywhere, run live by the tracked backdoor replay.
        "external_browser_surface_linux_smoke",
        // Both macOS IOSurface paths on field-encode's real ring, on MoltenVK (image import) or
        // KosmicKrisp (host-pointer buffers). Needs a Vulkan window and the helper binary, so CI
        // compiles it; the backdoor replay runs it live.
        "iosurface_ring_roundtrip_smoke",
        // Zero-copy Syphon receive against field_syphon_probe, read back exactly. Needs a Vulkan
        // window and the optional Syphon library, so CI compiles it; the backdoor replay runs it live.
        "syphon_zero_copy_receive_smoke",
        // Needs a camera, which CI does not have. Compiling without evaluating is also what keeps this
        // hermetic: the box's `clipEncode(…)` calls and its Depth Anything session are behind lazy
        // initialisation, so nothing here fetches the ~600MB of encoders that running it would.
        "onnx_webcam_live",
        // The madp tetra port: running a box loads both CLIP towers (~600 MB fetched on first use),
        // which a hermetic build must not do. `onnx.clip.smoke` encodes the same photographs and checks
        // their simplex is exact and repeatable; EmbeddingSimplexTest holds the geometry.
        "madp_tetra",
        // Both open a native Vulkan renderer and dispatch compute. Their numbers are checked
        // elsewhere and far more sharply than a replay could: `four.neural-particles.smoke` grades
        // the GPU's trajectories against the CPU integrator per particle, and
        // `four.shader-boids.smoke` grades both the written and the learned rule against a CPU
        // brute force. What is left here is the window.
        "neural_particles",
        "learned_boids",
        // The same deal again, one tier up: both open a native Vulkan renderer to draw a trained
        // field, and both are graded far more sharply than a replay could manage — the
        // interpolation they demonstrate is measured in `WeightInterpolationTest`, endpoint
        // fidelity and all, on the CPU and with no window in the room.
        "letter_interpolation",
        "letter_interpolation_panels",
        // The two demo documents that were compiled by nothing until 2026-09-21. Both are here
        // because their transcripts exist to close the hole [afield.tools.docs.AgentDemoRegistrationTest]
        // names, and neither can be EXECUTE.
        //
        // `skinned_characters` opens a GLFW window and a Vulkan renderer in `skin_renderer` and
        // draws into it from three boxes. It is also the case that proves the hole is not
        // hypothetical: from 2026-09-19 to 2026-09-20 its `twist_compare` box did not compile at
        // all -- four `println`s left behind by a two-mesh-to-one-mesh rewrite named `rL`, `rR`,
        // `outR` and `rRest`, so the box failed with "Unresolved reference 'rRest'" from the
        // moment it landed, while `check` stayed green because box code is compiled by the K2
        // REPL against a different import set from jvmMain and no Gradle task sees it. It was
        // found by a human launching every box by hand. This entry is that sweep, as CI.
        "skinned_characters",
        // `marf_demo`'s fourth box ends on `patSpatial()`, which opens a realtime audio device
        // through Dough and Steam Audio's HRTF path -- and a patSynth launch outlives the box that
        // made it, so this is not something to start on a build machine at 2am. Its other three
        // boxes are pure CPU and would run; they stay here rather than being split into a second
        // EXECUTE transcript, because the one point the document makes is that the bank does not
        // know whether it is being drawn or heard, and that argument does not survive the split.
        "marf_demo",
        // The mqtt.md and oscquery.md demos: `loop { }` boxes over Field's own broker and a published
        // OSCQuery service on fixed ports, meant to be launched and stopped by hand. MqttDemoBundleTest
        // checks the bundle opens.
        "mqtt_demo",
        "oscquery_demo",
        // dmx_lighting.md's demo: `loop { }` boxes on Art-Net's and sACN's fixed UDP ports (6454,
        // 5568). DmxLightingDemoBundleTest checks the bundle; the page's fences run each box's
        // loopback on its own, and its hero capture replays this transcript with the launches.
        "dmx_lighting_demo",
        // sonic_pi.md's demo: two of its three boxes play through Sonic Pi. SonicPiPatternDemoDriftTest
        // holds the page's fences to the document.
        "sonicpi_pattern_demo",
        // rtp_midi.md's demo opens real RTP-MIDI sessions on UDP 5004-5007 and announces over Bonjour.
        "rtp_midi_demo",
        // aiFeatures.md's tool demo: its geometry tool renders a mesh for the model to look at.
        "ai_tool_demo",
        // fieldml_host_bridge.md's demos. Their boxes are FieldML under a FieldML root, a guest
        // language this harness cannot run (it does not install the @RunsAt language registry), so
        // only the Kotlin window-and-renderer boxes of the GPU ones are compiled here;
        // FieldMLSmokeExamplesTest and FieldMLSourceMacroTest run the FieldML documents themselves.
        "fieldml_macro_demo",
        "fieldml_host_bridge_demo",
        "fieldml_lambda_demo",
        "fieldml_afield_four_demo",
        "fieldml_frames_demo",
        "fieldml_frames_sequence_demo",
        "fieldml_macro_module_demo",
        "fieldml_mesh_demo",
        "fieldml_suspend_demo",
        // stacksl_examples.md's gallery draws into a native window.
        "stacksl_gallery",
        // ShaderTrinket: shadertoyHere boxes drawing into a shadertoyCanvas the renderer window shows,
        // one reading the camera, two publishing and receiving over WebRTC (Electron only). ShadertoyChannelsTest compiles the image preamble and the
        // channel passes; the orientation and colour were checked by native capture.
        "shadertrinket",
        // The madp StackTrinket port: StackSL feedback programs on a native Window + Renderer, so
        // compile-only here. StackSLFeedbackTest compiles every preset and its fragment shader to
        // SPIR-V without a GPU.
        "stacktrinket_feedback",
        // Continuing a gesture (2026-10-19): boxes 5 and 6 start a Timestral worker, which needs a
        // downloaded checkpoint no CI machine has, and boxes 3 and 4 are live frames loops. The oracle
        // and mocap code runs in VariableMarkovOracleTest, SeveralTakesTest and BvhTest, and the dance
        // boxes run as docs captures; TimestralSessionTest runs the worker when opted in.
        "temporal_memory",
    )

internal val KOTLINJS

KotlinJS "code in the head" demos: the box body is Kotlin compiled to JavaScript for a remote head, NOT JVM Kotlin — so it is compiled through the REAL KotlinToJsCompiler via the head profile the // {{ marker }} selects (the identical path a live box takes), and asserted to carry no error diagnostics. Faithful replay for markers like // {{ tone }} / // {{ remoteAR }}.

    internal val KOTLINJS = setOf(
        "tone_synth_sequence",
        "remotear_start",
        "remotear_scene_motion",
        "visionos_start",
        "vision_geometry_shapes",
        "vision_geometry_heightfield",
        "vision_geometry_animated",
    )

User-facing starters must carry a real KotlinJS root → child topology, not only compile offline.

    private val KOTLINJS_STARTERS = setOf("remotear_start", "visionos_start")

Ops that build the box graph without compiling/running any box Kotlin.

    private val GRAPH_VERBS = setOf(
        "create", "new-box", "box", "connect", "disconnect", "edit", "set", "set-string", "move", "frame", "set-rect", "wait",
    )

fun main(args: Array<String>)

body — 76 lines, from line 969
    @JvmStatic
    fun main(args: Array<String>) {
        // First line, before anything can touch an AWT class, and for the same reason every jvm-main
        // gallery does it: this harness runs under `-XstartOnFirstThread` (added for GLFW by
        // `requiredAgentJvmArgs` in agent_backdoor.kt:856) and opens no window, so nothing pumps the
        // process's first-thread run loop. Non-headless AWT then hangs the moment a box asks for a
        // glyph outline — `sun.font.CFontManager.loadFonts` dispatches there and waits. Headless AWT
        // does not, and this harness has no use for a screen. See `Typefaces.warm`.
        System.setProperty("java.awt.headless", "true")
        // Exercise an authored local document through the same macro/preamble/K2 path without
        // registering private documents in the public demo catalog or evaluating their GPU code.
        val localFile = args.indexOf("--compile-file")
        if (localFile >= 0) {
            val yaml = File(args.getOrNull(localFile + 1) ?: error("--compile-file needs a transcript path"))
            require(yaml.isFile) { "Transcript not found: $yaml" }
            startRunloopDaemon()
            val failures = mutableListOf<String>()
            runCompileOnlyDemo(yaml, yaml.nameWithoutExtension, failures)
            check(failures.isEmpty()) { failures.joinToString("\n") }
            println("COMPILE_ONLY_PASS ${yaml.absolutePath} (GPU evaluation skipped)")
            return
        }
        val transcriptsDir = locateTranscriptsDir()
        println("DemoTranscriptExecSmokeTest: transcripts dir = ${transcriptsDir.absolutePath}")

        startRunloopDaemon()

        // Iterating on one demo otherwise means waiting three minutes for the other twenty-odd. The
        // filter is for development only — CI runs the whole set, and an unfiltered run is the
        // default — but without it a single failing demo is painful to debug.
        val only = (args.firstOrNull() ?: System.getenv("DEMO_TRANSCRIPT_ONLY"))
            ?.split(',')?.map(String::trim)?.filter(String::isNotEmpty)?.toSet()
        only?.let { println("DemoTranscriptExecSmokeTest: FILTERED to ${it.joinToString()} — not a full run") }

        val yamls = (transcriptsDir.listFiles { f -> f.isFile && f.extension == "yaml" } ?: emptyArray())
            .sortedBy { it.name }
            .filter { only == null || it.nameWithoutExtension in only }
        require(yamls.isNotEmpty()) { "No transcript yaml files found in ${transcriptsDir.absolutePath}" }

        val failures = mutableListOf<String>()
        for (yaml in yamls) {
            val name = yaml.nameWithoutExtension
            try {
                when (name) {
                    in EXECUTE -> runExecuteDemo(yaml, name, failures)
                    in COMPILE_ONLY -> runCompileOnlyDemo(yaml, name, failures)
                    in KOTLINJS -> runKotlinJsDemo(yaml, name, failures)
                    else -> {
                        val msg = "$name: UNCLASSIFIED — add to EXECUTE or COMPILE_ONLY in DemoTranscriptExecSmokeTest"
                        failures.add(msg)
                        println("[unclassified] $msg")
                    }
                }
            } catch (t: Throwable) {
                val msg = "$name: harness threw ${t.javaClass.simpleName}: ${t.message}"
                failures.add(msg)
                System.err.println("[error] $msg")
                t.printStackTrace(System.err)
            } finally {
                // Each transcript owns a disposable document session. Dropping only the thread-local
                // reference leaves every box's K2 REPL engine alive, so the full corpus accumulates
                // dozens of FIR sessions and eventually loses otherwise-standard imports in later
                // boxes. Use the same lifecycle boundary as closing a real Field document.
                thisSessionInfo.get()?.cleanup()
                thisSessionInfo.remove()
            }
        }

        if (failures.isNotEmpty()) {
            System.err.println("DemoTranscriptExecSmokeTest FAILED with ${failures.size} problem(s):")
            failures.forEach { System.err.println("  - $it") }
            exitProcess(1)
        }
        println("DemoTranscriptExecSmokeTest ok — ${yamls.size} demos verified " +
            "(${EXECUTE.size} executed, ${COMPILE_ONLY.size} compiled, ${KOTLINJS.size} kotlinjs)")
    }

KOTLINJS: build the graph, then compile each kotlinjs box body Kotlin -> JS for real

internal fun runKotlinJsDemo(yaml: File, name: String, failures: MutableList<String>)

body — 80 lines, from line 1048
    internal fun runKotlinJsDemo(yaml: File, name: String, failures: MutableList<String>) {
        val document = freshSession(name)
        val ops = loadOps(yaml)
        val graphOps = ops.filter { (it.keys.firstOrNull()?.lowercase() ?: "") in GRAPH_VERBS }

        val macrosDir = File(document, "macros").also { it.mkdirs() }
        val filtered = linkedMapOf<String, Any?>("name" to name, "operations" to graphOps)
        File(macrosDir, "$name.yaml").writeText(Yaml().dump(filtered))
        val build = OperationTranscript.runMacro(document.absolutePath, name)
        if (!build.success) println("[kotlinjs] $name graph build note: ${build.message}")

        val boxIds = graphOps.mapNotNull { op ->
            if ((op.keys.firstOrNull()?.lowercase() ?: "") in setOf("create", "new-box", "box")) {
                val p = op.values.firstOrNull() as? Map<*, *>
                (p?.get("box") ?: p?.get("uid") ?: p?.get("id") ?: p?.get("target"))?.toString()
            } else null
        }
        if (name in KOTLINJS_STARTERS) {
            kotlinJsTopologyNeedsUpdating(thisSessionInfo.get().boxes.values)
        }

        println("[kotlinjs] $name (${boxIds.size} box(es)) …")
        var demoOk = true
        var compiled = 0
        for (id in boxIds) {
            val target = runCatching { box(id, createIfMissing = false) }.getOrNull() ?: continue
            if (target.getPropertyRaw("_tombstone").isTrue()) continue
            // Language-root boxes intentionally have no code property; do not force the non-null
            // delegated accessor merely to discover that this graph node is structural.
            val code = target.getPropertyRaw("code")?.toString().orEmpty()
            if (code.isBlank()) continue
            // Only KotlinJS-head boxes: a `// {{ marker }}` first line, or mainLanguage == kotlinjs.
            val mainLang = target.getPropertyRaw("mainLanguage", "")?.toString()?.trim().orEmpty()
            val selection = runCatching { afield.lang.remotejs.KotlinJsHeadProfiles.resolve(code) }.getOrNull()
            if (selection == null || (!selection.explicit && !mainLang.equals("kotlinjs", ignoreCase = true))) {
                println("[kotlinjs]   $id: not a kotlinjs-head box — skipped")
                continue
            }
            if (name in KOTLINJS_STARTERS && target.ke !is KotlinJsExecution) {
                demoOk = false
                val msg = "$name/$id: starter code box is not beneath a KotlinJS language root"
                failures.add(msg)
                println("[kotlinjs]   $id: FAIL $msg")
            }
            val prepared = selection.profile.prepareSource(selection.source)
            val result = afield.lang.remotejs.KotlinToJsCompiler.compile(
                prepared, moduleName = "demo_${name}_${id.take(8)}", wrapImplicitMain = false,
            )
            val errors = result.diagnostics.filter { it.severity == "error" }
            when {
                errors.isNotEmpty() -> {
                    demoOk = false
                    errors.forEach {
                        val msg = "$name/$id: ${it.message} (line ${it.line})"
                        failures.add(msg)
                        println("[kotlinjs]   $id: FAIL $msg")
                    }
                }
                !result.success || result.js == null -> {
                    demoOk = false
                    val msg = "$name/$id: Kotlin->JS compile failed: ${result.rawLog.takeLast(400)}"
                    failures.add(msg)
                    println("[kotlinjs]   $id: FAIL $msg")
                }
                else -> {
                    compiled += 1
                    println("[kotlinjs]   $id: PASS (${selection.profile.id}, ${prepared.length} chars Kotlin -> ${result.js.length} chars JS)")
                }
            }
        }
        if (demoOk) {
            if (compiled == 0) {
                val msg = "$name: no kotlinjs-head box compiled — is the marker / mainLanguage set?"
                failures.add(msg)
                println("[kotlinjs] FAIL $msg")
            } else {
                println("[kotlinjs] PASS $name")
            }
        }
    }

EXECUTE: run the whole transcript through the real runner and assert no error output

internal fun runExecuteDemo(yaml: File, name: String, failures: MutableList<String>)

    internal fun runExecuteDemo(yaml: File, name: String, failures: MutableList<String>) {
        val document = freshSession(name)
        val macrosDir = File(document, "macros").also { it.mkdirs() }
        File(macrosDir, "$name.yaml").writeText(yaml.readText())

        println("[execute] $name …")
        // Opened the way Field opens a document, so code that asks which open document a box is in
        // (BoxStudies.documentOf, which a study's default revision directory needs) gets the answer
        // it gets in Field rather than "not in an open document". Removed again after the replay.
        afield.app.server.documents[document.absolutePath] = thisSessionInfo.get()
        val run = try {
            OperationTranscript.runMacro(document.absolutePath, name)
        } finally {
            afield.app.server.documents.remove(document.absolutePath)
        }
        val errorLines = run.output.filter { it.kind == "error" || it.kind == "err" }

        when {
            !run.success -> {
                val msg = "$name: transcript run failed: ${run.errors.joinToString("; ").ifBlank { run.message }}"
                failures.add(msg)
                println("[execute] FAIL $msg")
            }
            errorLines.isNotEmpty() -> {
                val detail = errorLines.joinToString(" | ") { "${it.uid}:${it.line} ${stripHtml(it.html)}" }
                val msg = "$name: box execution emitted ${errorLines.size} error diagnostic(s): $detail"
                failures.add(msg)
                println("[execute] FAIL $msg")
            }
            else -> println("[execute] PASS $name (applied=${run.applied}, output lines=${run.output.size})")
        }

        // A demo can PASS (no error diagnostics) while printing nonsense. Set
        // AFIELD_DEMO_TRANSCRIPT_VERBOSE=1 — or =<name> for one demo — to echo what the boxes
        // actually printed, so the numbers a worked example claims can be read back and checked.
        val verbose = System.getenv("AFIELD_DEMO_TRANSCRIPT_VERBOSE")
        if (!verbose.isNullOrBlank() && (verbose == "1" || verbose == "all" || verbose == name)) {
            run.output.forEach { println("[execute:$name] ${it.kind}: ${stripHtml(it.html).trim()}") }
        }
    }

COMPILE-ONLY: build the graph, then compile each box's Kotlin with eval skipped

internal fun runCompileOnlyDemo(yaml: File, name: String, failures: MutableList<String>)

body — 75 lines, from line 1174
    internal fun runCompileOnlyDemo(yaml: File, name: String, failures: MutableList<String>) {
        val document = freshSession(name)
        val ops = loadOps(yaml)
        val ownOps = ops.filter { (it.keys.firstOrNull()?.lowercase() ?: "") in GRAPH_VERBS }
        // A transcript that connects under a stable parent another transcript creates (the
        // graphics context) gets that transcript's graph first, so its boxes resolve `renderer`
        // the way they do in Field, through the parent's prop<Renderer>, rather than needing a
        // local lookup that exists only to make this harness compile them.
        val graphOps = stableParentOps(ownOps) + ownOps

        // Build the box graph with the real apply path — create/connect/edit/move only, no launch.
        val macrosDir = File(document, "macros").also { it.mkdirs() }
        val filtered = linkedMapOf<String, Any?>("name" to name, "operations" to graphOps)
        File(macrosDir, "$name.yaml").writeText(Yaml().dump(filtered))
        val build = OperationTranscript.runMacro(document.absolutePath, name)
        if (!build.success) {
            // A missing cross-transcript parent (skipped connect) is fine; only hard errors matter.
            println("[compile] $name graph build note: ${build.message}")
        }

        // Compile each created box, parent-first (creation order), so parent preamble (prop<>
        // delegate declarations) is populated before a child gathers it upward.
        val boxIds = graphOps.mapNotNull { op ->
            if ((op.keys.firstOrNull()?.lowercase() ?: "") in setOf("create", "new-box", "box")) {
                val p = op.values.firstOrNull() as? Map<*, *>
                (p?.get("box") ?: p?.get("uid") ?: p?.get("id") ?: p?.get("target"))?.toString()
            } else null
        }

        println("[compile] $name (${boxIds.size} box(es)) …")
        var demoOk = true
        for (id in boxIds) {
            val target = runCatching { box(id, createIfMissing = false) }.getOrNull()
            if (target == null) {
                println("[compile]   $id: not created (skipped)")
                continue
            }
            if (target.getPropertyRaw("_tombstone").isTrue()) continue
            val code = (target.getPropertyRaw("code") as? String).orEmpty()
            if (code.isBlank()) {
                println("[compile]   $id: no Kotlin `code` (shader-only box) — skipped")
                continue
            }
            // A box whose `code_language` is a guest language holds source this compiler does not
            // read. Feeding a Python cell to K2 does not report "this is not Kotlin", it reports
            // every word of it as an unresolved reference — 150 diagnostics for `python_cells`,
            // none of them about anything. The guest runtimes have their own checks; what this
            // harness owns is the Kotlin, including the Kotlin *root* that makes a box a guest one.
            val codeLanguage = target.getPropertyRaw("code_language")?.toString()?.trim().orEmpty()
            if (codeLanguage.isNotEmpty() && !codeLanguage.equals("kotlin", ignoreCase = true)) {
                println("[compile]   $id: code_language=$codeLanguage, not Kotlin — skipped")
                continue
            }
            // The other way a box becomes a guest one: it sits beneath a `mainLanguage` root
            // (`fieldcv_webcam`'s cells hang off a `mainLanguage: python` box rather than carrying
            // `code_language` themselves).
            val guestRoot = guestLanguageRoot(target)
            if (guestRoot != null) {
                println("[compile]   $id: beneath mainLanguage=$guestRoot root, not Kotlin — skipped")
                continue
            }
            val diags = compileBoxOnly(target)
            if (diags.isEmpty()) {
                println("[compile]   $id: PASS")
            } else {
                demoOk = false
                diags.forEach {
                    val msg = "$name/$id: $it"
                    failures.add(msg)
                    println("[compile]   $id: FAIL $it")
                }
            }
        }
        if (demoOk) println("[compile] PASS $name")
    }

The guest mainLanguage of the nearest ancestor language root, or null when box is Kotlin. kotlinjs roots are not guest here: their heads go through the KotlinJS pass above.

    private fun guestLanguageRoot(box: Box): String? {
        val seen = HashSet<Box>()
        var frontier = box.parents.toList()
        while (frontier.isNotEmpty()) {
            val next = ArrayList<Box>()
            for (p in frontier) {
                if (!seen.add(p)) continue
                val lang = p.getPropertyRaw("mainLanguage")?.toString()?.trim().orEmpty()
                if (lang.isNotEmpty() && !lang.equals("kotlin", true) && !lang.equals("kotlinjs", true)) return lang
                next.addAll(p.parents)
            }
            frontier = next
        }
        return null
    }

Compile box's Kotlin code through the real pipeline with evaluation skipped, returning any ERROR-severity diagnostics. Reuses the real macro expander, preamble evaluation and K2 compiler; only the terminal eval of the main body is omitted (that is where a graphics box would open a Window). //%- fragment breaks are Kotlin comments, so compiling the whole body as one snippet keeps cross-fragment declarations resolvable without evaluating anything.

    private fun compileBoxOnly(box: Box): List<String> = runBlocking {
        val errors = mutableListOf<String>()
        Box.withBoxContext(box) {
            // Expand macros first: this both rewrites moduleHere/prop/println/… AND writes the
            // prop<T>() delegate declarations into the box's preamble (applyInsertToPreamble).
            // Then substitute the `__line__` sentinel the print macros emit — standardExecutionPractice
            // (the real path) does this between applyMacros and eval; skipping it leaves an unresolved
            // `__line__` reference. The exact number is irrelevant to compilation.
            val expanded = box.ki.applyMacros(box, requireNotNull(box.code) { "${box.name} has no Kotlin code to compile" })
                .split("\n")
                .mapIndexed { i, line -> line.replace("__line__", "$i") }
                .joinToString("\n")

            // Evaluate setup blocks (preamble/introduction/attic). These are declaration-only and
            // never open a Window; evaluating them puts the Box.<prop> extension symbols (gathered
            // upward from parents too) into this engine's REPL context so the body resolves them.
            val swallow: (List<ScriptDiagnostic>) -> Unit = { }
            val swallowEx: (Throwable) -> Unit = { }
            checkPreambleAndExecute(box, swallow, swallow, swallowEx)
            checkIntroductionAndExecute(box, swallow, swallow, swallowEx)
            checkAtticAndExecute(box, swallow, swallow, swallowEx)

            box.ki.compileSnippetAndWait(expanded, implicits = listOf(box)) { diagnostics ->
                diagnostics
                    .filter { it.severity.ordinal >= ScriptDiagnostic.Severity.ERROR.ordinal }
                    .forEach { d ->
                        val loc = d.location?.start?.let { " (${it.line}:${it.col})" } ?: ""
                        errors.add("${d.message}$loc".trim())
                    }
            }
        }
        errors
    }

Stable parents a transcript may connect under without creating, and the transcript that creates each.

    private val STABLE_PARENTS = mapOf("agent_graphics_context" to "graphics_renderer_launch")

    private fun createdId(op: Map<String, Any?>): String? {
        if ((op.keys.firstOrNull()?.lowercase() ?: "") !in setOf("create", "new-box", "box")) return null
        val p = op.values.firstOrNull() as? Map<*, *>
        return (p?.get("box") ?: p?.get("uid") ?: p?.get("id") ?: p?.get("target"))?.toString()
    }

    private fun stableParentOps(ops: List<Map<String, Any?>>): List<Map<String, Any?>> {
        val created = ops.mapNotNull(::createdId).toSet()
        val text = ops.toString()
        return STABLE_PARENTS
            .filter { (parent, _) -> parent !in created && parent in text }
            .flatMap { (_, source) ->
                loadOps(File(locateTranscriptsDir(), "$source.yaml"))
                    .filter { (it.keys.firstOrNull()?.lowercase() ?: "") in GRAPH_VERBS }
            }
    }

shared setup

    private fun freshSession(name: String): File {
        thisSessionInfo.remove()
        val workspace = Files.createTempDirectory("afield-demo-exec-$name").toFile()
        workspace.deleteOnExit()
        val document = File(workspace, "doc").also { it.mkdirs() }
        File(document, SavingContext.METADATA_EDN).writeText("{:version 2 :boxes []}\n")
        val root = Box("root")
        val session = SessionInformation(
            filename = document.absolutePath,
            documentName = document.name,
            boxes = ConcurrentHashMap(),
            root = root,
            ephemeral = true,
        )
        root.inside = session
        session.boxes[root.id] = root
        thisSessionInfo.set(session)
        return document
    }

    @Suppress("UNCHECKED_CAST")
    private fun loadOps(yaml: File): List<Map<String, Any?>> {
        val loaded = Yaml().load<Any?>(yaml.readText())
        val raw = when (loaded) {
            is List<*> -> loaded
            is Map<*, *> -> (loaded["operations"] as? List<*>) ?: (loaded["ops"] as? List<*>) ?: emptyList<Any?>()
            else -> emptyList<Any?>()
        }
        return raw.mapNotNull { it as? Map<String, Any?> }
    }

internal fun startRunloopDaemon()

    internal fun startRunloopDaemon() {
        val t = Thread({ afield.runtime.lifecycle.Runloop.runLoop.start() }, "demo-transcript-harness-runloop")
        t.isDaemon = true
        t.start()
        val deadline = System.currentTimeMillis() + 30_000
        while (afield.runtime.lifecycle.Runloop.runLoop.frame < 1L) {
            check(System.currentTimeMillis() < deadline) { "runloop did not start within 30s" }
            Thread.sleep(20)
        }
        println("DemoTranscriptExecSmokeTest: runloop up (frame=${afield.runtime.lifecycle.Runloop.runLoop.frame})")
    }

internal fun locateTranscriptsDir(): File

    internal fun locateTranscriptsDir(): File {
        val rel = "dev_docs/agents/transcripts"
        val candidates = buildList {
            System.getProperty("afield.repo")?.let { add(File(it, rel)) }
            var dir: File? = File(System.getProperty("user.dir")).absoluteFile
            while (dir != null) {
                add(File(dir, rel))
                dir = dir.parentFile
            }
        }
        return candidates.firstOrNull { it.isDirectory }
            ?: error("Could not locate $rel from user.dir=${System.getProperty("user.dir")}")
    }

    private fun stripHtml(html: String): String =
        html.replace(Regex("<[^>]+>"), " ").replace("&lt;", "<").replace("&gt;", ">").trim()
}