Skip to content

v0.12.0

Choose a tag to compare

@ohdearquant ohdearquant released this 14 Oct 05:27
· 714 commits to main since this release
334a0c2

khive v0.12.0

Minor release. Twelve changes to public Rust types break source compatibility for code that names them directly; they are listed under Upgrading with what to change. Two behaviour changes reach MCP clients: a stdio host that forwards to the daemon is now strict whenever a daemon socket or PID file exists, and a daemon that is alive but slow now answers daemon_slow with the phase it was in.

The release moves to lattice 0.12.0, adds ordered peer embedding engines with explicit per-engine search and weighted RRF fusion, lets db_diagnostics inspect one backend and survive its read deadline, cuts each pooled SQLite connection's page cache from 64 MiB to 8 MiB, makes a keyword_only or vector_only memory.recall run only the retrieval arm it keeps (with optional per-call stage timings), and accepts the ADR-048 graph lint, ADR-037 and ADR-035 contracts.

Upgrading from 0.11.0

  • Memory's RecallFtsGatherMode now re-exports the canonical storage TextGatherMode instead of declaring a separate enum. Its public path, variant imports, Copy behavior, serialized config, and search options remain supported; code depending on the old nominal type identity must adapt. (#5104)
  • khive-runtime removes RuntimeError::Sqlite; concrete backend failures now use RuntimeError::Storage, retaining typed driver sources and the existing capacity, retry, and writer-settlement wire codes. Callers matching the removed variant should inspect StorageError instead. Typed policy reasons now report Storage, rendered backend messages use the storage envelope, and direct writer-checkout failures carry sql/runtime capability/operation context instead of nulls. (#5075)
  • khive-runtime accepts ordered peer [[engines]] entries with name, unit-default weight, and checked dims. EngineConfig replaces model, default, and fusion_weight; RuntimeConfig adds an authoritative optional peer list and KhiveConfig records an explicit empty list. Legacy TOML is converted with its former default first and canonical provider/index names preserved. Duplicate aliases, storage-key collisions, mixed schemas, invalid dimensions, and weights that are not applied by every retrieval path fail explicitly. Startup checks built-in provider dimensions; custom provider binding remains unavailable pending its startup integration. Direct struct literals and exhaustive ConfigError matches must be updated. Use disable_embedding_models() to clear peers and legacy projections together. Daemon reuse now requires the same ordered engine configuration. (#5102)
  • khive_db::diagnostics::ReaderContentionDiagnostics adds reader_discards; direct struct constructors and exhaustive destructurings must account for it. The db_diagnostics response now exposes this cumulative pool counter beside reader_replacement_open_failures, including successful reader recycling without changing pool behavior. (#5102)
  • khive-retrieval's fuse_search_results, hybrid::fuse_labelled_scored, and DualIndexRouter::merge_results now return Result<Vec<_>, khive_fusion::FuseError>; callers must handle or propagate failures. Invalid weighted-RRF parameters, source counts and score overflow now propagate through memory recall instead of appearing as no results, including empty candidate sets. The checked helper keeps its RetrievalError result type, and existing unchecked Weighted/Custom fallback behavior is preserved. (#5102)
  • khive_storage::NoteFilter adds max_created_at, max_expires_at, include_deleted, expiry_fallback and time_order; exhaustive struct literals must add them or use ..Default::default(). FilterOp adds MissingNullOrSpaceEmptyText and TrueOrTextTrue; exhaustive matches must handle both. Typed expiry fallback and native timestamp ordering preserve complete legacy cleanup predicates before LIMIT. The created_before / expires_before builders use inclusive Unix-microsecond bounds, and include_deleted affects reads only. Existing serialized filters retain live-only, unbounded defaults. AttachmentStore::delete_attachment_if adds a conditional ID/role/substrate/ref delete with an Unsupported default and an atomic SQLite implementation; comm cleanup adoption remains separate. (#5104)
  • KhiveRuntime::list_embedding_models now returns runtime-owned EmbeddingModelRecord values with all twelve persisted registry fields. Rust callers use dim instead of dimensions and the EmbeddingModelStatus enum instead of a status string. Status serde values remain lowercase; kkernel engine list/status retain their seven-field JSON shape. Invalid required fields, unknown statuses and invalid non-NULL optional dimensions/UUIDs are skipped with a warning. Existing optional timestamp decoding still maps non-integers to None; reads never fabricate missing identity or lifecycle history. (#5104)
  • Symmetric-edge runtime updates now use the guarded GraphStore capability. Direct-fallback backend failures carry graph/update_edge operation context, and blocking-task join failures return RuntimeError::Storage instead of Internal; reasons and rendered prefixes on those paths change. Concrete causes and writer-settlement distinctions are retained. Backends without the new transaction method explicitly return Unsupported. (#5105)
  • khive_runtime::AtomicOpPlan adds FinalizeEntity for runtime-owned entity admission. Downstream exhaustive matches must handle the new variant. Its plan has no public constructor; direct ingest uses EntityCandidateContext and try_commit_manifest_entity_candidate. (#5133)
  • khive-fusion adds WeightedRrf { k, weights }, positive-weight validation, and weighted-RRF fusion errors. This is source-breaking for consumers with exhaustive matches on FusionStrategy, FusionStrategyError, or FuseError; update those matches to handle the new rank-based strategy and its validation errors. Weighted RRF checks positive finite weights and source-count alignment, preserves empty source slots, and errors when a fused score exceeds the finite deterministic-score range. (#5031)
  • Breaking Rust API change: khive_types::RerankExecutedPayload::model_id is now optional, and the payload adds reranker, query_id, tiers, ignored_weights and unidentified_candidates. Direct constructors and exhaustive destructurings must account for those fields. Stored events without a discriminator still decode as native reranks; weighted memory reranking does not invent a model identity. (#5034)
  • khive-storage adds StorageError::SqliteWrite and khive-db adds SqliteError::{Write, WriteSettlementUnknown}, carrying native SQLite write evidence (SqliteWriteFailure: stage, primary and extended codes, settlement flag). Exhaustive matches must handle the new variants, SqliteWriteFailure literals must include every field, and EventsResponse::Error literals need the new optional sqlite_write_failure field. On the wire, a native BEGIN refusal now reports code/stage sqlite_begin_busy instead of writer_task_begin_busy, with retryability unchanged; without native evidence the code stays writer_task_begin_busy. A definite native write failure reports its stage as code and stage, with sqlite_write_stage, sqlite_primary_code and sqlite_extended_code beside it. (#5113)
  • A stdio MCP host that forwards to the daemon refuses, instead of silently dispatching locally, whenever a daemon socket or PID file exists. KHIVE_DAEMON_STRICT=0 restores the fallback; the full entry is under Changed (#5304).
  • Moodboard descriptors report inference version 0.12.0, so existing moodboard vectors need reindexing and preference bundles trained under an earlier lattice version need retraining. The pack is opt-in at build and run time (#5183, #5322).

Added

  • Weighted memory reranking recognizes a precomputed graph_proximity feature. RerankFeatures gains the public field and a zero-valued Default; direct struct constructors must supply it. Recall handlers supply zero pending graph gathering, default weights remain empty, and rerank provenance records the recognized feature. (#5210)
  • khive-brain-core exposes evidence-only posteriors with explicit event/read clocks, checked half-lives, decay-before-add and a fixed read-time prior. Regressed writes preserve the entry; invalid or overflowing updates refuse atomically. Existing posterior storage, folds and serving paths remain unchanged. (#5210)
  • Packs can declare note property policies and discover the validated registry boot snapshot. Comm declares its nine existing kind-specific protected properties; writer enforcement is unchanged. (#5210)
  • db_diagnostics accepts backend=<name> to inspect only the opened database serving that backend, and size_composition=false to skip the dbstat walk, which reads every page of each file. Every file-backed report now carries page_accounting (page_size_bytes, page_count, freelist_pages), three pragma reads that are reported either way. (#5312)
  • memory.recall accepts include_timings=true, which returns this call's retrieval stage wall times (embed_us, fts_us, ann_us, fresh_tail_us, hydrate_us, null for a stage that did not run), total_us, retrieval_rounds, and per-round candidate_limit, text_hits, vector_hits, hydrated and eligible counts. An array response becomes {results, timings}; an object response gains timings. No environment variable or restart is needed; KHIVE_RECALL_PROFILE keeps working as before. (#5319)
  • khive-retrieval adds pure query-variant normalization and bounded RRF-60 admission merging. Original-query fallback preserves its bytes, generated variants are validated as a whole, and admitted IDs retain variant and retrieval-source attribution. (#5133)
  • kkernel kg update <remote> [--ref <ref>] resolves a Git ref to a full commit SHA and atomically updates that remote in an existing schema.yaml, reporting both old and new commits while preserving archive-content pins and other YAML values. Rust clients with exhaustive KgCommand matches must handle the new Update variant. (#5148)
  • Runtime packs may expose an optional ContextContributor capability with owned request and slice types and explicit score semantics. Registry discovery preserves pack order; context composition and execution remain separate. (#5148)
  • EmbeddingCosineReranker scores every supplied entity candidate in a named engine, preserves missing vectors with their incoming scores and an explicit flag, and implements the retrieval Reranker trait without a namespace-wide candidate cut. (#5148)
  • khive-graph-diff compares in-memory entity, edge and note NDJSON states with deterministic additions, removals and field changes, preserving missing versus null. (#5159)
  • Add explicit event sink declarations in khive-types. All existing event kinds retain caller_event_store; the closed metadata API requires each new kind to declare its sink. Event routing and storage behavior are unchanged. (#5159)
  • A standalone session raw-value codec writes level-3 zstd frames with BLOB prefix 0x01, borrows legacy TEXT unchanged, and refuses unknown formats or malformed data. Callers select the decoded BLOB size bound; mirror storage paths and dictionaries remain unchanged. (#5160)
  • VectorStore::scan_vectors enumerates persisted vectors in UUID-ordered pages with exact metadata filters and an explicit row limit. SQLite reads stored bytes through reader admission; pages do not form a cross-request snapshot. (#5160)
  • The opt-in charter pack installs the seven recording tables from ADR-193 and provides revision-and-state checked transaction helpers. This schema foundation exposes no verbs, grants no action admission, and does not enforce merges. (#5095)
  • khive_runtime::hooks::refuse_shared_create provides a kind-owned creation refusal hook. Memory and channel-health hooks share its InvalidInput diagnostic, naming the specialized writer while retaining their update and proposal guards. (#5095)
  • KhiveRuntime::resolve_prefix_in resolves UUID prefixes in a named pack-owned table, reporting distinct-ID ambiguity and storage failures. A PrefixScope argument states which rows count: one namespace or all of them, and live rows only or soft-deleted ones too. Rows outside the scope neither match nor make a prefix ambiguous; any other selection policy stays with the caller. (#5102)
  • khive-retrieval::fuse_two_stage combines ordered engine arms before vector/text fusion, checks both strategies even on empty input, preserves the candidate union without a final limit, and accepts an async custom-executor adapter without a runtime dependency. (#5104)
  • Runtime vector_search_in, knn_in and rerank_in bind queries to an explicit engine, checking its dimensions and finite coordinates even for empty requests. Existing methods remain single-engine compatibility delegates to the configured first/default engine. (#5104)
  • khive-fs adds bounded directory-name listing and descriptor-relative tree enumeration with caller filters, explicit depth and entry caps, and optional within-root symlink following. Caps count hidden and non-UTF-8 entries and refuse overflow instead of truncating results. (#5133)
  • kkernel kg import accepts --format bibtex and .bib files, streaming bounded entries into document/paper records with authors properties, @string expansion and crossref edges. Malformed entries report skip counts and optional warnings; fatal source errors refuse the import before the target database opens. (#5133)
  • khive-runtime::BackendHandle provides five required core capabilities and optional per-binding vector, sparse, and text factories. SQLite construction performs no storage operation; first use retains existing readiness and repair, and unavailable retrieval tiers return named errors (#4706). (#5133)
  • brain.auto_feedback(signal="unjudged") records attributed serve telemetry without training or consuming a serve-ledger grade. Later caller-judged auto-feedback with the same scorer/serve pair commits its grade and posterior update exactly once; manual feedback behavior is unchanged. (#5133)

Changed

  • memory.recall with fusion_strategy="keyword_only" no longer embeds the query or searches the vector index, and fusion_strategy="vector_only" no longer runs the full-text search. The saved work was most of the cost of a keyword_only recall that finds no text match. keyword_only no longer applies the vector min_raw_relevance floor; its hits carry raw_score: null and score is the composite; rank order is unchanged. Widening re-runs only the arm the strategy keeps and stops when that arm is exhausted. A keyword_only recall therefore never reports degraded, and a verbose keyword_only request on a multi-model store returns the ordinary shapes instead of the vector_candidates_per_model envelope, since no vector recall ran. (#5319)
  • A keyword_only recall whose query the embedder cannot embed now returns its text hits instead of failing with recall: all embedding engines failed, and a vector_only recall whose full-text table is unreadable now returns its vector hits instead of failing. (#5319)
  • memory.recall leaves an English-only embedding model's vector list out of fusion when the query is CJK-dominant (its CJK characters outnumber its other letters). Today that model is all-minilm-l6-v2, which maps most CJK characters to [UNK], so its candidates for such queries carry no signal. The list gets weight zero under every strategy that fuses per-model vector lists, but only when another model contributed a candidate and the request does not name embedding_model. English and mixed queries fuse as before. The enable_cjk_fts_bypass documentation now states what the flag does: it skips term selection in the FTS gather path and is read only when FTS gather is enabled. (#5318)
  • db_diagnostics no longer fails as a whole when the request's read deadline arrives. The backends already inspected are returned, every remaining backend's error starts with not inspected: request deadline, and the new root field complete is false. Backend inspections stop slightly before the deadline (a tenth of the remaining time, at most two seconds) so the partial report reaches a client whose own deadline started first. A cancelled request still fails. When backend selects a database, the root fields describe that database rather than main. (#5312)
  • khive_db::diagnostics::collect_with_runtime_audit_metrics_for_process_interruptibly and KhiveRuntime::db_diagnostics_for_opened_backend_with_audit_metrics take a trailing collect_size_composition: bool; pass true for the previous behavior. khive_db::diagnostics::DbDiagnostics gains the public fields page_accounting and page_accounting_error; direct struct constructors must supply them. (#5312)
  • A stdio MCP host that forwards to the daemon is now strict by default whenever a daemon socket or PID file exists, or existed when the request arrived: a request that would have fallen back to local dispatch is refused with its reason code (no_socket, config_mismatch and the rest), as under KHIVE_DAEMON_STRICT=1. KHIVE_DAEMON_STRICT=0 (or false, no, off) restores the fallback; with neither file on disk the quiet fallback is unchanged; KHIVE_NO_DAEMON=1 remains the way to run without a daemon. (#5304)
  • A daemon that is alive but does not answer in time now returns reason: "daemon_slow" with phase: "before_dispatch" (nothing sent, not_committed, retry is safe) or phase: "after_write" (unknown; read state before any re-send) and the elapsed wait. A connection lost after the request was sent returns daemon_connection_lost, and an unreadable response parse_failure; all three post-write outcomes stay terminal. bridge.diagnostics reports daemon_slow counts per phase, outside fallback_total. (#5304)
  • Client daemon recovery acts on one rendezvous. With exactly one of KHIVE_SOCKET / KHIVE_PID set and a socket that does not answer, a request is refused with reason: "rendezvous_split" (not_committed) instead of reading the other half's PID file and signalling the daemon it names. A client also never signals a live daemon on a rendezvous a supervisor marker claims: it returns supervised_daemon_unresponsive (not_committed) naming the pid, the marker's job and the marker path. (#5305)
  • The daemon writes <pid file>.exit (running after it claims the PID file, exited with the reason and the pid that sent the shutdown signal), logs the previous record and the effective RLIMIT_NOFILE at WARN on every boot, and logs its shutdown at WARN. (#5305)
  • Each pooled SQLite connection now keeps an 8 MiB page cache instead of 64 MiB. The cache is per connection and on the heap, so a process with many connections paid the old size many times over. KHIVE_SQLITE_CACHE_KIB (512 to 1048576) and KHIVE_SQLITE_MMAP_BYTES (0 to 2^40, 0 disables memory-mapped reads; default 1 GiB) override the two settings; a value outside its range is logged and ignored. (#5288)
  • A stdio MCP host that forwards to the daemon no longer runs the legacy quarantine original repair at boot, whether it serves one store or several. The daemon runs it before it serves; the census read every message row on every backend, so each client start spent seconds pulling the stores through the page cache. The repair itself now stops after its first census when nothing is unowned, instead of reading the roster twice more to change nothing, and reports how many passes it made. (#5288)
  • Upgrade the shared rmcp dependency to 2.2, including its security and transport cancellation fixes. MCP, mounted tools and CLI consumers keep their existing feature configuration; MCP result tests use the renamed ContentBlock API. (#5208)
  • An oversized stdio line now receives Invalid Request (-32600, no id) instead of Parse Error (-32700): rmcp 2.2 ignores syntactically invalid JSON without replying. The line cap, drain and next-request recovery are unchanged. (#5208)
  • khive-retrieval weight updates and batch reads now accept &dyn SqlAccess instead of a raw SQLite connection. Pass the migrated backend's SQL access handle; updates and audit rows commit together through its writer route, and read-only handles refuse updates. Storage failures use PersistError::Storage. Core migrations now install atom_weights and weight_events; the standalone init_weight_schema bootstrap remains available for compatibility. (#5217)
  • Lattice dependencies move from 0.9.0 to 0.12.0 (lattice-embed, lattice-inference, lattice-fann). Moodboard descriptors now report inference version 0.12.0, so their fingerprints and model keys change and existing moodboard vectors need reindexing; preference bundles trained under 0.9.0 fail closed on load and need retraining. BLAKE3 no longer runs with the pure backend: std members enable blake3/std directly. (#5183, #5322)
  • khive-vamana greedy search keeps its candidate list sorted and bounded: each neighbor is placed by binary search and the next candidate to expand is read from a cursor, instead of re-sorting the whole list and scanning it for the nearest unexpanded entry after every expansion. Candidates with equal SQ8 distance now order by node id rather than by exact f32 distance; the exact f32 re-score of the final list still decides the returned top-k. An index built after this change can differ from one built before it where SQ8 distances tie. (#5316)
  • Public brain posterior folds use the shared filter and map combinators, preserving reducer state, standalone section-feedback handling and offered-event counts. (#5133)
  • Pool configuration, WAL ceiling policy and test-store guards move into an internal configuration module while preserving public paths, defaults and refusal behavior. (#5148)
  • Vamana v2 saves use one checkpoint-owned publication boundary: metadata is published and synced through the shared Unix helper before the four staged segments are promoted. Sidecar reads and cleanup share component-name conversion, preserving their refusal policy and native I/O errors (Refs #4498). (#5159)
  • Blob and WAL-pin publication use the shared descriptor-relative rename helper; blob-root verification uses the shared file identity type, preserving held handles and durability barriers. (#5102)
  • Vamana checkpoint staging, auxiliary sidecars and external-ID sidecars share Unix descriptor-relative atomic publication helpers. Checkpoints retain their separate metadata commit and segment promotion boundaries, stale-entry policies and I/O diagnostics. (#5102)
  • Git cursor inspection and annotation repair now share the bounded snapshot query, preserving their separate size limits, refusal messages and exact stored cursor bytes. (#5102)
  • Session mirror enable flags now use the shared boolean parser: on and surrounding whitespace on recognized true values enable the configured source. All four flags still default to disabled; the separately default-enabled backfill flag keeps its existing parser. Session maintenance uses shared typed SQL row accessors while preserving its error messages and rejection of negative counts. (#5104)
  • MCP configuration fingerprinting and verb catalog helpers now live together in the server config module, preserving public entry points, daemon identity values, and catalog output. (#5105)
  • Extract blob garbage-collection ownership and database lock handling into a private module while retaining the existing public API and lock behavior. (#5105)
  • khive-db moves checkpoint escalation and transaction-age state into a dedicated module, preserving the public khive_db::checkpoint paths and state-machine behavior. (#5113)
  • Extract runtime daemon wire frames and framing into a private module while preserving public paths, protocol constants, error normalization, and source-reader coverage. (#5113)
  • Move MCP server state, registration errors, and stdio settings into a dedicated core module, preserving public server entry points and existing function bodies. (#5133)
  • Split the kkernel exec run paths into a private module while preserving the public entry point, test module paths, and tracing targets. (#5133)
  • Entity create, claim, ordinary update, bulk, and direct code ingest now share manifest-aware candidate admission. For an admitted candidate, the entity, runtime stamp, required indexes, attachments and exemption audit commit together. Production manifests remain empty; non-empty fixtures are test-only. Notes and administrative/atomic updates retain their existing gates. (#5133)

Fixed

  • The secret gate treats a label ending in _token as a credential label, so a UUID or opaque value assigned to access_token, refresh_token, session_token or any other *_token label is refused the way one assigned to token is. Pagination and sync cursors (next_token, next_page_token, page_token, pagination_token, continuation_token, resume_token, sync_token and a few others, matched as the whole label) stay allowed, as does the sealed visibility_token receipt in a quoted memory.remember response. The label directly before a repository revision URL or a LaTeX fragment follows the same rule. (#5315)
  • The daemon registers its signal-sender recorder before its shutdown signal streams, so the exit record names the sender of a SIGTERM or SIGINT instead of intermittently writing sender_pid=unknown. Registering the recorder for a signal that already has a handler is logged on khive.boot. (#5314)
  • On a database without sqlite_stat1, an ID-filtered event read (including the events-split store's check of each new audit batch against the legacy store) and the schedule creator-provenance lookup now seek the primary key and idx_schedule_creator_provenance. Before, both walked every event of the namespace in created_at order. (#5313)
  • Volume-lease waiters acquire in FIFO order without changing deadline or re-entry errors. File-backed kkernel test fixtures use private lock directories to avoid unrelated contention (#5087). (#5210)
  • kkernel kg export checks file sync and removes its own temporary after a write, sync or rename failure. On Unix it publishes through shared atomic publication: it refuses an existing temporary instead of replacing it, keeps the original filename bytes, creates the file with mode 0666 under the umask and syncs the parent directory after the rename. Existing shared staging policies keep mode 0644 and their behavior (#5068). (#5210)
  • Named-vector and memory session exact search select equal-distance boundary hits by subject UUID before limiting candidates, keeping their order stable across database reopen. (#5217)
  • kkernel kg init now creates the canonical .khive/config.toml with valid engine settings, preserves existing project configurations, and refuses the obsolete config path before writing scaffolding. Only the exact old generated ignore file is migrated to the canonical filename. (#5133)
  • Identifier splitting restores acronym boundaries in DBUsers and APIUsage while keeping terminal plural suffixes in APIs, IDs, URLs, and APIsFoo. URLsafe now splits as ur, lsafe; XMLIsEmpty follows the dictionary-free rule as xmlis, empty (#5103). (#5133)
  • comm.read preserves typed storage errors from its initial message lookup, including admission-timeout code, stage, and retry guidance. Bulk read and mark-read validation retain the same error source without marking messages after a failed lookup. (#4958) (#5159)
  • GTD status and priority refusals now mask credential-shaped values with the existing secret masker. Plain invalid values, accepted-value hints, and error kinds remain unchanged. (#5081)
  • kg_name and IdentifierTokenizer keep plural acronyms such as APIs, IDs and URLs whole (a terminal lowercase s after an acronym), while still splitting acronym-to-word boundaries such as XMLParser, HTTPServer and XMLToJSON. (#5081)
  • Knowledge atom list, count, cursor, coverage, and search now share import/delete's exact decoded domain-tag rule, preserving legacy malformed-tag handling. Near-marker atom tags remain visible, and an additive migration rebuilds the cursor index without rewriting rows. (#5102)
  • git.push accepts the optional boolean force validation field and refuses true with force_denied before transport. False and omission retain ordinary push behavior; null and non-boolean values return invalid_params. (#5102)
  • Pack schema registration now checks table ownership in every statement of a schema entry, refusing collisions before applying any DDL. Semicolons inside quoted names, strings, and comments remain part of those tokens. (#5102)
  • Pack-version regressions compare each linked pack with its factory's declared version and cover independent factory versions through the production JSON and human list formatter (#5086). (#5113)
  • Finding create/update and findings-file ingestion mask credential-shaped rejected enum values while retaining ordinary values, error kinds, and accepted-value lists (Refs #4585). (#5148)

Documentation

  • Accept the ADR-048 graph lint contract for #4813: thirteen built-in rules, typed reports and statistics, and guarded fix receipts. The verb remains unimplemented. (#5102)
  • Accept the kg.proposal_cleanup operator contract: hide old applied or rejected proposals using an archive marker while retaining their identity and event history. Retention follows last projection activity. The handler remains unimplemented. (#5102)
  • Accept ADR-037 configuration-bound namespace and project-root rules for remote cached-entity reads, preserving origin and refusing remote mutations. The read remains unimplemented. (#5104)
  • Accept ADR-035 embedding pipeline configuration, legacy-key compatibility and import strictness rules, with an ADR-001 companion distinguishing explicit import from VCS snapshot degradation; edge relations remain closed. The configuration remains unimplemented. (#5105)

Full list of changes: v0.11.0...v0.12.0