diff --git a/.claude/skills/zio-golem-base-image/SKILL.md b/.claude/skills/zio-golem-base-image/SKILL.md new file mode 100644 index 0000000000..f601bcc8bd --- /dev/null +++ b/.claude/skills/zio-golem-base-image/SKILL.md @@ -0,0 +1,110 @@ +--- +name: zio-golem-base-image +description: Explains the zio-golem WIT folder structure and how to regenerate the agent_guest.wasm base image. Use when working with WIT definitions, upgrading Golem versions, or regenerating the guest runtime WASM. +--- + +# zio-golem Base Image (agent_guest.wasm) + +The base image `agent_guest.wasm` is a QuickJS-based WASM component that serves as the guest runtime for Scala.js agents on Golem. It must be regenerated whenever WIT definitions change. + +## WIT Folder Structure + +``` +golem/wit/ +├── main.wit # Hand-maintained world definition (golem:agent-guest) +├── deps.toml # wit-deps manifest — points to golemcloud/golem main branch +├── deps.lock # Auto-generated lock file (gitignored) +└── deps/ # Auto-populated by wit-deps (gitignored) + ├── golem-core/ + ├── golem-agent/ + ├── golem-1.x/ + ├── golem-rdbms/ + ├── golem-durability/ + ├── blobstore/ + ├── cli/ + ├── clocks/ + ├── config/ + ├── ... + └── sockets/ +``` + +- **`main.wit`** defines the `golem:agent-guest` world — the set of imports/exports the agent component uses. This file is checked in and maintained manually. +- **`deps.toml`** declares a single dependency source: the golem repo's main branch tarball. `wit-deps` downloads and extracts the WIT packages from it. +- **`deps/`** and **`deps.lock`** are gitignored — they are populated by running `wit-deps` from `golem/`. + +## When to Regenerate + +The base image **must be regenerated** whenever: + +1. **`wit/main.wit` changes** — adding/removing imports or exports +2. **WIT dependencies update** — e.g., upgrading from Golem v1.5.0 to a newer version (update `deps.toml` then regenerate) +3. **`wasm-rquickjs` updates** — a new version of the wrapper generator may produce different output + +The generated `agent_guest.wasm` is checked in at two locations (embedded in the sbt and mill plugins): +- `golem/sbt/src/main/resources/golem/wasm/agent_guest.wasm` +- `golem/mill/resources/golem/wasm/agent_guest.wasm` + +## Prerequisites + +### 1. Rust toolchain + +```bash +curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh +rustup target add wasm32-wasip1 +``` + +### 2. cargo-component + +```bash +cargo install cargo-component +``` + +### 3. wit-deps + +```bash +cargo install wit-deps-cli +``` + +### 4. wasm-rquickjs (pinned to 0.1.0) + +The script enforces a specific version of `wasm-rquickjs` and will refuse to run if the installed version does not match. The required version is defined by `REQUIRED_WASM_RQUICKJS_VERSION` in `generate-agent-guest-wasm.sh`. + +```bash +cargo install wasm-rquickjs-cli@0.1.0 +``` + +## How to Regenerate + +From the **repository root** (`zio-blocks/`): + +```bash +./golem/scripts/generate-agent-guest-wasm.sh +``` + +The script performs these steps: + +1. Runs `wit-deps update` in `golem/` to fetch the latest WIT dependencies into `golem/wit/deps/` +2. Stages a clean WIT package in `.generated/agent-wit-root/` (copies `main.wit` + `deps/`) +3. Runs `wasm-rquickjs generate-wrapper-crate` to produce a Rust crate from the WIT +4. Builds with `cargo component build --release` targeting `wasm32-wasip1` +5. Installs the resulting `agent_guest.wasm` into both plugin resource directories + +## Fetching Dependencies Only + +To update the WIT dependencies without regenerating the WASM: + +```bash +cd golem && wit-deps +``` + +The generation script always uses `wit-deps update` to ensure deps are fresh. To update deps without a full regeneration: + +```bash +cd golem && wit-deps update +``` + +## How It Fits Together + +At build time, the sbt/mill `GolemPlugin` extracts the embedded `agent_guest.wasm` from plugin resources and writes it to the user project's `.generated/agent_guest.wasm`. Then `golem-cli` uses this base runtime to compose the final component: it injects the user's Scala.js bundle into the QuickJS runtime and wraps it as a proper Golem agent component. + +The Scala SDK does **not** parse WIT to generate Scala bindings. Instead, Scala macros + ZIO Schema produce `AgentMetadata` at compile time, and `WitTypeBuilder` maps schema types to WIT-compatible JS representations at runtime. The WIT definitions only flow through the WASM guest runtime. diff --git a/.claude/skills/zio-golem-code-generation/SKILL.md b/.claude/skills/zio-golem-code-generation/SKILL.md new file mode 100644 index 0000000000..c38a4625c1 --- /dev/null +++ b/.claude/skills/zio-golem-code-generation/SKILL.md @@ -0,0 +1,168 @@ +--- +name: zio-golem-code-generation +description: "Generating Scala code in the golem subproject. Use when adding code generation steps, build-time source generators, scalameta AST construction, or sbt/Mill sourceGenerators to the golem/ subtree." +--- + +# ZIO Golem Code Generation + +Guidelines for writing Scala code generators in the golem subproject. + +## Core Principle: Scalameta AST, Never String Templates + +Always construct generated code as **scalameta AST nodes** using quasiquotes (`q"..."`, `t"..."`, `source"..."`, `param"..."`). Never use string interpolation or text templates to produce Scala source files. + +```scala +// ✅ Correct — typed AST +val tree = q""" + object $name { + def register(): Unit = { + ..$registrations + () + } + } +""" + +// ❌ Wrong — string interpolation +val code = s"""object $name { + def register(): Unit = { ... } +}""" +``` + +## Shared Codegen Library (`golem/codegen/`) + +All build-time code generation logic lives in `golem/codegen/`, a pure (no ZIO, no sbt, no Mill) Scala library that cross-compiles to **Scala 2.12** (for sbt) and **Scala 3.3.7** (for Mill). Both plugins depend on this shared library. + +### Cross-compilation constraints + +Because the library must compile under Scala 2.12: + +- Use `import scala.meta.dialects.Scala213` for the implicit dialect needed by quasiquotes and `.parse[T]` calls. +- For parsing with a specific dialect, use `dialects.Scala3(code).parse[Source]` (explicit dialect application) rather than `implicit val d: Dialect = ...` which causes ambiguity. +- Use `parseMeta[T](code)(implicit parse: Parse[T])` helper pattern for snippet parsing. +- Avoid Scala 3-only syntax in shared code. + +### Type and term references + +Parse dotted strings into scalameta AST nodes for use in quasiquotes: + +```scala +private def parseMeta[T](code: String)(implicit parse: Parse[T]): T = + Scala213(code).parse[T].get + +private def parseTermRef(dotted: String): Term.Ref = + parseMeta[Term](dotted).asInstanceOf[Term.Ref] + +private def parseType(tpe: String): Type = + parseMeta[Type](tpe) + +private def parseImporter(dotted: String): List[Importer] = + parseMeta[Stat](s"import $dotted").asInstanceOf[Import].importers +``` + +### API pattern + +Generators should expose a **pure, effect-free API** that accepts source text and returns generated outputs + diagnostics: + +```scala +object MyCodegen { + final case class GeneratedFile(relativePath: String, content: String) + final case class Warning(path: Option[String], message: String) + final case class Result(files: Seq[GeneratedFile], warnings: Seq[Warning]) + + def generate(inputs: ...): Result = { + // 1. Parse/scan inputs + // 2. Build scalameta AST via quasiquotes + // 3. Pretty-print via .syntax + // 4. Return GeneratedFile with relative path + content + } +} +``` + +The plugin wrappers (sbt/Mill) handle file I/O, logging, and build-tool integration. + +## Build Integration Pattern + +### sbt Plugin (golem/sbt/) + +The sbt plugin `GolemPlugin` is an `AutoPlugin` compiled as part of the meta-build via `ProjectRef` in `project/plugins.sbt`. It hooks into `sourceGenerators`: + +```scala +Compile / sourceGenerators += Def.task { + val inputs = scalaSources.map { f => + MyCodegen.SourceInput(f.getAbsolutePath, IO.read(f)) + } + val result = MyCodegen.generate(inputs) + result.warnings.foreach(w => log.warn(s"[golem] ${w.message}")) + result.files.map { gf => + val out = managedRoot / gf.relativePath + IO.write(out, gf.content) + out + } +}.taskValue +``` + +For new generators: +1. Add the pure generation logic to `golem/codegen/src/main/scala/golem/codegen/`. +2. Add sbt integration in `golem/sbt/src/main/scala/golem/sbt/`. +3. Hook into `Compile / sourceGenerators` as a `.taskValue`. +4. Use `FileFunction.cached` with `FileInfo.hash` if the generation has an input file (schema, WIT, etc.) to avoid unnecessary regeneration. + +### Mill Plugin (golem/mill/) + +The Mill plugin `GolemAutoRegister` is a trait mixed into `ScalaJSModule`. It uses `generatedSources` and `T { ... }` tasks. Follow the same pattern as `golemGeneratedAutoRegisterSources`. + +### Shared logic, not duplicated + +All generation logic lives in `golem/codegen/`. The sbt and Mill plugins are thin wrappers that: +- Collect source files and read their contents +- Call the shared `generate(...)` function +- Log warnings +- Write returned files under managed/generated roots +- Configure build-tool-specific hooks (module initializers, compile dependencies) + +When adding a new generation step, implement the logic **once** in `golem/codegen/`, then add thin wrappers in both `GolemPlugin.scala` and `GolemAutoRegister.scala`. + +## Existing Code Generation in Golem + +### 1. Auto-Registration (shared codegen + sbt/Mill wrappers) + +Scans sources for `@agentImplementation` classes using scalameta's parser, then generates `RegisterAgents.scala` and per-package `__GolemAutoRegister_*.scala` files using scalameta quasiquotes. + +**Files:** +- `golem/codegen/src/main/scala/golem/codegen/autoregister/AutoRegisterCodegen.scala` — shared logic +- `golem/sbt/src/main/scala/golem/sbt/GolemPlugin.scala` — sbt wrapper +- `golem/mill/src/golem/mill/GolemAutoRegister.scala` — Mill wrapper + +### 2. Scala 3 Macros (compile-time, not build-time) + +Macros generate code at compile time, not as a build step. They live in `golem/macros/` and use `scala.quoted.*`: + +- `AgentDefinitionMacro` — extracts `AgentMetadata` from `@agentDefinition` traits +- `AgentImplementationMacro` — generates implementation wrappers from `@agentImplementation` classes +- `AgentClientMacro` — generates RPC client types +- `AgentCompanionMacro` — generates companion object boilerplate (`get`, `getPhantom`, etc.) + +These are **not** build-time code generators. Do not confuse them with `sourceGenerators`. + +## Generation Pipeline Shape + +Follow this pipeline for new generators: + +``` +1. Load schema/input (WIT file, annotation scan, external spec) +2. Parse into models (typed case classes, not raw strings) +3. Classify/transform (determine what code to emit) +4. Build AST (scalameta quasiquotes) +5. Pretty-print (.syntax on the AST root) +6. Return (GeneratedFile with relativePath + content) +``` + +The plugin wrappers handle file writing, formatting, and incremental build integration. + +## Conventions + +- **Pure functions** — all generator methods are pure. No ZIO, no sbt/Mill types, no file I/O in the shared library. +- **Trait mixin composition** — split generators into traits (`ModelGenerator`, `ClientGenerator`, etc.) and mix them into the main codegen class if complexity warrants it. +- **Dialect-aware** — use `dialects.Scala3` for parsing user sources (with `Scala213` fallback). Use `Scala213` for quasiquote construction (compatible with both 2.12 and 3.x codegen host). +- **Generated file header** — include `/** Generated. Do not edit. */` as a comment in generated objects/classes. +- **Output location** — write to `sourceManaged` (sbt) or `T.dest` (Mill), never to source directories. diff --git a/.claude/skills/zio-golem-development/SKILL.md b/.claude/skills/zio-golem-development/SKILL.md new file mode 100644 index 0000000000..14747f5bb3 --- /dev/null +++ b/.claude/skills/zio-golem-development/SKILL.md @@ -0,0 +1,215 @@ +--- +name: zio-golem-development +description: "Compile, publish, and test the ZIO Golem Scala.js SDK. Use when working on the golem/ subtree: building the SDK, publishing locally, compiling/running the example demo, regenerating the agent_guest.wasm, or debugging end-to-end deployment." +--- + +# ZIO Golem SDK Development + +The Golem SDK for Scala.js lives under `golem/` in the zio-blocks monorepo. It targets the Golem WIT API v1.5.0 and produces WASM components that run on the Golem platform via a QuickJS-based guest runtime. + +## Repository Layout + +``` +golem/ +├── core/ # zio-golem-core (Scala.js facades, agent framework) — JS-only +├── model/ # zio-golem-model (WIT value types, RPC types) +├── macros/ # zio-golem-macros (Scala 3 macros, JVM-only) +├── codegen/ # Shared build-time code generation library +├── sbt/ # zio-golem-sbt (SBT plugin, Scala 2.12) +├── mill/ # Mill plugin +├── wit/ # WIT definitions (main.wit + deps/) +│ ├── main.wit # Primary WIT — package golem:agent-guest, world agent-guest +│ ├── deps/ # WIT dependencies (copied from golem repo) +│ └── dts/ # Generated TypeScript d.ts (source of truth for JS exports) +├── scripts/ # generate-agent-guest-wasm.sh +├── example/ # Standalone demo project (separate sbt build) +├── test-agents/ # Test agent definitions + implementations for integration tests +├── integration-tests/ # Integration test suite +└── docs/ # Documentation +``` + +## Scala Versions + +- **Scala 3.8.2** (`Scala3Golem` in `BuildHelper.scala`) — All Golem Scala 3 projects. Prefix sbt commands with `++3.8.2` (without `!` — upstream deps like `schema`, `chunk` compile at their own Scala version; only golem projects with 3.8.2 in crossScalaVersions are affected). +- **Scala 2.12.21** — The SBT plugin (`zioGolemSbt`) only. Use `++2.12.21!` (the `!` forces override). + +> **Important**: `sbt --client` mode preserves Scala version across invocations. Always specify the version explicitly to avoid version drift. + +## SBT Project Names + +| Project | Description | +|---------|-------------| +| `zioGolemCoreJS` | Core agent framework, Scala.js facades (JS-only) | +| `zioGolemModelJS` / `zioGolemModelJVM` | WIT value types, RPC types | +| `zioGolemMacros` | Scala 3 macros (JVM only, cross-used at compile time) | +| `zioGolemSbt` | SBT plugin (Scala 2.12) | +| `zioGolemTestAgents` | Test agents for integration tests | + +## Running All Tests + +Use these sbt aliases (from the monorepo root) to run all zio-golem tests: + +| Alias | What it runs | +|-------|-------------| +| `sbt --client golemTest3` | All unit tests (JVM + JS) + test-agents compile + integration tests — **Scala 3** | +| `sbt --client golemTest2` | All unit tests (JVM + JS) + test-agents compile — **Scala 2** (integration tests are Scala 3 only) | +| `sbt --client golemTestAll` | Both of the above (Scala 3 then Scala 2) | + +**Always run `golemTestAll` before considering a change complete.** + +Integration tests require the TypeScript SDK packages path. The `GOLEM_TS_PACKAGES_PATH` env var is forwarded automatically by `build.sbt`, but `sbt --client` doesn't propagate env vars. Use non-client `sbt` instead: + +```bash +GOLEM_TS_PACKAGES_PATH= sbt golemTestAll +``` + +## Compiling + +From the monorepo root (`/home/vigoo/projects/zio-blocks`): + +```bash +# Compile test agents (good smoke test) +sbt --client "++3.8.2; zioGolemTestAgents/fastLinkJS" + +# Compile core +sbt --client "++3.8.2; zioGolemCoreJS/compile" + +# Compile model +sbt --client "++3.8.2; zioGolemModelJS/compile" +``` + +Use the standard AGENTS.md sbt logging pattern: +```bash +ROOT="$(git rev-parse --show-toplevel)" && mkdir -p "$ROOT/.git/agent-logs" +LOG="$ROOT/.git/agent-logs/sbt-$(date +%s)-$$.log" +sbt --client -Dsbt.color=false "++3.8.2; zioGolemTestAgents/fastLinkJS" >"$LOG" 2>&1 +echo "Exit: $? | Log: $LOG" +# Query: tail -50 "$LOG" or grep -i error "$LOG" +``` + +## Publishing Locally + +The `example` project depends on `0.0.0-SNAPSHOT` artifacts. All golem projects have `publish / skip := true` by default, so you must override it. + +### Step 1: Publish Dependencies + Golem Libraries (Scala 3.8.2) + +```bash +sbt --client '++3.8.2; set ThisBuild / version := "0.0.0-SNAPSHOT"; set ThisBuild / packageDoc / publishArtifact := false; set every (publish / skip) := false; typeidJVM/publishLocal; typeidJS/publishLocal; chunkJVM/publishLocal; chunkJS/publishLocal; markdownJVM/publishLocal; markdownJS/publishLocal; schemaJVM/publishLocal; schemaJS/publishLocal; zioGolemModelJVM/publishLocal; zioGolemModelJS/publishLocal; zioGolemMacros/publishLocal; zioGolemCoreJS/publishLocal' +``` + +### Step 2: Publish SBT Plugin (Scala 2.12.21) + +```bash +sbt --client '++2.12.21!; set ThisBuild / version := "0.0.0-SNAPSHOT"; set ThisBuild / packageDoc / publishArtifact := false; set every (publish / skip) := false; zioGolemSbt/publishLocal' +``` + +> **Note**: The `golemPublishLocal` alias exists in `build.sbt` but may need `set every (publish / skip) := false` prepended to work correctly. The explicit commands above are the most reliable approach. + +## Building the Example Project + +The `example` project at `golem/example/` is a standalone sbt project (its own `build.sbt`, `project/plugins.sbt`). It depends on the SDK at `0.0.0-SNAPSHOT`. + +### Prerequisites +1. Publish the SDK locally (both steps above). + +### Clean Build +```bash +cd golem/example +rm -rf target project/target .bsp .generated .golem +sbt -batch -no-colors -Dsbt.supershell=false compile +``` + +### Key SBT Tasks +- `sbt golemPrepare` — Generates `.generated/agent_guest.wasm` (extracted from plugin resources) and `.generated/scala-js-template.yaml` (component manifest template). +- `sbt compile` — Compiles the Scala agent code. +- `sbt fastLinkJS` — Links the Scala.js bundle (produces the JS that QuickJS will run). + +### Project Structure +- `build.sbt` — Enables `ScalaJSPlugin` + `GolemPlugin`, sets `scalaJSUseMainModuleInitializer := false`, ESModule output. +- `project/plugins.sbt` — Adds `zio-golem-sbt` and `sbt-scalajs`. +- `golem.yaml` — Declares app name, includes `.generated/scala-js-template.yaml`, defines component `scala:demo`. +- `repl-counter.rib` — Rib script for end-to-end testing via `golem-cli repl`. + +## End-to-End Testing + +### Start the Local Golem Server +```bash +golem-cli server run --clean +``` +This starts the all-in-one Golem server on `localhost:9881`. + +### Using run.sh +```bash +cd golem/example +bash run.sh +``` + +The script does: +1. `sbt golemPrepare` — Generate wasm + manifest template +2. `golem-cli build --yes` — Build the WASM component (links QuickJS runtime + Scala.js bundle) +3. `golem-cli deploy --yes` — Deploy to local Golem server +4. `golem-cli repl scala:demo --script-file repl-counter.rib` — Run the demo + +### Manual Steps +```bash +cd golem/example +sbt golemPrepare +golem-cli build --yes +golem-cli deploy --yes --local +golem-cli repl scala:demo --script-file repl-counter.rib --local +``` + +> **Note**: `golem-cli` v1.5.0-dev is at `/home/vigoo/.cargo/bin/golem-cli`. The `--local` flag targets the local server. + +## Regenerating agent_guest.wasm + +The agent_guest.wasm is the QuickJS-based WASM runtime that wraps the Scala.js bundle. Regenerate it when WIT definitions change. + +### Script +```bash +./golem/scripts/generate-agent-guest-wasm.sh +``` + +### What It Does +1. Resolves WIT deps via `wit-deps update`. +2. Stages WIT package from `golem/wit/` (skipping the `all/` dep directory). +3. Generates TypeScript d.ts definitions via `wasm-rquickjs generate-dts` → saved to `golem/wit/dts/`. +4. Generates QuickJS wrapper crate via `wasm-rquickjs generate-wrapper-crate`. +5. Builds with `cargo component build --release`. +6. Installs the wasm into `golem/sbt/src/main/resources/golem/wasm/agent_guest.wasm` and `golem/mill/resources/golem/wasm/agent_guest.wasm`. +7. Copies d.ts files to `golem/wit/dts/`. + +### Requirements +- `wit-deps` (`cargo install wit-deps-cli`) +- `wasm-rquickjs` v0.1.0 (`cargo install wasm-rquickjs-cli@0.1.0`) +- Rust toolchain + `cargo-component` (`cargo install cargo-component`) + +## WIT Management + +### Files +- **Primary**: `golem/wit/main.wit` — The `golem:agent-guest` package definition. +- **Dependencies**: `golem/wit/deps/` — Copied from the main Golem repo at `/home/vigoo/projects/golem/wit/deps/`. +- **TypeScript reference**: `golem/wit/dts/` — Generated d.ts files showing exact JS types expected by the wasm runtime. `exports.d.ts` is the source of truth for what the JS module must export. + +### Updating WIT Dependencies +1. Delete `golem/wit/deps.lock` (prevents `wit-deps` from restoring stale versions). +2. Copy fresh deps from the Golem repo: `cp -r /home/vigoo/projects/golem/wit/deps/* golem/wit/deps/`. +3. Run `wit-deps update` from the `golem/` directory. +4. Regenerate `agent_guest.wasm` using the generate script. + +> **Gotcha**: `wit-deps` with a stale `deps.lock` can silently overwrite correct deps with old versions. + +### TypeScript SDK Reference +The TypeScript SDK at `/home/vigoo/projects/golem/sdks/ts/wit/` is the reference for correct WIT definitions when in doubt. + +## Common Errors and Solutions + +| Error | Cause | Solution | +|-------|-------|----------| +| `Function discover-agent-types not found in interface golem:agent/guest@1.5.0` | Stale `agent_guest.wasm` built from old WIT | Regenerate wasm with `generate-agent-guest-wasm.sh` | +| `Cannot find exported JS function guest.discoverAgentTypes` | Scala.js Guest object doesn't match WIT signature | Update `Guest.scala` to export all 4 functions with correct v1.5.0 signatures (including `principal` param) | +| `YAML deserialization error` in `golem.yaml` about `BuildCommand` | Old GolemPlugin manifest format | Update `GolemPlugin.scala` to use v1.5.0 format (`componentWasm`/`outputWasm`) | +| `wit-deps` restoring stale 1.1.7 dependencies | Stale `deps.lock` | Delete `golem/wit/deps.lock`, copy fresh deps from Golem repo | +| `Provided exports: (empty)` after deploy | QuickJS fails to evaluate the JS module silently | JS crashes during initialization — check for ESM strict-mode issues, bundle size limits, or import path mismatches | +| `publish / skip` preventing local publish | Default setting in `build.sbt` | Use `set every (publish / skip) := false` in the sbt command | +| Wrong Scala 2.12 version for plugin | Alias or cached sbt version uses wrong 2.12.x | Use the explicit `++2.12.21!` command to force the correct version | diff --git a/.claude/skills/zio-golem-integration-tests/SKILL.md b/.claude/skills/zio-golem-integration-tests/SKILL.md new file mode 100644 index 0000000000..9d60bf415e --- /dev/null +++ b/.claude/skills/zio-golem-integration-tests/SKILL.md @@ -0,0 +1,173 @@ +--- +name: zio-golem-integration-tests +description: "Run and debug Golem Scala SDK integration tests. Use when running golem integration tests, debugging test failures, or working with GolemExamplesIntegrationSpec." +--- + +# ZIO Golem Integration Tests + +Integration tests for the Golem Scala SDK live in `golem/integration-tests/`. They exercise test agents against a real local Golem server. + +## Prerequisites + +1. **`golem-cli`** on PATH (v1.5.0-dev at `~/.cargo/bin/golem-cli`) +2. **TS packages built** — the Golem TypeScript SDK packages at the path pointed to by `GOLEM_TS_PACKAGES_PATH` +3. **Port 9881 free** — the test suite starts its own Golem server +4. **SDK published locally** — run from the zio-blocks monorepo root: + +```bash +sbt --client '++3.8.2; set ThisBuild / version := "0.0.0-SNAPSHOT"; set ThisBuild / packageDoc / publishArtifact := false; set every (publish / skip) := false; zioGolemModelJVM/publishLocal; zioGolemModelJS/publishLocal; zioGolemMacros/publishLocal; zioGolemCoreJS/publishLocal' +``` + +## Running Tests + +The simplest way to run all tests (unit + integration, Scala 2 + 3) is with non-client `sbt`: + +```bash +GOLEM_TS_PACKAGES_PATH= sbt golemTestAll +``` + +The `GOLEM_TS_PACKAGES_PATH` env var is forwarded automatically by `build.sbt` to `javaOptions` and `envVars` for the integration tests. + +### Running specific tests with `sbt --client` + +With `sbt --client`, env vars don't propagate to the forked test JVM. Use the `set` override instead: + +```bash +# All integration tests +sbt --client '++3.8.2; set zioGolemIntegrationTests / Test / javaOptions += "-Dgolem.tsPackagesPath="; zioGolemIntegrationTests/test' + +# Only HTTP endpoint tests +sbt --client '++3.8.2; set zioGolemIntegrationTests / Test / javaOptions += "-Dgolem.tsPackagesPath="; zioGolemIntegrationTests/testOnly -- -t http-' + +# A specific test by name +sbt --client '++3.8.2; set zioGolemIntegrationTests / Test / javaOptions += "-Dgolem.tsPackagesPath="; zioGolemIntegrationTests/testOnly -- -t sync-return' +``` + +Use the standard AGENTS.md sbt logging pattern (redirect to log file, check exit code). + +## Test Architecture + +### Server Lifecycle + +The `GolemServer.layer` (ZLayer) handles everything: + +1. Checks `golem-cli` is on PATH +2. Checks `GOLEM_TS_PACKAGES_PATH` / `golem.tsPackagesPath` is set +3. Verifies port 9881 is free (fails if already in use — **kill any running golem server first**) +4. Cleans `golem-temp/` directory (stale REPL caches) +5. Starts `golem-cli -vvv server run --clean --disable-app-manifest-discovery` +6. Waits for port 9881 to accept connections (60s timeout) +7. Runs `golem-cli deploy` (with one retry) +8. On teardown: kills the server process tree + +### Two Test Categories + +1. **Sample tests** — TypeScript REPL scripts in `golem/test-agents/samples/*/repl-*.ts`. Each script is executed via `golem-cli repl scala:examples --language typescript --script-file