# Changelog
All notable changes to MicroResolve are documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
---
## [0.2.1] — Unreleased
### Added
- `microresolve-studio install <pack>` — fetches a reference pack tarball from
the GitHub release matching the binary version and extracts it into the
configured data dir.
- `microresolve-studio list-packs` — shows the 4 reference packs with their
install status against the data dir.
- `--config <PATH>` flag + `MICRORESOLVE_CONFIG` env var on the studio binary,
for sandbox testing and multi-instance deployments. The `config`
subcommand respects the override.
### Fixed
- PyPI project page now renders the README as long-description (was empty
in v0.2.0 because `pyproject.toml` lacked the `readme` field).
- Aligned package short-descriptions across crates.io, PyPI, and npm to a
single phrasing matching the v0.2.0 README hero ("System 1 relay for
LLM apps...").
### Build
- Release workflow now builds and uploads pack tarballs as release assets
(`pack-<name>-v<VERSION>.tar.gz`) so `microresolve-studio install <pack>`
has something to fetch.
### Removed
- **`IntentType` (Action/Context) classification removed entirely.** Field
was never consumed by scoring/routing — pure dead metadata. Old intent
JSON files with `"type": "action"` still load fine (serde ignores
unknown fields). Public API affected: removed `IntentEdit.intent_type`,
`IntentInfo.intent_type`, the `intent_type` parameter on
`add_intent`/`update_intent`, and the auto-classification at import time
(HTTP-verb-based for OpenAPI, `readOnlyHint`-based for MCP) is gone.
- **Per-intent `Stats` tab on the Intents page** — replaced with a
single-line metric strip above the phrases list (`12 examples · 3 langs
· 8 learned`). Saves a click and removes a tab that mostly duplicated
the phrases-tab count.
### UI
- **Richer manual seed generation — anchor examples.** The "Generate
examples with AI" panel on the per-intent Examples tab gains a
textarea for 1–3 anchor phrases the user already has in mind. The
LLM uses them as seeds and generates variations around them. Empty =
original behavior. Closes part of the quality gap between manual
intent creation and import (which has full operation context).
Wired through `phrase::build_prompt(... examples)` and
`POST /api/phrase/generate` accepts an optional `examples: string[]`.
- **Import seed count bumped 5 → 10 phrases per intent.** Matches
manual generate. Token-budget headroom stays well under 8192;
marginal cost increase ~$0.01 per 50-tool import on nano-class
models. The previous 5/intent floor was leaving recall on the table
for newly-imported namespaces.
- **`/l2` route renamed to `/intents`.** `/l2` now redirects via
`<Navigate>` for backward compat with bookmarks.
- **"Phrases" relabeled to "Examples"** in the per-intent tabbed panel.
Internal-facing names (`phrases`, `phrases_by_lang` API fields) stay
as-is — display change only.
- **Resolve page**: removed the duplicate-query block that was a
passthrough render of the `HighlightedQuery` component (the highlighter
itself was gutted when L1 was removed in v0.2.0; the duplicate query
block had no value).
---
## [0.2.0] — Unreleased
### Added — Reference packs
Four pre-curated namespace packs ship with the library, drop-in installable
into any `data_dir`:
- **`safety-filter`** — pre-LLM jailbreak / prompt-injection detection. 5
intents covering the canonical attack taxonomy (prompt injection,
system-prompt extraction, role override, safety bypass, encoding-based
evasion). 100 hand-curated seed phrases. On internal eval (50 attacks,
50 random benigns from CLINC150): **98% recall / 8% FP at default
settings (`min_voting_tokens=3, threshold=1.5`)**.
- **`eu-ai-act-prohibited`** — Article 5 prohibited-practice triage
filter. 6 intents (biometric categorization, emotion recognition in
workplace/education, exploitation of vulnerability, predictive policing
on natural persons, social scoring, subliminal manipulation). On
internal eval (60 prohibited, 50 benign): **85% top-1 / 6% FP at
default settings (`min_voting_tokens=2, threshold=1.5`)**.
- **`hipaa-triage`** — medical query triage routing patient inquiries to
one of clinical_urgent / clinical_routine / mental_health_crisis /
administrative / billing / scheduling. 743 hand-curated seeds drawn
from public clinical references (AHRQ ED chief complaints, ESI triage,
HIPAA Right of Access, No Surprises Act, SAMHSA crisis materials).
Best used as **triage / candidate filter** — pair with LLM judgment
for top-1 selection. On internal eval (97 medical, 85 benign):
**96.9% top-1 / 36.5% FP at default (`min=3, threshold=1.5`)**, or
**94.8% top-1 / 21.2% FP at `threshold=2.0`** for stricter precision.
NOT a HIPAA compliance solution; see pack description.
- **`mcp-tools-generic`** — generic tool-router for MCP-style agents
(web_search, send_message, fetch_url, file_operations, database_query,
code_execution, calendar_management). Default settings
(`min_voting_tokens=2, threshold=1.5`). Best for closed-domain
dispatch; open-ended chat traffic produces FPs from idiomatic English.
All packs ship as bag-of-words inverted index. Pack format is a directory
of JSON files (one per intent + `_ns.json`); install by copying into
`data_dir/<pack-name>/`.
### Added — Bands and Disposition
Every `resolve()` return now includes a `band` per intent (`High` /
`Medium` / `Low`) and an overall `disposition` (`Confident` /
`LowConfidence` / `NoMatch`). The pre-LLM filter pattern is the
recommended consumer:
```rust
match result.disposition {
Disposition::Confident => act(result.intents[0]), // deterministic
Disposition::LowConfidence => llm_disambiguate(result.intents),
Disposition::NoMatch => llm_fallback(query),
}
```
Same shape exposed in Python and Node bindings as string values.
See [Bands & Disposition](docs-site/src/content/docs/concepts-bands.mdx)
for thresholds and the canonical decision pattern.
### Added — Connected mode (live sync)
- **`MicroResolveConfig.server`** — when `Some(ServerConfig)`, the engine
pulls subscribed namespaces from a running server on startup and keeps
them in sync via a background poll thread. `resolve()` calls in
connected mode buffer log entries that are flushed each tick;
`correct()` proxies the correction to the server, which is the sole
writer.
- **Strict connect mode**: every mutation method on `NamespaceHandle`
(`add_intent`, `add_phrase`, `correct`, `decay_for_intents`, etc.)
returns `Error::ConnectMode` when the engine is connected. The library
is a read-only cache; the server is authoritative. Background sync
thread bypasses this guard via the internal `apply_ops` path.
- **Delta sync**: connected libraries pull additive op sequences
(`PhraseAdded`, `WeightUpdates`, etc.) from `/api/sync` rather than
full-state snapshots. CRDT-style merge: token weights take the max
across replicas. Cold-start uses `/api/snapshot`; subsequent ticks
use `/api/sync` with the local version as the watermark.
### Added — Pack format helpers
- **Per-namespace `default_threshold` cascade**: `_ns.json` may set
`default_threshold`; `update_namespace()` PATCHes it; `/api/resolve`
cascades request override → namespace default → engine compile-time
default (0.3). Lets pack authors ship calibrated thresholds with each
pack.
- **`POST /api/namespaces/{id}/concepts`** has been considered and
deferred — no concept-tagging system ships in v0.2.0. Concept research
is preserved on the `experiment/concept-index` branch as research
record; revisit in v0.3 with production-traffic validation.
### Added — Voting-token gate (false-positive reduction)
A per-namespace structural filter that suppresses single-word false
positives like `"let me speak from the heart"` matching a clinical
intent because of `"heart"` alone. For each candidate intent, count how
many distinct query tokens contribute weight > 0.05 to its score; if
fewer than `min_voting_tokens` vouch, scale the intent's score by a
sliding penalty (0.4 at 1 voter → 1.0 at the threshold). Default is
**1 (off)** — the gate is a no-op for existing namespaces. The 4
reference packs ship with calibrated values (see per-pack notes).
Cascade plumbing mirrors `default_threshold`: per-request override →
namespace `default_min_voting_tokens` → engine default (1). Persisted
in `_ns.json`. Exposed end-to-end through Rust API
(`NamespaceHandle::set_min_voting_tokens`), HTTP (`PATCH /api/namespaces`),
Python and Node bindings, and the Studio UI's per-namespace
TuningPanel (sidebar pill + Resolve right-rail).
On closed-set classification (CLINC150 / BANKING77) at default `min=1`
the gate is exactly a no-op — measured top-1 within ±0.07pp of 0.1.x
baselines. On open-world packs at calibrated defaults, FP drops
substantially at unchanged or barely-changed recall (safety: 36%→8% FP
at 98% recall; EU AI Act: 24%→6% FP at 85% top-1; HIPAA: see pack
notes). Eval is on small author-curated corpora — see Known
limitations.
### Removed
- **L0 (typo correction) and L1 (lexical-graph query rewriting) layers**
removed entirely. Both produced silent corruption on real workloads
(e.g. "please" → "leave"). The remaining engine is the IDF-weighted
inverted index + multi-intent decomposition + Hebbian learning ("L2").
- **`Resolver::resolve` (legacy single-best `Vec<Match>` form)**,
`Resolver::resolve_with`, `Match` struct, `ResolveOptions` struct.
Replaced by `NamespaceHandle::resolve()` returning `ResolveResult`.
### Breaking — Public API
- **Routing collapsed to two entry points.** Removed: `route_multi`,
`route_multi_with_trace`, legacy single-best `resolve`, `resolve_with`,
`RouteMultiOut`, `Match` tuple form, `ResolveOptions`. New surface:
- `NamespaceHandle::resolve(query) -> ResolveResult`
- `NamespaceHandle::resolve_with_trace(query) -> (ResolveResult, ResolveTrace)`
- **HTTP: `POST /api/route_multi` renamed to `POST /api/resolve`**.
Response body now: `{ intents: [{id, score, confidence, band}], disposition, routing_us, trace? }`.
No more `confirmed[]` / `candidates[]` split — consumers filter `intents[]` by `band`.
- **Rust API: `Resolver::train_negative` → `decay_for_intents`** at
both Resolver and NamespaceHandle level. Name reflects the actual
anti-Hebbian weight-decay behavior.
- **HTTP: `POST /api/namespaces/train_negative` → `POST /api/namespaces/decay`**
with response field `"trained"` → `"decayed"`.
- **Rust API: `IntentIndex::score_multi_traced(query, threshold, gap, with_trace)`
split** into `score_multi` (no trace) and `score_multi_with_trace`
(always returns `MultiIntentTrace`, not `Option`).
- **Rust API: `MicroResolve::effective_threshold/effective_languages/effective_llm_model`**
renamed to `resolve_threshold_for/languages_for/llm_model_for`.
- **Import response field `l2_unique_words` renamed to `vocab_size`** —
last `l2_*` key in the external API surface.
- **L2 internal naming cleanup**: `_l2.json` → `_index.json`,
`score_normalized` field renamed, source string `"router"` → `"learning"`.
No backward-compat shim; existing data dirs from 0.1.x need a one-line
rename (`_l2.json → _index.json`) or just regenerate via `rebuild`.
### Bindings
- **Python (PyO3)** + **Node (napi-rs)** parity sweep: 17 missing
methods added to both bindings. Both now expose the full
`NamespaceHandle` surface — `resolve`, `resolve_with_trace`,
`add_intent`, `add_phrase`, `remove_phrase`, `remove_intent`,
`correct`, `intent`, `update_intent`, `intent_ids`, `intent_count`,
`version`, `namespace_info`, `update_namespace`, `vocab_size`,
`confidence_for`, `training`, `training_by_lang`, `export_json`,
`check_phrase`, `domain_description`, `set_domain_description`,
`remove_domain_description`, `reinforce_tokens`, `rebuild_index`,
`rebuild_caches`, `index_phrase`, `decay_for_intents`,
`apply_review`, `flush`.
### Studio UI
- **Review queue** now shows all entries with status (auto-applied,
manually corrected, pending, escalated) — not just the pending subset.
- **Import domain UX overhaul** across all 5 importers (OpenAPI,
MCP, Postman, OpenAI Functions, LangChain): operator-decided domain
picker on every flow.
- **Studio bootstrap auth**: every `/api/*` route requires
`X-Api-Key`. Server auto-mints an admin key on first boot, persists
to `~/.config/microresolve/admin-key.txt`. Studio paste-screen
on first load. `KeyScope` schema (`admin` / `library`) on every key.
### Benchmarks
- **CLINC150**: seed-only **80.1%** top-1 (4500 test) — matches 0.1.x
published baseline within noise. After-learning (reinforce missed
queries with their true label, no LLM): **97.4%** top-1, an
unintended improvement of ~6pp over the 0.1.x published number,
attributable to engine work landed earlier in the 0.2.0 cycle
(L3-inhibition removal, IDF caching at index time).
- **BANKING77**: seed-only **73.15%** (matches 0.1.x exactly).
After-learning: **94.6%** top-1 (vs 0.1.x published 86.6%).
- Voting-gate at default `min=1` is a no-op on closed-set benches —
measured CLINC drift: -0.07pp (within noise).
- p50 latency: ~85µs / p95: ~190µs.
### Server / Ops
- **Default port: 4000** (was 3001 in 0.1.x).
- **`microresolve-studio --no-browser`** flag (was `--no-open`).
- **`--keys-file <path>`** for keystore isolation.
- **CRDT-mergeable Hebbian weights** for delta sync between
cooperating replicas (max-merge per `(token, intent)` edge).
### Known limitations
MicroResolve is the **System 1 relay** — deterministic, sub-millisecond,
running on every request. It always hands its output to your
**System 2**: typically an LLM, optionally a human reviewer for
high-stakes domains. MicroResolve never talks to the user. The
numbers below should be read in that architecture: high recall +
acceptable FP on the legitimate side is the design target, because
your System 2 filters out the remaining FPs — the LLM either acts on
the suggestion (the cheap path) or asks the user to confirm with the
candidate list we provide (when we flag uncertainty).
We're using Kahneman's framework as an architecture metaphor (fast
intuitive layer feeding a slow deliberative one), not making
cognitive-science claims about LLMs being literal System 2s.
- **Eval methodology**: pack eval corpora are **small (n=50–97 per
side) and author-curated** — same heads wrote seeds and tests, so
vocabulary leakage is unavoidable. Published recall/FP rates have
wide confidence intervals (~±2pp at n=50). Production deployments
should re-measure on captured customer traffic before relying on
absolute numbers.
- **HIPAA pack**: 36.5% FP on synthetic benigns at default reflects
the natural overlap between medical and everyday vocabulary. As a
System 1 relay feeding an LLM and/or human reviewer, this is workable
— each FP becomes a one-line "did you mean a clinical question?"
confirmation in the System 2 reply, not a misroute. **Do not run
unsupervised.** Mental-health-crisis intent has not been validated
in any production setting and must not be used for crisis routing
without explicit human review.
- **OOS detection**: on closed-set classification, ~96% of
out-of-scope queries still classify in-scope at default threshold.
The filter is calibrated for *in-scope* classification, not OOS
detection — pair with an explicit OOS classifier or raise the
threshold for open-world deployments.
- **mcp-tools-generic** at default settings: open-ended chat traffic
produces FPs from idiomatic English. Best for closed-domain dispatch
— raise threshold for chat applications.
- **safety-filter** is a pre-filter, not a complete defense.
Adversarial novel paraphrasing bypasses any deterministic
vocabulary-based classifier. Pair with a dedicated safety classifier
(LlamaGuard / Prompt-Guard / OpenAI Moderation) for production
adversarial coverage.
---
## [0.1.9] — 2026-05-01
### Breaking
- **Studio auth model: every `/api/*` route now requires `X-Api-Key`** —
including the UI's own fetches. The previous setup left UI fetches
unauthenticated and only `/api/sync` enforced auth. Combined with a
default bind to `0.0.0.0`, that meant any LAN host could mint keys
via the unauthenticated `/api/auth/keys` POST. Closed.
- **`POST /api/auth/keys` now requires auth** like everything else.
No more bootstrap exception.
- **Renamed CLI flag `--no-open` → `--no-browser`**. More
self-documenting; matches Jupyter's convention.
### Added
- **Auto-bootstrap admin key on first boot.** When `keys.json` is empty,
the server mints `mr_studio-admin_<hex>` (Admin scope), prints it to
stdout in a visible banner, and persists it to
`~/.config/microresolve/admin-key.txt` (mode 0600) so operators who
redirect stdout (Docker, systemd) can `cat` it later.
- **Studio paste-screen** — first browser visit renders a key entry
form. Pasted key lives in `localStorage`; every subsequent fetch
carries `X-Api-Key`. Single global `window.fetch` wrapper (in
`ui/src/api/client.ts`) attaches it to every relative `/api/*`
request, including the dozen-plus raw `fetch()` call sites scattered
across the import wizards and Layout polls.
- **`KeyScope` schema on every API key** — `admin` or `library`.
Persisted in `keys.json`, returned by `GET /api/auth/keys`, accepted
by `POST /api/auth/keys`, surfaced in the Auth Keys page (column
badge + scope dropdown on Generate). **Enforcement is permissive in
v0.1.9** (every scope grants every route); v0.2 will land
route-level scope checks. Existing keys without a scope field load
as `Admin` (backwards-compat default).
- **`--keys-file <path>` CLI flag** for keystore isolation. Used by
the integration test harness to give every spawned server its own
`keys.json`, so parallel test binaries don't race on the global
`~/.config/microresolve/keys.json`.
### Removed
- **Home page "Common shapes" use-case tile section.** Replaced with a
one-line stats strip (namespaces · intents · connected clients ·
pending review) and a single "see use cases" link in the empty
state — exactly where new operators look for examples, with no
clutter on the persistent home view for repeat visitors.
- **Home page "Try It" widget** (already removed in earlier branch
work, formalized in v0.1.9). The dedicated `/resolve` page provides
the same with full L0/L1/L2 trace.
- **"Start with" dropdown in the New Namespace modal.** Three options
that just navigated to different pages — friction at the moment of
creation. Now: create + close, namespace appears in the list, user
picks where to go from there.
### Changed
- **Hero on Studio Home** rewritten from "Pre-LLM intent routing" →
"The pre-LLM reflex layer" to match the README. Intent routing is
one use case; reflex layer covers the broader pitch (tool selection,
guardrail dispatch, refusal classification).
- **Test harness (`tests/common/mod.rs`)** captures the bootstrap
admin key from each spawned server's `admin-key.txt` and auto-injects
it via a thread-local on every helper call. Tests stay terse;
unauthorized-path tests bypass the auto-inject by setting
`X-Api-Key` explicitly.
## [0.1.8] — 2026-05-01
### Fixed
- **Python wheels**: `python/pyproject.toml` was out of sync with
`python/Cargo.toml` and `Cargo.toml`, causing maturin to build
v0.1.7 release artifacts as `microresolve-0.1.6-*.whl`. PyPI's
`--skip-existing` silently skipped them. v0.1.8 syncs the
pyproject version and re-publishes the Python wheel. Crates.io
and npm v0.1.7 are unaffected; tarball release for v0.1.7 is also
intact, but skipped in favor of v0.1.8 for cross-registry parity.
## [0.1.7] — 2026-05-01
### Added
- **Per-namespace reflex-layer toggles.** Each namespace now carries
four boolean fields — `l0_enabled`, `l1_morphology`, `l1_synonym`,
`l1_abbreviation` — accepted by `Resolver::update_namespace` and
exposed on `NamespaceInfo`. All default to `true`; namespaces saved
before this change load with all layers on. Flags are honored at
both index time and resolve time so trained vectors and runtime
preprocessing stay in sync. Use cases: turn L0 off for medical /
legal namespaces (auto-correcting domain terms is dangerous); turn
L1 abbreviations off for code search (short tokens carry literal
meaning).
- **`PATCH /api/namespaces` body** accepts the four new fields;
`GET /api/namespaces` returns them per namespace.
- **Studio: layer toggles in three places.** Namespaces edit modal has
all four switches (atomic save, setup flow); L0 page has an inline
toggle; L1 page has a top-level `on / off / partial` status badge
plus a per-column compact toggle for each edge kind. Sidebar L0 / L1
nav items show an `off` or `partial` pill when any layer is disabled
for the active namespace.
- **`LayerToggle` shared component** + `AppContext.layerStatus` —
single optimistic-update store for the active namespace's toggles,
so changing them on any page updates the sidebar instantly without
an extra GET. Reverts on PATCH failure.
- **`LexicalGraph::preprocess_with_kinds` /
`preprocess_grounded_with_kinds`** — public variants of the
preprocess methods that take three booleans gating Morphological,
Abbreviation, and Synonym edge kinds. The original `preprocess` /
`preprocess_grounded` are unchanged thin wrappers — no break to the
published API.
## [0.1.6] — 2026-05-01
### Added
- **Empty `subscribe` list now auto-subscribes to all namespaces** the
server exposes. Pass `subscribe=[]` (Python: omit / `None`; Node:
omit / `[]`) and the library queries `GET /api/namespaces` at connect
time, then pulls each one. Explicit lists still work as an allow-list
for multi-tenant cases. Zero-config for solo / single-team setups.
- **Studio shows connected library clients.** A new top-level "LIVE →
Connected" sidebar item with a live count badge, plus a dedicated
`/connected` page listing every authenticated client currently
syncing (name, library version, subscribed namespaces, tick interval,
last sync, expires-in countdown). Auto-refreshes every 3s.
- **`GET /api/connected_clients`** — read-only roster endpoint backing
the UI. Lazy GC on read: any entry older than `2 × tick_interval_secs`
is dropped before responding.
- **`POST /api/sync` body fields** (optional, advisory):
`tick_interval_secs` lets the server use each client's own freshness
window; `library_version` (e.g. `microresolve-py/0.1.6`) surfaces in
the connected-clients panel for "who's still on the old client?"
triage.
### Changed
- **API key format now embeds the label**: `mr_<name>_<64 hex chars>`.
The server extracts the name from the key string itself — no separate
index lookup needed for attribution. Old opaque keys (`mr_<hex>`
without the embedded name) are no longer accepted; v0.1 is pre-stable
and operators must regenerate keys via the Studio UI / `/api/auth/keys`.
- Auth-key names must be slug-safe: `[a-z0-9][a-z0-9-]{0,30}` (no
underscore — that's the field separator).
### Note
- Connected-clients tracking is **only active when API keys are
configured**. In open mode (no keys), the panel is empty by design —
there's no identity to attribute connections to.
---
## [0.1.5] — 2026-04-30
### Breaking
- **Connected-mode protocol unified.** The three v0.1.4 endpoints
(`GET /api/sync`, `POST /api/ingest`, `POST /api/correct`) are
removed and replaced by a single `POST /api/sync` that carries the
library's buffered logs + corrections + per-namespace local versions
in one request, and returns deltas in one response. v0.1.4 clients
cannot talk to v0.1.5 servers; pin matching versions for now.
- `MicroResolve.correct(...)` no longer makes an immediate HTTP call.
The correction is applied locally on the spot and shipped to the
server on the next sync tick. The server reconciles within
`tick_interval_secs`. Eliminates the per-correction stampede risk
at scale and gives clean eventual-consistency semantics.
- v0.1 is pre-stable; expect more breaking protocol changes before 1.0.
---
## [0.1.4.1] — 2026-04-30
### Changed
- **Studio binary now ships with UI embedded.** The downloaded GitHub
Release tarball is a single executable — no sibling `ui/` directory
required, no `npm run dev` step. Implementation: `rust-embed` with
compression bakes `ui/dist/` into the binary at compile time; the
`server` feature now implies the new `bundled-ui` feature so building
the studio is `cargo build --release --features server --bin
microresolve-studio` (single flag). Build prerequisite from source:
`cd ui && npm run build` once before cargo build, so `ui/dist/` exists.
- Crate description latency claim updated `~30μs` → `~50μs` to match
the v0.1.4 README.
### Note
- Library users (PyPI / npm / crates.io) are unaffected — `bundled-ui`
only activates when the `server` feature is enabled, which embedded
bindings never enable.
---
## [0.1.4] — 2026-04-30
### Added
- **Connect-mode examples** — `examples/connected.rs` (Rust) and
`python/examples/connected.py` (Python): minimal end-to-end demo of
a library instance subscribing to a running Studio, resolving
locally, pushing a correction, and watching the version bump on the
next sync tick. This loop — Studio writes, library subscribes, live
sync — is the headline feature of this release.
- **Prebuilt server binaries** via GitHub Releases: linux-gnu x86\_64 +
aarch64, linux-musl x86\_64, darwin x86\_64 + aarch64,
windows-msvc x86\_64. Install: download the tarball for your platform
and put `microresolve-studio` on `$PATH`.
### Changed
- **Binary renamed** — `server` → `microresolve-studio` in Cargo.toml;
the old `server` bin name is removed. Update any scripts or service
files that reference the old name.
- **TCP bind failure** — server now exits cleanly with a descriptive
error when the port is already in use; previously it panicked.
- **README** — hero rewritten to "learnable reflex layer for LLM apps";
added "In the box" block (Studio / Library / online learning / native
imports / multilingual / multi-namespace); Quick Start moved above
the comparison table; v0.1 disclaimer condensed to one line; median
resolve latency updated to 50µs.
---
## [0.1.3] — 2026-04-28
### Breaking
- **`Engine` renamed to `MicroResolve`** across all bindings (Rust, Python, Node.js).
Update all imports and call sites:
- Rust: `Engine` → `MicroResolve`, `EngineConfig` → `MicroResolveConfig`
- Python: `from microresolve import Engine` → `from microresolve import MicroResolve`
- Node.js: `const { Engine } = require('microresolve')` → `const { MicroResolve } = require('microresolve')`
---
## [0.1.0] — TBD
Initial public release. **MicroResolve** is a pre-LLM reflex layer for
intent classification, safety filtering, and tool selection — sub-millisecond,
CPU-only, with continuous learning from corrections.
### Library API
- **`MicroResolve` + `NamespaceHandle`** — multi-namespace decision engine. One
instance per application can run several classifiers in parallel: security
/ mood / MCP tool selection / intent. The single public entry point.
(Previously named `Engine`; renamed in v0.1.3.)
- **`MicroResolveConfig`** with cascade: `data_dir`, `default_threshold`,
`languages`, optional LLM config, optional server config for connected
mode. (Previously named `EngineConfig`; renamed in v0.1.3.)
- **`NamespaceConfig`** — per-namespace overrides for threshold,
languages, LLM model.
- **Connected mode** — set `MicroResolveConfig.server` to a `ServerConfig` and
the engine pulls subscribed namespaces from a server on startup,
spawns a single background sync thread, buffers query logs for
shipping, and pushes corrections inline. Replaces the legacy
`AppRouter` surface.
- **Typed errors** — `Error::IntentNotFound`, `Io`, `Parse`,
`Persistence`, `Connect`. No `Result<_, String>` in the public API.
- **`add_intent`** accepts `&[&str]` (English) or
`HashMap<lang, Vec<phrase>>` (multilingual). Returns
`Result<usize, Error>` (count of phrases indexed).
- **`update_intent` / `update_namespace`** patch metadata fields with
`IntentEdit` / `NamespaceEdit` structs.
- **`correct(query, wrong, right)`** — one call moves a phrase between
intents and reinforces; in connected mode, also pushes to the server.
### Bindings
- **Rust crate** `microresolve` on crates.io. Default features include
connected mode (`reqwest`); embedded users can opt out via
`default-features = false`.
- **Python** `microresolve` on PyPI — full `MicroResolve` + `Namespace` API
via PyO3 / maturin. Mono and multilingual `add_intent`, `resolve`,
`correct`, `intent`, `update_intent`, `add_phrase`, `version`,
`flush`. Connected mode supported.
- **Node.js** `microresolve` on npm — `MicroResolve` + `Namespace` API via
napi-rs. Connected mode supported.
### Server
- **HTTP API** (`microresolve-server`, `--features server`) wraps the
same `MicroResolve` instance for multi-tenant deployments.
- `/api/route_multi` — classify a query.
- `/api/intents`, `/api/namespaces`, `/api/domains` — CRUD.
- `/api/import/openapi/*`, `/api/import/mcp/*` — bulk-import tools.
- `/api/sync`, `/api/ingest`, `/api/correct` — connected-mode endpoints
for SDK clients.
- `/api/auth/keys` — API key management for connected-mode auth.
- **Background auto-learn worker** — ingested query logs are
LLM-judged in batch; corrections applied to the live namespace.
- **API key auth** — `X-Api-Key` middleware; keys stored at
`~/.config/microresolve/keys.json` (separate from data dir, never
git-tracked).
### Git-versioned training data
- The `--data` directory is automatically a git repo. Every namespace
mutation auto-commits with a meaningful message.
- `GET /api/namespaces/{id}/history?limit=N` — list commits scoped to
a namespace.
- `GET /api/namespaces/{id}/diff?from=&to=` — semantic diff between two
commits: intents added/removed, phrases added/removed, metadata
changes, weight-update count. Filters out raw L1/L2 weight noise.
- `POST /api/namespaces/{id}/rollback` — `git reset --hard` + reload
affected resolvers from disk.
- `GET /api/settings/git` / `PUT /api/settings/git` — runtime configure
a `git remote origin` so each commit pushes to a real GitHub/GitLab
repo. Auth uses whatever git is configured with on the server.
- `POST /api/git/push` — manual push trigger.
### Studio UI
- React + Vite single-page app served by the binary or run via
`vite dev` against an external server.
- **Namespaces, Intents, Domains, Layers, Languages, Models** management
pages.
- **Studio (Resolve / Simulate / Review)** — live classification, batch
simulation, query review queue, manual + auto-learn corrections.
- **History** — first-class sidebar entry. List-sidebar + main-pane
layout. For each selected commit: sha, relative time, message, and
author; amber warning when rolling back will discard newer commits;
semantic diff panel showing affected intents, sample phrases under
each added/removed intent, phrases-added/removed grouped by intent,
metadata changes side-by-side (before/after), and training-weight
summary; one-click rollback with confirmation.
- **Settings → Data sync (Git)** — set/clear the git remote URL,
manual push button, status indicator.
- **Settings → Auth Keys** — generate, list, revoke API keys.
- **Import** — OpenAPI / MCP / LangChain / OpenAI function-tool import
flows with LLM-assisted seed-phrase generation.
### Internal
- `Resolver` and the `scoring` / `ngram` / `phrase` / `tokenizer` /
`connect` modules are `#[doc(hidden)]`. Library users see only
`MicroResolve`, `NamespaceHandle`, and the public types in rustdoc.
- Server bin migrated off direct `Resolver` use to `MicroResolve` API
internally — single source of truth for namespace state.
- Cargo.toml `exclude` list keeps the published crate at ~174 KB
(only `src/`, `examples/*.rs`, `tests/*.rs`, `languages/*.json`,
`LICENSE-*`, `README.md`, `CHANGELOG.md`).
- License: dual MIT / Apache 2.0 across all artifacts (root crate,
Python wheel, Node tarball, UI bundle, docs site).
### Benchmarks
Benchmark numbers will be added to this section after the v0.1.0
benchmark sweep runs. End-to-end smoke against Llama 3.3 / Groq
verifies multi-namespace MCP import, cross-namespace isolation,
manual corrections, and the auto-learn loop in ~75 seconds.
### Known limitations
- Server bin still uses `pub fn with_resolver(_mut)` closure escape
hatches (marked `#[doc(hidden)]`). True `pub(crate) struct Resolver`
is a v0.2 cleanup that doesn't change behaviour.
- Node binding is missing `resolve_with`, `intent`, `update_intent`,
`add_phrase` methods that Python has (v0.2).
- History UI shows commit list + diff but no commit-graph visualisation
or filter chips.
- No CLA — contributions are accepted under the dual MIT/Apache license.
---
[0.1.0]: https://github.com/gladius/microresolve/releases/tag/v0.1.0