You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
This commit was created on GitHub.com and signed with GitHub’s verified signature.
0.6.1 — official GHCR image; client/server version handshake; SQLite + write-path concurrency fixes
Added
Official container image published to GHCR on every release. The release
workflow's new publish-image job builds examples/docker/Dockerfile (the
same image the Trivy PR-scan builds) and pushes a public, multi-arch
(linux/amd64 + linux/arm64) image to ghcr.io/opendikw/dikw-core:X.Y.Z after the PyPI publish (it waits for the
wheel to be installable, then pip installs it). There is intentionally no
floating :latest — downstream pins an exact X.Y.Z so its debug
environment stays reproducible. examples/docker/docker-compose.yml now pulls
this image by default (pinned via the required DIKW_VERSION env var, with the build: block retained as a local-source fallback), giving downstream systems
a stable, ready-to-run dikw-core to develop and debug their HTTP / dikw client integration against. See examples/docker/README.md and docs/deployment-docker.md.
Client/server version handshake.dikw client now probes GET /v1/info
once per invocation and compares the server's engine_version to the client's
own installed dikw-core version. A confirmed mismatch hard-fails the
command (exit 1, a version skew: line naming both versions) so a downstream
system catches silent wire drift immediately — dikw-core is alpha, so a skewed
client/server pair can misbehave in subtle ways. Ambiguous cases (server
unreachable, /v1/info non-200, engine_version missing, or the client run
from an uninstalled source checkout) skip the check and let the real request
surface its own error, so the handshake never raises a false skew. Set DIKW_ALLOW_VERSION_SKEW=1 to downgrade the hard-fail to a one-line stderr
warning for deliberate mixed-version debugging. The probe is layering-clean
(reads its own version via importlib.metadata, never imports the engine).
GitHub Release is now cut automatically on every tag.release.yml gains a github-release job (needs: publish) that creates the GitHub Release for the
pushed tag once the PyPI publish succeeds: the body is the tag's CHANGELOG.md
section (extracted from its ## X.Y.Z heading to the next ## ; falls back to
GitHub's auto-generated notes with a ::warning:: when no section matches), and
the built wheel + sdist ride along as downloadable assets. Previously the
workflow only published to PyPI + GHCR and opened the Dockerfile-bump PR —
Releases were created by hand, and several tags (v0.6.0 and earlier) shipped
without one. A backfilled v0.6.0 GitHub Release was created out-of-band.
Fixed
Concurrency: serialize the SQLite adapter and the synth/lint-apply write
path. Three race conditions could surface under concurrent task execution
on a single base. (1) Each SQLiteStorage instance shares one sqlite3.Connection across the asyncio.to_thread workers a verb fans out
(retrieval already runs its fts/vec/asset legs via asyncio.create_task),
and that connection's Python-level state is not thread-safe — overlapping
workers could trip sqlite3.InterfaceError / phantom rows (the same hazard
already worked around three times in storage/base.py, eval/runner.py, and server/tasks/store_sqlite.py). Every adapter method body now runs under a
per-instance threading.RLock (acquired inside the worker thread), closing
the window for every call site rather than one ad-hoc gather at a time.
(2) synth and lint apply previously took no lock, so they could
interleave with ingest/delete/wisdom write/each other — racing the same
deterministic doc_id rows + on-disk page with no enclosing transaction
(silent anchor loss, embed-version drift, or a healthy page mistakenly
deactivated). Both now acquire the server's existing base write lock
(ServerRuntime.ingest_lock), which already covered ingest/import/wisdom/
delete, so the whole D/K/W write surface is single-writer per base within a
process. (3) The SQLite adapter now sets an explicit busy_timeout=30000
(and opens with connect(timeout=30) so the budget also covers the
connection-time pragmas) — parity with the task store — so a cross-connection
WAL writer blocks-then-succeeds instead of immediately raising database is locked. No on-disk format, schema, Storage Protocol, or CLI change —
these are internal serialization fixes. ingest_lock stays per-process, so
multi-replica Postgres deployments are unchanged (cross-replica doc-level
locking is tracked separately). See docs/server.md § Storage concurrency.
Docs
Install-from-PyPI path for downstream consumers.docs/getting-started.md
§1 now splits installation into Option A (uv pip install 'dikw-core[...]' —
the published wheel, for systems that use dikw-core) and Option B (git clone
uv sync — for contributors), and adds an optional-extras matrix
documenting all three user-facing extras (postgres, cjk, otel): what each
pulls in, when to install it, and how the feature degrades without it. The
README install section gains a matching "Install from PyPI" block. Previously
both entry points led only with the from-source checkout flow, leaving the
pip-install consumer path (and the cjk extra entirely) undocumented.