Skip to content

BoxCodeCompileGuard.kt

src/jvmMain/kotlin/afield/examples/BoxCodeCompileGuard.kt · 78 lines

package afield.examples

import afield.app.server.thisSessionInfo
import java.io.File
import kotlin.system.exitProcess

object BoxCodeCompileGuard

The landing guard that box code still compiles: each named transcript goes down the path DemoTranscriptExecSmokeTest classifies it for. COMPILE_ONLY transcripts (the ones that would open a window) build their graph and compile with evaluation skipped; EXECUTE transcripts are headless-safe and run, because a later box may need what an earlier one defines when evaluated; KOTLINJS transcripts compile through the Kotlin/JS path.

It is scoped to what a landing changed. The paths come from the file named by AFIELD_GUARD_CHANGED_PATHS_FILE (one repo-relative path per line, written by tools/check_staged_generated_artifacts.py), or from the arguments; only top-level dev_docs/agents/transcripts/<name>.yaml entries select anything. With neither, every transcript is compiled, which takes as long as the smoke test's own sweep.

Running the code is the smoke catalog's job (examples.demo-transcript.exec), which the nightly runs; this guard only answers "did the edit break resolution or types".

fun main(args: Array<String>)

body — 54 lines, from line 24
    @JvmStatic
    fun main(args: Array<String>) {
        // As in DemoTranscriptExecSmokeTest: nothing here pumps the first-thread run loop.
        System.setProperty("java.awt.headless", "true")
        val transcriptsDir = DemoTranscriptExecSmokeTest.locateTranscriptsDir()

        val listed: List<String>? = args.toList().ifEmpty { null }
            ?: System.getenv("AFIELD_GUARD_CHANGED_PATHS_FILE")?.let { File(it).readLines() }
        val yamls = if (listed == null) {
            (transcriptsDir.listFiles { f -> f.isFile && f.extension == "yaml" } ?: emptyArray()).sortedBy { it.name }
        } else {
            listed.map(String::trim)
                .filter { it.startsWith("dev_docs/agents/transcripts/") && it.count { c -> c == '/' } == 3 && it.endsWith(".yaml") }
                .map { File(transcriptsDir, it.substringAfterLast('/')) }
                .filter(File::isFile)
                .distinct()
        }
        if (yamls.isEmpty()) {
            println("BoxCodeCompileGuard ok — no changed transcripts")
            return
        }

        DemoTranscriptExecSmokeTest.startRunloopDaemon()
        val failures = mutableListOf<String>()
        for (yaml in yamls) {
            val name = yaml.nameWithoutExtension
            try {
                // Same routing as the smoke test. Compile-only is wrong for the other classes: a
                // KOTLINJS transcript's code is Kotlin/JS against the browser API (onStart, THREE,
                // Tone), and an EXECUTE transcript may read types or values an earlier box defines
                // only when evaluated (shared_types_demo, the DataFrame plugin's generated schema).
                when (name) {
                    in DemoTranscriptExecSmokeTest.COMPILE_ONLY -> DemoTranscriptExecSmokeTest.runCompileOnlyDemo(yaml, name, failures)
                    in DemoTranscriptExecSmokeTest.EXECUTE -> DemoTranscriptExecSmokeTest.runExecuteDemo(yaml, name, failures)
                    in DemoTranscriptExecSmokeTest.KOTLINJS -> DemoTranscriptExecSmokeTest.runKotlinJsDemo(yaml, name, failures)
                    else -> failures.add("$name: UNCLASSIFIED — add to EXECUTE or COMPILE_ONLY in DemoTranscriptExecSmokeTest")
                }
            } catch (t: Throwable) {
                failures.add("${yaml.nameWithoutExtension}: harness threw ${t.javaClass.simpleName}: ${t.message}")
            } finally {
                thisSessionInfo.get()?.cleanup()
                thisSessionInfo.remove()
            }
        }
        if (failures.isNotEmpty()) {
            System.err.println("BoxCodeCompileGuard FAILED with ${failures.size} problem(s):")
            failures.forEach { System.err.println("  - $it") }
            exitProcess(1)
        }
        println("BoxCodeCompileGuard ok — ${yamls.size} transcript(s) compiled")
        // The runloop daemon and REPL engines hold non-daemon threads.
        exitProcess(0)
    }
}