JSON AST Agent Harness
This project is a local code-generation harness for turning text specs into Clojure programs. The validated local setup was tested with Gemma 4 E2B GGUF served by llama.cpp. Other models may work, but they are untested.
The core idea is to stop asking a small local model to write full source code directly. Instead, the model emits constrained JSON AST fragments. The harness validates those fragments, assembles larger structures programmatically, lowers the accepted AST to Clojure, and records every generation/repair artifact in SQLite.
What It Does
- Runs a local Gemma 4 E2B GGUF model through
llama-server. - Can target another OpenAI-compatible chat endpoint experimentally.
- Uses a planner pass to derive a sanitized contract from a text spec.
- Builds deterministic skeleton ASTs before asking the model for detailed logic.
- Uses micro-stepped passes for query filters, derived columns, and GUI components.
- Validates generated JSON AST with Malli, JSON Schema/GBNF-compatible constraints, field provenance checks, type-flow checks, and semantic structure guards.
- Compiles accepted JSON AST into Clojure source.
- Logs prompts, raw model output, accepted ASTs, rejected attempts, and execution traces into a per-run SQLite DB.
Business Logic Path
The business-rule plugin generates Clojure data-transformation functions.
bb json-run \ --self-plan \ --skeleton-first \ --where-pass \ --column-pass \ --gguf /path/to/gemma4-e2b.gguf \ --llama-server-bin /path/to/llama-server \ --llama-port 18700 \ --llama-ctx-size 4096 \ --llama-gpu-layers 24 \ --task-file specs/blind/dynamic_pricing_matrix_spec.txt \ --project-dir tmp/dynamic_pricing \ --repair-attempts 0 \ --max-tokens 4096 \ --where-max-tokens 2048 \ --column-max-tokens 2048
Outputs are written under the project directory:
candidate.ast.jsoncandidate.cljreport.jsonsession.sqlite3llama-server.log
Native Swing GUI Path
The :gui-page plugin generates simple native Java Swing programs. The model emits a sanitized GUI contract and component subtrees; the harness owns Swing lowering, safe event wiring, and jar packaging.
bb gui-run \ --self-plan \ --component-pass \ --gguf /path/to/gemma4-e2b.gguf \ --llama-server-bin /path/to/llama-server \ --task-file specs/gui/inventory_dashboard_spec.txt \ --project-dir tmp/gui_inventory
Build the generated Swing program into a runnable jar:
bb gui-jar tmp/gui_inventory/candidate.clj tmp/gui_inventory/app.jar
Run it manually when you want to open the desktop window:
java -jar tmp/gui_inventory/app.jar
GUI actions are intentionally closed and deterministic. Buttons can use safe action maps such as show-message and clear-form; the model cannot emit arbitrary Java, Swing listener code, Clojure forms, file actions, network actions, or shell commands.
Smoke Tests
bb json-smoke tmp/json_smoke bb gui-smoke tmp/gui_smoke
These validate the local Babashka classpath, JSON AST validation, Clojure codegen path, Swing AST validation, and Swing source load path without calling a model or opening a GUI window.
Benchmark Honesty
By default, json-run is text-only generation. Semantic fixtures are not used for generation unless you explicitly pass --allow-semantic-guidance.
If you use semantic tests, pass them explicitly:
--semantic-test-file path/to/test.edn \ --semantic-oracle-source user|benchmark|assistant|unknown
Clean benchmark evidence should use fixtures that were not generated by the assistant during the run.
Runtime Files
See RUNTIME_FILES.md for the minimal file manifest.
Extension Boundary
The harness has a plugin boundary under bb/harness/plugins/.
data_transformation.cljowns business-rule generation.gui_page.cljowns Swing GUI generation.api_router.cljis a blueprint plugin showing how another use case can register lifecycle methods without changing the root pipeline.