All notable changes to this project will be documented in this file.
The format follows Keep a Changelog, and this project adheres to Semantic Versioning.
(no changes yet)
:mdartifact format inchachaml.serialize. UTF-8 string stored astext/markdown. Closes the documentation/API gap where(log-artifact "report.md" md {:format :md})was a natural call that previously threw a no-method error.chachaml.core/current-run— re-export ofchachaml.context/current-runso the common "what run am I in?" question doesn't need a separate require.chachaml.core/compare-runs— re-export ofchachaml.repl/compare-runsto fix the asymmetry where/api/compareexisted but the Clojure-side fn lived in a REPL ns.- Pipelines UI page at
/pipelines, with detail at/pipelines/:idlinking each step to its underlying run. Navbar updated. Backed by/api/pipelinesand/api/pipelines/:idJSON endpoints. Closes the gap where pipeline records existed in the store but had no UI surface. - Auto-discovered experiments on the
/experimentspage. When a run uses an experiment that was never explicitly created viacreate-experiment!, it now appears in the list (badged "auto").
best-runreturns enriched data: result now includes:metric-valueand:metric-stepso callers don't have to re-query the metric history.register-modelerror is clearer when:artifactlooks like a filesystem path. The:artifactoption expects the name under whichlog-file/log-artifactrecorded the file (e.g."checkpoints/foo.pt"), not the on-disk path.- Pipeline
:statusis now a keyword (:completed/:failed/:running), matching how runs report their status. Previously pipelines returned the string"completed"while runs returned the keyword:completed, forcing callers to type-switch.
- These changes were proposed by clj-bdh, the first non-toy downstream user of chachaml — the friction they hit while wiring up a real ML showcase against v0.6.3 became this changelog. Test count: 205 → 219 (489 → 524 assertions); all green.
Documentation release. Library code is unchanged from 0.6.0; this release ships a Diátaxis-shaped doc set and hosted API references.
doc/cljdoc.edntree shape: children are inline siblings, not wrapped in an extra vector. v0.6.2 published cleanly to Clojars but cljdoc rejected the malformed tree.build.cljwrites<licenses>(MIT) into the pom; required by Clojars as of late 2025.:builddeps alias includesslipset/deps-deploy, which thedeploytask inbuild.cljrequires.
v0.6.1 was tagged in git but never fully reached Clojars: the pom and jar uploaded but the metadata.xml step failed because the pom was missing the licenses block. Clojars locks coordinates on first attempted upload, so the version became unusable.
v0.6.2 published to Clojars cleanly, but the doc/cljdoc.edn tree
was malformed (children wrapped in an extra vector), causing
cljdoc's analysis step to throw "Don't know how to create ISeq from:
clojure.lang.Keyword". The Clojars artifact at 0.6.2 is installable
and equivalent to 0.6.3 — only the hosted API ref is broken.
0.6.3 is the first version that's both installable and renders on
cljdoc. The v0.6.1 and v0.6.2 git tags are retained for
traceability.
- Doc set following the Diátaxis split:
doc/TUTORIAL.md(45-minute walkthrough),doc/DESIGN.md(rationale consolidating ADRs),doc/WEB_UI.md(page-by-page tour),doc/MCP.md(16-tool reference),doc/FAQ.mdanddoc/TROUBLESHOOTING.md, plus six P0 how-to guides underdoc/howto/(SQLite→Postgres migration, Docker team deployment, MCP+Claude Code, Slack alerts, sklearn tracking, HTTP write API). - Hosted API reference via cljdoc (
doc/cljdoc.ednbundle config) and codox (:codoxdeps alias +bb docstask). - gh-pages publishing via
.github/workflows/docs.yml. - README: documentation jump-table near the top with cljdoc and gh-pages badges.
Full backlog complete: S3 artifacts, Slack webhooks, run cleanup, Docker, HTTP write API.
- M14 S3 artifact storage (
chachaml.store.s3): ArtifactStore against S3/MinIO via Cognitect aws-api. Metadata in Postgres/SQLite, bytes in S3.:s3deps alias. - M16 Docker: Dockerfile (multi-stage JDK21) + docker-compose.yml
(Postgres + chachaml UI).
docker compose upfor team deployment. UI server accepts env varsDB_TYPE,JDBC_URL,DB_USER, etc. - M17 HTTP write API: POST endpoints at
/api/w/runs,/params,/metrics,/end,/artifacts,/models. Full lifecycle test. Enables Python/Go/curl clients. - M18 Slack webhook for alerts:
webhook_urlon alerts. Whencheck-alerts!triggers, POSTs Slack-formatted JSON to the URL. - M19 Run cleanup:
archive-runs!marks old runs as:archivedby age + experiment.delete-archived!permanently removes data. 2 tests.
- UI server
start!now delegates tochachaml.store/open— accepts:type :postgresand all store options directly. - Refactored: left-to-right style throughout (threading macros, let bindings, named predicates, DRY).
- Deterministic seeds per showcase use case — fixed flaky tests.
Team readiness: Postgres backend, user attribution, pipelines, alerts, chat-with-data.
- M10 Pipelines (
chachaml.pipeline):run-pipeline!executes named step sequences, each step tracked as awith-run. Results chain via:prev-result.defpipelinemacro.pipelines+pipeline_stepstables. - M11 Alerts (
chachaml.alerts):set-alert!defines metric threshold rules,check-alerts!evaluates against latest runs, records triggered events.alert-history,deactivate-alert!. - M12 Chat-with-data (
chachaml.chat):asksends questions to Anthropic/OpenAI API with chachaml tools injected. Tool-use loop./chatUI page with provider selector and localStorage API key. - M13 Postgres backend (
chachaml.store.postgres): full protocol implementation with HikariCP pool. Same API as SQLite, team-shared database.chachaml.store/opendispatcher::type :sqliteor:type :postgres.:postgresdeps alias. - M15 User attribution:
created_bycolumn on runs, auto-captured from system user, filterable in queries and visible in UI. - Markdown notes with math: runs and models support markdown
notes rendered via marked.js + KaTeX.
set-note!stores markdown; the UI renders it with LaTeX math support.set-model-note!added to the registry for model descriptions. - 16 MCP tools (was 6):
search_runs,best_run,add_tag,set_note,get_tags,get_datasets,list_experiments,create_experiment,export_runs,diff_model_versions. - 7 new API endpoints: tags CRUD, note POST, datasets GET, search, model note, version diff.
- Showcase demonstrates v0.4: UC01 uses
log-table+log-dataset!, UC08 useswith-batched-metrics, UC17 usescreate-experiment!, UC19 usesadd-tag!, UC25 usesset-note!with markdown+math,-mainusesbest-run+export-runs. - JS/CSS bundled locally: marked.min.js, katex.min.js, auto-render.min.js, katex.min.css.
- Refactoring: extracted
chachaml.formatnamespace with sharedshort-id,size-str,fmt-instant,fmt-duration,pad,metric-summary. Eliminated 4 duplicated helper fns fromchachaml.replandchachaml.ui.views. Extracted->experiment-mapin sqlite.clj (was copy-pasted 3 times). Consolidated sklearn interoprequire/resolvepattern intocore-fnhelper. - Mutable tags + run notes:
add-tag!,set-note!,get-tags— annotate runs after they complete. Tags stored in a dedicated table with upsert semantics; merged with inline tags in(run id). - Dataset tracking:
log-dataset!,get-datasets— capture row/col counts, feature names, content hash, data source per run. Newdatasetstable. Included in(run id)results. - Metric-based run search:
search-runswith:metric-key,:op,:metric-valuefilters.best-runshortcut for{:metric :accuracy :direction :max}. - Batch metric logging:
with-batched-metricsmacro — buffers metrics in an atom, flushes to store in one batch on exit (or on exception).log-metric/log-metricsrespect*metric-buffer*. - Structured table artifacts:
log-tablestores tables as EDN with content-typeapplication/x-chachaml-table. Added to the serialize content-type→format mapping. - Image artifact preview: run detail UI detects
image/*content-type and renders inline<img>tags. Other artifacts get a download link via/api/artifacts/:id/download. - Export runs to CSV:
export-runsreturns flat maps (params + final metrics per run)./api/export?format=csvendpoint. - Experiment metadata:
create-experiment!,experiment,experiments— name, description, owner. Newexperimentstable with upsert. - Model version diff:
chachaml.registry/diff-versionscompares the runs behind two versions of the same model. - 18 tests covering all new features.
sklearn interop and 25 ML use case showcase.
- M9 libpython-clj2 sklearn interop (
chachaml.interop.sklearn):tracked-fit!— fit a sklearn-compatible model, logging training time + model hyperparameters.tracked-predict— predict with timing.evaluate!— compute + log accuracy/R² metrics.train-and-evaluate!— full pipeline: fit, predict, evaluate, save artifact, optionally register model.extract-params— pull sklearn's.get_params().- All Python calls go through a
*bridge*dynamic var so tests run without Python (mock bridge replaces libpython-clj2). requiring-resolvefor the Python bridge — namespace compiles without libpython-clj2 on the classpath.:pythonalias in deps.edn for users with Python + sklearn.examples/sklearn_iris.clj— runnable withclojure -M:python:examples -m sklearn-iris.- 8 mock-based tests covering fit, predict, evaluate, the full pipeline, and param extraction.
MCP server, web UI with local JS deps, and SQLite WAL mode for concurrent multi-agent access.
-
M7 MCP server (
chachaml.mcp):- JSON-RPC 2.0 over stdio; run via
clojure -M:mcp [db-path]. - 6 tools:
list_runs,get_run,compare_runs,list_models,get_model,get_model_version. - Configurable in
.claude/mcp.jsonor any MCP-compatible client. org.clojure/data.jsonadded as an optional dep (:mcpalias for runtime, test/coverage aliases for CI).- 18 tests covering handshake, all tools, error paths, and a ByteArrayStream integration test.
- JSON-RPC 2.0 over stdio; run via
-
SQLite WAL mode +
busy_timeout=5000enabled on everyopen— concurrent readers (UI) never block writers (training agents), and simultaneous writers wait instead of failing with SQLITE_BUSY. Includes a concurrent-write integration test. -
M8 Web UI (
chachaml.ui.*):- Ring + Reitit + Hiccup + HTMX + Vega-Lite + Tailwind CDN.
- Start with
clojure -M:ui [db-path] [port](default 8080). - 5 screens: runs dashboard (auto-refreshes every 10s via HTMX), run detail (params, scalar metrics, time-series Vega-Lite charts, artifacts), run comparison (param diff + overlaid metric charts), model registry, model version detail.
- JSON API at
/api/{runs,runs/:id,compare,models,models/:name, experiments}— foundation for a future chat-with-data layer. - 22 UI tests via
ring-mock(HTML + JSON routes, 404s, filters).
- Cleanup pass after the v0.1.0 review:
- Stale namespace docstrings in
chachaml.coreandchachaml.store.sqliteupdated to reflect shipped milestones. - Collapsed duplicated
status->db/stage->db(and inverse) helpers into a singlekw->db/db->kwpair inchachaml.store.sqlite. chachaml.serialize/auto-formatno longer has a dead:nippybranch for strings; behavior is unchanged (strings still go to:nippy) but documented as intentional.chachaml.repl/padreduced to a one-lineformatcall.chachaml.schema/validatedocstring tightened (no behavior change).
- Stale namespace docstrings in
- New shared test fixture
chachaml.test-helpers/with-fresh-store, used via(use-fixtures :each h/with-fresh-store)acrosscore_test,registry_test,repl_test,tracking_test, andexamples_test. Drops ~30 lines of inline(with-fresh-store (fn [] …))boilerplate per file. - New
use-store!-returns-resolved-storetest for the store-value path.
First feature-complete release covering the full minimal MLOps loop: run tracking, artifacts, model registry, tracking macro, REPL helpers.
- M0 project skeleton: dual build (deps.edn + project.clj), Babashka task
runner (
bb.edn),tools.buildentry points (build.clj). - Quality tooling:
clj-kondoconfig (warning-level fail),cljfmtconfig, kaocha runner (tests.edn),cloverage85% line gate. - Namespace skeletons for
chachaml.{core,context,env,serialize, registry,tracking,repl,schema}andchachaml.store.{protocol,sqlite}. - Baseline load test asserting every namespace compiles.
- GitHub Actions CI: lint + format + 4-job test matrix
(
{deps,lein} × JDK {17,21}) + coverage gate. - Process artifacts:
CONTRIBUTING.md,LICENSE(MIT),.github/PULL_REQUEST_TEMPLATE.md, ADRs 0001–0004. - M1 storage:
chachaml.store.sqliteimplementsRunStoreandLifecycleagainst either a file-backed or in-memory SQLite database. Schema migrates idempotently on everyopen. Tests cover CRUD, EDN value round-tripping, query filters, and tempfile persistence across opens. - M2.1
chachaml.env: best-effort capture of git SHA/branch/dirty state, JVM, OS, user, and Clojure version. - M2.2
chachaml.context:*store*and*run*dynamic vars plus a delay-initialized default SQLite store at./chachaml.db. - M2.3–2.5
chachaml.corepublic API:use-store!,with-store,start-run!,end-run!,with-run,log-params,log-param,log-metrics,log-metric,runs,run,last-run. Exceptions insidewith-runmark the run:failedand re-throw; completed runs aretap>-ed. chachaml.schema: Malli schemas for run lifecycle and logging inputs (Status,Run,StartRunOpts,Params,Metrics,MetricRow,QueryFilters) plus avalidatehelper.doc/USING-LOCALLY.md: how to depend on a local checkout from another Clojure project (:local/root,lein install, leincheckouts/).- Runnable ML examples under
examples/(added to the test classpath via the:examplesalias):linear-regression— gradient-descent univariate fit on synthetic data; logs per-epoch loss and final weight errors.kmeans— Lloyd's algorithm on 2D Gaussian blobs; logs per-iteration inertia and final cluster sizes. Each has arun-experiment!fn (called by tests against an in-memory store) and a-main(called byclojure -M:examples -m <name>).
- M3 artifacts:
chachaml.serialize: multimethod-dispatched encoding for:nippy(default),:edn,:bytes, and:fileformats. Auto-detectsbyte[]/Fileand falls back to:nippy.- SQLite
ArtifactStoreimpl with filesystem-backed bytes under<db>-artifacts/<run-id>/<name>(default./chachaml-artifacts/). SHA-256 hash + size captured. Unique(run_id, name)index rejects duplicates. chachaml.core/log-artifact,log-file,load-artifact,list-artifacts.(run id)now also includes:artifacts.ArtifactandArtifactFormatMalli schemas.- In-memory mode now creates a per-store temp artifact directory
that is removed on
close!. - Examples now persist their trained models as nippy artifacts; tests round-trip and assert on the loaded values.
- Property-based serialize round-trip (
test.check) covers arbitrary nested EDN values for:nippyand:edn.
- ADR-0005: filesystem-backed artifacts (rationale for keeping bytes outside SQLite).
- M4
chachaml.tracking/deftracked:defn-shaped macro that wraps the body inwith-run. Supports docstring + opts map (:experiment,:name,:tags); a:tracked-fntag carrying the qualified symbol is auto-added. Nests under existing runs. clj-kondo:lint-asconfig registers it as a defn-like macro. - M5 model registry:
modelsandmodel_versionstables added to the SQLite schema.- SQLite
ModelRegistryimpl with atomic stage transitions in a transaction. chachaml.registrypublic API:register-model,models,model,model-versions,promote!,load-model(with:version/:stageselectors; default = latest production).Model,Version,Stage,RegisterOptsMalli schemas.- Property-based test asserting at most one
:productionversion per model survives arbitrary add/promote sequences. - ADR-0006 records production-stage exclusivity.
- Both ML examples now register their trained model as a
:stagingversion oflinear-regression-baseline/kmeans-baseline. Tests round-trip through the registry too.
- M6 REPL helpers in
chachaml.repl:runs-table— compact one-row-per-run table of recent runsinspect— polymorphic pretty-printer for runs (by id or map), models (by name or map), and model versionscompare-runs— diff params + latest metric values across N runs, returns{:runs … :params {…} :metrics {…}}with:same,:differ,:partialpartitionsprint-comparison— render thecompare-runsmap for humans- 10 tests covering empty store, multi-run diff, partial-key detection, version inspection, model fallback by string id