AGENTS.md · git:20261001.143ecba · 2026-10-01 · sha256 614033e456e5d895
AGENTS.md git:20261001.143ecbaA
Immutable. This exact content never changes and is served at /api/v1/blob/614033e456e5d895.
# Kiln Engine Agent Guide
## Scope
This repository turns model-authored source into GLBs: deterministic rendering, QA,
primitives, agent tools, arena ranking, scene composition, CLI and MCP + skills.
Read [README.md](./README.md) before changing exports or package contents. Runtime and tests
live under `src/`; repository checks under `scripts/`. `package.json` explicitly lists shipped
TypeScript through `files` and `exports`. [CHANGELOG.md](./CHANGELOG.md) states what each
release changed. Dated files under `docs/plans/` and `docs/reviews/` are records of their
day: they never override current code, this guide or the changelog, and never imply release
acceptance. The 0.9.0 state is the changelog's 0.9.0 section; the 2026-09-26 foundation
checkpoint is history.
## Authoring an asset is a different task from changing the engine
This guide covers engine changes. Author assets in a separate workspace with a live
`kiln_workspace` server: exposing engine source and examples changes model outputs.
Before asset authoring, read `kiln-setup-workspace` and create a workspace. Its maintained
copy is `skills/kiln-setup-workspace/`, registered at `.claude/skills/` and `.agents/skills/`.
Feed demonstrated authoring defects back into the maintained skills at the relevant
boundary: shared shape and fit guidance in
`skills/kiln-author-asset/references/geometry-recipes.md`; destination traversal,
interaction and performance evidence in the QA integration reference. Keep scene-specific
controllers and asset dimensions in authored workspaces. Interactive proving environments
use the optional `skills/kiln-compose-scene/` workflow and its runtime-scenes reference;
scene terrain, controllers and optimization derivatives never become requirements for
standalone assets. Guidance changes do not retroactively qualify an existing asset or
replace its owner review. Generated workspaces copy these skills; verify the copies and
package contents without silently rewriting existing authors' installed instructions.
## Optional projects
Standalone assets, materials and review must work without projects. Do not infer membership
from nearby projects or impose pack-specific defaults. Read the
[selection and delivery contracts](docs/projects-and-live-review.md): configured
`KILN_PROJECT` is opt-in; `--no-project` / `projectId: null` overrides it. The collection
named `project` is only a destination. Keep `skills/`, registered setup copies and the
generated `AGENTS.md`/`CLAUDE.md` in `scripts/create-workspace.mjs` aligned; qualify fresh
setup and managed upgrade without overwriting user customizations.
## Library and gallery assets
The Workshop Library aggregates configured asset collections; collection `project` is
workspace storage, not project membership. Keep collection-qualified IDs through browsing,
revision selection and project links. Projects pin exact saved revisions. Live Review's
optional host `KILN_WORK_ITEM` groups one authoring item across CLI/MCP sessions; never
infer it from project, model name or source equality. Explicit observation workspaces stay
separate sources. Build and check viewer changes through the installed package and the
actual Library/Projects/Live Review flow, not only a separate comparison page.
Gallery assets are unvetted historical showcases, not golden outputs; regression comparisons
do not establish their quality.
## The tool registry is the single source of truth
`src/tools/registry.ts` owns tool names, descriptions and schemas. **Both Strands
(`src/agent/tools.ts`) and stdio MCP must iterate it.** Never hand-write definitions in a
skin. Keep the name-parity test passing. `docs/tools.md` is generated from it by
`scripts/generate-tool-reference.ts`.
`createKilnProgramToolRegistry` supplies the fourteen base MCP tools. The packaged local host
also injects `kiln_project`, `kiln_material` and `kiln_review` (seventeen); embeddings
advertise these only when the corresponding host services are supplied. Unavailable host
services must not become advertised tools that always fail.
`createKilnNativeToolRegistry` uses the same definitions and adds `kiln_finish`: ten tools
by default, fifteen with an injected `assetLibrary` (the five delivery tools). A configured
skill-resource reader adds `kiln_skill_resource` to either native set.
Both skins use unified `kiln_render`: metrics, part paths, QA, images and fidelity share one
evaluation. `kiln_screenshot`, the mutable-buffer factories and the surface selector are
retired. `kiln_validate` is the image-free syntax check. Review results are compact by
default and `detail: 'full'` returns the complete QA report; compaction happens at the
output boundary, after the retained artifact is recorded (`src/tools/review-detail.ts`).
Native completion uses `kiln_finish({programRef})`, defined in the registry. It selects an
exact retained reviewed artifact without re-evaluation; completion and QA acceptance stay
separate. A completion call runs alone; immutable edits and reads may run concurrently. The
MCP surface has no terminal action because its host owns the outer loop and delivery. Do
not restore the old submit/finalize factories or silently route old selectors.
Strands harness guidance is registered by `src/agent/native-workflow.ts`, outside the shared
`skills/` tree. Keep its completion/recovery protocol out of CLI/MCP setup, generated
workspace instructions, Discovery and shared skills. Geometry, material and inspection
contracts stay shared through explicitly selected technical references. See
[the native workflow boundary](docs/runtime.md#optional-native-strands-workflow).
## Host-injected render and cache boundaries
Kiln Discovery is `kiln_discover` / `kiln discover`; helper specs and the typed catalog live
under `src/discovery/`, and `./discovery` is the public entrypoint. Search is ranked local
text retrieval with reviewed metadata: no models, network, provider keys or GPU. An optional
semantic enhancement needs an explicit adoption decision and cannot weaken that baseline.
The engine defines the host-injected `PbrRenderPort`. Exported `captureViewsViaPort` is the
**single owner** of the deadline, renderer/PNG validation, grid composition and never-throw
CPU fallback. Do not duplicate that degrade policy in a host or add network/service knowledge
to deterministic engine paths. Keep the two injection deadlines separate: the post-loop
artifact sheet can wait; `KilnToolContext.viewRenderPort` serves the blocking in-loop
`kiln_render` grid with a much lower per-call deadline.
Route by `sceneNeedsPbrShading(root)`: bound texture or `metalness > 0`, never material type
(`gameMaterial` and `pbrMaterial` both make `MeshStandardMaterial`). Host telemetry uses
`onViewsRendered`; every `kiln_render` returns model-visible `ViewFidelityV1`. CPU geometry
images are not material evidence. Tool definitions are prompt-cached: keep input schemas
stable unless a change explicitly versions them.
`review-neutral-v1` is the default review rig: a calibrated white rig with Review Neutral
tone mapping, so a matte surface reads back near its authored albedo. Explicit lighting IDs
stay in capture cache keys. `src/views/background.ts` owns the named backdrops;
`render-service/src/backdrops.mjs` mirrors them, guarded by `backdrop.test.ts`, and
`render-service/src/display-transform.mjs` makes GPU and CPU backdrop pixels match. Captures
accept only `neutral` (default), `dark` or `light`, never free colours. Both capture-cache
keys include the id; results echo `capture.backdrop`; `kiln_save` records
`preview.backdrop`.
**One render service per machine, and the socket is its registry.** Sessions share port 8000
(or `KILN_RENDER_SERVICE_PORT`) and join only a validated compatible protocol, instance,
build, dependency and capture identity. A timed-out listener is unknown, not a verified busy
renderer. Stale, incompatible and foreign listeners are reported without replacement;
`kiln service stop` is the explicit action. Managed services have a bounded idle lifetime
(five minutes by default) protected by all admitted work; host exit must not kill a shared
service or another client's work. Manual services stay long-lived. Service and host share
`render-service/src/build-identity.mjs`; exact resolved dependency pins count. Do not add
lease files or another discovery path. In-session `kiln_renderer` reprobe refreshes
availability for the existing route and does not install dependencies.
Render requests are self-contained GLBs. Admission, upload time, decoded PNG allocations,
geometry and scene depth are bounded before loading. Cancellation removes queued work.
Propagate the optional second-argument execution signal through every `PbrRenderPort`
wrapper, including capture caches, while deadline and CPU degradation stay in
`captureViewsViaPort`.
**The GPU is a view producer only, never gate evidence.** `QaContext` is deliberately
image-free so a QA rule structurally cannot read a render buffer. Do not add pixels to it.
`src/views/renderer-id.ts` calls `readFileSync` at MODULE LOAD. Reach
`CPU_RASTER_RENDERER_ID` through existing lazy `await import('../views')` paths; static
imports add a Node-only filesystem edge. No test catches this.
Prompt-cache transports differ deliberately. Native Anthropic and Bedrock adapters consume a
system `[TextBlock, CachePointBlock]`; OpenRouter-hosted Anthropic keeps plain system text
and receives top-level `cache_control: { type: 'ephemeral' }` because its Vercel bridge drops
cache-point blocks. Preserve that and the provider usage fields when changing model routing.
## Toolchain and validation
Maintainer toolchain: Bun `1.4.2`; Node `22.23.2`; npm `12.0.2`, pinned in `toolchain.json`.
Consumer Node compatibility is defined separately in `src/runtime-support.mjs` and
`engines.node`; do not add Bun or npm maintainer pins to end-user `engines` or use a Bun
canary or implicit latest for a release gate.
```bash
bun install --frozen-lockfile
bun run check:toolchain
bun run check:skills
bun run typecheck
bun run lint
bun run test
bun run test:render-service
bun run test:coverage
```
`bun run build` is a typecheck only. After runtime source changes, rebuild the Node bundles
with `node scripts/build-runtime.mjs all` before CLI/MCP dogfood and full gates; use
`bun run build:runtime` when viewer assets also changed. A typecheck does not refresh `dist/`
or its build identity.
Fast loop: nearest test file first, then `bun run typecheck && bun run lint && bun run test`.
The full offline gate adds `bun run test:coverage`, which emits `coverage/lcov.info` and
enforces the line/function ratchet in `bunfig.toml`. Raise thresholds when practical; never
lower them without a measured rationale. Live model tests are opt-in via `bun run test:live`
and may spend money.
`bun run test` is `bun test src scripts` and does **not** reach `render-service/`; run
`bun run test:render-service` whenever you change it (CI requires it). Its fixtures need no
GPU; real GPU and installed-package checks are separate gates.
Tests use `--timeout 20000` for cold Windows startup; larger budgets need an explicit third
`test()` argument with a measured-duration comment. Tests and CI pin `KILN_RENDER=cpu`, so the
coverage ratchet cannot vary with the runner's GPU. Coverage is measured over `src/` alone;
tests under `scripts/` still run and answer for their own assertions.
Use strict test-driven development for behavior changes: add a focused failing test, observe
the expected failure, implement the smallest fix, and rerun both the focused and full gates.
Preserve determinism: no `Date.now()` or `Math.random()` in render/rasterizer compute paths.
## Safety
- Never print, read unnecessarily, or commit provider keys, cloud credentials, `.env*`, or
secrets.
- Keep normal validation offline. Do not invoke live providers unless explicitly requested.
- Do not commit, push, force-push, create remotes/releases, or publish packages without
explicit user approval.
- Live model and arena runs spend money. Keep them manual and out of CI.