Skip to content

ochakai 0.13.0

Choose a tag to compare

@na0fu3y na0fu3y released this 26 Jul 12:24
Immutable release. Only release title and notes can be modified.
8fa8ec1

The first release since 0.12.1, and the largest so far: 71 commits, four
migrations, ten new design docs. Two changes are breaking — the compile
surface is gone, and exported bundles now speak OKF v0.2 — and the rest
is the write-back loop finally closing: an agent reports a bad answer, the
entry surfaces in a re-verification feed, a human verifies it, and the
provenance says who did what.

Breaking: compile_sql and the semantic-model surface are gone (design doc 0028)

ochakai never executed SQL (0001), but it did assemble it: compile_sql
turned a semantic-model entry into a query string. That put a
type-specific behavior registry in a product whose own position is that
types are a vocabulary, not a registry — and nobody was using it as of
0.12.1. It is removed rather than deprecated.

  • MCP tool compile_sqlremoved.
  • POST /api/v1/compileremoved.
  • CLI ochakai compileremoved.
  • The Semantic Model type keeps working as an ordinary type: it is
    stored, searched, exported and rendered like everything else. Only the
    behavior attached to it is gone.

Callers that used compile must build their own SQL from the entry body,
which is where that knowledge belonged.

Breaking: exports speak OKF v0.2 (design doc 0034)

OKF moved to v0.2 and gave provenance, trust and lifecycle standard
spellings, so ochakai stops inventing its own. Bundles now declare
okf_version: "0.2" and carry the spec's keys:

generated: { by: agent:analyst@example.iam.gserviceaccount.com, at: 2026-07-26T04:12:00Z }
verified:
  - { by: human:tanaka@example.co.jp, at: 2026-07-26T09:30:00Z }
  • timestampgenerated.at; the body's # Citations heading →
    sources. Both are v0.2's own breaking renames (SPEC §13.1) and the
    v0.1 spellings are no longer written.
  • verified_by / verified_at → a one-element verified list. An entry
    with status verified always emits the key, since its presence is
    what carries the trust tier.
  • status maps to the OKF vocabulary: draftdraft, verified
    stable + verified:, deprecated and rejecteddeprecated with
    a status_note (rejected also keeps rejected_by / rejected_at as
    extension keys).
  • Delegation (0027) rides along as a sibling key: generated: { by: …, via: … }, so by stays a single well-formed actor.

Import still reads v0.1 spellings, so old bundles load. As before, it does
not adopt provenance values from a payload — the one thing it reads is
whether a verified key exists, which decides stable → verified vs
draft.

Two envelope fields come with it, on every surface: updated_by (who
produced the content as it now stands — created_by was a lie after
anyone else edited) and stale_after, v0.2's lifecycle date.

Closing the write-back loop (design docs 0025, 0033)

An agent could already report that an entry led it wrong; nothing consumed
the report. Now failed reports have an exit:

  • sort=failed lists entries by failed outcome reports — a
    re-verification feed, alongside the existing sort=verified_at (oldest
    verification first) and sort=usage.
  • POST /api/v1/verify/{id} and ochakai verify <id> record a human's
    verification against the entry as it stands — promoting a draft,
    re-affirming an already-verified entry (which is what takes it out of
    both feeds), or clearing a rejection. Previously only the Web UI could
    do this.
  • MCP cannot overwrite or delete an entry a human has decided on
    not just verified but the whole human-decided set. Agents write
    freely up to the moment a person signs off.

get_context also changed shape: hits is now purely the ranking behind
the pack, not a second copy of the knowledge, which travels once under
entries (0033). The byte budget covers the whole response, and entries
that do not fit come back as outlines inside that same budget. Search
results are unaffected — there the hits are the answer.

purge: a second stage of delete (design doc 0031)

Delete leaves a tombstone so an id can never be silently reused. Purge is
the deliberate second step that frees it: DELETE /api/v1/knowledge/{id} ?purge=true and ochakai purge <id>. Two calls, deliberately — the
first is reversible, the second is not, and purging a live entry is a
409. It is not exposed over MCP or in the Web UI: destroying history
is a human decision, and an irreversible button next to the reversible
one is a trap. The purge itself is recorded in knowledge_purge — which
id, by whom, how much history went with it — the one table a purge does
not touch. It loses to a concurrent revival, and it does not reclaim
attachment bytes; as with detach, nothing collects them afterwards, which
the docs now say plainly instead of implying otherwise.

Optimistic locking with If-Match (design doc 0030)

If-Match on update, opt-in: send the version you read and a concurrent
edit fails with 412 instead of overwriting. Documented in the OpenAPI
document and exposed in the CLI, so the contract is the same on every
surface.

Identity: delegation and IAP (design docs 0027, 0032)

  • A caller acting for an end user can now pass that user through
    (A via B), so provenance records the human, not just the integration.
    The Web UI shows the "via".
  • serve-ui takes the browser user from the IAP assertion and never from
    the browser — client-supplied identity headers are dropped
    unconditionally.
  • MCP resolves the actor from each call's own HTTP headers, on every
    tool, not only on writes.

Search, embeddings, and the read path

  • Lexical search is rewritten to be answerable and indexable: a stored
    search_text column with a working trigram index (migration 0016),
    query-fragment matching with IDF weighting, and scoring done over an id
    projection. Empty queries are a 400 instead of a Postgres 500, and
    ILIKE wildcards are escaped.
  • HNSW index on the pgvector column when the dimension is within the
    index limit; ef_search is sized to the search's own limit.
  • Embeddings take their input budget from the model, truncate on rune
    boundaries, refuse to start when the configured dimension changes under
    an existing corpus, and reembed now runs to completion — covering
    attachment vectors, paging past entries that always fail, and reporting
    what is actually left.
  • Usage recording is off the read path (0029): buffered and flushed
    periodically instead of writing on every search, with flush failures
    logged rather than swallowed.

Export, move, and correctness

Export streams entries from a single snapshot, can leave attachments out,
and no longer claims a determinism it did not have; failures after the
header are always logged. Move rewrites the moved entry's own relative
links to absolute form, folds that rewrite into the move revision, and
locks the referrers it rewrites. create can no longer resurrect a
tombstone. Verification timestamps are stamped at stored precision, so
what you read back is what was written. Rejections answer in the
documented error envelope; boolean query parameters are rejected rather
than read as unset; serve waits for Shutdown to finish before
returning, and the SIGTERM drain leaves room for the final usage flush.

Upgrade

Four migrations run on start (0016–0019): the search_text column and its
index, delegated-provenance columns, the purge audit table, and
updated_by / stale_after with a backfill from revision history. They
are additive; a rebuild of search_text and the backfill both run over
the full table, so expect the first start on a large corpus to take
longer than usual.

Before upgrading, check for callers of compile_sql, POST /api/v1/compile, or ochakai compile — those break. Consumers that parse
exported bundles need to read v0.2 keys (generated, verified,
sources); ochakai still imports v0.1. Clients reading get_context
should take the knowledge from entries and treat hits as a ranking.
Existing entries keep their embeddings; run reembed only if you change
the model or dimension.