Skip to content

Decisions Ledger and Learning

Laith0003 edited this page May 28, 2026 · 1 revision

Decisions Ledger and Learning Loop

The .ux/decisions.jsonl ledger is the substrate v3.0's intelligence loop runs on. Every recommend, design, lint, evolve, and synthesize call appends one line. The recommender consults the log on the next call and bumps candidates that have shipped clean before. Cold-start safe, fully offline.

Schema (locked at _v: 1)

Column names are a public contract. Renaming is a breaking change. New columns can be added with default null for compat-readers.

{
  "_v": 1,
  "ts": 1727380123.456,
  "command": "design",
  "brief_id": "ab12cd34e5f67890",
  "context_hash": "f0e1d2c3b4a59687",
  "industry": "fintech-payments",
  "ui_type": "dashboard",
  "mode": "brand_anchor",
  "picked_brand": "stripe",
  "picked_style": "swiss-grid",
  "picked_palette": "stripe-cool",
  "axes": {"warmth": 0.35, "contrast": 0.55, ...},
  "lint_score": 92,
  "lint_high": 0,
  "lint_med": 1,
  "lint_low": 3,
  "artifact_path": "/path/to/out.html",
  "user_accepted": true,
  "duration_ms": 1240
}

File locations (mirror writes)

  • .ux/decisions.jsonl — per-project (gitignored by convention via .gitignore)
  • ~/.uxskill/decisions.jsonl — per-user, cross-project memory

Both files are append-only JSONL. Best-effort writes — filesystem errors are swallowed (e.g., read-only mount, permission denied). A failed write never blocks the command.

Privacy switches

  • UXSKILL_NO_LOG=1 env var disables all writes
  • --no-log CLI flag has the same effect
  • Scope.PROJECT writes only to project file
  • Scope.USER writes only to user file
  • Scope.BOTH writes both (default)

The recommender's re-rank reads from whichever scope is enabled, so disabling logging means the system stops learning (the trade-off is yours).

How the recommender uses it

Each of the 5 ranking lanes (_lane_style, _lane_palette, _lane_type, _lane_motion, _lane_brands) now:

  1. Computes the manifest-only score per candidate.
  2. Drops candidates with forbidden penalty (score < -50).
  3. Reads the decisions ledger filtered by (industry, ui_type).
  4. Counts wins per candidate id (decisions where lint_score >= 80 AND user_accepted = true).
  5. Bumps each candidate's score by +5 per matching prior win.
  6. Re-sorts the resulting list.

Cold-start protection

Below 3 matching priors for the (industry, ui_type) bucket, the re-ranker is a no-op. This prevents the system from "learning" from N=1 noise. Below threshold, the recommender behaves exactly like v2.0.

Why these specific filters?

  • lint_score >= 80 — a "clean enough" decision worth learning from
  • user_accepted = true — explicit signal that the user agreed with the output
  • (industry, ui_type) bucket — keeps learning context-specific

The combination is conservative on purpose. The recommender doesn't pretend to learn aesthetic preferences from random taste signals. It learns "for this (industry, ui_type), these specific tokens reliably ship clean."

CLI

# Disable logging for this run
UXSKILL_NO_LOG=1 uxskill lint <file>
# or
uxskill lint <file> --no-log

# Inspect what's been logged
uxskill stats --decisions

# Local HTML dashboard
uxskill stats --html
# → writes .ux/stats.html — open in browser

MCP tools

Over stdio, the same data is exposed:

  • ux_decisions_query — filter by industry / ui_type / command / min_score / accepted_only
  • ux_decisions_stats — aggregate (total, by_command, top_brands, lint_score_median, acceptance_rate)

Python API

from engine.decisions import record, read_all, query, stats, Scope

record({
    "command": "design",
    "industry": "fintech-payments",
    "ui_type": "dashboard",
    "picked_brand": "stripe",
    "lint_score": 92,
    "user_accepted": True,
}, scope=Scope.BOTH)

winners = query(
    industry="fintech-payments",
    ui_type="dashboard",
    min_score=80,
    accepted_only=True,
)
# → list[dict] of past wins

agg = stats(scope=Scope.PROJECT)
# → {total_decisions, by_command, top_brands, lint_score_median, ...}

What is NOT in the ledger

  • No raw user prompts
  • No external IDs
  • No network/IP/user-agent data
  • No machine fingerprint
  • No analytics events to any service
  • No "telemetry uploaded to vendor"

It's a file. On your disk. Owned by you. That's the entire surface.

What ChatGPT (T) said about this in the v3 review

"Decision ledger is the real backbone — but currently underpowered. You added engine/decisions/ JSONL, but you are not explicitly using it as retrieval memory, ranking bias, failure correction signal. Right now it's a log, not a learning substrate. That's the biggest missed opportunity in v2.1."

v3.0 fixed exactly this. The recommender now reads the ledger and biases ranking. Closed loop.

Related

Clone this wiki locally