“werbos-0.0.1.”

Day 29 closed at midnight with the first ratchet click — a 1B LoRA trained on the operator’s own dogfood corpus produced a +39.6% composite delta over baseline. The trainer worked. Day 30 was making the result visible enough to ship.


The 3B run

Same script, bigger numbers. BASE=mlx-community/Llama-3.2-3B-Instruct-4bit ITERS=500 EVAL_N=100 scripts/train-werbos-self.sh. Backgrounded; about 2 hours wall time end-to-end:

  • Ingest: 1424 files / 7664 rows / 19 secrets filtered
  • Split: 7281 train / 383 eval (then sampled to 100)
  • Baseline (3B): composite 0.0795 (slightly lower than 1B’s baseline; within noise)
  • Train: 500 iters batch-size 2 on Llama-3.2-3B-Instruct-4bit
  • Candidate (3B + werbos-0.0.1 LoRA): composite 0.1201

Delta +0.0406, 1.51× baseline. Bigger absolute and bigger relative than the 1B run. The 27 MB adapter sits at /tmp/werbos-0.0.1-3b/.

TR.1 — dashboard backend (edf872d)

The operator wanted the eval loop visible somewhere richer than CLI tail. TR.1 is the data layer for the future /dashboard/training/ React surface. Three endpoints:

GET /api/training/cycles?limit=N
GET /api/training/baseline
GET /api/training/cycles/{name}/eval?side=candidate&limit=N

A convention locked for v1:

~/werbos/adapters/<name>/
  training_meta.json    # SE.7.1 writes
  adapters.safetensors  # mlx-lm writes
  eval/
    baseline.json       # werbos data score --out
    candidate.json      # same
    noise.json          # --runs N --out
  promoted.json         # touched on promote

The reader is tolerant: a cycle that finished training but hasn’t been scored is a valid CycleSummary with null score fields. Pre-SE.7.1 adapter directories from older mlx-lm runs (v1, v2-coder, v5-ttt-bf16 — 13 of them) sit in the operator’s ~/werbos/adapters/ and get silently skipped. The dashboard shows zero of them; only werbos-0.0.1.

Schema: tr/1, camelCase wire format (observe-aligned, matches the existing /api/budget + /api/activity convention). Forty one tests pin the wire format, the discovery layer’s tolerance of partial state, and the HTTP route boundaries.

Promotion

werbos-0.0.1 moved from /tmp/ to its proper home:

mkdir -p ~/werbos/adapters
cp -r /tmp/werbos-0.0.1-3b ~/werbos/adapters/werbos-0.0.1
mkdir -p ~/werbos/adapters/werbos-0.0.1/eval
cp /tmp/werbos-self-work/baseline.json …/eval/baseline.json
cp /tmp/werbos-self-work/candidate.json …/eval/candidate.json
echo '{"promoted_at_iso":"2026-06-01T21:15:00Z","reason":…}' \
  > ~/werbos/adapters/werbos-0.0.1/promoted.json

Live HTTP test against ~/werbos/adapters/:

GET /api/training/cycles
→ werbos-0.0.1  composite=0.1201  delta=+0.0406  promoted=True

GET /api/training/baseline
→ {
    "schemaVersion": "tr/1",
    "name": "werbos-0.0.1",
    "baseModel": "mlx-community/Llama-3.2-3B-Instruct-4bit",
    "composite": 0.12008,
    "threshold25Sigma": 0.12008,
    "promotedAt": "2026-06-02T05:46:59.830385+00:00",
    "adapterPath": "/Users/jasonsutter/werbos/adapters/werbos-0.0.1"
  }

The ratchet is engaged. Subsequent cycles have to beat werbos-0.0.1’s threshold, not raw llama3.1:8b’s.

Polish: werbos chat --model <adapter-dir> Just Works (9e737a5)

The launch-video command sequence used to be:

werbos chat --backend mlx \
            --model mlx-community/Llama-3.2-3B-Instruct-4bit \
            --adapter ~/werbos/adapters/werbos-0.0.1

Three flags, one of which the operator has to copy-paste correctly. After this commit:

werbos chat --model ~/werbos/adapters/werbos-0.0.1

One flag. build_backend_from_args runs _detect_trained_adapter on the model identifier; when it points at a directory with training_meta.json, the backend upgrades ollama → mlx, the base model gets pulled from the meta, the adapter path gets filled in. Explicit operator-provided values always win.

Same auto-detect contract as werbos data score already uses (both read config.base_model from the same JSON). Single sourced.

Eleven new tests cover the detect matrix + the integration into build_backend_from_args. The args mutation keeps everything in sync downstream (logging, telemetry, debug surfaces).

Numbers worth keeping

  • Day 30 commits: 3 (TR.1 backend + werbos-0.0.1 1B milestone + UX polish)
  • werbos-0.0.1: 27 MB at ~/werbos/adapters/werbos-0.0.1/
  • Composite: 0.1201 vs baseline 0.0795 (+0.0406, 1.51×)
  • 3B run: ~2 hours wall, 500 iters, batch-size 2, max-seq-length 1024
  • Tests: 41 new for TR.1 + 11 for auto-detect = 52 Day-30 tests, 5370 broad-suite green
  • Promote marker: promoted.json in the adapter dir
  • Dashboard: three endpoints live, returning structured JSON for a real adapter

The launch demo, end-to-end

# 1. Build the corpus
werbos data ingest ~/Documents/Companies/werbosLLM \
    --include '*.md' --include '*.py' \
    --strategy heuristic \
    --out me.jsonl
# → 7664 rows from your own work

# 2. Split + baseline
werbos data split me.jsonl --eval-fraction 0.05 --out-prefix me
werbos data score me.eval.jsonl --backend mlx \
    --base mlx-community/Llama-3.2-3B-Instruct-4bit \
    --model mlx-community/Llama-3.2-3B-Instruct-4bit \
    --out baseline.json
# → baseline composite

# 3. Train
werbos train --corpus me.train.jsonl \
    --base mlx-community/Llama-3.2-3B-Instruct-4bit \
    --out werbos-0.0.1 \
    --iters 500
# → 22 MB LoRA adapter

# 4. Score + promote
werbos data score me.eval.jsonl --model werbos-0.0.1
# → candidate > baseline + 2.5σ ⟹ promote

# 5. Use it
werbos chat --model werbos-0.0.1
# → terse, declarative, knows your stuff

Five lines. Different corpus produces different model. Same engine. This is the homepage video.

AHAs

  • “werbos-0.0.1.” The first werbos-trained model exists. On the operator’s M1. Trained on the operator’s own corpus. Composite delta clears noise. The ratchet is engaged.
  • The UX has to match the engineering. The one-flag werbos chat --model <adapter-dir> polish is what separates “we built a thing” from “we built a thing anyone can use.”
  • The dashboard convention is also a public commitment. Once ~/werbos/adapters/<name>/{training_meta,eval/*,promoted}.json is the locked shape, every future tool consumes it the same way. TR.1 today, TR.2 (React) next, whatever comes after.

Tomorrow

The plan from here splits clean:

  1. TR.2 — React surface over TR.1. The cards + sparkline
    • per-row table sketched in Day 28’s “known gaps.” ~1 day.
  2. Launch video. The five-line demo works end-to-end now. Cut it, push to werbos.ai, ship.
  3. werbos.ai domain + org repo. The launch surface itself.
  4. NX.1 npm publish. Still gated on A.5 PyPI.

The ratchet click happened. Everything after is distribution.

“werbos-0.0.1.”