> ## Documentation Index
> Fetch the complete documentation index at: https://docs.aethis.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Changelog

> Changes across the Aethis platform — the engine, CLI, MCP server, and Python SDK.

What changed across the Aethis platform. Each entry is derived from the source `CHANGELOG.md` of the package it belongs to and is labelled with that package. Subscribe via the RSS feed for this page.

<Update label="aethis-mcp v0.13.0" description="2026-07-19" tags={["aethis-mcp"]}>
  Adds the Authoring Coach surface to MCP (aethis-mcp#57, workspace epic #514) —
  skill-building feedback for rule authors, advisory only, never a gate.

  **Engine gate:** the `POST /api/v1/public/projects/{id}/review` endpoint and the
  ambient `review_hint` fields are produced by aethis-core (epic phases P1/P4).
  This release must not be published to npm until that endpoint is live on
  `api.aethis.ai`; a released client calling a not-yet-deployed route would 404.

  * **`aethis_review_project` (new tool).** Reviews an authoring project against
    the deterministic authoring-coach rubric and renders the report: a score,
    per-check evidence across grounding / process / lifecycle, strengths, and the
    single highest-leverage next skill. Advisory only — it never blocks
    publishing. The deterministic layer needs no LLM key; `coach=true` (with an
    Anthropic key, via the usual `anthropic_key_env` / `anthropic_key_keychain` /
    `anthropic_key` forms) adds an opt-in LLM-synthesised coaching narrative on
    top. All server free-text (evidence / strengths / next-skill message /
    coaching) is fenced with `fenceUntrusted` before it reaches the model.
  * **Ambient `review_hint` render.** `aethis_generate_and_test`, `aethis_refine`,
    and `aethis_publish` now render a one-line coach hint when the server includes
    one on the response. The hint is computed entirely server-side (aethis-core
    P4); the client only renders it (fenced), never computes it.
  * **`X-Aethis-Client: mcp/<version>` on every request.** The client now sends a
    per-surface identifier header so the engine can attribute telemetry (e.g.
    `review_hint`-shown counts) to MCP vs CLI vs SDK.
  * **31 tools, up from 30.** `tests/tool-endpoint-map.ts` and the drift suite are
    updated in the same change. Note: the drift suite's live-alignment checks stay
    red against staging until the `/review` endpoint deploys there (expected epic
    ordering); the offline structural checks pass.
  * **Tests.** New mocked unit coverage in `tests/client.test.ts` (reviewProject
    request shape, the client-id header) and `tests/server.test.ts`
    (`aethis_review_project` render + fencing, coach key resolution, ambient hint
    render on generate/publish).
</Update>

<Update label="aethis-cli v0.27.0" description="2026-07-19" tags={["aethis-cli"]}>
  * **feat: `aethis review [<project>]`** — the Authoring Coach report for a project. Runs the server-side rubric and prints an authoring score, 2–3 evidence-cited strengths, and the single highest-leverage next improvement (with its docs link and the lever that fixes it). Defaults to the current project in `.aethis/state.json`; pass a `proj_…` id to review any of your projects from anywhere. `--verbose` shows the full per-check table; `--json` (and any piped/`--output json` invocation) emits the raw `ReviewReport`. The deterministic report needs only your API key; `--coach` opts into LLM mentoring prose billed to your own Anthropic key (`ANTHROPIC_API_KEY`). Advisory only — the exit code is always 0 regardless of score. New `AethisClient.review()`.
  * **feat: every request now sends `X-Aethis-Client: cli/<version>`** so the server can attribute per-surface telemetry (CLI vs MCP). The header carries no credentials and no PII, and is set once at client construction for all commands.
  * Requires aethis-core with the `/api/v1/public/projects/{id}/review` endpoint live (epic aethis-workspace#514, P1). The public release of this version is held until that endpoint is live on `api.aethis.ai`.
</Update>

<Update label="aethis-sdk-python v0.9.0" description="2026-07-17" tags={["aethis-sdk-python"]}>
  * **feat(models): `robot_hints` + `engine_version` on the rulebook schema; `engine_version` on the ruleset schema.** New `RulebookSchemaResponse` model (`rulebook_id`, `sections`, `fields`, `robot_hints`, `engine_version`) for `GET /api/v1/public/rulebooks/{id}/schema` — `robot_hints` is the rulebook's natural-language conversational-agent guidance keyed by beat (`general_context`, `preamble`, `session_start`, `postamble`, `session_end`, `stuck`), `None` for a rulebook authored before the field existed. `SchemaResponse` (ruleset schema) gains `engine_version: str | None = None` for parity, also back-compat (defaults `None` when the engine doesn't send it — true of the ruleset schema route today).
  * **feat(models): `graph`/`GraphResponse` for the new `/graph` endpoint.** New `GraphResponse` (`ruleset_id`/`rulebook_id`, `slug`, `name`, `graph`, `mermaid`) and `RulesetGraph` (`nodes`, `edges`, `sections`, `stats`) model the ruleset/rulebook dependency graph (field → criterion → group → outcome) plus its rendered Mermaid diagram. Node/edge shape varies by node `type`, so nodes/edges stay loosely-typed dicts rather than a rigid per-type schema — deliberately permissive so a legacy or empty graph (`nodes: []`) still parses.
  * **feat(client): `get_graph(ruleset_id)`** (sync + async) — wraps `GET /api/v1/public/rulesets/{id}/graph`, returning `GraphResponse`. Public rulesets can be inspected without an API key, same as `get_schema()`.
  * **feat(decide): `include_graph_overlay` parameter on `decide()` / `decide_rulebook()`** (sync + async), and a matching `graph_overlay: dict[str, Any] | None = None` field on `DecideResponse`. Set `include_graph_overlay=True` to get this decision's per-criterion status stamped onto the ruleset's dependency graph, in the same shape `get_graph()` returns.
  * All additions are additive and backwards-compatible: every new field defaults to `None`/`False`/an empty collection, so a legacy response (no `robot_hints`, no `engine_version`, no `graph_overlay`) still deserialises unchanged.
</Update>

<Update label="aethis-mcp v0.12.0" description="2026-07-17" tags={["aethis-mcp"]}>
  Propagates the aethis-core 0.37–0.40 authoring batch to the MCP surface
  (aethis-mcp#49, workspace epic #327). Engine gate: live on `api.aethis.ai`
  0.45.2, confirmed via the drift suite's live-alignment checks before this
  release.

  * **`aethis_graph` (new tool).** Fetches the ruleset-map graph — either for a
    single published ruleset (`ruleset_id`, may be public/anonymous for a public
    showcase ruleset) or a composed rulebook (`rulebook_id`, always requires an
    API key) — the same mutual-exclusivity shape as `aethis_decide`. Returns
    `{ruleset_id|rulebook_id, slug, name, graph: {nodes, edges, sections,
    stats}, mermaid}`: each node's `display.sentence`/`display.routes`/
    `display.expr` shows how that branch composes, and `mermaid` is a
    ready-to-render diagram string.
  * **`include_graph_overlay` on `aethis_decide` (additive).** Stamp a specific
    decision's per-criterion outcome (`satisfied`/`not_satisfied`/`pending`)
    onto that same graph and return it as `graph_overlay` in the decide
    response — a "you are here" map for those inputs. Off by default; the
    response is unchanged when omitted.
  * **`aethis_create_rulebook` / `aethis_update_rulebook` (new tools).** Create
    an empty draft Rulebook (name/domain/slug/description) or update one, both
    accepting `robot_hints` — beat-keyed natural-language guidance for the
    conversational agent (active beats: `general_context`, `preamble`,
    `session_start`, `postamble`, `session_end`, `stuck`; reserved:
    `persona`, `conversational_style`, `section_transition`). An unknown beat is
    rejected client-side before the round-trip, mirroring aethis-cli's
    `_validate_robot_hints` (v0.23.0). Rulebook composition (`outcome_logic`,
    `ruleset_refs`) is a larger surface not covered by these two tools yet.
  * **`years_between` in the DSL helper reference (README).** Documents the new
    completed-whole-years, leap-correct date operator (mirrors aethis-core
    `Operator.YEARS_BETWEEN`, commit `3607558`) alongside `days_between` so
    generation can use it for age-from-date-of-birth instead of
    `days_between(...) / 365` (division isn't supported anyway).
  * **30 tools, up from 27.** `tests/tool-endpoint-map.ts` and the drift suite
    are updated in the same change; every new operation/field/param is verified
    against the live `api.aethis.ai` OpenAPI document (engine 0.45.2).
  * **Tests.** New mocked unit coverage in `tests/client.test.ts` /
    `tests/server.test.ts` for the graph client methods, the create/update
    rulebook client methods, `robot_hints` beat validation (known + unknown +
    reserved), and `include_graph_overlay` pass-through; the nightly staging
    integration lane gains a real `aethis_graph` fetch, a `aethis_decide
    include_graph_overlay` round-trip, and an `aethis_create_rulebook` →
    `aethis_update_rulebook` `robot_hints` round-trip (with best-effort archive
    cleanup of the probe rulebook).
</Update>

<Update label="aethis-cli v0.26.0" description="2026-07-17" tags={["aethis-cli"]}>
  * **feat(rulebooks): `aethis rulebooks graph <id>`** — fetch and render the rulebook-level ruleset-map dependency graph (field -> criterion -> group -> outcome). Prints a node-count summary + a table of nodes (id, type, the criterion's human-readable `display.sentence`, field count); `--mermaid` prints the raw Mermaid diagram source for piping into a renderer; `--output json` returns the full payload (`{rulebook_id, graph: {nodes, edges, sections, stats}, mermaid}`), including each node's `display.routes`/`display.expr` for programmatic consumers. This endpoint requires a valid API key even for a public rulebook (confirmed against the live engine) — unlike the ruleset-level graph below, there's no anonymous path. New `AethisClient.get_rulebook_graph()`.
  * **feat(rulesets): `aethis rulesets graph <ruleset_id>`** — the single-ruleset analogue, open for public rulesets with no API key required (`load_client_or_anon`). Same table/`--mermaid`/`--output json` shape. New `AethisClient.get_ruleset_graph()`.
  * **feat: `--include-graph-overlay` on `aethis decide` and `aethis rulebooks decide`** — stamps the decision's per-criterion status onto the rule-map graph, returned as a `graph_overlay` field on the response (`--output json` to inspect it). Additive request flag; a plain-text hint is printed when the overlay is present and JSON wasn't explicitly requested.
  * **feat(rulebooks): `aethis rulebooks schema` surfaces `engine_version`.** The schema response already carries the aethis-core build that served it (e.g. `aethis-core@0.45.2`); the CLI now prints it as a header line ahead of the schema payload instead of leaving it buried in the JSON.
  * Requires aethis-core 0.40.0+ (live on `api.aethis.ai` / `staging.api.aethis.ai` as of this release) for `/graph`, `include_graph_overlay`, and `engine_version` on `/schema`. `robot_hints` (shipped v0.23.0) is unaffected by this release.
</Update>

<Update label="aethis-core v0.45.1" description="2026-07-16" tags={["aethis-core"]}>
  ### Fixed

  * An API key with `expires_at` set no longer 500s every request: the
    Mongo-stored (tz-naive) expiry is normalized to UTC before comparison, so
    an expired key gets its intended 401 `api_key_expired`. Latent since the
    expiry field existed — no key carried a non-None expiry until 2026-07-16
    (issue #275).
</Update>

<Update label="aethis-sdk-python v0.8.0" description="2026-07-15" tags={["aethis-sdk-python"]}>
  * **feat(errors): typed 401/403/429 exceptions carrying the structured error envelope.** `classify_response` now raises `AethisAuthError` (401), `AethisPermissionError` (403), or `AethisRateLimitError` (429) — each a subclass of `AethisAPIError`, so existing `except AethisAPIError` handlers keep catching them (non-breaking). `AethisError` gains `.reason_code`, `.missing_permissions`, and `.hint`, lifted out of the public API's structured envelope (`{"detail": {"error", "reason_code", "missing_permissions", "hint", ...}}`), so a caller can branch on `err.reason_code == "denied_missing_permission"` or read `err.missing_permissions` without re-parsing `err.body`. Plain-string and FastAPI-422-list details are untouched (fields stay `None` / `[]`). Constructor stays backwards-compatible (new args default to `None`).
  * **test(staging): live integration lane against `staging.api.aethis.ai`.** New `tests/integration/` (marker `staging`, excluded from the PR gate) mints a real API key the way a user does — Clerk sign-in ticket → frontend-API JWT → `POST /api/v1/keys/` → teardown — and exercises every public method on `Aethis` + `AsyncAethis` (decide, decide\_rulebook, list\_rulesets, get\_schema, whoami, explain, explain\_failure, get\_source, sync/async session flows) plus live 401/403 typed-error assertions and a contract cross-check. Reports red (never green-by-skip) when creds are missing or staging/contract is unreachable.
  * **test(parity): recorded-live fixture parity.** `tests/shapes.compare_shape` diffs the mocked `conftest` fixture builders (`make_decide_response`, `make_schema_response`, `make_ruleset_summary`) against real staging payloads so the mocked suite can't silently drift from reality; the builders were updated to match the current engine shape (`slug`/`rulebook_id`, `graph_overlay`/`timing`, richer `next_question`/schema fields).
  * **chore(ci): coverage floor (`--cov-fail-under=45`) + `staging` marker + nightly `staging-integration.yml`** (report-only, `workflow_dispatch` + `schedule`, uploads a `qa-run-record` artifact for the `sdk-staging` lane). The coverage flags live in the CI command, not in `addopts`, so a bare `pytest` / `uv run pytest` works without `pytest-cov` (which is only in the `dev` extra) installed; the floor is still enforced in CI.
</Update>

<Update label="aethis-mcp v0.11.0" description="2026-07-15" tags={["aethis-mcp"]}>
  Test-infra only — no runtime/behaviour change to the server or its tools.

  * **Tool-schema drift suite (`tests/drift.test.ts`).** Guards that the 27
    `server.tool()` input schemas never silently drift from the engine. Reads each
    tool's real zod shape (no vendored schema copy) and compares field names,
    types, and required-ness against the deployed staging OpenAPI document — the
    oracle. An explicit, checked-in `tests/tool-endpoint-map.ts` records the
    tool → operation correspondence and field renames (e.g. `force → force_unsafe`);
    a tool missing from the map, an unknown extra tool, an unclassified input
    field, a mapped operation absent from the engine, or a mapped body field the
    engine no longer has all fail loud. Runs in the PR gate (network-tolerant) and
    nightly (network-required).
  * **Staging integration lane (`tests/integration/`, nightly).** Runs the built
    server as a subprocess with a freshly minted staging key and drives it over
    the real MCP protocol: `tools/list` (== 27), a read-only core loop, and
    `aethis_decide` against a public showcase ruleset; a negative path proves an
    invalid key returns a structured error result while the server stays alive.
    Keys are minted via the self-serve path (server-default scopes), named
    `e2e-dx-mcp-*`, and revoked + swept in teardown.
  * **`staging-integration.yml`** — nightly + manual, report-only, emits a
    QA-run-shaped run record artifact for downstream ingestion; missing secrets
    or unreachable staging fail red, never skip-green.
</Update>

<Update label="aethis-cli v0.25.0" description="2026-07-15" tags={["aethis-cli"]}>
  * **feat: authorization errors now render the server's `hint` and the missing scope readably.** A `403 denied_missing_permission` (and `401`) previously printed the raw error object on commands that render their own errors (`projects`, `whoami`, …); the CLI now renders one clean line naming the missing permission plus, on its own dim line, the server's follow-up hint (e.g. how to request access). The top-level handler and the per-command renderer now share one formatter (`aethis_cli.output.format_error_detail` / `render_api_error`), so every command surfaces the same readable message. The hint is rendered with markup disabled (so a hint containing `[brackets]` isn't dropped) and non-string `missing_permissions` items are coerced (so a server quirk can't turn the error into a traceback).
  * **test: new staging integration lane (`tests/integration/`, marker `staging`).** Acquires an API key the self-serve way (a fenced e2e user's session → mint with the server's default scopes, no `scopes` field), drives the CLI core loop against deployed staging (`whoami`/`status`, `projects list`/`archive`, `rulesets`/`explain`/`fields`/`decide` against a public showcase ruleset), and asserts the negative paths a caller actually sees — a scope-reduced key's 403 and a revoked key's 401 — with the error envelopes checked against the machine-readable public-API contract. Report-only nightly workflow (`staging-integration.yml`); never gates a merge. Run locally with the one-liner in `tests/integration/README.md`.
  * **test: the spacecraft authoring e2e moved to its own weekly lane** (`authoring-e2e-weekly.yml`). It drives the LLM authoring pipeline, so it is kept out of the nightly LLM-free cadence; the model is passed explicitly via `X-Anthropic-Key`, generation is bounded by an explicit iteration cap (`SPACECRAFT_GENERATION_TIMEOUT`), and the `manual` marker stays as the local escape hatch.
</Update>

<Update label="aethis-core v0.45.0" description="2026-07-15" tags={["aethis-core"]}>
  ### Added

  * **Read-only cross-tenant `/api/v1/admin/*` router** (epic
    aethis-workspace#480, P1): `/admin/keys`, `/admin/usage` (daily
    rate-limit counters), `/admin/generation-jobs` (+ `/{id}` with trace),
    `/admin/decisions` (list excludes `field_values`; `/{decision_id}` full
    record), `/admin/publish-audits`, `/admin/rulebooks`, `/admin/rulesets`
    (metadata; DSL source additionally requires `rulesets:source`).
    Gated by the new internal-only `admin:read` scope AND
    `api_key.internal == true` AND a hard-fail under `DISABLE_AUTH=true`
    (503) — scope alone is deliberately not the boundary. `admin:read` is
    never self-servable (`ALLOWED_SCOPES`) and never an alias target.
    Uniform `{items, next_cursor, limit, skipped_invalid}` envelope,
    cursor pagination, server-side max page size, default 30-day lookback
    on time-series lists, per-doc validation (one malformed record never
    500s a list), typed filters (`tenant_id=None` only via explicit
    `anonymous_only=true`, never a wildcard). New `admin` rate-limit
    category enumerated in every tier (internal ≈ unlimited). Routes stay
    in the live `/openapi.json` but out of the published public contract.
</Update>

<Update label="aethis-core v0.44.0" description="2026-07-15" tags={["aethis-core"]}>
  ### Added

  * **Decision log** (epic aethis-workspace#480, P0): every `/decide` call can
    now be persisted server-side as a `DecisionRecord` (collection
    `decisions`), fulfilling the `DecideResponse` docstring's deferred
    "server-side audit persistence". Gated by `DECISION_LOG_ENABLED` (default
    **off**, fail-closed) with TTL retention via `DECISION_LOG_TTL_DAYS`
    (default 90) — the TTL index is declared in code and asserted at boot
    (logging disables loudly if the live index lacks `expireAfterSeconds`).
    The write is fire-and-forget off the hot path: bounded pending-task set,
    client-side insert timeout, fully exception-isolated (no decision-log
    failure can alter a `/decide` response). Write failures log structured
    ERROR, increment a counter, and can alert via
    `DECISION_LOG_ALERT_WEBHOOK` (Google Chat, rate-limited; unset = off).
  * `DecideRequest.caller_ref` — optional opaque caller metadata (flat
    string→string dict, ≤2 KB, no `$`/dotted keys) stored on the decision
    record for the caller's own attribution (e.g. tda-server sends
    `{firm, application_id}`). Never an authorization key or cross-principal
    predicate (defect shape DS-25); invalid values are dropped with a
    warning, never rejected.
</Update>

<Update label="aethis-core v0.43.0" description="2026-07-10" tags={["aethis-core"]}>
  ### Added

  * `FieldDefinition` (and the `/schema` + `/decide` `next_question` /
    `optimal_path` envelope) now carries an optional `x_ui_widget: Optional[str]`
    authoring override. Currently only `"free_text"` is recognised: it tells
    downstream consumers (Lisa's `expected_input` emission in tda-server) to
    suppress the schema-derived structured-answer affordance (chips / select /
    date-picker) for that field and render a plain text composer, even though
    the field has a typed `sort`. Defaults to `None` — purely additive.
    ([#255](https://github.com/Aethis-ai/aethis-core/issues/255),
    epic [aethis-workspace#422](https://github.com/Aethis-ai/aethis-workspace/issues/422))
</Update>

<Update label="aethis-core v0.42.0" description="2026-07-10" tags={["aethis-core"]}>
  ### Added

  * `/decide`: the `next_question` (and each `optimal_path` entry) now carries
    optional `sort` and `enum_values` fields, exposing the field's answer type
    (`Int` / `Bool` / `String` / `Enum` / `Date` / `Duration`) and, for `Enum`
    sorts, its allowed values. Lets callers render typed input affordances
    (yes/no chips, a date picker, an option list) without a second `/schema`
    round-trip. Both default to `None`, so the change is additive — existing
    consumers are unaffected. Mirrors the `sort` / `enum_values` pair already on
    `/schema`'s `FieldInfo`. Populated on the ruleset path; rulebook `/decide`
    callers continue to source the type from `/schema`.
    ([#254](https://github.com/Aethis-ai/aethis-core/issues/254),
    epic [aethis-workspace#422](https://github.com/Aethis-ai/aethis-workspace/issues/422))
</Update>

<Update label="aethis-mcp v0.10.1" description="2026-07-08" tags={["aethis-mcp"]}>
  * **docs: correct stale paper citation in the construction-insurance demo.** The
    demo cited the withdrawn v3.6/v3.7 claim that GPT-5.4 at `reasoning_effort=low`
    scores 7/11 on the exception-chain subset; the paper withdrew that result in
    v3.8 (instrumented replication: 11/11). The demo now attributes 7/11 to
    GPT-5.3 only and pins the paper citation at v3.11. No code changes.
</Update>

<Update label="aethis-sdk-python v0.7.0" description="2026-07-04" tags={["aethis-sdk-python"]}>
  * **fix(errors): attach the API's `detail` (and full `body`) to `AethisAPIError`.** On the primary error path, `classify_response` parsed the 4xx `detail` only to log it, then raised `AethisAPIError("Aethis API returned 422")` — blinding callers to *why* the request failed. The exception message now reads `"Aethis API returned 422: <detail>"` when a detail is present, and `AethisError` gained `.detail` / `.body` attributes carrying the parsed payload (both `None` for timeouts / connection errors). Constructor signatures stay backwards-compatible (new args default to `None`).
  * **fix(models): `DecideResponse.explanation` is a single object, not a list.** The field was typed `list[dict] | None` but the engine returns `Optional[Dict[str, Any]]` (`{decision, groups: [...], unused_facts: [...]}`), a latent `ValidationError` for any caller that actually requested one. Retyped to `dict[str, Any] | None`.
  * **feat(decide): `include_explanation` parameter on `decide()` / `decide_rulebook()`** (sync + async). The engine has always accepted `include_explanation` on `POST /decide`, but the SDK never sent it, leaving `DecideResponse.explanation` permanently `None`. Passed through in the request payload alongside `include_trace`; defaults to `False`.
  * **feat(models): typed `FieldNote` and `NextQuestion.notes`.** The engine attaches structured author guidance (`note_text`, `source`, `metadata`) to each `next_question`; the SDK silently dropped it. Adds the `FieldNote` model (exported from the package) and `notes: list[FieldNote]` on `NextQuestion`, defaulting to `[]` so older responses without notes keep parsing.
  * **feat(client): `list_rulesets(limit=20, offset=0)`** (sync + async) — wraps `GET /api/v1/public/rulesets`, returning the previously-exported-but-unreachable `RulesetSummary` model. Anonymous callers get public rulesets; an API key additionally surfaces that key's own rulesets. `limit` is clamped by the engine to 1-50.
  * **docs(readme, `_base`): capability-table + docstring fixes.** README's "What's included" table now lists `explain_failure`, `decide_rulebook`, `list_rulesets`, `include_explanation`, and `FieldNote`; the `build_headers` docstring no longer names a nonexistent `/next_question` endpoint.
</Update>

<Update label="aethis-mcp v0.10.0" description="2026-07-04" tags={["aethis-mcp"]}>
  Cross-surface review batch (aethis-mcp#50).

  * **Surface `next_question.notes` (additive).** `aethis_next_question` now renders
    a **Notes** block after the question when the ruleset author attached notes to
    it (each note carries `note_text`, `source`, and `metadata`). Notes are labelled
    by `metadata.type` (e.g. `why`, `legal_background`) when present, and each
    note's text is wrapped with `fenceUntrusted(...)` since it is author-provided
    server content. Output is unchanged when no notes are present. The tool
    description now mentions the Notes block.
  * **Fence `aethis_list_guidance` output.** The `guidance_text` and `source` fields
    returned by the server were interpolated into the tool result unfenced, unlike
    every sibling handler. They are now wrapped with `fenceUntrusted(...)` under the
    `UNTRUSTED_PREFACE` warning (GHSA-ph7q-r9q4-922g hardening).
  * **Send a single provider header.** The per-request LLM key was sent under both
    `X-Anthropic-Key` and `X-OpenAI-Key`. It is now sent only as `X-Anthropic-Key`,
    matching how `resolveLlmKey` resolves the key.
  * **Correct the stale latency figure.** Two guidance strings claimed decisions are
    `<5ms`; corrected to `<1ms` to match the README and the canonical figure.
  * **Docs:** rewrote the `CLAUDE.md` architecture section to the real layout
    (`src/index.ts` + `src/client.ts` + `src/credentials.ts`, tests under `tests/`)
    instead of the non-existent `src/server.ts` + `src/tools/` tree.
</Update>

<Update label="aethis-cli v0.24.0" description="2026-07-04" tags={["aethis-cli"]}>
  * **fix: network errors now render one actionable line, not a raw traceback.** When the API is unreachable, times out, or a DNS/TLS error occurs, every command now prints `Could not reach the Aethis API at <url>: <reason>.` plus a "check your connection" hint and exits non-zero, instead of dumping an `httpx` stack trace. The top-level handler catches `httpx.HTTPError` (the umbrella over connect/timeout `RequestError`s), matching the graceful handling `login`/`account` already had.
  * **feat: non-interactive environments bypass confirmation prompts.** A truthy `AETHIS_NONINTERACTIVE` or `CI` env var (values `1`/`true`/`yes`, case-insensitive) now flips the whole process non-interactive, so destructive commands (`account revoke`, `rulesets archive`, `projects archive`, `rulebooks archive`, `rulebooks tests delete`) proceed without waiting on stdin, so a background job or CI step no longer hangs on a `[y/N]` prompt. The bypass prints a one-line notice so it's never silently active. The explicit per-command `--yes`/`-y` flags keep working unchanged. New shared `aethis_cli.prompts.confirm_or_abort` helper.
  * **docs: refreshed the worked examples in `decide`/`explain`/`fields` help** to use public showcase rulesets (`aethis/spacecraft-crew-certification`, `aethis/consumer-credit-prequalification`) instead of product-specific slugs.
  * **chore: `make install` uses `uv pip install -e ".[dev]"`** (matching the README) instead of bare `pip`.
  * **minor:** dropped the unused upgrade-command strings from `update_check._detect_install_method` (it now returns just the detected method; the concrete upgrade argv is still built by `update`'s `_upgrade_argv`); `decide` reads the decision field with a safe default so a payload without `decision` renders as `unknown` rather than raising `KeyError`.
</Update>

<Update label="aethis-cli v0.23.0" description="2026-06-25" tags={["aethis-cli"]}>
  * **feat(rulebooks): declare `robot_hints:` in a rulebook file and push them to the engine.** Rulebook authors can now provide natural-language guidance for the conversational assistant alongside the rulebook's other configuration.
    * **`aethis rulebooks create <name> --file rulebook.yaml`** — a new `--file`/`-f` option reads a `robot_hints:` block (a sibling of `name`/`domain`/`outcome_logic`) from a `rulebook.yaml`/`.json` and sends it on create. CLI flags still own `name`/`domain`/`slug`/`description`; only the hints are taken from the file. No `--file` (or a file without a `robot_hints:` key) is a clean no-op — behaviour is unchanged.
    * **`aethis rulebooks set-logic <id> -f rulebook.yaml`** now also accepts a *wrapped* form: when the top-level object carries an `outcome_logic:` key, a sibling `robot_hints:` block is pushed in the same update. A bare Expr AST file (the prior shape) is still accepted unchanged.
    * `robot_hints` is a mapping of beat-name to a natural-language string. Active beats: `general_context`, `preamble`, `session_start`, `postamble`, `session_end`, `stuck`. Reserved beats (accepted, not yet acted on): `persona`, `conversational_style`, `section_transition`. Unknown beat keys and non-string values are rejected client-side with a clear message before the round-trip.
    * New optional `robot_hints` parameter on `AethisClient.create_rulebook()` / `update_rulebook()`; omitted from the request body when not supplied, so calls against an older engine are unaffected.
    * Requires aethis-core with the rulebook `robot_hints` field (aethis-core#220); mid-deploy to staging at time of writing. Against an engine without it, the field is ignored/rejected server-side.
</Update>

<Update label="aethis-cli v0.22.0" description="2026-06-16" tags={["aethis-cli"]}>
  * **feat(fields): `aethis fields` is now a command group for the full field-authoring loop.** Bare `aethis fields [-b <ruleset>]` still shows a ruleset's field schema (unchanged); three subcommands manage the local `fields/fields.yaml`:
    * **`aethis fields discover`** — uploads the project's `sources/` (creating the project if needed), runs server-side LLM field discovery, and merges the proposals into `fields/fields.yaml` so you start from a real draft instead of a blank file. Existing entries are preserved — only new keys are appended — so hand-authored labels/questions/hints are never clobbered. Prints the completeness score and any critical gaps. Needs an LLM key (`ANTHROPIC_API_KEY`), same as `generate`; without one it fails with a clear message naming the env var instead of a raw server header error. New `AethisClient.discover_fields()`.
    * **`aethis fields pull`** — syncs the server's authoritative produced fields (key + type + enum values) back into `fields/fields.yaml` so local matches reality after a generate. Local-only `label`/`hints` are preserved; fields absent from the server schema are kept and reported rather than silently dropped.
    * **`aethis fields validate`** — checks `fields/fields.yaml` before upload: valid `type` (int/bool/string/enum/date/duration), no duplicate keys, `enum` requires `enum_values`. The same validation now also runs inside `aethis generate`, per contributing file (rulebook + ruleset), so duplicate keys within a file fail fast before any server state changes.
    * `discover`/`pull` only ever write a vocabulary that re-validates: an unknown server type or an `enum` with no values falls back to `string` instead of producing a file the next `validate`/`generate` would reject. Writes also preserve any hand-authored keys the tool doesn't model (e.g. `description`) rather than dropping them.
  * **feat(generate): the field spec/produced diff is surfaced after generation.** After a successful `aethis generate`, the CLI compares the pinned field vocabulary against what the engine actually produced and prints pinned-but-not-produced / produced-but-not-pinned fields (with a pointer to `aethis fields pull`) instead of the drift passing silently.
  * **feat(init): rulesets can declare rulebook membership explicitly.** A `rulebook:` key in a ruleset's `aethis.yaml` (a path to the enclosing rulebook) now declares membership directly; the directory-position convention (`<rulebook>/rulesets/<ruleset>/`) remains the fallback. The `init` scaffold documents the key.
  * **perf: source uploads are now idempotent.** `discover` and `generate` share one project-resolution + upload path, and a per-file mtime ledger in `.aethis/state.json` means a `discover` followed by a `generate` (or repeated generates) only re-uploads sources that actually changed instead of re-pushing the whole `sources/` tree each time.
  * **fix(generate): don't lose the ruleset id on a fast success.** The poll loop occasionally saw the job flip to `success` a beat before `latest_ruleset_id` was populated, writing a null id to state and leaving `fields pull` / the field diff with nothing to work from. It now re-polls briefly for the id and only records a real one — never clobbering a prior good id with null.
  * **example + e2e:** `examples/community-grants-rulebook/` is a generic rulebook (one shared field) with two member rulesets, and `tests/e2e/test_rulebook_hierarchy_e2e.py` (gated by the `manual` marker) drives discover/validate/generate/pull against a live API and asserts the shared rulebook field propagates into both members.
  * No engine change required — all endpoints (`/fields/discover`, `/rulesets/{id}/schema`, `/fields/spec`) are already served by aethis-core and used by the MCP server.
</Update>

<Update label="aethis-cli v0.21.0" description="2026-06-16" tags={["aethis-cli"]}>
  * **feat(init): field definitions get a real home (`fields/fields.yaml`).** `aethis init` now scaffolds a `fields/` directory with a `fields.yaml` for declaring the field vocabulary (key + `type` + optional `label`/`question`/`hints`). Previously fields had no dedicated home and only surfaced implicitly as the `inputs:` keys inside `tests/scenarios.yaml`. `aethis generate` reads `fields/fields.yaml`, pins the field keys/types via the project field-spec endpoint, and routes each field's label/question/hints through guidance so a field is defined once.
  * **feat(init): `--kind rulebook` scaffolds a rulebook.** `aethis init <name> --kind rulebook` lays down a rulebook directory with shared `guidance/` and `fields/` plus a `rulesets/` directory for member rulesets. When a ruleset lives under a rulebook (`<rulebook>/rulesets/<ruleset>/`), `aethis generate` propagates the rulebook's guidance hints and field vocabulary into the ruleset — **the rulebook definition wins on shared field keys** — so a common field (e.g. date of birth) is defined once at the rulebook level and the end user is asked for it only once. `--kind` defaults to `ruleset`, so existing behaviour is unchanged.
    * New `AethisClient.set_field_spec()` (project field-spec endpoint, already served by aethis-core / used by the MCP server). No engine change required.
</Update>

<Update label="aethis-cli v0.20.0" description="2026-06-03" tags={["aethis-cli"]}>
  * **feat(rulebooks list): anonymous fallthrough to the public rulebook catalogue.** With no cached API key, `aethis rulebooks list` now lists the cross-tenant public catalogue (rulebooks with public visibility, active status) instead of printing the v0.19.1 pointer message — completing the parity with `aethis rulesets list`. A dim one-liner ("No API key — showing public rulebooks…") distinguishes the anonymous view; with a key, the tenant listing is unchanged.
    * New `AethisClient.list_public_rulebooks()`; use with `make_anonymous_client` so a cached key doesn't promote the call to an authenticated tenant listing.
    * Requires aethis-core v0.29.0+ on the target API (live on api.aethis.ai). Against an older engine the anonymous path surfaces the server's 401 cleanly.
</Update>

<Update label="aethis-cli v0.19.1" description="2026-06-03" tags={["aethis-cli"]}>
  * **fix(rulebooks list): stop prompting browser sign-in for anonymous users.** `aethis rulebooks list` with no cached API key used to trigger the lazy-auth browser login — bad first-contact DX for a read-only browse command. Rulebooks are tenant-scoped, so an anonymous caller has nothing to list; the command now prints a pointer to the anonymous public catalogue (`aethis rulesets list`) and to `aethis login`, and exits 1 without ever opening a browser.
    * True anonymous fallthrough (listing public rulebooks without an account, mirroring `aethis rulesets list`) needs engine support for a public rulebook catalogue and is tracked separately; this release removes the login prompt in the meantime.
</Update>

<Update label="aethis-cli v0.19.0" description="2026-06-03" tags={["aethis-cli"]}>
  * **feat(update): `aethis update` — self-update the CLI to the latest release.** Detects how the CLI was installed (uv tool, pipx, or pip) and runs the matching upgrade command. `aethis update --check` reports whether a newer release exists without installing anything.
    * Editable (development) installs are refused with a pointer to `git pull && uv sync` instead of clobbering the checkout.
    * The exit-time "new release available" banner now points at `aethis update` rather than a method-specific command.
    * **fix:** the banner's uv upgrade hint was `uv tool install --upgrade aethis-cli`, which re-resolves from scratch and silently drops any extra `--with` requirements (e.g. plugin packages installed alongside the CLI). Both the banner's install-method detection and `aethis update` now use `uv tool upgrade aethis-cli`, which honours the original install receipt.
    * A successful (or no-op) `aethis update` refreshes the banner's 24h cache, so the notice goes quiet immediately after updating.
</Update>

<Update label="aethis-mcp v0.9.0" description="2026-05-29" tags={["aethis-mcp"]}>
  `aethis_refine` now performs **finding-driven incremental re-authoring**: it
  seeds generation from the section's active ruleset and asks the engine for the
  **minimal edit** to fix failing test cases while keeping passing tests green,
  instead of re-authoring the whole section from scratch. `aethis_generate_and_test`
  is unchanged (from-scratch authoring).

  Why this matters: fixing one wrong case in a published ruleset previously meant a
  full-section regenerate — expensive, and prone to silently regressing carefully
  tuned behaviour (e.g. caseworker-review criteria that intentionally yield
  `undetermined`). Refine keeps the blast radius to the criteria that actually need
  to change; the full-suite gate still guarantees no regression ships.

  Requires aethis-core with the `mode` parameter on `/generate` (engine ≥ the
  release shipping seed-from-existing refine). Older engines ignore the body and
  fall back to from-scratch generation.

  * `client.generate()` / `generateAndTest()` accept an optional `mode` and send
    `{mode:"refine"}` on the generation request body.
</Update>

<Update label="aethis-cli v0.18.0" description="2026-05-29" tags={["aethis-cli"]}>
  * **feat(refine): `aethis refine` + `aethis generate --mode refine` for incremental, seed-from-existing re-authoring.** Instead of re-authoring a whole section from scratch, refine seeds generation from the section's active ruleset and makes the **minimal edit** to fix failing tests while keeping passing tests green.
    * `aethis refine [--hint "..."] [--seed-ruleset-id <id>]` — the phase-3 TDD-loop command: optionally add a guidance hint, then refine. Defaults to seeding from the section's active ruleset.
    * `aethis generate --mode refine [--seed-ruleset-id <id>]` — the same capability via a flag on `generate`; `--mode fresh` (default) is unchanged from-scratch authoring.
    * `AethisClient.generate()` gains optional `mode` / `seed_ruleset_id`; a no-arg call still sends no body, so it stays backwards-compatible against engines without the parameter.
    * Requires aethis-core with the `mode` parameter on `/generate` (live on `api.aethis.ai`). Against an older engine the flags no-op (empty body = fresh).
</Update>

<Update label="aethis-mcp v0.8.0" description="2026-05-27" tags={["aethis-mcp"]}>
  Add the rulebook tier to the MCP read surface. Closes
  [aethis-mcp#43](https://github.com/Aethis-ai/aethis-mcp/issues/43) for the
  two endpoints the engine exposes today; the public-catalogue equivalent
  (`aethis_discover_rulebooks`) is deferred until aethis-core ships a no-auth
  rulebooks catalogue endpoint.

  Why this matters: until now, an agent connected via MCP could see the parts
  (rulesets via `aethis_discover_rulesets` / `aethis_list_rulesets`) and
  evaluate the whole (`aethis_decide` with `rulebook_id`), but had no way to
  *find* a rulebook or inspect *how* its rulesets compose. Concrete failure
  mode from a real 2026-05-27 session: asked whether `aethis/uk-fsm` was
  "three rulebooks or one rulebook with three rulesets", the MCP gave no
  read path that could answer.

  ### Added

  * `aethis_list_rulebooks` — lists rulebooks in the current tenant
    (auth-required, tenant-scoped). Returns the fields needed to
    distinguish one composed rulebook from N independent rulesets:
    `rulebook_id`, `slug`, `name`, `domain`, `status`, `version`,
    `outcome_logic` (the composition Expr AST), `ruleset_refs`,
    timestamps. Mirrors `aethis_list_rulesets`.
  * `aethis_rulebook_schema` — fetches one rulebook's composition,
    bridged rulesets (with names + slugs + ruleset\_ids), and aggregated
    input fields. Accepts either a slug (`aethis/uk-fsm`) or an opaque
    `rb_*` id. Mirrors `aethis_schema` but at the rulebook tier.
  * `AethisClient.listRulebooks()` / `getRulebookSchema()` — wrap
    `GET /api/v1/public/rulebooks/` and
    `GET /api/v1/public/rulebooks/{slug-or-id}/schema`. The schema helper
    preserves the literal `/` in slugs (so `aethis/uk-fsm` hits the
    engine's `{namespace}/{name}` matcher) and URL-encodes opaque ids.

  ### Deferred

  * `aethis_discover_rulebooks` — the cross-tenant public catalogue
    equivalent of `aethis_discover_rulesets`. The engine's
    `/api/v1/public/rulebooks/` endpoint is tenant-scoped + auth-required
    on prod today; no anonymous catalogue variant exists. Will land
    once aethis-core adds it.
</Update>

<Update label="aethis-cli v0.17.0" description="2026-05-27" tags={["aethis-cli"]}>
  * **feat(output): gh-style machine-readable output mode (`--output json`, `--json fields`, `--jq`).** Every list/show command (and the decision commands) now emit structured JSON on demand, so `aethis rulesets list --output json | jq '.[0].slug'` just works instead of trying to scrape ANSI-coloured Rich tables.
    * `--output table|json` — pick the format. Default: `table` on a TTY, `json` when piped (matches gh's pipe-friendly autodetect).
    * `--json FIELDS` — implies `--output json`; takes a required comma-separated value (`--json id,name`) that limits the payload to those fields. (gh's bare-`--json` introspection trick is not yet exposed — Click/Typer's option parser can't cleanly distinguish "flag with no value" from "flag followed by positional", so it's deferred to a future `--list-fields` flag.)
    * `--jq EXPR` — pipe JSON output through `jq` before printing. Requires the `jq` binary on PATH; clear error with install hint if missing.
    * Commands migrated: `rulesets list/show`, `rulebooks list/show/get-fields/tests list/schema/explain/decide`, `projects list/show`, `account keys`, `profile list`, `guidance list`, `fields`, `explain`, `decide`, `status`. Each command has a sensible JSON shape — `status --output json | jq .identity.key_id` returns the live key id without rooting through any prose.
    * Footer hints (`Try: aethis ...`) are suppressed in JSON mode so pipes get clean output.
    * New module `aethis_cli/render.py` is the single emit point; new test file `tests/test_render.py` covers the matrix.
  * **breaking(guidance export): `--output` renamed to `--output-file`** to avoid clashing with the new global `--output` flag. Short form `-o` unchanged. Affects scripts that pipe to a named file: `aethis guidance export --output foo.yaml` → `aethis guidance export --output-file foo.yaml` (or `-o foo.yaml`).
</Update>

<Update label="aethis-cli v0.16.3" description="2026-05-27" tags={["aethis-cli"]}>
  * **fix(status, whoami): read the multi-profile credentials file the same way every other command does.** `aethis login --api-key ...` writes `profiles.<name>.api_key` to `~/.config/aethis/credentials` (the multi-profile schema introduced in v0.10), but `aethis status` and `aethis whoami` had stale local resolvers that only looked for a flat top-level `api_key` (and `whoami` was looking at the wrong filename, `credentials.yaml`). Result: after a fresh `aethis login`, `aethis status` reported `no API key` and `aethis whoami` reported `No Aethis API key configured`, even though the same key worked for `aethis projects list`, `aethis generate`, and every other authoring command.
    * Both commands now route through the canonical `resolve_cached_key()` helper in `auth_helpers.py`, which honours `AETHIS_API_KEY` env → active profile → keychain → legacy `.yaml` file.
    * The `_resolve_cached_key` symbol is renamed to `resolve_cached_key` (public). The legacy `_resolve_key_silent` (status\_cmd) and `_resolve_api_key_lax` (whoami\_cmd) are removed.
    * Regression test in `tests/test_status_cmd.py` writes a real multi-profile credentials YAML to a temp `XDG_CONFIG_HOME` and asserts both commands surface the key.
</Update>

<Update label="aethis-mcp v0.7.2" description="2026-05-26" tags={["aethis-mcp"]}>
  * **chore(server): tighten MCP instructions against decision extrapolation.** Adds a "Reporting decisions" section to the server `instructions` block (visible to every client model as part of its system prompt on connect). New rules forbid asserting facts that are not in the tool response, generalising a single-ruleset decision to a composite outcome, naming rulesets/rulebooks not yet observed in the session, and offering follow-up calls against unverified slugs. Triggered by a real user trace where a model summarising a `uk-fsm-child-eligibility` decision closed with an offer to run the broader `aethis/uk-fsm` rulebook "to get the complete household-level decision" — the rulebook exists but currently 422s on prod (empty `ruleset_refs`, see aethis-core#90), so the offer overstated what would actually happen. Advisory, not enforced — but client models reliably honour `instructions` blocks.
</Update>

<Update label="aethis-sdk-python v0.6.0" description="2026-05-25" tags={["aethis-sdk-python"]}>
  * **feat(explain-failure): `Aethis.explain_failure()` + `AsyncAethis.explain_failure()`** — wraps `POST /api/v1/public/rulesets/{ruleset_id}/explain-failure`, returning the failing criterion and a targeted fix hint for a mismatched `/decide` result. Accepts `field_values`, `expected_outcome` (`"eligible"` | `"not_eligible"` | `"undetermined"`), and an optional `test_name` (default `"test"`). Return type is `dict[str, Any]` to match `explain()` / `get_source()` — can be tightened once the response shape stabilises. Note: `ruleset_id` must be the concrete identifier (not a slug); the underlying endpoint does not currently resolve slugs. Previously, callers had to drop to raw `httpx` for this endpoint — flagged in `recipes/evaluate-a-case.mdx` and `recipes/debug-a-decide.mdx`.
</Update>

<Update label="aethis-sdk-python v0.5.1" description="2026-05-22" tags={["aethis-sdk-python"]}>
  * **docs(readme): rulebook surface advertised on the PyPI landing page.** The v0.5.0 release shipped `decide_rulebook()` and `rulebook_id` on `DecideResponse`, but the README still framed the SDK as ruleset-only. Adds a dedicated "Composed rulebook" section with a runnable UK FSM example, the always-scope-gated note, and the async equivalent.
  * **docs(install): switch `pip install` to `uv add` per workspace no-pip rule.** The PyPI landing page is a public-facing surface bound by `.claude/rules/no-pip.md`. Adds `uv pip install` as a venv-friendly alternative.
  * **docs(engine\_version): update sample audit-field comment from `aethis-core@0.10.0` to `aethis-core@0.27.0`** — matches live prod engine.
  * **docs(beta): clarify that decision endpoints are anonymous only for single rulesets** — rulebook decide is always scope-gated, so the SDK's "anonymous when no key" claim needed a footnote.
</Update>

<Update label="aethis-sdk-python v0.5.0" description="2026-05-22" tags={["aethis-sdk-python"]}>
  * **feat(rulebook): `Aethis.decide_rulebook()` + `AsyncAethis.decide_rulebook()`** — evaluate a composed multi-ruleset rulebook through the SDK. Mirrors `decide()` but sends `rulebook_id` in the payload. Accepts either an opaque `rb_<id>` or a slug (e.g. `aethis/uk-fsm`). Requires an API key — rulebook evaluation is always scope-gated. Closes [#14](https://github.com/Aethis-ai/aethis-sdk-python/issues/14). Requires aethis-core v0.27.0+ live on the target API for slug-form rulebook paths.
  * **feat(models): add `rulebook_id: Optional[str]` to `DecideResponse`** — surfaces the rulebook identifier when the response was a composed-rulebook decide. Backwards-compatible: ruleset-only decides keep `rulebook_id=None`.
</Update>

<Update label="aethis-mcp v0.7.1" description="2026-05-22" tags={["aethis-mcp"]}>
  * **docs(readme): v0.27.0 accuracy pass.** Three fixes for fresh-developer accuracy:
    * Documented `rulebook_id` as an alternative to `ruleset_id` on `aethis_decide` — mutually exclusive; composed-rulebook evaluation always requires an API key.
    * Quickstart example: corrected field name from `species` to `space.crew.species` (the actual field ID in the spacecraft-crew-certification ruleset).
    * Windsurf config path: corrected from `.windsurf/mcp.json` to `~/.codeium/windsurf/mcp_config.json` (canonical path per aethis-cli README).
</Update>

<Update label="aethis-mcp v0.7.0" description="2026-05-22" tags={["aethis-mcp"]}>
  Add rulebook surface to `aethis_decide` — closes the converged-2-term
  client-completeness gap for MCP. The tool now accepts either
  `ruleset_id` (single ruleset) or `rulebook_id` (composed rulebook),
  mutually exclusive. Mirrors `aethis-sdk-python` v0.5.0 and
  `aethis-cli` `rulebooks decide`.

  ### Added

  * `aethis_decide` tool accepts `rulebook_id` as an alternative to
    `ruleset_id`. Pass an opaque `rb_<id>` or a slug like
    `aethis/uk-fsm`. Composed-rulebook evaluation is always
    scope-gated by the engine — anonymous callers get HTTP 401.
  * `AethisClient.decideRulebook(rulebookId, fieldValues, options?)` —
    parallel to `decide()`; sends `rulebook_id` in the `/decide` payload.

  ### Changed

  * `aethis_decide` description and schema updated to reflect both
    paths. Tool validates that exactly one of `ruleset_id` / `rulebook_id`
    is provided.

  ### Requires

  * aethis-core v0.27.0+ live on the target API for slug-form rulebook
    paths. The `rulebook_id` body field on `/decide` has been supported
    since aethis-core v0.18.x.
</Update>

<Update label="aethis-cli v0.16.2" description="2026-05-22" tags={["aethis-cli"]}>
  * **docs(readme): v0.27.0 accuracy pass.** Three fixes for fresh-developer accuracy:
    * Install block: removed the `pip install` fallback (uv and pipx are the recommended forms per workspace policy). Development section: `pip install -e ".[dev]"` → `uv pip install -e ".[dev]"`.
    * Added **Rulebooks** command-group section documenting the converged 2-term model surface shipped in v0.14.0–v0.16.1 (`aethis rulebooks` + `aethis rulesets` promote-to-live).
    * Updated engine\_version example to `aethis-core@0.27.0` (was absent; clarified to current production version).
</Update>

<Update label="aethis-cli v0.16.1" description="2026-05-22" tags={["aethis-cli"]}>
  * **docs(rulebooks set-logic):** the docstring example for `field_ref.key` now matches engine behaviour. Phase A.16 (aethis-core v0.26.0+) added per-section aggregate group synthesis, so `field_ref.key = <ruleset_name>` resolves to the AND of that ruleset's groups. The unscoped group-name and scoped `<ruleset_name>.<group>` forms remain available for advanced compositions. Requires aethis-core v0.26.0+ live on the target API.
</Update>

<Update label="aethis-cli v0.16.0" description="2026-05-21" tags={["aethis-cli"]}>
  * **feat(rulebooks): `aethis rulebooks set-logic` — set the composition expression on a rulebook.** The composition expression (server field `outcome_logic`) is an Expr AST that combines per-ruleset outcomes into the rulebook's final decision. Previously settable only via raw PATCH; now exposed via the CLI for multi-ruleset rulebooks (e.g. UK FSM's `child_eligibility AND (household_criteria OR universal_infant)`).
    * `aethis rulebooks set-logic <id> -f logic.yaml` — load from YAML/JSON file
    * `aethis rulebooks set-logic <id> --logic '<json>'` — inline JSON
    * Exactly one of `--file` / `--logic` is required; both forms reject non-object payloads at the client side so server validation isn't the first line of defence.
</Update>

<Update label="aethis-cli v0.15.0" description="2026-05-21" tags={["aethis-cli"]}>
  * **feat(rulesets): ruleset lifecycle commands scoped to a rulebook.** Phase B.1b of the converged 2-term model. Adds four new sub-commands under `aethis rulesets`:
    * `aethis rulesets list <rulebook>` — list rulesets in a rulebook (grouped by `ruleset_name` with version counts, live version, and observed states). The legacy `-p <project_id>` and `--public` modes are preserved while the project-scoped authoring pipeline retires in a future phase.
    * `aethis rulesets create <rulebook> <ruleset_name> [-n "Display name"]` — create a new draft Ruleset inside the rulebook. The display name auto-derives from `ruleset_name` if not provided (`child_eligibility` → `Child Eligibility`).
    * `aethis rulesets show <rulebook> <ruleset_name>` — full version history for one ruleset name (bundle\_id, version, state, created), with live version highlighted.
    * `aethis rulesets promote-to-live <rulebook> <ruleset_name> <ruleset_id> [--note "..."]` — atomically promote a `testing`-state ruleset version to `live` via the Phase A.4 service. Auto-cuts a new rulebook version; previous live ruleset is archived.
  * feat(client): four new `AethisClient` methods — `create_ruleset_in_rulebook`, `list_rulesets_in_rulebook`, `show_ruleset_in_rulebook`, `promote_ruleset_to_live`.
  * Requires aethis-core v0.20.0+ live on the target API (Phase A.8 endpoints).
</Update>

<Update label="aethis-cli v0.14.0" description="2026-05-21" tags={["aethis-cli"]}>
  * **feat(rulebooks): new `aethis rulebooks` command group.** First user-facing surface for the converged 2-term authoring model (workspace PR #64, aethis-core PRs #133-139). A Rulebook is the whole form — the execution unit — that owns a locked field vocabulary, composition logic, rulebook-level test cases, and an integer version history.
    * `aethis rulebooks list` — list tenant rulebooks
    * `aethis rulebooks show <id-or-slug>` — full configuration
    * `aethis rulebooks create <name> --domain <d> [--slug ...]` — create draft
    * `aethis rulebooks set-fields <id> -f fields.yaml` — replace locked vocabulary
    * `aethis rulebooks lock-fields <id>` / `unlock-fields <id>` / `get-fields <id>`
    * `aethis rulebooks tests add <id> -f scenario.yaml` — embed full-form test case
    * `aethis rulebooks tests list <id>` / `delete <id> <tc_id>`
    * `aethis rulebooks activate <id>` / `archive <id>` — lifecycle
    * `aethis rulebooks decide <id> -i '{...}' [--explain]` — evaluate composed rulebook
    * `aethis rulebooks schema <id>` / `explain <id>` — combined schema + explanations
  * feat(client): new `AethisClient` methods for every rulebook REST endpoint (create / list / show / update / activate / archive / set-fields / lock-fields / unlock-fields / get-fields / add-test / list-tests / delete-test / decide-rulebook / get-rulebook-schema / explain-rulebook).
  * Requires aethis-core v0.19.0+ live on the target API (the Phase A.6 endpoints).
  * The legacy `aethis projects` / `aethis generate` / `aethis test` / `aethis publish` command tree is unchanged in this release — replacement lands in the next minor (Phase B.1b: ruleset lifecycle + project retirement). No backward-compat shims are planned past public release.
</Update>

<Update label="aethis-sdk-python v0.4.6" description="2026-05-20" tags={["aethis-sdk-python"]}>
  * feat(models): add `name: Optional[str]` to ruleset response models — surfaces the human-readable section name introduced in aethis-core v0.18.0. Adds `RulesetSummary` (anonymous catalogue / `GET /api/v1/public/rulesets`) and `RulesetListItem` (project-scoped / `GET /api/v1/public/projects/{id}/rulesets`) as typed models, and adds the same `name` field to `SchemaResponse`. Backwards-compatible: pre-backfill rulesets serialise with `name=None`.
</Update>

<Update label="aethis-mcp v0.6.0" description="2026-05-20" tags={["aethis-mcp"]}>
  Add optional `name` parameter to `aethis_publish` tool — lets clients
  override the human-readable section name when publishing a ruleset.
  Companion to aethis-core v0.18.0's `PublishRequest.name` field.

  ### Added

  * `aethis_publish` tool now accepts an optional `name` parameter. When
    supplied, it overrides the section name stored on the ruleset (default
    is a titlecase of `section_id`, e.g. `"english_language"` →
    `"English Language"`). Section names are surfaced in rulebook
    responses so end users can see which sections compose a rulebook.
  * `AethisClient.publish()` now accepts a third `name?: string` argument
    and includes it in the POST body when set.

  ### Tests

  * `server.test.ts` — two new `aethis_publish` cases: forwarding `name`
    to the client and echoing it in output; confirming `name` is omitted
    when not provided.
  * `client.test.ts` — two new `publish()` cases: body contains `name`
    when provided; body is absent when neither `label` nor `name` is set.
</Update>

<Update label="aethis-mcp v0.5.1" description="2026-05-20" tags={["aethis-mcp"]}>
  Surface the human-readable section `name` in `aethis_list_rulesets` and
  `aethis_discover_rulesets` tool output. The engine has been returning
  `name` on both `RulesetSummary` (public catalogue) and `RulesetListItem`
  (project-scoped) responses since aethis-core v0.18.0; the MCP server
  already forwarded every API field verbatim via `JSON.stringify`, so the
  data was reaching the LLM, but the tool descriptions didn't advertise
  the field. The descriptions now mention `name` so models know to read
  and surface it to users (e.g. "Knowledge of language and life in the
  UK" instead of just `b_123…`).

  ### Changed

  * `aethis_list_rulesets` tool description now mentions the
    human-readable `name` field returned alongside ruleset ID, status,
    version, field count, and rule count.
  * `aethis_discover_rulesets` tool description now lists `name` in the
    documented response shape.

  ### Tests

  * `aethis_list_rulesets` and `aethis_discover_rulesets` server tests
    assert the `name` field passes through to the LLM-facing JSON output.
</Update>

<Update label="aethis-cli v0.13.1" description="2026-05-20" tags={["aethis-cli"]}>
  * feat(rulesets): show the human-readable section `name` column in `aethis rulesets list` output (both the public showcase and project-scoped tables). Surfaces the new field from aethis-core v0.18.0.
</Update>

<Update label="aethis-cli v0.13.0" description="2026-05-20" tags={["aethis-cli"]}>
  * feat: pluggable auth providers. Profiles now carry an optional `auth_mode` (default `"api_key"`) and `audience` field. The new `aethis_cli.auth_providers` module exposes a process-local registry; plugins (e.g. `aethis-cli-internal`) can `register_provider("gcloud_id_token", ...)` to add staff/internal auth schemes without touching the published package. `AethisClient` accepts an optional `auth_provider` callable, and `make_authed_client(...)` picks the right provider based on the active profile's mode.
  * feat: `aethis status` now prints the active profile name + auth mode (plus audience when set). For non-`api_key` modes it shows "provider-minted at request time" instead of calling `/me`, which is X-API-Key-only.
  * chore: un-hide the `--base-url` global flag in `aethis --help` (it was already implemented, just `hidden=True`).
</Update>

<Update label="aethis-mcp v0.5.0" description="2026-05-19" tags={["aethis-mcp"]}>
  Security hardening pass. Bundles the v0.5 security review fixes into one
  release. Closes #33, #34, #35; addresses GHSA-ph7q-r9q4-922g (disclosed
  on publish).

  ### Security

  * **GHSA-ph7q-r9q4-922g** (high) — prompt injection via unsanitised API
    response text in `aethis_explain_failure`. `formatExplainFailure` now
    wraps every API-supplied free-text field (`diagnosis`, `dsl_hint`,
    criterion `title` / `rule_text` / `source_refs`) in an
    `<api_response>` fence and prepends a one-line preface telling the
    model the contents are data, not instructions. Literal closing tags
    inside payloads are neutralised so a payload cannot break out of the
    fence. The same `fenceUntrusted` helper has been applied to other
    free-text API surfaces (`aethis_next_question`,
    `aethis_discover_sections`, `aethis_refine_sections`,
    `aethis_discover_fields`, `formatTestResults`).
  * **#33** — `src/credentials.ts` now resolves the credentials file via
    `fs.realpath` and asserts the canonical path sits under `$HOME` (or
    under an absolute `XDG_CONFIG_HOME` the user controls); refuses with
    `UnsafeCredentialsError` otherwise. Permissions check now matches
    `ssh` / `aws-cli` behaviour: any group/other bit set on the
    credentials file → refuse with `Permissions 0NNN ... too open. Run:
    chmod 600 <path>`.
  * **#34** — `progress_detail` from the polling API is sanitised before
    it lands on stderr: control characters are stripped (TAB preserved)
    and the body is capped at 120 visible chars + `…`. Full-fidelity
    output is gated behind `AETHIS_MCP_VERBOSE=1`. Prevents the server
    from injecting terminal escape sequences or PII into anything that
    captures the MCP process stderr.

  ### Changed

  * **#35** — Authoring tools now accept safer per-call key forms.
    * New `anthropic_key_env: string` — name of an env var the MCP server
      reads at call time. **Preferred.** The raw value never appears in
      the tool call so it does not land in the MCP host's session
      transcript JSONL.
    * New `anthropic_key_keychain: string` — macOS keychain reference,
      either `service:account` or just `account` (service defaults to
      `aethis-anthropic-key`).
    * Raw `anthropic_key` / `openai_key` arguments remain accepted for
      backwards compatibility but are now marked `[sensitive — do not
      echo or log]` in the schema; deprecated in tool descriptions.
    * `resolveLlmKey` (exported from `src/credentials.ts`) consolidates
      the resolution chain and throws `MissingLlmKeyError` if every form
      is empty.
    * Applies to `aethis_generate_and_test`, `aethis_refine`,
      `aethis_discover_fields`, `aethis_refine_fields`,
      `aethis_discover_sections`, `aethis_refine_sections`.

  ### Docs

  * README: new "Passing your Anthropic key safely" section showing env /
    keychain forms first; raw-key form marked deprecated.
  * CLAUDE.md: new gotchas covering safe-key resolution and the
    untrusted-content fencing helper.
</Update>

<Update label="aethis-cli v0.12.3" description="2026-05-19" tags={["aethis-cli"]}>
  * fix(decide): `aethis decide --explain` no longer crashes with `AttributeError: 'str' object has no attribute 'get'`. The CLI previously treated the engine's `explanation` field as a flat `list[dict]`, but the public decide route returns a layered `{decision, decision_path?, groups: [{group, status, criteria: [{title, status, supporting_facts?, ...}]}], unused_facts}` shape. The "Rules" block now walks the actual structure and renders each group + criterion with PASS/FAIL marks, supporting fact field/value pairs underneath satisfied criteria, and a final list of unused fields (provided answers that no satisfied criterion referenced — useful for catching field-name typos).
</Update>

<Update label="aethis-cli v0.12.2" description="2026-05-19" tags={["aethis-cli"]}>
  * fix(login): default `AETHIS_CLERK_CLIENT_ID` to the OAuth Application registered on the `clerk.aethis.ai` Clerk instance. The previous default belonged to a different Clerk app, so `aethis login` returned `invalid_client` against the dev-tools domain set in 0.12.1.
  * fix(account): default `AETHIS_CLERK_DOMAIN` to `clerk.aethis.ai` for `aethis account generate` (matching the 0.12.1 change to `aethis login`); previously still pointed at the immigration domain.
</Update>

<Update label="aethis-cli v0.12.0" description="2026-05-13" tags={["aethis-cli"]}>
  * feat: `decide`, `explain`, and `fields` no longer prompt for sign-in when no API key is present. Public rulesets are now accessible with zero setup — the CLI silently uses an anonymous client and lets the server return an error only if a private ruleset is requested.
  * fix: hide `--base-url` global flag from `aethis --help` (internal dev override; `AETHIS_BASE_URL` env var unchanged)
  * docs: reorder `aethis --help` to lead with the no-auth explore flow, then authoring
</Update>

<Update label="aethis-mcp v0.4.4" description="2026-05-12" tags={["aethis-mcp"]}>
  * docs: surface `aethis-skills` as the optional agent workflow layer on top of MCP.
</Update>

<Update label="aethis-cli v0.12.1" description="2026-05-12" tags={["aethis-cli"]}>
  * fix: default Clerk domain changed from `clerk.aethis.legal` to `clerk.aethis.ai` so developer portal users can authenticate via `aethis login` (closes aethis-cli#40)
</Update>

<Update label="aethis-mcp v0.4.3" description="2026-05-11" tags={["aethis-mcp"]}>
  * fix: align `package.json` repository metadata with GitHub provenance so npm Trusted Publishing can verify the package source.
</Update>

<Update label="aethis-mcp v0.4.2" description="2026-05-11" tags={["aethis-mcp"]}>
  * fix: pin `zod` to v3 so the MCP SDK tool registration types match the build-time schema shape; `npm publish` now runs the `prepublishOnly` TypeScript build successfully.
</Update>

<Update label="aethis-sdk-python v0.4.5" description="2026-05-10" tags={["aethis-sdk-python"]}>
  * fix: update `examples/session.py` to use `AETHIS_RULESET_ID` env var (was deprecated `AETHIS_BUNDLE_ID`) and replace stale internal default with the public `aethis/construction-all-risks` slug
</Update>

<Update label="aethis-mcp v0.4.1" description="2026-05-10" tags={["aethis-mcp"]}>
  * docs: fix stale `bundle`/`bundle_id`/`aethis_create_bundle` terminology in `docs/demo-construction-insurance.md` and `docs/agentic-decision-systems.md` — these files were not caught by the v0.3.0 rename sweep. All references now use `ruleset`/`ruleset_id`/`aethis_create_ruleset`
  * chore: bump `server.json` version to 0.4.1 (was lagging behind `package.json`)
  * security: regenerate `package-lock.json` — bumps `hono` 4.12.12 → 4.12.18, `fast-uri` 3.1.0 → 3.1.2, `ip-address` 10.1.0 → 10.2.0, `postcss` 8.5.8 → 8.5.14; clears all 6 open Dependabot alerts (closes #19)
</Update>

<Update label="aethis-mcp v0.4.0" description="2026-05-10" tags={["aethis-mcp"]}>
  * feat: new `aethis_discover_rulesets` tool — lists the cross-tenant
    public showcase catalogue (no authentication required). Mirrors the
    no-auth policy of `aethis_decide` / `aethis_schema` /
    `aethis_explain`. Returns slug, ruleset\_id, description, field\_count,
    rule\_count for each entry; the slug or ruleset\_id can then be passed
    to the existing decision tools. Distinct from `aethis_list_rulesets`,
    which remains tenant-scoped and authenticated. Tool count: 24 → 25.
  * feat: `client.discoverRulesets(limit, offset)` wrapping
    `GET /api/v1/public/rulesets`.
  * docs: `aethis-decide` prompt and server-instructions now point at
    `aethis_discover_rulesets` for first-time discovery (no key) before
    falling back to `aethis_list_projects` → `aethis_list_rulesets` for
    authenticated tenant browsing.
</Update>

<Update label="aethis-cli v0.11.1" description="2026-05-10" tags={["aethis-cli"]}>
  * fix: remove `examples/demo_core.sh` (internal dev script referencing `aethis-core` by name and a private API path — not intended for public release)
  * fix: update `tests/e2e/test_spacecraft_e2e.py` to resolve the spacecraft fixture from `examples/spacecraft-crew-rules/` instead of an internal path; drop internal service name from comment
  * docs: fix "rule bundle" → "ruleset" in `examples/spacecraft-crew-rules/README.md`
</Update>

<Update label="aethis-cli v0.11.0" description="2026-05-10" tags={["aethis-cli"]}>
  * feat(updater): gh-style update-check banner. On startup the CLI
    kicks off a background thread that queries PyPI; if a newer release
    is available it prints a one-line notice to stderr at exit:
    "A new release of aethis-cli is available: 0.11.0 → 0.12.0 — to
    upgrade, run: \<method-aware command>". Detects whether the install
    came via uv tool, pipx, or pip and renders the matching upgrade
    command. Result is cached for 24 h at
    `~/.config/aethis/update_check.json`. Suppressed automatically when
    stderr is not a TTY (CI, piped output). Disable with
    `AETHIS_DISABLE_UPDATE_CHECK=1`. The check never blocks the
    command — failures are silent.
</Update>

<Update label="aethis-cli v0.10.0" description="2026-05-10" tags={["aethis-cli"]}>
  * feat(rulesets): `aethis rulesets list --public` lists the cross-tenant
    public showcase catalogue (no auth required). When run with no
    `--project-id` and no project context, falls through to the public
    catalogue automatically with a one-line hint — so a fresh signup
    sees something the moment they install the CLI instead of an empty
    list. Combine with `aethis fields -b <slug>` /
    `aethis explain -b <slug>` / `aethis decide -b <slug>` to fully
    exercise a ruleset without an API key.
  * feat(profiles): named credential profiles with both per-invocation
    flag (`aethis --profile new-dev …`) and sticky default
    (`aethis profile use new-dev`). Manage with `aethis profile
    list/use/add/remove`. Reserved profile name `anonymous` forces
    unsigned mode — handy for testing what a fresh signup sees without
    losing your admin key. `aethis login --profile <name>` writes into
    the named slot. Credentials file format upgraded to
    `{active_profile, profiles: {...}}`; legacy single-key files are
    read transparently and rewritten to the new shape on next save.
  * feat(client): `AethisClient(unsigned=True)` and
    `make_anonymous_client()` helper for paths that must hit the
    anonymous surface without accidentally sending a cached key.
  * feat(client): `client.list_public_rulesets(limit, offset)` wrapping
    `GET /api/v1/public/rulesets`.
</Update>

<Update label="aethis-cli v0.9.0" description="2026-05-08" tags={["aethis-cli"]}>
  * feat(publish): thread `--force` through to the server-side TDD gate
    introduced in `aethis-core` 0.11.0. `client.publish()` gains a
    `force_unsafe: bool = False` keyword; `aethis publish --force` now
    passes `force_unsafe: true` in the request body so the server-side
    gate is bypassed (and a `publish_force_bypass` audit event is
    recorded). Older engines ignore the field — no breakage. Without
    `--force`, the new gate refuses publishing over a failing test
    suite even when the CLI's own test gate is bypassed (e.g. by a
    direct curl that doesn't use the CLI). Closes the cli/server
    asymmetry that nearly shipped a 10/11 ruleset to a canonical
    `aethis/*` slug on 2026-05-07.
</Update>

<Update label="aethis-sdk-python v0.4.4" description="2026-05-07" tags={["aethis-sdk-python"]}>
  * docs: link to the test-driven authoring guide on docs.aethis.ai and surface the publish-gate guarantee (rulesets cannot publish with a failing test) in the private-beta callout. Reference surface only — no code changes
</Update>

<Update label="aethis-mcp v0.3.5" description="2026-05-07" tags={["aethis-mcp"]}>
  * docs: surface the test-gate guarantee — `aethis_publish` refuses to publish a ruleset with a failing test, derived from positioning bible §5/§7. Strengthens the existing Note to an Important callout and annotates the publish line in the four-stage workflow
  * docs: drop `force=true` mention from troubleshooting — surfacing the override on the public README undermines the "cannot be published with failing tests" guarantee. The API parameter remains in the engine; whether to deprecate it is tracked separately
  * docs: fix tool count (25 → 24); tools table sums to 24 (5 + 7 + 8 + 2 + 2). Fixed in README header and in CLAUDE.md
</Update>

<Update label="aethis-mcp v0.3.4" description="2026-05-07" tags={["aethis-mcp"]}>
  * docs: link to docs.aethis.ai/agents/onboarding from Install section
</Update>

<Update label="aethis-cli v0.8.4" description="2026-05-07" tags={["aethis-cli"]}>
  * docs: link to docs.aethis.ai/agents/onboarding from MCP one-liner section
</Update>

<Update label="aethis-sdk-python v0.4.3" description="2026-05-06" tags={["aethis-sdk-python"]}>
  * docs: remove positioning paragraph above Install — reference surface (per aethis.os/positioning/surface-types.md); the tagline is enough
</Update>

<Update label="aethis-sdk-python v0.4.2" description="2026-05-06" tags={["aethis-sdk-python"]}>
  * docs: add private-beta callout for authoring endpoints (decision endpoints remain anonymous)
</Update>

<Update label="aethis-sdk-python v0.4.1" description="2026-05-06" tags={["aethis-sdk-python"]}>
  ### Changed

  * docs: align README with positioning bible — add problem/solution/methodology intro paragraph before Install section.
  * docs: add `aethis-bible:` markers to derived copy blocks (sourced from `public-messaging.md §3/§4`).
  * fix: terminology audit found no deprecated "rule bundle" or `<5ms` instances in README; no replacements needed.
</Update>

<Update label="aethis-sdk-python v0.4.0" description="2026-05-06" tags={["aethis-sdk-python"]}>
  ### Changed

  * `Aethis(api_key=...)` and `AsyncAethis(api_key=...)` now accept `api_key=None` (or no argument) for the developer beta. Evaluation endpoints (`/decide`, `/schema`, `/explain`, `/source`) work anonymously, so the SDK no longer forces a key on instantiation. When `api_key` is omitted, the `x-api-key` header is simply not sent. Authoring endpoints will still return 401 without a key. Existing callers passing `api_key="..."` are unaffected.
  * README quickstart now shows `Aethis()` (no key) as the primary form, targets `aethis/uk-fsm/child-eligibility` (a live public ruleset) instead of the dated `eng_lang:20250912-ec5d7c23`, and prints the audit fields (`inputs_hash`, `decision_id`, `decision_time`, `engine_version`) added in 0.3.2. Configuration table updated: `api_key` is now documented as optional during the developer beta.
  * `examples/oneshot.py` refreshed to match: no key required by default, `AETHIS_BUNDLE_ID` env var renamed to `AETHIS_RULESET_ID` (catching the 0.3.0 `bundle → ruleset` rename it had missed), targets the live UK Free School Meals ruleset, prints the audit fields.

  ### Notes

  * Backwards-compatible: `Aethis(api_key="ak_live_...")` continues to work exactly as before.
  * This pairs with the public-surface positioning that evaluation is free during the developer beta — see `docs.aethis.ai`.
</Update>

<Update label="aethis-sdk-python v0.3.2" description="2026-05-06" tags={["aethis-sdk-python"]}>
  ### Added

  * `DecideResponse.decision_id` — per-call audit identifier returned by the engine.
  * `DecideResponse.inputs_hash` — canonical SHA-256 fingerprint of the input set.
  * `DecideResponse.decision_time` — ISO-8601 timestamp of the decision.
  * `DecideResponse.engine_version` — `aethis-core@<semver>` string identifying the engine that produced the decision.

  ### Fixed

  * `DecideResponse` previously declared `ruleset_id` twice; Pydantic silently overrode the first declaration with the second. Deduplicated.
  * The four audit fields above were already returned by `/api/v1/public/decide` but were silently dropped by Pydantic because the model didn't declare them. Callers can now read them directly off the typed response — no need to reach for the raw JSON. This is the audit-trail fingerprint that the docs and homepage prominently advertise (`inputs_hash`, `decision_id`); shipping an SDK that hid it was a defect.

  ### Notes

  * Backwards-compatible. All four new fields default to `None`, so older engines that don't emit them still parse cleanly.
</Update>

<Update label="aethis-sdk-python v0.3.1" description="2026-05-06" tags={["aethis-sdk-python"]}>
  ### Fixed

  * `aethis_sdk.__version__` now resolves from installed package metadata via `importlib.metadata` instead of a hardcoded constant. Previously reported `"0.1.0"` on every install regardless of the actual package version. Falls back to `"0.0.0+unknown"` only if the package is imported without being installed (editable dev or zip-on-PYTHONPATH).
  * Package description on PyPI: `"…and bundle schemas"` → `"…and ruleset schemas"` to match the v0.3.0 public-surface rename.

  ### Added

  * README PyPI / Python-version / License shields.
</Update>

<Update label="aethis-mcp v0.3.3" description="2026-05-06" tags={["aethis-mcp"]}>
  * docs: restructure README as dev MCP docs — Install / Quick start / Tools / Setup leads, positioning sections (Problem, Accuracy, When to use this, How it works, Example walkthrough) removed; their content belongs in docs.aethis.ai or the benchmarks repo
  * docs: trim narrative paragraphs across Quick start, Conversational eligibility, and Authoring; collapse repeated Tips into terse callouts
  * docs: header tagline rewritten to a single factual line; link bar updated to the new structure
</Update>

<Update label="aethis-mcp v0.3.2" description="2026-05-06" tags={["aethis-mcp"]}>
  * docs: normalise tone to documentation register — replace argumentative Proof section with one-liner accuracy claim, neutralise example framing, trim sales-y bullets in When to use this
  * docs: add private-beta callout for authoring tools (decision tools remain public, no key required)
</Update>

<Update label="aethis-mcp v0.3.1" description="2026-05-06" tags={["aethis-mcp"]}>
  * docs: align README with positioning bible — promote 225-scenario accuracy framing
  * docs: add aethis-bible: markers to derived copy blocks
  * docs: fix latency claim to \<1ms (was \<5ms)
  * fix: replace deprecated "rule bundle" terminology with "ruleset"
</Update>

<Update label="aethis-cli v0.8.3" description="2026-05-06" tags={["aethis-cli"]}>
  * docs: remove Why Aethis section — package README is a reference surface (per aethis.os/positioning/surface-types.md); install / quick start / authentication is the right lead, not a problem statement
</Update>

<Update label="aethis-cli v0.8.2" description="2026-05-06" tags={["aethis-cli"]}>
  * docs: add private-beta callout for authoring tools (decision tools remain public, no key required)
  * docs: clarify in Authentication that aethis login requires an invite during the beta
</Update>

<Update label="aethis-cli v0.8.1" description="2026-05-06" tags={["aethis-cli"]}>
  * docs: align README with positioning bible — add Why Aethis section, solution framing, TDD methodology beat
  * docs: add aethis-bible: markers to derived copy blocks
  * fix: replace deprecated "rule bundle" terminology with "ruleset" in pyproject.toml description
</Update>

<Update label="aethis-sdk-python v0.3.0" description="2026-05-05" tags={["aethis-sdk-python"]}>
  ### Changed (Breaking)

  * Renamed the public *bundle* concept to *ruleset* throughout the SDK to match the `aethis-core 0.10.0` API contract. Every `bundle_id` parameter and JSON key is now `ruleset_id`. URL paths inside the client moved from `/api/v1/public/bundles/...` to `/api/v1/public/rulesets/...`. The `Session` constructor now takes `ruleset_id` and exposes `session.ruleset_id` instead of `session.bundle_id`. Class names: `BundleSummary` → `RulesetSummary`.

  ### Required

  * Engine `aethis-core 0.10.0` or newer. Older engines respond at the legacy `/bundles/*` paths and this client will 404. Pin `aethis-sdk==0.2.0` to keep working against an older engine.
</Update>

<Update label="aethis-mcp v0.3.0" description="2026-05-05" tags={["aethis-mcp"]}>
  * **Breaking**: renamed the public *bundle* concept to *ruleset* throughout the MCP tool set, to match the `aethis-core 0.10.0` API contract. The compiled rule artefact is now called a **ruleset** in every tool name, parameter, and prose description. Specifically:
    * Tools: `aethis_create_bundle` → `aethis_create_ruleset`, `aethis_list_bundles` → `aethis_list_rulesets`, `aethis_archive_bundle` → `aethis_archive_ruleset`
    * Parameters: every `bundle_id` → `ruleset_id`
    * JSON keys returned to the agent: `bundle_id`/`latest_bundle_id`/`bundle_version`/`deprecated_bundles`/`result_bundle_id`/`bundle_refs` → `ruleset_id` etc.
    * URL paths inside the client: `/bundles/...` → `/rulesets/...`
  * This release **requires `aethis-core 0.10.0` or newer.** Older engines respond at the legacy `/bundles/*` paths with `bundle_id` JSON keys; this client expects `/rulesets/*` and will 404. Pin `aethis-mcp@0.2.6` if you need to keep working against an older engine until you can deploy.
  * **MCP tool renames are part of the public LLM-facing contract.** Coding agents that have learnt the old tool names (`aethis_list_bundles` etc.) from training data will get "no such tool" errors and need to retry against the new names. Tool descriptions explicitly call out the new naming so the LLM picks it up on first read.
</Update>

<Update label="aethis-cli v0.8.0" description="2026-05-05" tags={["aethis-cli"]}>
  * **Breaking**: renamed the public *bundle* concept to *ruleset* throughout the CLI to match the `aethis-core 0.10.0` API contract. The compiled rule artefact is now called a **ruleset** everywhere — in command names, in flag names, in JSON keys, and in prose. Specifically:
    * `aethis bundles list/archive` → `aethis rulesets list/archive`
    * `--bundle-id` flag → `--ruleset-id`
    * `client.list_bundles()` / `archive_bundle()` / `get_bundle_schema()` / `explain_bundle()` / `get_bundle_source()` / `set_bundle_visibility()` SDK methods → `*_ruleset`
    * JSON keys `bundle_id` / `latest_bundle_id` / `bundle_version` / `bundle_refs` → `ruleset_id` etc.
    * Default scope strings `bundles:read/explain/write` → `rulesets:*` (validated against the engine's permission registry)
  * This release **requires aethis-core 0.10.0 or newer**. Older engines return `bundles:*` scopes and the CLI will reject them as invalid. Pin to `aethis-cli==0.7.2` if you need to keep working against an older engine until you can deploy.
</Update>

<Update label="aethis-mcp v0.2.6" description="2026-05-03" tags={["aethis-mcp"]}>
  * Docs: replaced two stale `aethis.ai/sign-up` request-access pointers in the README authoring section with `aethis.ai/developer-access`. After the Clerk cutover, `/sign-up` serves the Clerk SignUp form for invitees rather than the Notion request-access form. No code or behaviour changes.
</Update>

<Update label="aethis-cli v0.7.2" description="2026-05-03" tags={["aethis-cli"]}>
  * Docs: replaced the stale `aethis.ai/sign-up` request-access link with `aethis.ai/developer-access` in the README "Author your own rules" section and in the `aethis whoami` hint shown when the active key has no authoring scope. After the Clerk cutover, `/sign-up` serves the Clerk SignUp form for invitees rather than the Notion request-access form, so external "Request access" pointers were broken. No code path changes.
</Update>

<Update label="aethis-mcp v0.2.5" description="2026-05-01" tags={["aethis-mcp"]}>
  * Docs: README Quick start now leads with `aethis mcp install --target all` (via [aethis-cli](https://github.com/Aethis-ai/aethis-cli) v0.5.0+). The manual `claude mcp add` and per-client JSON tabs are demoted to "Manual install" beneath. Setup section gains a **Keys & security** subsection covering `AETHIS_API_KEY` vs `ANTHROPIC_API_KEY` placement (MCP client config, not shell), rotation workflow (`aethis account generate` + `aethis account revoke`), and multi-machine guidance.
  * Discoverability: `package.json` `keywords` extended with `regulation`, `policy`, `eligibility-check`, `deterministic-decision` — matches the highest-intent search terms used by developers in regulated domains. Existing keywords retained.
  * CLAUDE.md updated to note the `aethis mcp install` install path so future contributors don't re-document the manual JSON as primary.

  No code or behaviour changes.
</Update>

<Update label="aethis-cli v0.7.1" description="2026-05-01" tags={["aethis-cli"]}>
  * Docs: README gains a dedicated **Authentication** section explaining the three modes (`aethis login` for explicit setup, lazy auth for inline mid-command sign-in, `--no-prompt` for CI). Authoring quickstart leads with `aethis init` (the v0.7.0 wizard prompts for a name and runs sign-in itself, so `aethis login` as a separate step is no longer needed). Environment-variable table expanded to cover `AETHIS_BASE_URL` and `ANTHROPIC_API_KEY`. Troubleshooting entry for `Auth error` now mentions the lazy-auth prompt and `--no-prompt`. CLAUDE.md updated to document the `aethis mcp install` path, lazy-auth helper, and `--no-prompt` flag for future agents working on the CLI. No behaviour change.
</Update>

<Update label="aethis-cli v0.7.0" description="2026-05-01" tags={["aethis-cli"]}>
  * New: `aethis init` first-run wizard. With no args, prompts for the project name (default = current directory name); a positional `aethis init <name>` keeps working unchanged. If no API key is cached, triggers the same OAuth flow as `aethis login` *before* any filesystem writes — Ctrl-C during browser sign-in no longer leaves a half-scaffolded project on disk. After scaffolding, prints the next-step ladder (`aethis sections discover` → `fields discover` → `generate --poll`) so new users have a clear path forward. New `--no-prompt` flag for scripted use; with that flag, missing required values fail fast and missing auth surfaces a clean `AuthRequired` error instead of opening a browser. 10 new tests covering prompted, non-prompted, no-auth + interactive, no-auth + `--no-prompt`, and name-validation paths. Closes [#15](https://github.com/Aethis-ai/aethis-cli/issues/15).
</Update>

<Update label="aethis-cli v0.6.0" description="2026-05-01" tags={["aethis-cli"]}>
  * New: lazy auth. Authenticated commands (`aethis projects list`, `generate`, `publish`, etc.) now detect missing credentials or 401 responses and offer an inline browser sign-in prompt: `"No API key. Open browser to sign in? [Y/n]"`. On accept, the same OAuth flow as `aethis login` runs, the key is cached, and the original command retries — exactly once, no infinite loops. Non-TTY stdin/stdout (CI, pipes) and the new `--no-prompt` global flag skip the prompt and surface a clean `AuthRequired` error. `--api-key <key>` still bypasses the helper entirely. New helper module `aethis_cli/auth_helpers.py`; the OAuth flow inside `commands/login_cmd.py` was factored into a reusable `run_browser_login()`. 17 new tests in `tests/test_lazy_auth.py`. Closes [#12](https://github.com/Aethis-ai/aethis-cli/issues/12).
</Update>

<Update label="aethis-cli v0.5.0" description="2026-05-01" tags={["aethis-cli"]}>
  * New: `aethis mcp install --target <client>` writes the MCP server entry into your editor's config in one shot. Supports `claude-code` (project-level `.mcp.json`), `cursor` (`~/.cursor/mcp.json`), `claude-desktop` (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, `~/.config/Claude/...` on Linux), `windsurf` (`~/.codeium/windsurf/mcp_config.json`), and `--target all` for everything at once. Idempotent, preserves any other configured MCP servers. `aethis mcp uninstall --target <client>` reverses the install. Closes [#16](https://github.com/Aethis-ai/aethis-cli/issues/16).
</Update>

<Update label="aethis-cli v0.4.4" description="2026-05-01" tags={["aethis-cli"]}>
  * UX: `aethis login --help` now reads "Sign in and store an API key locally. First-time setup — this is all you need." `aethis account generate --help` clarifies it's for *additional* keys (rotation, multi-machine, scoped access). After successful `aethis login`, a tip line points at `aethis status` / `aethis account keys`. README quickstart collapses any "first login then generate" sequence into a single `aethis login` step. No behaviour change. Closes [#13](https://github.com/Aethis-ai/aethis-cli/issues/13).
</Update>

<Update label="aethis-cli v0.4.3" description="2026-05-01" tags={["aethis-cli"]}>
  * Docs: README install section now leads with `uv tool install aethis-cli` (recommended) and `pipx install aethis-cli`, with `pip install` in a venv as the third option. Pairs with [Aethis-ai/docs#12](https://github.com/Aethis-ai/docs/pull/12). Closes [#14](https://github.com/Aethis-ai/aethis-cli/issues/14).
</Update>

<Update label="aethis-mcp v0.2.4" description="2026-04-28" tags={["aethis-mcp"]}>
  First version published to npm since 0.2.2. The `v0.2.3` tag exists in git but predates the publish workflow — it never reached npm. This release rulesets all work since 0.2.2.

  ### Registry

  * **MCP Registry submission ready.** Added `mcpName: io.github.aethis-ai/aethis-mcp` to `package.json` and a top-level `server.json` declaring the npm package, transport, and environment variables. Submit via `mcp-publisher` after `npm publish`.

  ### Breaking Changes

  * **`openai_key` parameter renamed to `anthropic_key`** on `aethis_generate`, `aethis_generate_and_test`, and `aethis_refine`. The old parameter name is still accepted for backwards compatibility but will be removed in a future release.

  ### Improvements

  * **Better error messages on generation failure.** Failed jobs now surface classified error details (invalid key, rate limit, connection failure) instead of "unknown error".
  * Sends both `X-Anthropic-Key` and `X-OpenAI-Key` headers for backwards compatibility with older API versions.
  * **`aethis_explain_failure` clarification.** Tool docs now note that `ruleset_id` must be the concrete ID from a `/decide` envelope; slugs are not yet resolved on this endpoint (tracked in aethis-core#51).

  ### Docs

  * **Proof section updated to cite the Simpson et al. 2026 benchmark paper.** Replaced the pre-paper 11-scenario table (GPT-5.4-mini 82%, GPT-5.3 27%) with paper-backed figures from Table 8b of the published benchmark. Removed the 27% GPT-5.3 claim — the paper identifies that figure as a harness-configuration bug; the corrected value is 63.6%.
  * **Proof section: add §6.10 LegalBench external-validation paragraph.** v3.8 of the paper adds external validation across 9 LegalBench tasks (949 held-out cases). Combined paired-binomial McNemar's: *p* \< 0.001 vs Sonnet 4.6, *p* = 0.003 vs Opus 4.7, *p* \< 0.001 vs GPT-5.4. Linked to the public LegalBench harness at `confidently-wrong-benchmark/legalbench/`.
  * **Proof section: replaced 11-scenario subset table with v3.8 adversarial extension (§6.4.1).** The v3.7 11-scenario exception-chain table no longer differentiates current frontier models from the engine (GPT-5.4 default and low both 11/11, Opus 4.7 11/11). The Proof section now leads with the v3.8 adversarial extension (20 newly-authored scenarios; engine 20/20; Opus 4.7 18/20; GPT-5.4 default 19/20 with 0 reasoning tokens; Sonnet 4.6 19/20) and the shifting-ground argument from paper §6.5 Finding 6.
  * **Use `aethis/construction-all-risks` slug in CAR proof example** for stable URL across ruleset regenerations.
  * **Invite-only beta messaging** replaces "rolling out now" framing throughout README — explicit approval-gated framing aligned with current onboarding.
  * **`docs.aethis.ai` badge** added to README.

  ### Internal

  * Added `.github/workflows/publish.yml` (provenance via OIDC + `NPM_TOKEN`) so future tag pushes auto-publish.
  * Added Claude PR review workflow (dry-run mode).
  * Added internal `CLAUDE.md` for agent onboarding.
</Update>

<Update label="aethis-cli v0.4.2" description="2026-04-28" tags={["aethis-cli"]}>
  Two bug fixes that block the documented quickstart against public bundles.

  ### Bug fixes

  * **`aethis decide -b <slug>` / `explain -b <slug>` / `bundles archive -b <slug>` now accept slugs.** The classifier in `_id_utils.classify_id` previously returned `"unknown"` for slugs (e.g. `aethis/uk-fsm/universal-infant`), and `require_bundle_id` rejected them with `"is not a valid Bundle ID"`. The public API resolves both bundle IDs and slugs on `/decide`, `/schema`, and `/explain`, so the CLI now passes both through. Error message updated to mention slugs and link to `aethis bundles list`.
  * **`aethis fields -b <bundle>` no longer requires an `aethis.yaml`.** It now uses the same `load_client_or_fallback()` helper as `decide`, `explain`, `bundles`, and `projects` — read-only commands work from any directory. Previously this command errored out with `"No aethis.yaml found"` even when called with a concrete bundle reference.
</Update>

<Update label="aethis-sdk-python v0.2.0" description="2026-04-27" tags={["aethis-sdk-python"]}>
  ### Added

  * `DecideResponse.slug` — stable, human-readable handle for the ruleset
    (e.g. `aethis/uk-fsm/child-eligibility`). Set when the resolved ruleset
    was published under a slug; `None` otherwise. Prefer this over
    `ruleset_id` for any reference that should survive ruleset regeneration.
  * `SchemaResponse.slug` — same handle, surfaced from
    `GET /rulesets/{id}/schema`.

  ### Notes

  * Backwards-compatible. Existing code that reads `ruleset_id` keeps
    working unchanged; `slug` is purely additive.
  * Requires the `aethis-core` engine release that surfaces the field in
    `/decide` and `/rulesets/{id}/schema` responses (rolling out 2026-04).
    Older engines will simply leave `slug=None`.
</Update>

<Update label="aethis-cli v0.4.1" description="2026-04-19" tags={["aethis-cli"]}>
  ### `aethis status` output polish

  * Server line now shows just the URL when it's the default (`https://api.aethis.ai`) — the `(default — no override)` suffix was noise in the common case. Overrides (`AETHIS_BASE_URL`, `aethis.yaml`) still show source with a green marker.
  * Identity line now says `✗ API key rejected (run \`aethis login\` to re-authenticate)`when`/me`returns 401/403/404, instead of the raw`✗ 404 from /me (Not Found)\` HTTP message. Other HTTP errors keep a contextual message.
</Update>

<Update label="aethis-cli v0.4.0" description="2026-04-19" tags={["aethis-cli"]}>
  This release ships the rich-status and read-only-from-anywhere work that the 0.2.0 notes already described but which hadn't actually been merged into a published release yet. (The code was sitting in a local branch; the prior 0.2.x/0.3.x wheels still had the minimal status command.)

  ### `aethis status` — context-aware summary

  * `aethis status` with no args now prints CLI version, resolved server URL (with source — env / yaml / default), loaded `aethis.yaml`, bundle id from `.aethis/state.json`, and `whoami` identity (key id, tenant, tier, scopes, `can_author`). Helps answer "what will my next command actually hit?" before running it.
  * `aethis status -p <project_id>` (or from inside a project dir) still shows generation progress, appended after the global summary.

  ### Read-only commands usable from anywhere

  * `aethis explain`, `decide`, `bundles list`, `bundles archive`, `projects list`, `projects show`, `projects archive` no longer require an `aethis.yaml` in the current directory — they fall back to `AETHIS_BASE_URL` (or the default `https://api.aethis.ai`).
  * `aethis explain` / `decide` now reject Project IDs (`proj_*`) passed to `-b/--bundle-id` with a one-line hint pointing at the Bundle column of `aethis projects list`, instead of silently 404'ing.

  ### Internals

  * New `resolve_base_url_with_source()` / `load_client_or_fallback()` helpers in `aethis_cli/config.py` that the above commands share.
  * New `aethis_cli/commands/_id_utils.py` + test coverage for bundle-id validation.
  * New tests for `explain`, `status`, and `_id_utils`.
</Update>

<Update label="aethis-cli v0.3.1" description="2026-04-19" tags={["aethis-cli"]}>
  ### Docs cleanup

  * README and [docs.aethis.ai/interfaces/cli](https://docs.aethis.ai/interfaces/cli) no longer document `AETHIS_BASE_URL` or show `base_url:` in the `aethis.yaml` example — public users always hit `https://api.aethis.ai`, and the documented values were just duplicating the default. The env var still works as an override for devs and CI; it's intentionally undocumented.
  * Dropped the `AETHIS_CLERK_DOMAIN` env var from the README (marked "development only" and confusing for public users). The override still works in code.
</Update>

<Update label="aethis-cli v0.3.0" description="2026-04-19" tags={["aethis-cli"]}>
  ### Trim public CLI to the developer API surface

  The public CLI now only ships commands every developer can use against `https://api.aethis.ai`. Privileged and staff-only commands have been removed and will live in a separate internal plugin package.

  **Breaking changes:**

  * **Removed `aethis source`** — internal-only DSL viewer; moved to the `aethis-cli-internal` plugin.
  * **Removed `aethis account permissions`** — IAM permission registry; internal-only.
  * **Removed the `aethis guidance domain …` group** (and the deprecated `aethis domain guidance …` alias) — domain-level guidance is staff-managed.
  * **Removed the global `--base-url` flag** (plus the per-command `--base-url` on `login`, `account generate`, `account keys`, `account revoke`). The `AETHIS_BASE_URL` env var still overrides the default. The flag had no meaning for the public API target and cluttered `--help`.

  **New: third-party plugin support.**

  * The CLI now discovers plugins via Python entry points under the `aethis_cli.plugins` group. A plugin exposes one callable `register(app: typer.Typer) -> None` and attaches extra commands to the root app. Plugin load failures print a single warning to stderr and never crash the CLI.
  * The staff-facing `aethis-cli-internal` package uses this hook to re-attach `source`, `domain guidance`, `permissions`, and the `--base-url` flag.
</Update>

<Update label="aethis-cli v0.2.1" description="2026-04-19" tags={["aethis-cli"]}>
  ### Consolidated guidance command tree

  * `aethis domain guidance ...` moved under `aethis guidance domain ...` — the `domain` group exists only to host `guidance`, so having two top-level trees for the same concept was confusing. All four subcommands (`add`, `list`, `import`, `export`) behave identically on the new path.
  * The old `aethis domain guidance ...` path still works as a hidden deprecated alias: invocations continue to succeed and emit a one-line deprecation notice to stderr. It is no longer shown in `aethis --help`. Planned removal in a future release.
</Update>

<Update label="aethis-cli v0.2.0" description="2026-04-19" tags={["aethis-cli"]}>
  ### `aethis status` — global CLI context

  * **New behaviour**: `aethis status` (no args) now prints a one-screen summary of the current CLI context: CLI version, resolved server URL (with source — `--base-url` / env / yaml / default), loaded `aethis.yaml` + project, bundle id from `.aethis/state.json`, and whoami identity (key id, tenant, tier, scopes, `can_author`). Answers "what will the next command hit?" — the usual cause of "why is my project missing?" is talking to the wrong server.
  * **Backward compatible**: `aethis status -p <project_id>` (or invoked from a project dir) still shows generation progress, now appended after the global summary.

  ### UX improvements for read-only commands

  * `aethis explain`, `decide`, `bundles list`, `bundles archive`, `projects list`, `projects show`, and `projects archive` no longer require an `aethis.yaml` in the current directory — they fall back to `AETHIS_BASE_URL` (or the default `https://api.aethis.ai`) when invoked from anywhere.
  * `aethis explain` and `decide` now reject Project IDs (`proj_*`) passed to `-b/--bundle-id` with a one-line hint pointing at the `Bundle` column of `aethis projects list`, instead of silently proceeding to a 404.
  * `aethis --base-url <url>` is now a top-level flag, equivalent to setting `AETHIS_BASE_URL` for one invocation. Lets you hit staging or a self-hosted instance without editing `aethis.yaml`.
  * `aethis projects list` prints a short tip after the table showing how to copy a Bundle value into `aethis explain -b …`.
  * Configuration and authentication errors now render as a single red line via the existing `cli()` handler, not a Rich traceback panel. `pretty_exceptions_enable=False` is set on every Typer app.

  ### Better `--help`

  * Top-level `aethis --help` now shows common flows (status, list, explain, decide), authoring flow, and how to target a different server.
  * `explain`, `decide`, `bundles list`, `projects list`, and `status` all have "Examples:" blocks in their per-command help.
</Update>

<Update label="aethis-mcp v0.2.2" description="2026-04-14" tags={["aethis-mcp"]}>
  ### New Tools

  * **`aethis_add_domain_guidance`** — Add cross-section guidance hints at domain level (e.g. `uk_citizenship`). Applies automatically to all projects in the domain during generation.
  * **`aethis_list_domain_guidance`** — List all active domain-level guidance hints.
  * **`aethis_list_guidance`** — List all guidance hints accumulated for a project. Use before adding new guidance to avoid duplicates.
  * **`aethis_explain_failure`** — Diagnose a failing test case. Returns criterion statuses with DSL metadata and a targeted fix hint.

  ### Improvements

  * **`aethis_add_guidance` now accepts `process_type`** (`"rule_generation"` | `"field_extraction"`). Use `field_extraction` for field design principles (solicitor navigation, raw-facts principle). Defaults to `"rule_generation"`.
  * **`aethis_add_domain_guidance` accepts `notes`** — SME commentary or legislation provenance stored on the hint. Never sent to the LLM.
  * Two-level hint retrieval: generation now fetches domain-level hints (cross-section) alongside project-level hints in a single pass.
</Update>

<Update label="aethis-mcp v0.2.1" description="2026-04-09" tags={["aethis-mcp"]}>
  ### New Tools

  * **`aethis_discover_fields`** — Discover input fields from source text. Returns field names, types, and completeness assessment. Call before writing test cases.
  * **`aethis_refine_fields`** — Iterate on field discovery with targeted feedback.

  ### Improvements

  * Added `aethis-author` and `aethis-decide` MCP prompts for compatible clients (Claude Desktop, Cursor, VS Code Copilot).
</Update>

<Update label="aethis-mcp v0.1.0" description="2026-04-05" tags={["aethis-mcp"]}>
  Initial release.

  ### Features

  * **Decision tools**: `aethis_schema`, `aethis_decide`, `aethis_next_question`, `aethis_explain`
  * **Discovery tools**: `aethis_list_projects`
  * **Authoring tools** (TDD workflow): `aethis_create_ruleset`, `aethis_generate_and_test`, `aethis_add_guidance`, `aethis_refine`, `aethis_publish`, `aethis_archive_project`, `aethis_archive_ruleset`
  * HTTPS enforcement for remote hosts
  * Exponential backoff with retry on 429/502/503/504
  * Works with Claude Desktop, Claude Code, Cursor, and Windsurf

  > Note: v0.1.0 used `aethis_create_ruleset` (renamed to `aethis_create_ruleset`) and `aethis_project_status` (replaced by `aethis_list_projects`). These tools were removed in v0.2.x.
</Update>

<Update label="aethis-cli v0.1.0" description="2026-04-05" tags={["aethis-cli"]}>
  Initial release.

  ### Features

  * **Account management**: `aethis account generate` (browser OAuth), `aethis account keys`, `aethis account revoke`
  * **Project authoring**: `aethis init`, `aethis generate --poll`, `aethis test`, `aethis publish`
  * **Decision tools**: `aethis decide`, `aethis fields`, `aethis explain`
  * **Project management**: `aethis projects list`, `aethis bundles list`, `aethis bundles archive`
  * **Security**: HTTPS enforcement, OS keychain storage, PKCE OAuth flow
  * **Example**: Spacecraft Crew Certification Act 2049 with 5 golden test cases
</Update>
