# Run and debug a language binding or adapter

Start with your language's workbench project. Keep its tactic and launcher
files together, change the tactic, then build and run the project. Hosted
profiles supply the SDK and its dependencies; they do not need a copied
binding in your source tree.

For local work, install the SDK and the runtime or compiler named in the
language's generated guide. The examples here use `/opt/clunkyard/sdk`.
The [API name table](names.md) and each linked language guide contain the
callable names, record shapes and complete small robots. Use
[local trials](../local-trials.md) to run a project against opponents.

## Which process owns the turn?

A **native binding** converts language values to the core's C ABI in the
same process. Python's `ctypes`, Java's FFM and a compiled Lua module all
fit this description. They do not launch another robot for each query.

A **process adapter** keeps a tactic in another language process and
forwards its generated API calls to a Python or Node binding. Forth, GNU
APL and BQN use persistent tactic processes. CLIPS runs in Python through
clipspy; MiniZinc solves a plan at startup and Python follows it. These are
separate execution models, even though all use the same core for canonical
queries, tracking and controls.

The arena meters the complete robot workload, including adapter processes
and solver children. Choosing an adapter does not make its CPU free.
Standard uses the launched runtime's Season 3 tier: Forth, APL, CLIPS and
MiniZinc use the Python route; BQN uses Node. The VM's JVM profiles use the
same configured JVM mode. Own time includes CPU and time spent sleeping or blocking, with measured
scheduler/VM delays subtracted; a blocked tactic can exhaust its own-time
limit. DSL operations and guest WebAssembly fuel are the Reflex
routes. [Resources](../resources.md) explains the measurements and limits.

## Forth, GNU APL and BQN

These profiles accept a tactic directly as `bot.json`'s `entry`. The hosted
launcher starts the adapter and keeps one tactic process for the match.
For example:

```json
{
  "name": "My Forth robot",
  "language": "forth",
  "protocol": 2,
  "entry": "tactic.fs",
  "supportedModes": ["2d", "3d"],
  "supportedRulesets": ["classic", "inertial-2d", "gravity-flat", "gravity-hills", "zero-g"]
}
```

Use `language: "apl"` with an `.apl` entry, or `language: "bqn"` with a
`.bqn` entry, for the other two. The starter projects also include Python
or JavaScript launchers so you can add host-language setup around the tactic.
The selected entry decides which route launches. You do not need to write
protocol JSON in the tactic.

| Profile | Tactic contract | Installed components | Local direct-tactic command |
| --- | --- | --- | --- |
| [Forth](forth.md) | `on-turn`, optional `on-start`; preserve both stacks across a turn | Python core binding, `clunkyard_forth`, Gforth and generated words | Command below |
| [GNU APL](apl.md) | Niladic `OnTurn`, optional `OnStart`; generated `Cy*` functions | Python core binding, `clunkyard_apl`, GNU APL and generated functions | Command below |
| [BQN](bqn.md) | Import `cy` from `•args`; `Turn`, optional `Start` | Node core binding, `clunkyard_bqn`, CBQN and generated namespace | Command below |

Run these from the project directory; the absolute tactic argument avoids
runtime-dependent working-directory resolution:

```sh
SDK=/opt/clunkyard/sdk
PYTHONPATH="$SDK/bindings/forth:$SDK/bindings/python" \
  python3 -c 'import clunkyard_forth,sys; clunkyard_forth.run(sys.argv[1])' \
  "$PWD/tactic.fs"

PYTHONPATH="$SDK/bindings/apl:$SDK/bindings/python" \
  python3 -c 'import clunkyard_apl,sys; clunkyard_apl.run(sys.argv[1])' \
  "$PWD/tactic.apl"

NODE_PATH="$SDK/bindings/bqn:$SDK/bindings/javascript" \
  "$SDK/bindings/javascript/run" \
  -e 'require("clunkyard_bqn").run(process.argv[1])' "$PWD/tactic.bqn"
```

These commands wait for protocol input. Redirect a fixture to smoke-test
one, or put the command in a local manifest for a match. For the teaching
projects' `bot.py` or `bot.js` launchers, use the same environment prefixes
and pass the launcher file instead of `-c`/`-e` and its expression.

Forth's canonical words use kebab case, with floating-point arguments on
the floating stack, integer/enum arguments on the data stack and text as
address plus length. `cy-result` turns status into a presence flag and
throws negative errors. APL passes enclosed text and flattened records;
`CyPresent` distinguishes absent values from present empty text. BQN uses
record namespaces and `@` for absence. Their generated guides specify list
and record order; do not infer it from a printed protocol object.

Keep the persistent tactic pipe available for adapter traffic. Log through
canonical session logging, for example Forth's `cy-session-log`, APL's
`CySessionLog` or BQN's `cy.SessionLog`, using that language's documented
argument convention. Unframed output on a tactic's RPC stdout can be read
as a reply. Adapter/runtime errors appear in stderr; an error return from a
core call should be handled before consuming its result. The adapters'
`play` helpers can run a tactic against recorded lines without a referee.

## CLIPS and MiniZinc

Both profiles use a Python launcher as their entry. Include the `.clp` or
`.mzn` tactic beside it; the builder supplies the adapter package.

```sh
SDK=/opt/clunkyard/sdk
PYTHONPATH="$SDK/bindings/clips:$SDK/bindings/python" python3 bot.py
PYTHONPATH="$SDK/bindings/minizinc:$SDK/bindings/python" python3 bot.py
```

[CLIPS](clips.md) uses `clunkyard_clips.ClipsRobot` and the installed
`clipspy` module. It loads the rules once, resets facts for each state,
restores match facts and asserts current observations before running rules.
Globals persist between turns. Canonical `(cy-...)` functions call the core;
output facts such as `drive`, `scan`, `fire`, `status` and `forfeit` are
another control surface. Later facts replace the fields they supply.
CLIPS router output is sent to stderr. Use a rule trace or a short status
fact to inspect why a rule fired, and check whether a more recent output
fact replaced the same control.

[MiniZinc](minizinc.md) uses `clunkyard_minizinc.Planner`. Set `model`,
include the supplied `clunkyard.mzn` declarations in the model, and implement
Python `on_turn` using `self.plan`. `model_data` can add parameters and call
`core_query` while preparing them. The adapter compiles with MiniZinc,
solves with `fzn-gecode` and decodes the result once at hello; it does not
start a solver for each state. Live observations and controls remain the
Python binding's. The installed image supplies MiniZinc and Gecode.
Check model compilation/solver stderr when startup fails, then inspect plan
values separately from the turn logic. Solver JSON uses solver precision.

## Shared JVM and .NET bindings

| Languages | Import/reference | Build or launch locally |
| --- | --- | --- |
| [Java](java.md) | `clunkyard.*` from `bindings/java/clunkyard.jar` | `javac -cp "$SDK/bindings/java/clunkyard.jar" Bot.java`; then `java --enable-native-access=ALL-UNNAMED -cp ".:$SDK/bindings/java/clunkyard.jar" Bot` |
| [Kotlin](kotlin.md) | `clunkyard.*` from the same jar | Compile with `kotlinc` and that jar on its classpath; launch the output's main class with the jar on the Java classpath. |
| [Scala](scala.md) | `clunkyard._` from the same jar | Compile with `scalac`; include Scala's runtime and the binding jar on the Java classpath. |
| [Clojure](clojure.md) | Normal interop with `clunkyard` classes | Put the binding jar and Clojure jar on the classpath; launch `clojure.main bot.clj`. |
| [C#](csharp.md), [F#](fsharp.md) | `Clunkyard.dll`, with the reference's `Private=false` | `dotnet publish -c Release -o out -p:ClunkyardSdk="$SDK"`; launch with `dotnet "$SDK/bindings/csharp/host/Clunkyard.Host.dll" out/bot.dll`. Use the assembly name your project emits. |
| [PowerShell](powershell.md) | `[Clunkyard.Robot]` and shared assembly | `pwsh -NoProfile -File "$SDK/bindings/powershell/run.ps1" bot.ps1` |

Java, Kotlin, Scala and Clojure share Java's Foreign Function and Memory
binding to the native core. Use a JDK/runtime with the FFM API required by
the binding; the release image supplies it. `--enable-native-access` is
required by the launch recipe. Hosted JVM launches also use
`-XX:TieredStopAtLevel=1`, so local JVM timing without that flag is a
different runtime configuration.

C#, F# and PowerShell share one P/Invoke assembly. The .NET host loads the
installed binding while the robot artifact contains its own program and
dependencies. Launch PowerShell through its SDK script so the assembly is
loaded before robot class declarations are parsed. Do not copy an old
`Clunkyard.dll` into the project output. The shared native library is
`libclunkyard.so.2`; these bindings accept `CLUNKYARD_LIB` for a local path.

The generated APIs convert absent values to each family's optional/null
form. Their run loops log ordinary callback exceptions and send controls
already set; explicit `Forfeit` exceptions use the core's forfeit reply.
Import/classpath errors happen before a turn: fix those in the build/start
log before debugging tactics.

## Other bindings in the robot process

Set `SDK=/opt/clunkyard/sdk` before using the prefixes in this table.
A local shared-library installation must make `libclunkyard.so.2` available;
use `CLUNKYARD_LIB` only where that language's guide supports it. Packages
with compiled shims are installed in the builder, so the robot needs only
the runtime and the supplied package.

| Language | Bridge and import | Local launch |
| --- | --- | --- |
| [Python](python.md) | `ctypes`; `import clunkyard as cy` | `PYTHONPATH="$SDK/bindings/python" python3 bot.py` |
| [R](r.md) | Compiled `.Call` package; `library(clunkyard)` | `R_LIBS="$SDK/bindings/r" Rscript --vanilla bot.R` |
| [Ruby](ruby.md) | `Fiddle`; `require 'clunkyard'` | `RUBYLIB="$SDK/bindings/ruby" ruby bot.rb` |
| [Perl](perl.md) | Compiled XS; `use Clunkyard` | `PERL5LIB="$SDK/bindings/perl" perl bot.pl` |
| [Lua](lua.md) | Lua 5.4 C module; `require('clunkyard')` | `LUA_PATH="$SDK/bindings/lua/?.lua;;" LUA_CPATH="$SDK/bindings/lua/?.so;;" lua5.4 bot.lua` |
| [PHP](php.md) | PHP FFI; installed `clunkyard.php` | `php -d ffi.enable=preload -d include_path="$SDK/bindings/php" bot.php` |
| [Julia](julia.md) | `ccall`; `using ClunkyardSDK` | `JULIA_LOAD_PATH="$SDK/bindings/julia:@stdlib" julia bot.jl` |
| [Common Lisp](common-lisp.md) | SBCL `sb-alien`; `cy` package | `sbcl --noinform --disable-debugger --load "$SDK/bindings/common-lisp/clunkyard.fasl" --script bot.lisp` |
| [Erlang](erlang.md) | In-process NIF; `clunkyard` module | `erl -noshell -pa "$SDK/bindings/erlang" -pa . -s bot start` after `erlc` compilation |
| [Elixir](elixir.md) | Facade over the Erlang NIF | `elixir -pa "$SDK/bindings/erlang" -pa "$SDK/bindings/elixir" bot.exs` |
| [Prolog](prolog.md), [CHR](chr.md) | SWI-Prolog foreign module; `library(clunkyard)` or `library(clunkyard_chr)` | `swipl -q -p library="$SDK/bindings/prolog" -s bot.pl -g main -t halt`; CHR also needs `-p library="$SDK/bindings/chr"` |
| [Dart](dart.md) | `dart:ffi`; `package:clunkyard/clunkyard.dart` | Compile using the installed package configuration; run the resulting executable. |
| [OCaml](ocaml.md) | Compiled C stubs; `Clunkyard` | Link the installed `clunkyard.cmxa`; run the executable with the image's shared core. |

The hosted BEAM recipes constrain schedulers for the robot's single CPU;
the Julia recipe uses its supplied system image and runtime options. Use
the hosted-profile route when comparing their resource use. Package/version
mismatches can fail before any ready reply: use the matching builder/runtime
pair and check the native-loader error first.

## Native toolchains and generated shims

These profiles compile a native executable. Their binding marshals calls in
that process and links the core statically. Include/import paths and any
precompiled binding object differ by language; follow its generated guide.

| Languages | SDK surface |
| --- | --- |
| [C](c-api.md) | Generated `clunkyard.h`; no extra wrapper |
| [C++](cpp.md) | `clunkyard.hpp` |
| [Rust](rust.md) | `clunkyard` crate passed with `--extern` |
| [Go](go.md) | `import "clunkyard"`; cgo package in the installed GOPATH, or the builder's module workspace |
| [Hew](hew.md) | `import hew.clunkyard`; generated scalar/bytes C shim |
| [Swift](swift.md), [Zig](zig.md) | Generated native C-compatible declarations and language wrappers |
| [Haskell](haskell.md) | `Clunkyard`; FFI and generated `Storable` records |
| [Fortran](fortran.md) | `clunkyard` module using `ISO_C_BINDING` |
| [D](d.md) | `clunkyard` interface and compiled object |
| [Ada](ada.md) | `Clunkyard` package with C-convention records |
| [Nim](nim.md) | `clunkyard` module through Nim's C backend |
| [Mercury](mercury.md) | Installed Mercury library; typed native references and records |
| [Free Pascal](pascal.md) | `Clunkyard` unit |
| [FreeBASIC](basic.md) | `clunkyard.bi` and compiled loop helper |
| [COBOL](cobol.md) | `clunkyard.cpy` and generated by-reference C shim |

A generated shim bridges a calling convention or record representation; it
is not another tactic process. Keep compiled objects and module interfaces
from the same SDK and target architecture. The arena's runtime contains no
compiler. Linker errors naming `cy_*` usually mean the core archive is
missing or in the wrong link order; missing module/type declarations point
to an include/import path or a stale SDK copy.

The [C API guide](c-api.md) explains ownership, statuses, lengths and the
session lifecycle underneath these bindings. Exception handling follows
the language guide: for example, Hew returned errors are recoverable but a
Hew panic ends the process; FreeBASIC runtime errors also end the process.

## Node, DSL, WebAssembly and shell

[JavaScript and TypeScript](javascript.md) import `clunkyard` from the
installed Node package. It loads the core's WebAssembly build in Node;
this is an ordinary Standard Node robot. Launch through
`"$SDK/bindings/javascript/run"` with `NODE_PATH="$SDK/bindings/javascript"`.
The launcher reserves the protocol descriptor before loading user code and
sends stdout diagnostics to stderr. TypeScript uses the same package and
types, compiled through the hosted TypeScript profile.

[Vane, Cadence and Glyph](../dsls.md) run through the built-in interpreter,
which obtains its vocabulary and helper values from the same core. Local
source entries run as `node src/dsl/runner.js vane /absolute/path/bot.vane`
(or `cadence`/`glyph`). Their corresponding `*-wasm` profiles compile the
same source into a guest module. The interpreted profiles count DSL
operations; compiled guests count fuel and imports.

[ABI 4 guests](wasm.md) cover WAT and AssemblyScript, C, Rust and Zig
compiled to WebAssembly, as well as the compiled DSL profiles. The trusted
Wasmtime host owns protocol I/O; the guest receives `clunkyard.*` imports
and exports startup/turn functions. It has no WASI environment. A Node
binding internally using a WebAssembly core does not make the robot an
ABI 4 guest; its user code still runs in Node and is metered as Node.

The [shell helper](../../sdk/shell/clunkyard.jq) is a raw protocol 2 route.
Its launcher uses one long-running `jq`, whose module supplies JSON helpers
and the persistent turn/memory loop. It does not call the C ABI. Use the
shell starter's installed/copied helper arrangement and `jq --unbuffered`
so each reply reaches the referee. `cy::log` writes diagnostics to stderr.
Avoid spawning a command for every small calculation inside a turn.

## Diagnose a failed start or turn

First reproduce with the project's actual launcher, imports and dependencies.
Use a short protocol fixture or the binding's `play` helper, then run a real
match. A process waiting for its first hello is normal when launched without
input. Build errors, missing shared libraries and invalid imports must be
fixed before a ready reply is possible.

Keep protocol output reserved for the session. Native stdio sessions move
stdout diagnostics to stderr at open; Node does so in its launcher. Memory
sessions leave process streams alone. Raw protocol programs must write and
flush exactly the expected ready or act reply. For process adapters, also
keep the tactic-to-adapter RPC pipe free of unrelated output.

Use `status` text/value to expose a decision in the viewer and stderr for
longer diagnostics. Optional absence is normal: check it before reading a
contact, a shot solution or a region. Ordinary callback exceptions generally
log and send controls already set; consult the binding guide for explicit
forfeit and uncatchable-error behaviour. A core session fault follows the
shared [core recovery contract](c-api.md#session-and-stdio-lifecycle).

The installed routes above are selected by
[the hosted worker](../../sandbox/worker.js). Runtime keys and accounting
families come from [the rates implementation](../../src/rates.js), and the
language-specific surfaces are generated under
[sdk/bindings](../../sdk/bindings/).
