data-olympus
Memory serverOfficial Knaisoma project
Created by Knaisoma
Published in knaisoma/data-olympus. Publisher page
30 GitHub stars
What it is
Governance-grade, OKF-compatible knowledge-base format with a single-writer MCP server and CLI. Pre-release.
Summary from the brain’s metadata
Governance-grade, OKF-compatible knowledge-base format with a single-writer MCP server and CLI. Pre-release. New here? Start with WHY.md. It is the story behind the project: the problem we kept hitting with coding agents, what data-olympus does differently, how it relates to Google's Open Knowledge Format, and where our benchmarks say it is strong and where it is not. The rest of this README is the technical reference.
How it works
Memory model: Markdown vault
AutoGineer overview, from the brain’s own files
Memory model: Markdown vault. Folders: .github (agent config, 20 files); bin (scripts, 7 files); docs (docs, 47 files); scripts (scripts, 29 files); src (source code, 81 files); tests (tests, 286 files). The agent reads .rules/versioning.md, example-bundle.
| Folder | Role | Files |
|---|---|---|
| .github | agent-config | 20 |
| bin | scripts | 7 |
| docs | docs | 47 |
| scripts | scripts | 29 |
| src | source-code | 81 |
| tests | tests | 286 |
| .rules | folder | 5 |
| benchmarks | folder | 328 |
| deploy | folder | 19 |
| example-bundle | folder | 27 |
| release | folder | 2 |
The agent reads
- .rules/versioning.md evidence: AGENTS.md
- example-bundle evidence: README.md
Requirements
Runtimes: Python >=3.13
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Google Antigravity, Kiro, OpenCode, Amp, Roo Code, Cline, JetBrains Junie, Devin (ex-Windsurf)
Set it up
- Not scanned by AutoGineerIts dependencies are installed by pip or uv when you run it and were not scanned by AutoGineer (5 declared). https://pypi.org/project/data-olympus/
- Not scanned by AutoGineerAutoGineer could not scan everything it runs. https://pypi.org/project/data-olympus/
Created by Knaisoma. Published in knaisoma/data-olympus. Licence: Apache License 2.0.
Usage
From the brain’s own files
- Requires Python 3.13+ and uv.
- Run the stable CLI directly from PyPI:
- Install it persistently when you are ready to create a bundle and run the server:
- An announced candidate remains opt in through its exact PyPI version.
- Replace X.Y.ZrcN with the candidate named on the releases page, if any:
- See release channels for what each channel means, how to verify a candidate before adopting it, and how to roll back.
- See docs/quickstart.md for bundle initialization, server startup, readiness, agent registration, and the contributor source installation.
- See docs/adoption.md for the full bundle authoring guide.
Documentation by Knaisoma
data-olympus
Image: knaisoma/data-olympus MCP server Image: MCP Marketplace
<!-- mcp-name: io.github.knaisoma/data-olympus -->
New here? Start with WHY.md. It is the story behind the project: the problem we kept hitting with coding agents, what data-olympus does differently, how it relates to Google's Open Knowledge Format, and where our benchmarks say it is strong and where it is not. The rest of this README is the technical reference.
data-olympus is a governance-grade knowledge-base format and server for agent workforces. It is readable by Open Knowledge Format (OKF) v0.2 consumers: it inherits OKF's directory structure, frontmatter conventions, reserved filenames, and link model, then layers governance extensions on top (stable id, controlled type/status/tier fields, supersedes chains) plus a single-writer MCP server and a CLI. CI proves two concrete directions against official Google OKF v0.2 commit ad30107c31c06aec8a7d5636e0d1058118604e6f: its reference visualization consumer reads every concept in example-bundle, and data-olympus imports, lints, indexes, searches, and retrieves the pinned official Bitcoin sample. This is fixture-scoped interoperability evidence, not a blanket guarantee for every OKF bundle or future upstream revision. The result is a git-native, version-controlled document graph of engineering standards, architectural decisions, and project knowledge that agents and humans can read, search, and extend without any proprietary service.
It governs decisions, not code. When an agent is about to make a choice (a library, a pattern, a migration), data-olympus surfaces the established standard or decision that should govern that choice. It is deliberately not a code-search, reference-finding, or "where is X used" tool: LSP, grep, and Sourcegraph already do that well. The retrieval task it targets is coding-intent to governing-rule, and it helps where current model interaction during vibe-coding is weakest: keeping the model aligned to patterns the team has already established as correct.
Status: pre-1.0 beta. Stable releases are distributed through PyPI and GHCR.
Why
Portable, no lock-in. The entire KB is a directory of markdown files in git. No database, no proprietary schema, no vendor.
Git-native diffs and review. Every change is a commit. Proposed edits go through a pending queue before commit; history is plain git log.
Agent and human readable. Plain markdown with YAML frontmatter. No SDK required to read or author a document.
Governed multi-agent writes. The single-writer MCP pipeline (advisory locks, per-session worktrees, durable push queue) prevents concurrent write races without requiring distributed locking infrastructure.
Queryable by status, tier, and type. Filter by
status: accepted,tier: T1, ortype: decisionwithout post-processing. Thesupersedeschain makes it possible to trace decision history across the graph.Tested with official OKF tooling. CI pins an exact Google OKF revision and proves both consumption directions over committed fixtures. The pin, fixture checksum, and Apache 2.0 license provenance live in
tests/okf/reference.json.
Quickstart
Requires Python 3.13+ and uv. Run the stable CLI
directly from PyPI:
uvx --from data-olympus data-olympus --helpInstall it persistently when you are ready to create a bundle and run the server:
uv tool install data-olympus
data-olympus init my-kb
data-olympus-mcp --helpAn announced candidate remains opt in through its exact PyPI version. Replace
X.Y.ZrcN with the candidate named on the
releases page, if any:
uvx --from 'data-olympus==X.Y.ZrcN' data-olympus --helpSee release channels for what each channel means, how to verify a candidate before adopting it, and how to roll back.
See docs/quickstart.md for bundle initialization, server startup, readiness,
agent registration, and the contributor source installation.
See docs/adoption.md for the full bundle authoring guide.
Documentation
SPEC.md: format specification (bundle layout, frontmatter schema, serving contracts).docs/quickstart.md: verified local-run procedure.docs/adoption.md: bring-your-own-KB guide (author, lint, index, serve, wire an agent).docs/serving.md: single-replica serving model, read-only replicas, git pull loop, health/readiness/liveness split, proxy headers, audit-log rotation.docs/operations.md: production runbook: backup, upgrade, recovery playbooks (degraded/fetch-failed, history rewrite, frozen/demoted push entries, orphaned locks), the health/alerting model, and release channels (stable and candidate, verification, rollback).docs/comparison.md: how data-olympus relates to OKF, enterprise catalogs, markdown KB tools, agent-context conventions, RAG, and ADR tooling.docs/okf-profile.md: field-by-field OKF profile: which governance extensions are stable, which are runtime-only serving fields, and which are experimental candidates.docs/glama.md: Glama registry claim, release, and score-maintenance notes.docs/mcp-registry.md: official MCP Registry notes, whatserver.jsondeclares, and the checklist for publishing.docs/enforcement.md: turning the KB into a mandatory consultation gate (hooks,kb enforce).benchmarks/README.md: retrieval benchmark methodology and how to reproduce the numbers indocs/comparison.md.SECURITY.md: supported versions and how to report a vulnerability.
License
CHANGELOG.md
Changelog
All notable changes to this project will be documented in this file.
The format follows Keep a Changelog. This project uses Semantic Versioning.
<!-- CONVENTION: The topmost section should always be an [Unreleased] block for changes merged to main but not yet tagged. When a release is cut, rename that block to the version and date, then open a new empty [Unreleased] block above it. Never hand-edit the release date on the [Unreleased] heading. -->
Unreleased
Removed
The old release path is retired now that the new-model pipeline has shipped 0.11.1 and 0.11.2. Removed workflows:
rc-publish.yml,tag-release.yml,set-channel.yml,release-image.yml,release-image-reusable.yml,publish-pypi.ymlandpublish-pypi-reusable.yml. Removed old-path scripts:rc_tag.py,release_upload.pyandworkflow_validation.py. Candidates are built byrc-build.yml, published byrc-publish-stage.ymland promoted bypromote-release.yml; hotfixes start withhotfix-cut.yml. The pull request packaging dry run moves unchanged topackage-dry-run.yml.hotfix-cut.ymlandrecut-release-branch.ymlno longer list old-path runs and no longer holdactions: read. Thercimage channel is no longer moved by any workflow whileSDLC_RC_CHANNELis unset.
Security
Dependencies: the 2026-W44 Pillow remediation (issue #426, items 1 and 3) updates the locked
pillowfrom 12.3.0 to 13.0.0. Pillow 13 needs fastembed 0.9.1, the first release that allows it, so theembeddingsextra and the dev extra now requirefastembed>=0.9.1,<0.10(locked 0.8.1 to 0.9.1). data-olympus calls onlyTextEmbedding(model_name=...).embed(...), which fastembed 0.9 keeps. Upgrade note for theembeddingsextra: fastembed 0.9 downloads built-in models from Hugging Face only, without the Google Cloud Storage fallback; a model cached by an older fastembed still loads, with aFutureWarning. The lock is regenerated with the pinned uv 0.12.23, which writes lock revision 5. The container image installs neither extra, so its contents are unchanged.Container image: git refuses the
http,https,ftpandftpstransports. The image's system gitconfig setsprotocol.<name>.allow=neverfor each, so git stops with "transport 'https' not allowed" before it starts a libcurl remote helper. SSH and local remotes are unchanged; the server's KB clone, fetch and push run over SSH. Breaking for image users whoseKB_REMOTE_URLorKB_GIT_REMOTE_URLis an HTTPS URL: switch to an SSH remote. A server installed from PyPI keeps the host's git configuration. Thelibcurl3t64-gnutlspackage stays installed as a git dependency, so its scanner findings remain. A new CI job builds the image and runstests/image/git_protocol_policy.shinside it.Container image: uv is pinned by content. The image copies the static
uvanduvx0.12.23 binaries from astral's officialghcr.io/astral-sh/uv:0.12.23image by its multi-arch index digest (sha256:61d393e4..., which carries astral's signed build provenance fromastral-sh/uv'spublish-docker-image.yml) into/usr/local/bin, instead ofpip install uv==0.12.23, which checked the version but not the bytes. The uv version is unchanged; the binary is now the musl build, and uv is no longer a Python package in the image's system site-packages.Container image: rebuild on the current
python:3.13-slimindex digest (sha256:70729b46..., Python 3.13.16 on Debian trixie with current security updates), raise the pinned uv from0.5.4to0.12.23, and stop installingcurl, which nothing in the image uses (probes arehttpGet/tcpSocket; thebin/kbclient scripts that call curl run on operator machines). On the release candidate assessment this takes the blocking vulnerability-gate matches from 358 to 106 per platform; the rest are findings without a fix or with a fix only on Python 3.15. git still pulls inlibcurl3t64-gnutlsfor its http helpers. Anyone who rancurlinside the container by hand needs another tool, such aspython -cwithurllib.request.
Changed
The old-path
set-channel.ymlandtag-release.ymlworkflows now take the shareddata-olympus-promotionconcurrency group (unconditional,cancel-in-progress: false), so they cannot run at the same time aspromote-release.yml,rc-publish-stage.yml,recut-release-branch.ymlorhotfix-cut.yml.tag-release.ymlenters the lock only when dispatched frommainand drops its per-candidate group, so two different candidates now serialise. Recorded as an R9 deviation (operator follow-up decision of 2026-10-10). GitHub keeps one pending run per group and a newer pending run cancels an older pending one: dispatch again if that happens.
Added
The vulnerability gate judges fix availability by release line (operator ruling, SDLC section 13.2). A fix counts when it is on the installed line or on a newer line eligible for adoption.
release/adoption-lines.jsonlists newer lines not yet eligible (Python 3.15: general availability 2026-10-09, eligible from 2026-12-08); a finding whose fixes are only on such a line follows the no-fix rules until that date and is listed with its fix versions andeligible_fromin the evidence and job output, then blocks as fixable. Unlisted newer lines count (fail closed). The release record names the file's Git blob at S. Seedocs/releases/vulnerability-gate.md.Vulnerability gate for promotion (SDLC section 12, rulings of 2026-10-18). A new
vulnerability-gatejob inpromote-release.yml(read-only, no secrets) scans the candidate image by digest on both platforms with grype0.119.0(download checked against its published SHA-256) and a fresh database, records the scanner version and database build time as evidence, and blocks promotion on any finding with a fix available, on Critical, High or Unknown severity findings without a fix unless a scoped, approved, unexpired exception in the newrelease/exceptions.jsoncovers them, and on expired exceptions. Medium, Low and Negligible findings without a fix are recorded. Malformed scanner output, a report naming another database, or a database built more than five days before the run refuses the gate.scripts/vulnerability_gate.py validatechecks the exception file. Thesecurity_alerts.pygate is unchanged. Seedocs/releases/vulnerability-gate.md.Per-artifact SBOMs for release candidates (SDLC section 9).
rc-build.ymlgenerates one SPDX 2.3 document per wheel, sdist and image platform with syft1.52.0(download checked against its published SHA-256); stage two binds each document to the artifact it verified, stores it as a candidate release asset and signs it withactions/attest(predicatehttps://spdx.dev/Document/v2.3) on the wheel or sdist hash or the platform manifest digest;promote-release.ymlrefuses a candidate unless every SBOM attestation verifies with the stage-two signer and carries the stored document.release-record.jsongains an optionalsbomslist (schema version unchanged). Seedocs/releases/sbom.md.Add
docs/releases/repository-settings.md, a checklist of the GitHub repository and organization settings the release pipeline requires (immutable releases, variables, environments, Trusted Publishers, rulesets, code scanning, Dependabot, the GitHub App and the Actions policy), each with a read-onlygh apiread-back.This is the first release cut under the STD-U-821 branch model (
release/new, content-derived versions, reviewed promotion). Candidates are built unprivileged and published by a separate trusted stage; releases are drafted, filled with assets and published last, which GitHub immutable releases require. Candidate0.11.1-rc.2of the first live run is burned.Add a disabled-by-default trusted RC publication stage (
rc-publish-stage.yml) that runs onlymainscripts, recomputes the candidate version fromH's history as data, verifies package hashes and the OCI digest, splits reservation, PyPI OIDC upload and image push into least-privilege jobs, refuses duplicate identities, reuses same-Hobjects on retry, attests the published image digest and Python files for promotion to verify, and records branch-based staging selection with hotfix priority. Credentials may only be environment secrets onmain-only environments. Only admitted runs take the shared promotion lock, signing runs in a separate job that never parses the candidate archives, and thercchannel moves only onceSDLC_RC_CHANNEL=enabledis set after the first new-model release.Add disabled-by-default promotion of reviewed release squashes, with exact parent, tree, version and release-note proofs. Compare stable Python files with both candidate payloads and reuse the RC image digest with provenance mapping the reviewed head, squash and stable tag.
Build normal and hotfix release candidates with read-only permissions, injected Python versions, an OCI archive, and provenance. Cut builds and dry runs build and verify artifacts without allowing promotion.
Wire the ratified STD-U-821 adoption record into candidate builds, the trusted publication stage and promotion. Builds of an adoption cut take the pinned ratification and vendored amendment from
main's Git blobs rather than the candidate tree, the publication stage uses its ownmaincheckout, promotion reads them from the candidate's recordedmain, and only dispatched dry runs evaluate the record unratified. The engine now refuses a ratification passed without the standard file, and the build parser refuses any binding of the pinned names other than one top-level literal. The cut build stays unpromotable and promotion still refuses while the record exists at the reviewed head. Add the adoption cut runbook (docs/releases/adoption-cut-runbook.md).Plan verified release branch recuts and stable hotfix cuts, preserve pending commits on a backup branch, and refuse stale or unreconciled state, including stray stable tags and active old-path release runs. The new workflows share the promotion lock, apply only from a main-restricted environment with a per-run GitHub App token, and remain disabled until W9 bot activation. See branch recut and hotfix cuts.
Changed
Pin the build backend to
hatchling==1.32.4in[build-system] requires, the exact version recorded in the published 0.11.1 wheel (Generator: hatchling 1.32.4), so release artifacts are rebuilt reproducibly. A test fails if the requirement floats again.Record that the
pypiandpypi-rcenvironments have no required reviewers by operator decision of 2026-10-09, with the consequence for promotion dispatches and the read-back commands.Record the lessons of the first adoption cut in the adoption cut runbook, including the main-first ordering, the burned candidates and the minimum commit count on
release/new.scripts/sdlc_recut.py --applynow replacesrelease/newby deleting it and creating it again at the new base, instead of one atomic--force-with-leaseupdate. The protected-branch rulesets require pull requests for updates and no App holds a bypass, while creation and deletion stay permitted. The old head is backed up to its preservation ref first, each single-ref push is guarded by remote reads before and after it, each deletion is a compare-and-swap push leased on the planned old head, creations carry no lease, no push uses a plain force, a+refspec or--atomic, and a verifiedhotfix/newis deleted only after the recreation. If recreation fails after the deletion, one recovery push restores the old head, also when the remote cannot be read, and the error names the old head, the backup ref and the exact recovery command.docs/releases/branch-recut.mddocuments the leased deletion, the explicit decision any plain-delete fallback would need, and the controller's ruleset toggle.
Security
Promotion now verifies the stage-two GitHub artifact attestations of the candidate wheel, sdist and image digest (
gh attestation verifywith therc-publish-stage.ymlsigner workflow andrefs/heads/mainsource ref) in theprovejob, before any publication. The exact-source check gate moves intorelease_record.py checks: the reviewed head needstest, and the squash, whose tree the proof shows equal to the head's, needstest,CodeQLand every configured analysis, sincerelease/newheads are not guaranteed CodeQL while the squash onmainis. Only a completed run with conclusionsuccesscounts.Validate manual publication identities and reject collisions, bind Python builds to validated commit SHAs, and move image channels by verified digest. Remove tag-triggered rebuild publication and OIDC grants from PR packaging builds.
Fixed
The release record
notescommand requires--outputand no longer prints the rendered notes to standard output, which removes the log sink that code scanning flagged (a false positive carried through the ratification inputs).Release an onboarding bootstrap in-flight marker only for the claim that wrote it.
releaseremoved the marker unconditionally, so a bootstrap whose claim outlived its TTL, and whose slot another bootstrap then reclaimed, deleted the new holder's live marker when it finally failed and released, admitting a third bootstrap into the convergence window. Each claim now stores a random token in its marker and returns it, andreleaseremoves the marker only when the token matches, reading, comparing and unlinking under the same per-key lock as the claim. A mismatched, missing or repeated release returnsFalseand does not raise. A marker written by an earlier version has no token and is never released; it holds the slot until its recorded expiry and is then reclaimed, the same bound a crashed claimer has.Make the onboarding bootstrap in-flight claim single-winner. The claim took a lock-free
O_CREAT|O_EXCLfast path and locked only the reclaim of a stale marker, and the two paths did not exclude each other. A marker created but not yet written read as expired and was reclaimed under its writer, and a reclaimer's unlink let a fast-path contender recreate the marker in the gap. Concurrent claims could therefore both win, reopening the double-bootstrap race the guard exists to close, or fail withFileExistsError(about 1% and 10 to 20% of rounds at 16 threads). This is also whytest_concurrent_reclaim_grants_exactly_one_winnerfailed intermittently in CI. Claim and release now run under a per-keyflock, which the kernel drops if the holder dies, so a crash no longer leaves a.reclaimlock file that blocks reclaim forever. The marker is written to a temporary file and renamed into place.Let the STD-U-821 adoption record name the highest stable tag as its base, as authorized by STD-U-821 amendment 1.5 (ratified 2026-10-10, company-knowledge #236, vendored at
docs/releases/std-u-821-amendment-1.5.md). The version engine accepted the record only with base0.11.0, so oncev0.11.1existed andmainhad advanced past it (#358 to #365) the next cycle could be cut neither atmain(missing_tags_in_released_product) nor at the tag (recut_required). The engine now takes the base from the record: a strictX.Y.ZwhosevX.Y.Zexists, is the highest stable tag by SemVer precedence, is an ancestor of the cut and is a strict ancestor of the anchor. It refuses the record when anyv-digit tag in the repository is not a strict stable or prerelease version, and it requires both ratification lines (amendment 1.3 and amendment 1.5, pinned asRATIFIEDandEXTENSION_RATIFIEDinscripts/adoption_ratification.pyand read frommainby every stage); without either it refuses withadoption_unratified. Every other record rule is unchanged. The record is single use per record, only while its base is the highest stable tag. The runbook gains "Reusing the record route after a release", with a check thatsrc/andpyproject.tomlare unchanged between the tag and the anchor. Known out-of-scope commits onmainafterv0.11.1: #363 and #365 (onboarding product fixes, outside amendment 1.5 scope: product changes must userelease/new), and #362 (build backend pin, borderline: build configuration). The next adoption issue records the handling decided by the operator or lead at cut time: (a) revert them onmainand re-land throughrelease/new, or (b) a recorded exception in the issue. Nothing is blocked now because there is no release to cut.Continue a
promote-release.ymlpromotion after its stable tag exists. The engine counted the newvX.Y.ZonSas a released version, so the recheck after tagging recomputed the release against a stable tag above its own base, and every promotion of an adoption cut (as well as hotfixes and first releases) failed its post-tag recheck withbad_adoption. The first live promotion stopped there withv0.11.1pushed and nothing after it.compute_versiontakesignore_tags(exactvX.Y.Znames, empty by default). The resume proofs pass only the release's own tag, and only after verifying that it is annotated, namesS, has the App bot as tagger and carries the generated notes byte for byte. Theresume-pypiphase accepts that tag,proveresolves the App identity before its proof, andresume-staterequires GitHub's ref to name the verified tag object. It also accepts the states the later jobs leave behind (GHCRvX.Y.Zabsent or on the candidate digest, a channel on the candidate only aftervX.Y.Z), and it checks a release visible to its token before any channel moves: generated notes, not a prerelease, only this run's assets, and all four on a published release.create-tagstays idempotent. Any other tag fails closed. The docs no longer claim a ruleset restrictsv*tag creation; the control is content equality with the generated tag.Verify the stable PyPI upload of
promote-release.ymlwithout the attestation files. The PyPI publish action writes a<file>.publish.attestationnext to each uploaded file, and the inline verifier compared every file in the directory with PyPI's two, so the first live promotion uploaded0.11.1to PyPI and then failed before the tag, the image channels and the GitHub release.release_record.py verify-pypinow compares exactly the wheel and the sdist named by the stable provenance with PyPI, requires both attestations when the upload ran (and their absence when it did not) and refuses any other file. A newresume_pypidispatch input completes such a promotion: it provesSis still onmain(which may have advanced), that PyPI already holds exactly the stable wheel and sdist, and that the stable tag, the GHCR version tag, a published GitHub release visible to the job's token and thestable/latestmove to the candidate are all absent, then skips only the upload while keeping thepypiapproval. A draft release is probably invisible to that read-only token; thereleasejob refuses a foreign draft.Make the promotion check gate (
release_record.py checks) ask each commit for the evidence GitHub produces there. The aggregateCodeQLcheck run exists only on pull request heads, so it was never present on the squashSpushed tomainand the gate could never pass.H, the release pull request head, now requirestestand aggregateCodeQLfrom the GitHub Advanced Security app, the only observed producer of that check run (github-code-scanningis kept as the alternate slug; a run namedCodeQLfrom GitHub Actions is refused);Srequirestestand every analysis inSDLC_REQUIRED_CODEQL_CHECKS. The proof that the trees ofSandHare equal ties the two results to the same code. All other rules stay fail closed.Accept the subject
release: X.Y.Z(STD-U-821) in the PR title lint, only for a same-repository pull request fromrelease/neworhotfix/newintomain; every other base or head still rejects thereleasetype, and malformed versions stay rejected. For that subject the squash-impact comparison is skipped, since the engine computes the version from the commits and promotion proves subject and body byte for byte. The workflow passes the branch names and repositories as environment variables. The workflow runs the base-ref copy of the linter, so this change applies to pull requests only after it lands on the base branch they target.Tolerate GitHub release listing lag after creating a draft and after uploading assets, in stage 2
reserveand in the stable release step: the read is repeated with bounded backoff (about 60 seconds) and then fails closed as before.Publish candidate and stable GitHub releases draft first, for immutable releases. Stage 2
reservenow creates a draft prerelease, uploads and reads back every asset, and a new lastfinalizejob publishes the draft only after PyPI, the image and the attestations are complete. Drafts are found by listing releases and matching the exact tag.promote-release.ymlcreates the stable release as a draft andrelease_record.py releasepublishes it after verifying every asset. A published release with a missing or different asset is refused as burned;0.11.1-rc.2is the known burned candidate. The candidate tag message now binds every asset's SHA-256 so the read-only jobs check bytes,reserverefuses a created release that is not a draft, andattestandfinalizestill run whenpypiis skipped.Show a sanitized, single-line, redacted stderr excerpt of at most 300 characters when a stage-two
ghorskopeocommand fails, never stdout or arguments.Accept the documented
0.0.0+unreleasedplaceholder package version only where the engine-managed cycle puts it.version-free-guardskips it with an explicit message on pull requests intorelease/newandhotfix/new, and intomainonly when the pull request head tree equalsorigin/release/newororigin/hotfix/new(the reviewed release or hotfix squash) ormainalready declares it unchanged. It fails closed on the placeholder anywhere else, on any other nonX.Y.Zversion and on an unexpected decision. Theserver.jsontests waive version equality on those branches while still requiring a concrete stable entry. The rule lives inscripts/placeholder_version.pyand is described indocs/releases/placeholder-version.md.Accept the ratified Amendment 1.3 heading for adoption verification while retaining the proposed heading form. Include the ratified amendment and pinned verification arguments so CI can verify it without private KB access.
Require an adoption cut to add only its record, block promotion until that record is absent from the candidate tree, and validate dated ratification references against UTC. The CLI requires a standard file with a matching ratification line in the Proposed amendment 1.3 section.
Preserve the gate server's uncovered signals and suggested consult intent in pre-tool hook denials. Replace controls and characters outside printable ASCII and Unicode letters with spaces for every dialect, then retain workspace/session tokens within a 1200-character cap on the whole message. Empty reasons continue to use the generic consultation guidance.
[0.11.0] - 2026-10-05
Added
derived_from: name the dependents of a retired document (#300). A document may declarederived_from, a concept id or a list of ids its guidance was drawn from. When a source leaves force (graph-excluded,deprecated,superseded,rejectedor expired),kb_getnames the in-force dependents on the source (dependents_to_review) and the retired sources on the dependent (derived_from_retired), anddata-olympus lintwarns on both ends.rejectedcounts as retired on purpose;draftandproposednever governed and do not. It is a cue for a person: nothing is filtered, demoted, ranked or invalidated, andkb_searchandkb_consultare unchanged. Lint errors on a malformed or unresolved target (downgradable with--unresolved-targets warn), self-reference, derivation cycles, and deriving from a document the same document supersedes; the write path refuses a newly introduced unresolved or malformed target but never a write because a source retired. The format moves to0.5, a backward-compatible addition; no migration.Capture provenance on memory proposals (first slice of #141). Memory proposals accept an optional
captureenvelope for a memory distilled from a passive capture such as a hook event or an external transcript:capture_source,capture_event_id,source_event_hash,transformationandraw_retentionare required, andcapture_session,classificationand a server-verifiedderived_memory_hashare optional. It holds identifiers, hashes and enums only. It is written to the memory's frontmatter, shown in the pending listing and readback, recorded in the committed and pending audit events, and counted by the session recap ascapture_derived; an operator edit at resolve keeps it. It is a label, not authority: it never forces review and never makes a memory in force. An invalid envelope is refused asrejected_invalid_capture(HTTP 400) and a credential-shaped value asrejected_secret_detected.Writing rules on the documents agents write (#283). The write pipeline now applies the project's raw writing rules (no em-dash, no en-dash used as one, no agent authorship credit) to the lines a write ADDS, on every commit path: propose memory, propose edit, resolve, and onboarding bootstrap. A write never fails on text it did not add: a line counts as added only when it occurs more often in the postimage than in the target's current content, so moving an existing line is not adding it, while a new offending line is always caught. A line ending in
<!-- prose-lint: allow -->is exempt.KB_WRITING_RULES_MODEselectsenforce(reject asrejected_writing_rule, HTTP 422),warn(the default: commit, return the findings inwriting_rule_findings, record rule names and line numbers in the audit event, and log a warning) oroff; an unknown value fails startup.KB_WRITING_RULES_EXCLUDE_PATHSskips paths by glob. The check runs after the secret scan, so a postimage with both a credential and a finding is still reported as the redacted secret rejection, and a failure of the check itself rejects inenforcerather than being skipped. The machine-rendered maintenance ledger is exempt through its own call path, which no client can reach. Upgrade note: the defaultwarnchanges nothing for existing writes; setenforceonce a knowledge base adopts these rules.The stable release now publishes the MCP Registry entry (#303, part of #111). After the GitHub release, the promotion workflow signs in to the official registry with GitHub OIDC and publishes
server.jsonasio.github.knaisoma/data-olympus, then reads the entry back. It first checks thatserver.jsonnames the released version and that the release's PyPI description carries the ownership marker, and it runs a pinned, hash-verifiedmcp-publisher. No secret or personal sign-in is involved.
Changed
MCP SDK 1.30.0, Starlette 1.7.0, uvicorn 0.54.0 and sse-starlette 3.5.0, with a direct
mcp>=1.30.0,<2requirement. The MCP SDK now closes a streamable HTTP session that has had no request in flight for 30 minutes and holds at most 10000 sessions (HTTP 503 beyond that); an open SSE stream keeps a session alive. See Upgrading in the release notes.Onboarding bootstrap creates only new files under the workspace it onboards. Every target must lie under
projects/<workspace>/, or underprojects/<workspace>/components/<component>/when a component is given, in every onboarding state; other paths are refused asrejected_path_not_indexable_or_blocked, and a target that already exists is refused at commit time asrejected_already_onboarded. Usekb_propose_editto change an existing document. The bootstrap commit subject lists every path written.kb_propose_editacceptsbase_commitonly asHEAD(orhead) or a commit id of 7 to 64 lowercase hex characters. Any other value is refused asrejected_invalid_baseand is not recorded.Configuration is checked at startup where it used to be ignored. A set but unusable
KB_AUTH_PRINCIPALS(invalid JSON, not a list, an empty list, an entry that is not an object or has no non-emptytoken) stops startup with an error naming the setting and entry number.KB_SECRET_SCAN_EXTRA_PATTERNSis parsed once at startup, accepts a JSON array of patterns, no longer splits a pattern at a comma inside{...}or[...](soACME_[A-Z0-9]{20,40}stays one pattern), and an invalid or nested-quantifier pattern stops startup instead of being skipped.KB_READ_ONLY,KB_DISABLE_VERSION_CHECKandKB_STATUS_AUTOFILLaccept1,true,yes,on,0,false,noandoff(case-insensitive) and stop startup on any other value; unset or empty keeps each default, so autofill stays on (#314, #315; theKB_STATUS_AUTOFILLcase was raised by @thejedi433).The benchmark receipt guard accepts a measured commit held by a
benchmarks/receipt-*tag. A change to a benchmark corpus has to be re-measured in its own pull request, because corpora are compared with the current tree, and a squash merge then removes the measured commit frommain. A receipt tag pointing exactly at that commit keeps it resolvable, so the pull request check accepts it; the other receipt checks are unchanged.benchmarks/README.mddescribes the three refresh routes.Every path under
universal/is tier T1 in the default taxonomy. Only sevenuniversal/subdirectories were classified; any other path there, such asuniversal/README.md, fell through tometa. Those seven keep their categories, other paths get the categoryuniversal, and the CLI fallback matches. Search facets for those documents move frommetato T1, andKB_WRITE_BLOCK_TIERS=T1now covers the whole ofuniversal/.kb enforce reportjudges consult coverage like the live gate (#309). The report and its--stagedcommit gate count a governed commit as verified only when the explicit consults in the window cover every signal of its governed paths, with the same family rules as the gate. Theconsultaudit event records the consult'scoverage, and the audit API now returns it withtrigger. Rows written before this release keep timing-only judgement and are reported asTIMING ONLY(timing_onlyin--json);KB_GATE_CLEARANCE=pairkeeps timing-only behaviour throughout, and an invalid value makes the report exit 2. The gate's suggestedkb_consultcall now stays well formed for a path containing a quote character.data-olympus lintwarns when frontmatter disagrees with the path taxonomy (#304). A document whose declaredtierorcategorydiffers from what its path implies (honouringKB_TAXONOMY_PATH) gets a warning naming the path value, because the index uses the declared value while the write blocklist governs writes by the path tier. It is never an error and never changes the exit code.A consult now clears only the governed actions it covers (#296). The enforcement gate used to clear any governed action after any fresh explicit consult in the session, whatever that consult was about. Each consult now records the keywords, command fragments and paths its intent names, and a governed action is allowed only when all of its signals are covered by a fresh consult, directly or through a small family mapping (a "dependency" consult covers manifest edits and install commands; a "schema" or "migration" consult covers migration and SQL files). Coverage from several fresh consults combines, and each part expires on its own TTL. Naming the action's own path or command always clears it. A denial still answers
consult_required; its reason lists the uncovered signals and suggests one intent that covers them all, and thegate_blockaudit event gains anuncoveredfield. Upgrade note:KB_GATE_CLEARANCE=intentis the new default; setKB_GATE_CLEARANCE=pairto keep the previous rule. An invalid value fails startup. A session that consulted before the upgrade needs one new consult.fastembed 0.8.1 for the optional embeddings extra (#298). It fixes a path traversal in model archive extraction that could write outside the model cache, and a padding regression on mixed-length batches. Upgrade note: fastembed now resolves the default
BAAI/bge-small-en-v1.5(andBAAI/bge-base-en-v1.5) to a repository name with different casing, so on a case-sensitive filesystem the cached model is not reused and downloads once more. A deployment withKB_EMBEDDINGS_MODE=onthat runs without network access must refresh its model cache before upgrading, or startup fails with the model unavailable. Lexical-only deployments are unaffected.ruff 0.16.9 for development (#297).
The CLI's fallback taxonomy is held to the index's (#304). The dependency-free search fallback (
bin/_kb_fallback.py) keeps its own copy of the path taxonomy; tests now assert that both copies list the same rules in the same order and classify the same paths identically, including with a customKB_TAXONOMY_PATH. A comment was the only thing aligning them before.The writing-rule linter ships inside the package (groundwork for #283). The vendored gate moved from
scripts/prose_lint.pytodata_olympus._vendor.prose_lint, so the installed server can apply the same rules the CI gate applies.scripts/prose_lint.pyremains as a shim that runs it and now needs the package installed, so run it asuv run python scripts/prose_lint.py <path>.
Fixed
A session closed by the idle reaper now leaves the session table. With MCP SDK 1.29.1, reaped sessions stayed listed, so
live_sessionsin health never decreased and the closed session objects were kept until restart.REST routes refuse a non-string value in a string field (#310) with HTTP 400 naming the field, and record nothing. Previously consult and record event could write such a value to the audit log, after which the audit and compliance readers failed.
kb_auditandkb_compliancenow skip a malformed audit line and report it in a newskippedcount.An empty bootstrap bundle is refused as
rejected_empty_bundle(HTTPbefore any claim, rate-limit slot, commit or pending entry (#311). Previously it answered 500, or reported a pending bootstrap that was never parked.
Without
KB_REMOTE_URL, the MCP toolskb_list_pending,kb_get_pendingandkb_auditanswer like their REST equivalents instead of failing with an empty tool error (#312).REST resolve answers 400, not 200, for a symlink-escape refusal (#313).
REST resolve no longer answers 200 for an unknown decision (#307). A
decisionother thanapprove,rejectoredit(for example the typoaprove) returned HTTP 200 withstatus: rejected_bad_decision, so a client checking only the status code read it as applied. It is now HTTP 400. A test pins the HTTP code of every status the write tools can return, so a new one cannot fall through to a default unnoticed.A bare
glibc.malloc.trim_thresholdtunable no longer stops the trim threshold pin (#302). The server leaves the trim threshold alone when the operator has set it, and it treated anyglibc.malloc.trim_thresholdentry inGLIBC_TUNABLESas such a setting, including one with no=value. glibc ignores a tunable without a value, so under that malformed configuration nobody set the threshold and the memory retention fixed in 0.10.0 (#284) came back. Only an entry with a non-empty value now counts.
Security
PyJWT 2.15.1 (GHSA-r6x4-923q-g947, GHSA-w2cx-738m-mc7w, both high). The version 0.10.0 resolved, 2.13.0, could accept public key material, such as a JWK container or a BOM-prefixed key, as an HMAC secret. PyJWT reaches Data Olympus only through the MCP SDK's optional crypto extra, and Data Olympus does not call it directly; the lock now resolves the fixed release.
urllib3 2.8.0 (GHSA-8988-9cw3-xx77 and GHSA-vxq7-64xx-v4gw, high; GHSA-gh4c-6fx4-qh6g, medium): HTTPS proxy TLS settings could be ignored, and a chunked response could make the client buffer an unbounded line. urllib3 reaches Data Olympus only through
requests, which the optional embeddings extra uses to download its model. Upstream notes that a configuration relying on destination TLS settings for an HTTPS proxy may needproxy_ssl_contextinstead.cryptography 50.0.2, whose wheels are built against OpenSSL 4.0.3.
0.10.0 - 2026-09-28
Added
KB_GIT_BRANCH: serve a knowledge base whose trunk is not calledmain. Every git operation the server ran against the knowledge-base remote namedmainliterally, so a repository onmaster(or any other trunk) could not be served: the refresh loop'sfetch origin mainfailed and health stayed degraded with the index frozen at whatever was built at bootstrap. Two further consequences were quieter. A write supplying an ENFORCEABLE base marker (a pinnedbase_commit, abase_blob_shaor atarget_file_hash;base_commit: "HEAD"is advisory and does not count) was refusedrejected_stale_base, because the compare-and-swap base could not be refreshed. A write with no enforceable marker committed and pushedHEAD:main, creating that branch on a remote that had none. The refresh loop then fetched and merged the branch it had just been given, so a server could end up following a trunk its writes had invented while the repository's real trunk went on being ignored. The setting now feeds the fetch, the fast-forward, the session worktree rebase, the push, and the reachability checks that decide whether a session worktree still holds unpushed commits and may be garbage collected. Every ref built from it is fully qualified (refs/heads/<branch>,refs/remotes/origin/<branch>), so a name shaped like a ref path or colliding with a tag cannot select another namespace. It defaults tomain, so an existing deployment behaves exactly as before, and it is validated when configuration loads: a value that is not a usable branch name fails startup with the setting named and the reason stated, rather than reaching a git command line, which matters most for a value starting with-that git would read as an option. A blank value means unset and yields the default, as the path settings already document. (#289)
Changed
Release workflows publish only what is on
main.rc-publish.ymlnow stops unless it is dispatched frommainand itsrefinput resolves to an ancestor ofmain, so a candidate can no longer be built from an unmerged branch or an arbitrary SHA.tag-release.ymlalso refuses a dispatch from any other branch, and prints the candidate tag, source SHA and image digest it resolved, so the exact promotion source is visible in the run log before the protected PyPI environment is approved. (#293)Benchmark extra:
sentence-transformers6.0.1 to 6.1.0. Lock only, and the benchmark extra is not installed by the server or by the ordinary test job, so nothing a deployment runs changes. The regenerated lock moves that one package and no transitive dependency shared with a runtime package. (#285)The doc-consistency CI guard now also checks documented configuration defaults, not just schema enums. For each session reaping variable it reads every
(default ...)the prose ofdocs/serving.mdstates immediately after that variable, outside fenced examples, and compares it with the correspondingConfigfield, so lowering a default in code and updating only one of the places the documentation states it fails CI instead of ageing quietly. The guard reports the document line and both values. It covers this one document and this one list of variables rather than every setting the project has, and it requires the value as a whole number in the unit the configuration uses, with any gloss after a comma, so that a form it cannot read whole fails loudly instead of being certified from its leading digits. (#286)
Fixed
Resident memory no longer ratchets up with every index rebuild. A server with a 1 GiB limit could be OOM-killed after a burst of writes and, short of that, sat for hours at several times its starting size while idle. Nothing leaked at the Python level. A rebuild frees its working set when it returns, but glibc keeps free memory in its per-thread arenas, and rebuilds and request handlers run on different long-lived threads; glibc also raises its trim threshold after large frees, so each worker arena kept its free top as well. The server now pins the trim threshold at startup (
mallopt) and returns free memory after the bootstrap build and at the end of every refresh tick (malloc_trim(0)). Both are glibc only and do nothing elsewhere. An operator's own trim threshold (MALLOC_TRIM_THRESHOLD_orGLIBC_TUNABLES) is left alone. Replaying the burst that triggered the report with concurrent read traffic, the unchanged server held 321 MiB after 32 rebuilds and a short idle, from a start of 110 MiB; with this change it held 126 MiB after 24, from 81 MiB, and its peak fell from 359 MiB to 208 MiB.docs/serving.md("Memory between rebuilds") has the measurements. (#284)Several documented settings were read and then silently ignored.
load_config()readKB_CONSULT_TTL_SEC,KB_PENDING_CLAIM_TTL_SEC,KB_TRIGRAM_MODEandKB_TRIGRAM_FALLBACK_THRESHOLDcorrectly, butbuild_apprebuilt its configuration from its own keyword arguments, and it took none of these as parameters, so the running server used their defaults. In practice the trigram typo and identifier fallback could not be enabled in a deployment started through the shipped entry point; the consultation freshness window, forkb_consult, forkb_gate_checkand for the consultation ledger's retention, was fixed at 300 seconds; and the claim guard used a 900-second claim TTL while the background sweep in the same process used the configured one. The same rebuild also droppedledger_pathand the co-occurrence settings from the running state and hardcodedhttp_portto 8080 there, which was harmless only because those values happen to be read from the original configuration elsewhere. None of this was new in 0.10.0. The production entry point now hands the loaded configuration to the server unchanged, so the running state is exactly what was loaded. A parity test moves every configuration field it can safely change off its loaded value, names any field that does not reach the running state, and asserts the running configuration is the loaded object itself, which also covers the few fields it leaves alone; an end-to-end test checks the objects the server builds from these four settings. (#291)docs/serving.mddescribed session reaping as it behaved before 0.3.3. Its session reference section still saidKB_SESSION_IDLE_TIMEOUT_SECdefaults to1800s / 30 min, and still said a quiet long-livedGETSSE stream stamps no activity and is reaped on that timer, with excluding open streams from reaping listed as a possible follow-up. None of that has been true since the SSE session-churn fix, which is in 0.3.3: the default is300, the activity middleware runs a keep-alive task for an open stream and re-stamps the session everyKB_SESSION_TOUCH_INTERVAL_SEC, and the server clamps that interval to at most a third of the idle window, so the stamps are scheduled at least three times per window. The summary bullet near the top of the same document already said all of this, so the document contradicted itself, and the reference section is the half an operator reads when sizing a client keep-alive. Both halves now agree, and the corrected text also states the condition the old text left out: the reaper measures elapsed time since the last activity stamp, so a client that polls less often than the idle window is reaped between calls. (#286)
0.9.0 - 2026-09-21
Added
New
kb_curatetool: which in-force documents are due for review, most overdue first. Aggregates thefreshness/freshness_reasonsignalkb_searchandkb_getalready expose per-document into one list, so finding what needs a look no longer requires already knowing to filter forvalidity_state:stale. Advisory and read-only, with no write-pipeline dependency; registered in read-only replicas alongsidekb_search/kb_get. Candidates are exactly thekb_search(in_force=true)set, and an empty or nothing-review-due result is a valid empty list, never an error. The tool name is reserved on issue #31 (avoiding a collision with the existingkb_auditevent-log tool), but #31's request -- pattern promotion: detecting repeated patterns and proposing to hoist them up the tier chain -- is not implemented here and #31 stays open. (#142)Review-due can now be derived from how long a document has actually gone unverified, not only from a hand-set deadline.
last_verifiedused to be advisory and evaluated by nothing, so a document nobody ever re-verified looked exactly as fresh as one checked yesterday. With the newKB_REVIEW_DUE_AFTER_DAYSsetting, a document with no explicitrecheck_byis reportedfreshness: "stale"once itslast_verifiedis older than that many days, or has nolast_verifiedat all -- a document never verified does not default to fresh, since that would hide exactly the documents most worth looking at. An explicitrecheck_byremains an override in either direction: past, it isstaleregardless of verification age; future, it suppresses the automatic check entirely. A newfreshness_reasonfield onkb_searchhits andkb_getnames the field and the date or day count behind a non-emptyfreshness. Unset (the default): behaviour is unchanged from before this release. This is advisory only and never affectsin_forceor default search. (#142)A proposal can now declare that it contests existing indexed knowledge (#241).
kb_propose_editaccepts an optionalcontestmapping carryingcontradictsdocument IDs and an optional disputereason, so a resolved contradiction becomes distinguishable from an ordinary edit. A valid contest always parks for confirmation, even at high confidence with auto-commit otherwise permitted: disputing in-force knowledge is a human decision, never an automatic one. Validation runs in two stages: structural bounds and a credential scan before the rate limiter, then contradiction IDs are resolved against the index after it, rejecting a target that contradicts itself and never echoing a submitted ID or a resolved path back to the caller. Duplicatecontradictsitems are rejected withrejected_invalid_contest.PendingQueuepersistsintent: "contest"andcontradictsin the path lock, which the claim and restore transitions preserve, andkb_list_pendingprojects the proposal's operational reason and its dispute rationale as separate fields.Index.id_to_path_mapnow propagates database operational errors (sqlite3.Error) instead of returning an empty map, so contest validation and governed-target classification can tell a failed index read from an empty index and fail closed on it; the duplicate-id checks inwrite_gatekeep their deliberate fail-open behaviour. Read-side projection inheld_locks()is deliberately left to a later slice; it is not blocked, since #270's health allow-list already covers these fields by construction. Contributed by @RemanenetSpy in #278.
Fixed
A path lock is now published atomically, and a failure after a proposal is published no longer strands it unlocked. The lock file used to be created empty and then written into, so a reader (health, GC, the #241 contest projection) could observe an existing-but-empty or truncated lock for the whole write. It is now written complete to a private temp file and published in a single
os.link, which still fails if the path is already locked, so exclusive-create semantics are unchanged. Separately,atomic_write_jsonpublishes a pending entry before its trailing parent-directory fsync; a failure in that fsync used to release the entry's lock unconditionally even though the entry was already live on disk, letting a second proposal take the same path. The lock is now released only when the entry never actually landed. A crash-leftover publish temp file is reaped by the existing periodic GC pass, past the auto-commit lock TTL; seedocs/operations.md§4.6. (#272)
Security
Health no longer hands pending ids to unauthenticated callers. When auth is configured,
GET /api/v1/health, the degraded 503 body every read route can return, and MCPkb_healthnow omit eachpath_locksrecord'spending_idfor a missing or invalid token; a valid token still sees it. Behaviour is unchanged when auth is not configured. Responses served with auth configured now carryCache-Control: privateandVary: Authorization. (#270)Caller-supplied text that cannot be encoded as UTF-8 no longer suppresses an audit event, and no longer breaks reads for everyone else. An unpaired surrogate passed every write-path gate:
json.loadsaccepts the escape, the secret scanner is a regex match, and the entry is persisted withensure_ascii=True. Two consequences, both fixed. Identity and routing fields (agent_identity,source_session,target_path,pending_id,edited_text) are now REJECTED withrejected_invalid_encodingbefore any side effect: an unencodable one used to raise inside the suppressed block in_emit_audit, so the write proceeded and no audit record was written for it. Advisory text (reason) is replaced with a placeholder, matching the existing secret-scan treatment of that field, andevidenceitems are rejected, matching theirs. Separately, the pending listing, the pending detail response and the healthpath_locksprojection now degrade an unrenderable value to a placeholder instead of failing the whole response, which covers records written before this release. Encodability is tested rather than the surrogate code-point range, so correctly-paired astral characters are unaffected. A resolve refused this way returns HTTP 400; the resolve route's status map used to fall through to 200 for any status it did not name, so the refusal would have read as success. (#280)
Changed
Healthy REST reads no longer enumerate or read every path lock. The degraded-precheck every read route runs first listed the lock directory and opened/parsed each
*.lockfile even though it only neededdegraded, so cost scaled with the pending queue on the overwhelming-common healthy path.get,search,outline,list,/readyzand/metricsnow skip the enumeration entirely when the index is healthy; a degraded response still carries the fullpath_lockslist in its 503 body. (#271)The benchmark receipt is now checked for a reachable
source_commitbefore merge. A receipt refreshed on a pull request branch named a commit that the squash merge then removed frommain, so the benchmark-docs guard passed on the pull request and failed afterwards, and a reproduction could not check out the revision the numbers were measured at. CI now fails the pull request that introduces such a receipt, with a message explaining how to re-measure.benchmarks/README.mddocuments the resulting workflow. (#268)Writing rules are enforced in CI.
scripts/prose_lint.pyruns over the repository's Markdown on every pull request and fails on an em-dash, a spaced en-dash used as a sentence dash, agent authorship credit, and a short list of LLM diction. It is a vendored copy of the operator's canonical linter; changes belong upstream first, and the header records the upstream commit and sha256 so the copy is verifiable..prose-lintignorelists what is out of scope, notably the benchmark material whose CURRENT bytes a measurement receipt hashes: the generated result documents in_OUTPUT_PATHSand the measured corpus underbenchmarks/corpus/andbenchmarks/governance/. The rest ofbenchmarks/is linted, README included: its Python sources are verified against the bytes committed at the receipt's ownsource_commit, so editing a comment today does not invalidate a past measurement.Documentation prose backfilled to match. 118 em-dashes across README, SPEC, WHY, CHANGELOG,
docs/anddeploy/are replaced with a colon, a comma or two sentences, chosen per sentence rather than mechanically. Prose only: no behaviour, no interface, no documented enum or value changed, and the SPEC edits are punctuation inside existing paragraphs. (No issue; raised by the operator after banned patterns reached public output.)
0.8.2 - 2026-09-15
Security
Hardened MCP argument-validation errors and diagnostic logging. Argument-validation failures now return concise rule summaries without submitted values. Trusted parameter names are retained; untrusted names use placeholders. Unknown tools return
Unknown tool. This applies to direct calls and the search-modecall_toolproxy. Other errors raised while a tool runs are returned as before, except that pydantic validation errors raised inside a tool are summarized.Log records emitted by the MCP SDK, FastMCP (excluding its client modules) and
sse_starletteare rewritten before they are written. Message arguments become<redacted>, except the request type in the SDK's request trace and the tool name in the invalid-arguments warning. A message that arrives already formatted is replaced by a marker naming its source file and line, and tracebacks keep frames and exception types, up to a nesting limit, but not exception messages. Uvicorn access lines keep the method, path and status and drop the query string. This application's own log records are unchanged. A client that parsed pydantic's raw validation text will see the shorter message instead.
Fixed
A malformed lock file no longer takes down health, readiness and REST reads. A lock whose content was valid JSON but not an object made the lock listing raise, which failed
/api/v1/health,/readyz,/metrics, MCPkb_healthand the REST outline, search, get and list routes, since they all build health first. It is now listed asowner_kind: "unreadable", and the ownership and cleanup helpers treat it as a lock whose owner cannot be established and leave it in place. (#269)
0.8.1 - 2026-09-14
Fixed
kb_list_pendingno longer fails when one claimed entry is damaged. A claimed record whosereconcilevalue is not an object (for example a string left by a hand edit) raised an error that hid every other entry in the listing. It is now listed asclaimed, which is how the running-contest check already reads it. (#265)A proposal with a malformed base marker is refused when it is made.
kb_propose_editaccepted any string astarget_file_hashorbase_blob_sha. A git blob id in the sha256 field parked a proposal that could never be approved: every approval came backrejected_stale_basealthough the file had not changed. Both markers are now checked for their exact format and a bad one is refused asrejected_invalid_base, naming the field without echoing it.docs/serving.mddescribes how to recover a proposal parked before this release. (#263)An approval's claim record no longer keeps the edited text. Approving with
edited_textwrote the full text into the claim on the state volume, where nothing reads it back. After an interrupted approval whose outcome could not be proven, it stayed there indefinitely, including text committed under the explicit secret-scan override. The claim now records only a sha256 digest, and a claim written by 0.8.0 has its text replaced by the digest on the first reconciliation pass after it is older thanKB_PENDING_CLAIM_TTL_SEC. Recovery behaves exactly as before. (#264)A supersession that names a missing document is refused and fails lint. A
supersedesvalue naming an id that does not exist retires nothing, so a document it was meant to retire could stay in force, and asuperseded_bynaming a missing id records a successor that does not exist. Both passed the write gate with only a lint warning. A write that introduces such a target is now refused asrejected_invalid_document(unresolved_supersedes_targetorunresolved_superseded_by_targetin the reason; a wrong shape ismalformed_supersedes/malformed_superseded_by), checked against the commit being made, so a document created in the same bootstrap bundle resolves. Targets already present in a document, and unchanged malformed values, keep passing.data-olympus lintanddata-olympus importreport an unresolved target as an error (format spec0.4);--resolve-root <dir>resolves against a larger bundle and--unresolved-targets warnrestores the warning while a backlog is fixed. Bootstrap commit-time rejections now carry areason, and among a bundle's commit-time checks a credential-shaped file is reported before a duplicate id or a content defect. A blank or whitespace-only target is refused as malformed. Validating a write also reads the committed tree with three git processes instead of one per document. (#259)
Upgrading
data-olympus lintanddata-olympus importcan now exit 1 for a bundle that linted clean before: an unresolvedsupersedesorsuperseded_bytarget is an error (format spec0.4). Rundata-olympus lint --unresolved-targets warnto list them, fix each, then drop the flag. Lint a subtree with--resolve-root <bundle root>.Among a bootstrap bundle's commit-time checks, a credential-shaped file is now reported (
rejected_secret_detected) before a duplicate id or a content defect in the same bundle. Structural path checks still run first.
0.8.0 - 2026-09-14
Added
A session can read back its own parked proposal. A write that parks returned a pending id, and the listing showed its target path, confidence and park reason, but nothing returned the text. So an agent could learn THAT it proposed something about a path and never WHAT it proposed: it could not re-read its own draft, quote it back to the operator, or check it against a second proposal.
GET /api/v1/pending/<id>and thekb_get_pendingMCP tool now return the postimage, within_force: falseand a note saying the content governs nothing until approved. It stays out ofkb_consultand everyin_forceretrieval exactly as before.Ownership is the AUTHENTICATED principal recorded when the proposal was made, or any principal holding
resolve; an entry parked before this release is resolver-only, since its owner cannot be established. A postimage the secret scanner flagged is withheld from everyone but a resolver, since handing one back would turn the queue into a place to retrieve a credential from. Only the postimage is owner-scoped: the pending LISTING remains visible to every authenticated principal with each entry's target path, reason and evidence, unchanged by this release and documented indocs/serving.md.Because a principal name is now an ownership identity, the server refuses to start on a configuration with two credentials sharing a name, including two unnamed ones, which both defaulted to
agent. Give each credential a uniquenamebefore upgrading, and avoidoperatorwhenKB_AUTH_TOKENsupplies that identity. Requested in #256 after a public discussion of read-your-own-writes.The governed action vocabulary is configurable. The enforcement gate classified actions from three lists compiled into
enforce_policy.py, and the shipped entry point never threaded a classifier, so extending coverage meant embedding the server in your own Python.KB_GOVERNED_EXTRA_KEYWORDS,KB_GOVERNED_EXTRA_PATH_GLOBSandKB_GOVERNED_EXTRA_COMMAND_PATTERNSnow add to those lists. They extend and never replace, so a configuration error cannot silently remove enforcement the product already provided, and this changes only what counts as governed: a fresh explicit consultation is still the only thing that clears the gate. Part of #257; the SubagentStart half of that issue remains open and needs its payload verified first.A reproduction of the benchmarks can now be compared against the published one.
python -m benchmarks.receipt compare --reference <path> --candidate <path>reports two levels that fail independently: whether the recorded inputs match (commit, environment, commands, seeds, configuration) and whether the recorded results match (artifact digests and the reported summary). A run can match the first and not the second, which is the discrepancy worth reporting, and it can match the second and not the first.reproduction_statusis therefore the outcome of a comparison against a named reference, never a property a single receipt carries. A receipt missing a field the comparison needs is rejected rather than counted as a match, and the CI guard now fails if the committed receipt stops being usable as a reference. Exits 0 when both levels match, 1 on a difference, 2 on a missing field.benchmarks/README.mddocuments how a third party publishes a reproduction and reports a discrepancy. The comparison is over RECORDED inputs and results: this tool does not execute or observe a run, and the published tables stay labelled maintainer-produced and not independently reproduced.Release channels are documented for users.
docs/operations.mdsection 7.1 states the stable and candidate names on every surface (GHCR immutable tags and therc,stableandlatestchannels, PyPI versions, GitHub releases), how to try a candidate by its exact version, how to check it withdata-olympus verifyand what each exit code means, what promotion guarantees (the stable image is the candidate digest, and the stable wheel is compared with the candidate), and how to roll back. The README and quickstart candidate examples no longer pin an old version. The maintainer procedure is unchanged and now section 7.2. Part of #151.data-olympus can be listed in the official MCP registry. The registry is where
modelcontextprotocol/serversnow sends new server implementations, and data-olympus was absent from it: two searches of its v0 API returned zero results. Aserver.jsonat the repository root declaresio.github.knaisoma/data-olympusagainst the PyPI package, with thestreamable-httptransport this server actually serves rather than stdio, andREADME.mdcarries themcp-nameownership marker the registry looks for in the description PyPI serves. 0.8.0 is the first stable release whose description carries that marker, which is what unblocks verification.This release declares the entry; it does not publish it. Publication authenticates as a maintainer through GitHub OAuth or from Actions through OIDC, which is a deliberate step rather than a file change.
docs/mcp-registry.mdrecords what it needs. Part of #111.Groundwork for telling a contested decision apart from an ordinary edit.
PendingQueue.derive_running_contest(target_path)reports, at read time and without writing anything, whether a path is held by a proposal and whether that proposal declares itself a contest:intent: "contest",dispute: true, or acontradictsreference in its metadata.supersedesis ordinary succession and deliberately does not count. The receipt carries astatespelled askb_list_pendingspells it, so a claimed or uncertain entry is distinguishable from one awaiting a decision, and an entry file that cannot be opened is reported asunreadablerather than raising. An entry file that is not valid JSON is still treated as absent, unlike the listing, andunder_review=Falsedoes not by itself mean a path is free. No tool or endpoint calls it yet, and nothing writes the contest fields today: this is the read-side half of #241. Contributed by @RemanenetSpy in #261.
Changed
OKF v0.2: data-olympus writes
generatedinstead oftimestamp. OKF v0.2 supersedestimestampwithgenerated: { by, at }.data-olympus init, the importers and server-rendered memories now writegenerated, andinitdeclaresokf_version: "0.2"and formatspec_version: "0.3". When data-olympus synthesizes this record, its writer is always the tool actordata-olympus/<version>, never a principal name or agent identity, so that metadata never attributes agent-written content to a human author; memories keepcreated_byandcreated_at. A proposed edit still carries whatever frontmatter its author supplies. The OKF importer keeps a source'sgeneratedand legacytimestampas written, and flags a malformedgeneratedfor review. When a source uses two aliases for one field, the canonical key wins, thenidentifieroveruid,kindoverdoctypeandupdatedoverdate, and the dropped value is reported; previously the later key in the source won, so the importedid,typeor date could depend on key order. Lint accepts agenerated.ator a legacytimestampas the content-change time and warns on a structurally malformedgenerated. Existing bundles are not rewritten; a document relying ontimestampsees no new finding, but one that already carries a malformedgenerated(for examplegenerated: null) now gets a warning. Compatibility: an OKF v0.1-only consumer that requirestimestampwill not accept newly written documents. Part of #173.
Fixed
Interoperability evidence is pinned to where OKF now lives. OKF moved to
GoogleCloudPlatform/open-knowledge-format, and theokf/directory the pin pointed at inknowledge-catalogbecame a frozen snapshot. The pin, the conformance script, CI and the freshness workflow now use the dedicated repository at OKF v0.2 commitad30107c; the vendored sample has nine concepts; the frozen copy is rejected as a pin. The freshness workflow monitors the fixture only, and says so. Closes #173.A dropped connection mid-approval is now visible, and recovers on its own whenever the evidence allows. Approving a pending write claims the entry, holds its path lock, commits, then releases both. If the process died in between, the entry was left in a claimed state that nothing could reach: it was excluded from
kb_list_pending, the orphan-lock GC treated its sidecar as a legitimate lock holder, and pending-owned locks were never TTL-reclaimed. The path stayed refused withrejected_path_lock_busyacross restarts, while the queue read as empty, which is indistinguishable from "the decision was applied".Entries now carry an explicit
state(pending,claimedoruncertain) and claimed ones are listed rather than hidden, health reportspath_lockswith each lock's target path and age instead of only a count, and a background pass reconciles claims older thanKB_PENDING_CLAIM_TTL_SEC(default 900). Reconciliation runs on evidence, not on age: the write records its own outcome durably before anything else, and failing that the commit is looked up by its newKB-Pending-Idtrailer on the recorded session ref. Only a recorded failure restores an entry to pending. Anything unproven staysuncertainwith its lock held and is re-checked on every later pass, because a rebase or a squash can remove a commit that really was made, and restoring on its absence would offer an already-applied decision for approval a second time. An entry that stays uncertain keeps its lock deliberately, anddocs/serving.mddocuments the manual recovery for it. The named history-rewriting paths (the base rebase, the push recovery that calls it, and the worktree GC) reconcile first and defer while a claim on that branch has begun its write.Because the claim now stores the approved content, an operator edit (
edited_text) is scanned for credential-shaped values before the entry is claimed. A flagged edit is refused asrejected_secret_detectedand the pending entry is left exactly as it was, so the refused text is never written to the state volume. An entry restored after any other rejection returns to the proposal as enqueued, without the edit. The explicit operator override is unchanged.Indexing a modest corpus no longer needs more than a gigabyte of memory. The co-occurrence pass that powers query expansion counted every token pair in the corpus in one in-memory table. The per-document token cap bounded each document's contribution but nothing bounded the union across documents, and the union is what was held: on a 536-file, 3.6 MiB corpus that was 6.2 million distinct pairs and a 1445 MiB peak, above a 1 GiB container limit on its own, so every index rebuild OOM-killed the pod. Pair counting now drops terms that cannot appear in any surviving pair, keys pairs by integer rather than by string tuple, bounds the per-term candidate lists, and spills to a temporary SQLite database once it reaches
KB_COOCCURRENCE_MAX_PAIRSdistinct pairs (default 500000). The same corpus now peaks at 105 MiB, measured for the co-occurrence build itself. For identical inputs and configuration, a successful build produces a byte-for-byte identical related-terms table, so no search result moves; a build that degrades on a spill failure has no table at all until the next successful one. The trade is build time: that corpus goes from 6.6 s to 16.3 s, measured on one machine. SetKB_COOCCURRENCE_MAX_PAIRS=0to disable the spill and keep the previous speed where memory allows. Seedocs/serving.md.A failed co-occurrence build no longer fails the whole index build. The spill writes to a temporary file, so it can fail on a full or read-only disk. Because the index only rebuilds when the corpus changes and
/readyzrejects a failed build, that would have left the service unready until the next commit. The build now logs a warning and continues without a related-terms table: the index still serves, and only query expansion is lost until the next successful build.
0.7.3 - 2026-09-07
Fixed
A blank
KB_MAIN_PATHno longer makes the server index its own working directory.KB_MAIN_PATH=andKB_INDEX_PATH=-- what an unsubstituted compose, Helm or CI variable produces -- reachedPath(""), which isPath("."), so the server silently indexed whatever happened to be in its working directory. A blank or whitespace-only value now means unset and the documented default applies, while a non-blank value is used verbatim. And when the corpus path does not resolve to a directory, startup now fails with a message naming the setting, the path it resolved to, and whether that path came from the environment or the built-in default, instead ofKB root not a directory: /kb-main-- a path the operator never configured and no setting name to search for. An existing but empty corpus keeps starting normally, as before. A deployment that relied on blank-means-cwd changes behaviour; that reliance was on the silent defect being fixed here. (#244)The Kubernetes first-boot log no longer prints the configured git remote. The StatefulSet initContainer echoed the remote URL immediately before cloning it. The remote is documented as "SSH or HTTPS", and an HTTPS remote may carry a credential inside the URL, so an adopter using one wrote that credential into the pod log on every first boot, where no application-level redaction reaches it. The message now names no URL, matching the Docker entrypoint. Nothing else about the clone changes. (#248)
0.7.2 - 2026-09-07
Changed
The README links the Lulu MCP marketplace listing. Lulu aggregates several registries including Glama, which this README already badges, so the listing may well be derived from that one rather than being an independent signal. It is one more place an agent or a person can find the server. The badge is a static label, so treat it as a pointer rather than as live listing status.
The release contract now says what to do when
mainadvances past the reviewed revision. The delivery rule assumed the release merge was still the tip ofmain. That stops being true when anything else merges between review and publication without itself carrying a qualifying release review and human merge: the reviewed revision is no longermain,main's tree has not been reviewed as a unit, and no revision satisfies the rule as written. The contract now names the preferred resolution (make the reviewed revision the tip again) and defines a narrow fallback with explicit conditions, including that every intervening commit was independently reviewed and human-merged, that none of them changes an input to a published artifact, that a change to a publication workflow always blocks the fallback, and that provenance and the OCIorg.opencontainers.image.revisionlabel name the exact source rather thanmain.Python 3.14 now has test coverage.
requires-pythonhas always been>= 3.13, so 3.14 was inside the supported range and users could install there, but nothing verified it. CI now runsmypyand the pytest suite on 3.14 alongside 3.13. Installed-artifact smoke tests still run on 3.13 only, so this is interpreter coverage of the test suite rather than a full parallel validation, and the shipped container image is unchanged and still runs 3.13.The release contract now separates candidate remediation from post-merge alert closure. Publication still requires zero open security alerts, and that requirement is never waived. The contract now states the one narrow case in which a final release pull request may reach its human merger while an alert is still open: the alert's fix is in the exact reviewed candidate and its closure is mechanically waiting only for that fix to reach the default branch. It requires per-alert remediation proof against that exact candidate, agreement from both the implementer and the independent reviewer, and fresh zero-alert clearance after the merge before anything is published. Previously the rule read as an unqualified zero-alert gate, which no branch fixing a default-branch alert could ever satisfy before merging.
Fixed
Product source changes no longer fail the benchmark guard. The receipt's
source_treedigest was compared against the working tree, so any edit undersrc/data_olympus/orbenchmarks/failedbenchmark-docswithsource_tree sha256 does not match the repository. That blocked every product fix, which is the same defect already corrected fordependency_lockand the same wrong assertion: that today's source produced numbers measured before it existed. The digest is now recomputed from the source as committed at the receipt's ownsource_commit, read from git history. Integrity of the historical record is stronger, not weaker: the recorded file list is compared as well as the digest, so a file that existed at the measurement commit but was dropped from the receipt is now caught, which the previous per-file walk could not see because it iterated the very list being shortened. What the guard deliberately stops asserting is any correspondence between today's source and the published numbers. It never established that the benchmarks actually ran at the declared commit, and it does not now.Bootstrap
/kb-mainon the docker compose path. The first-boot clone indeploy/docker/entrypoint.shkeyed offKB_GIT_REMOTE_URL, butdeploy/docker/compose.yamlcarries onlyKB_REMOTE_URL, which is also the variabledocs/adoption.mdanddocs/quickstart.mddocument. A compose deployment that configured a remote therefore started a read-write server on an empty/kb-main, and the first symptom wasgit_pull_loop iteration failed: … rev-parse HEAD … exit status 128, which does not point at the cause. The clone now falls back toKB_REMOTE_URL;KB_GIT_REMOTE_URLstill takes precedence, so the k8s path where the initContainer has already cloned is unchanged.Bind the benchmark receipt to its measurement commit instead of the working tree. The receipt's
dependency_lockwas compared against the currentuv.lock, so every dependency change failed thebenchmark-docsCI guard withdependency_lock does not match uv.lock, including security updates. The comparison now runs againstuv.lockas committed at the receipt's ownsource_commit, read from git history and still checked byte for byte, while the current lock is free to move. A past measurement is not invalidated by a later dependency bump, and the guard no longer asserts that today's dependency set produced numbers measured before it existed. Every other receipt failure is preserved unchanged.Correct the receipt's recorded lock binding.
dependency_lock.sha256had been regenerated in place against a neweruv.lockwhilesource_commitwas left pointing at the original measurement commit, so the receipt described a pairing that never existed. It is restored to the lock actually committed at that commit; the measurement revision and all benchmark outputs are unchanged.fix(release): require human merge and preserve published artifacts (#240)
fix(operations): report why a command failed, not just which one (#230)
fix(operations): make the release no_action deterministic (#226)
Security
All locked dependencies updated to their latest resolved versions. 71 packages move, including
mcp1.28.1 to 1.29.1,fastmcp3.4.4 to 3.4.7,starlette1.3.1 to 1.6.0 andregex2026.7.10 to 2026.9.3.fastmcp3.4.7 carries an upstream CIMD assertion-audience security fix. This is the first full dependency refresh possible since the benchmark guard defect described below was blocking every lockfile change. The three major-component moves (sentence-transformers5.6.0 to 6.0.1,setuptools83 to 84,websockets16.0 to 17.1) do not affect a default install:sentence-transformersbelongs to the optionalbenchextra, which the runtime never imports.Update
cryptographyfrom 49.0.0 to 50.0.1 (GHSA-g6cj-pr64-35w5). PKCS#7EnvelopedDatadecryption reported failures in distinguishable ways, one of which disclosed the length recovered from the RSA operation, and the same distinction was observable by timing. An application decrypting attacker-suppliedEnvelopedDataand reflecting the outcome therefore exposed a Bleichenbacher oracle against the content-encryption key. Affected from 44.0.0, fixed in 50.0.0. The GitHub Advisory Database rates it high; the upstream advisory rates it moderate.Data Olympus does not depend on
cryptographydirectly, and no Data Olympus source file imports it or calls the affected PKCS#7 entry points. This release does not establish whether the affected code is reachable through a transitive dependency, so the remediation is scoped to the dependency version rather than to a claim about exploitability. The locked environment and the container image install exactly 50.0.1. A clean installation of the wheel resolved 50.0.1 when tested, but the metadata does not guarantee it: this distribution declares no minimum forcryptography, and the combined transitive requirements (authlib >= 45.0.1,joserfc >= 45.0.1, andpyjwt >= 3.4.0for itscryptoextra) still permit 49.0.0. An existing environment is not upgraded automatically. Seedocs/releases/v0.7.2.mdfor the explicit upgrade command.The first-boot clone no longer logs the remote URL.
KB_REMOTE_URLis documented as an SSH or HTTPS push target and the compose path mounts no SSH key by default, so a writable remote there is most likely an HTTPS URL with an embedded token. The entrypoint printed that URL verbatim before cloning, into a container log no application-level redaction can reach. It now reports only that it is cloning the configured remote. That removes the unconditional disclosure rather than every path to one: git inherits the same stdout and stderr, and a token embedded in a clone URL is still written to/kb-main/.git/configonce the clone succeeds. Provisioning the credential outside the URL is the way out of both.Scan for LLM provider API keys. The built-in secret-scan set covered private keys, GitHub, AWS, Slack,
password=assignments and connection strings, but no LLM provider key format, so ansk-ant-…orsk-proj-…key in a proposed memory was committed clean. Users of this project hold these credentials by definition, which makes them the class most likely to be pasted into a memory. Addsllm_provider_api_key(Anthropic, OpenAI project/service-account/legacy, OpenRouter, Hugging Face, Groq, xAI, and OpenAI-compatible gateways) andgoogle_api_key(AIza…, which authenticates Gemini and the other Google APIs that accept API keys).Detect two further forms of existing credential classes.
github_tokennow coversghu_user-to-server tokens alongsideghp_/gho_/ghs_/ghr_, andaws_access_key_idnow covers theASIAtemporary key id issued by STS as well asAKIA.Secret-scan boundaries no longer depend on word characters. The new key classes above anchored with
\b, which cannot fire between_and a letter because both are word characters. A key wrapped in Markdown emphasis (_KEY_) was therefore invisible to the scanner, for every provider, and the same shape occurs in YAML flow keys and dunder names. The boundaries now exclude alphanumerics instead, which still rejects an immediately preceding ASCII letter or digit while letting a delimiter sit against the key. That is narrower than refusing every longer token: text joined to a key by-or_can still match, which is the direction a credential scanner should err in. The xAI alphabet also gained_, without which a key containing one was missed completely. The gate remains shape-based: it can still miss a credential whose format it does not model, and it does not scan or revoke anything already in history.
0.7.1 - 2026-08-02
Fixed
fix(release): bind no action review to remote main
Fixed
Bound no action review to current remote main. Release admission now preserves the freshly fetched
origin/mainrevision in the compact review evidence, so an independent reviewer can verify that the admitted source and current remote source are identical before accepting that no release is due.
0.7.0 - 2026-08-02
New features
feat(automation): execute weekly release outcome
feat(automation): make weekly releases outcome based
Fixed
fix(release): complete protected workflow delivery
fix(release): serialize workflow number input
fix(release): retry unbound gateway reads (#217)
fix(release): ground autonomous review packets (#215)
fix(release): preserve prepared roll forward (#213)
fix(automation): preserve recovery result binding (#212)
fix(release): resume unpublished preparation
fix(release): retry bound tag inventory read (#208)
fix(release): isolate prereview model evidence (#206)
fix(release): inspect exact GHCR version tag (#203)
fix(release): isolate development quality tools
fix(release): retry transient artifact smoke (#198)
fix(release): remove unavailable quality gate (#196)
fix(release): require approval before merge (#195)
fix(release): wait for complete check matrix (#193)
fix(release): wait for complete check conclusions (#191)
fix(release): allow metadata-only lock version bump (#189)
fix(automation): bind release consultation to run
fix(automation): accept FastMCP text content
fix(automation): read pull number from gateway receipt
fix(automation): make release imports deterministic (#182)
fix(automation): align final main checks
fix(security): update setuptools to 83.0.0
fix(ci): allow immutable history reconciliation
Fixed
Separated deterministic release controls from model review. The release review packet now carries an explicit compact response contract and omits the already validated native prereview decision, which is not a model judged release gate. A failed model subprocess is now reported as an invocation failure instead of being misclassified as invalid JSON.
Made the GHCR immutability guard exact and credential independent. The release gate now inspects the public
vX.Y.Zregistry tag directly instead of requiring GitHub Packages API scope or scanning a bounded version list. Only an explicit missing manifest proves the version is free; every other client, timeout, or registry failure still blocks the bounded, noninteractive check without exposing command diagnostics.Isolated autonomous release quality tools. The release runtime now installs the declared
devextra when it runs Ruff, mypy, and pytest. This prevents a missing project executable from falling through to an unrelated global Python environment and validating the wrong installed Data Olympus version.Moved autonomous release approval before merge. The release routine now reviews and approves the exact unmerged candidate, rederives every control replaced by the deliberate GitHub ruleset bypass, conditionally merges the expected head, and proves exact parent and tree transfer before any publication or deployment. The routine preserves exact CodeQL, alert, review, ruleset, and tree transfer evidence without depending on the paid GitHub Code Quality product, which is unavailable on the approved Free plan.
Accepted the canonical FastMCP pull request receipt. The automated release routine now derives the new pull request number from the exact repository URL when the gateway receipt omits a separate numeric field.
Accepted the FastMCP standard text content envelope. Release automation now reads the gateway's legacy tool bound text result when optional structured content is absent, while rejecting unbound or mismatched output.
Made release commits satisfy the governance gate autonomously. Before changing release files, the routine now records a run scoped Data Olympus MCP consultation, validates receipt freshness, and requires a server side gate receipt bound to the exact run and workspace. The unchanged precommit audit gate still verifies the staged governed change.
Fixed
Resume a merged but still unpublished release preparation only when its version, empty Unreleased block, release note, canonical changelog prefix, source SHA, and tree exactly match the current computation. The current main SHA receives fresh independent review without inventing a branch, pull request, merge, or parent. Candidate spelling is then confirmed absent from PyPI, public GHCR manifest inspection, and GitHub releases before the ordinary publication, deployment, verification, and notification tail runs.
Retry one transient unbound FastMCP response during the idempotent postmerge tag inventory, while preserving strict tool binding and exact merge evidence when the bounded retry still fails.
Fixed
Give the autonomous release reviewer the exact bounded, hash bound candidate diff, changelog section, and release note, and preserve its bounded reason when it blocks a release instead of reducing the diagnosis to a generic verdict.
Keep the operations runner candidate revision null when resuming a release that is already prepared on
main, while retaining the exact admitted source revision throughout review, approval, delivery, and verification evidence.Select exact prepared recovery only when the current release documents match the complete computed change set. A new reviewed change now reaches the existing atomic roll forward pull request path instead of blocking recovery.
Fixed
Resolve release image digests from the exact public GHCR tag without requiring GitHub Packages API scope.
Advance only the exact reviewed release workflow through its single pending
pypienvironment approval, preserving the protected environment gate while removing a manual scheduled-run follow-up.
0.6.0 - 2026-07-18
Added
Added searchable MCP discovery.
tools/listnow exposes seven core tools plustool_searchandcall_toolby default. Operators can setKB_TOOL_DISCOVERY_MODE=allfor the complete native catalog. Hidden tools remain directly callable and retain the same authorization checks.Added executable OKF interoperability checks. CI verifies both Data Olympus consuming the pinned Google Knowledge Orchestration Framework sample and the pinned upstream reference consumer reading the Data Olympus example bundle. The immutable upstream revision and fixture checksums are recorded in the repository.
Added complete release candidates across every public channel. One exact source SHA now produces a PyPI prerelease wheel and sdist, GHCR image, GitHub prerelease, and
release-provenance.json. Stable promotion is an explicit human approved dispatch that accepts only the highest complete candidate.Added deterministic benchmark provenance receipts. The committed receipt records source, corpus and query hashes, dependency lock, environment, tokenizer and model identities, exact commands, seeds, result hashes, and metric summaries. The documentation drift guard verifies it before checking generated benchmark tables.
Security
Bumped transitive dependency
mcp1.28.0 to 1.28.1 to close CVE-2026-59950 / GHSA-vj7q-gjh5-988w (Dependabot #18, high severity). The advisory covers the MCP Python SDK WebSocket server transport, which data-olympus does not use (Streamable HTTP only); the bump is applied as a precaution and to clear the release security gate. No API surface change. (KNA-146)Bumped transitive benchmark dependency
torch2.12.1 to 2.13.0 to close CVE-2025-3000 / GHSA-rrmf-rvhw-rf47 (Dependabot #19, low severity). The affectedtorch.jit.scriptpath is available only through the optionalbenchextra and is not used by the production embedding implementation. The patched dependency remains compatible withsentence-transformers.
Changed
Made PyPI the primary installation and onboarding path. The README and quickstart now lead with
uvxanduv tool install. Every packaging workflow runs clean wheel and source distribution smoke tests at the expected version. They exercise both console entry points, packaged enforcement files, REST health, setup doctor, compact discovery, hidden direct calls, and complete catalog mode.Hardened candidate to stable provenance. Candidate and stable Python artifacts are rebuilt from the same exact source, compared with only version metadata normalized, and published through Trusted Publishing with no stored PyPI credential. Stable OCI tags reuse the verified candidate digest and never rebuild the image.
Updated the supported runtime and development dependency baseline. FastMCP is now locked to 3.4.4 and requires
>=3.4.4,<4, guaranteeing the catalog search transform used by the default MCP tool-discovery mode. Regex is updated to 2026.7.10 with the secret-scanning timeout and false-positive regressions green. Mypy, types-regex, and Ruff were updated to their current Dependabot revisions and pass the strict type and lint gates. (gh #159, #160, #161, #162, #163)Added Glama related server metadata and maintenance notes.
glama.jsonnow names adjacent knowledge and memory MCP servers, anddocs/glama.mdrecords the maintainer response and usage seeding steps for the Glama maintenance profile. (gh #155 / KNA-145)Improved tool definitions for 7 tools (
kb_gate_check,kb_onboarding_status,kb_compliance,kb_cleanup_plan,kb_get,kb_list,kb_outline). Each docstring now includes a "Use when / use X instead when Y" guidance sentence.kb_gate_checkalso discloses its audit-log side effect (readOnlyHint=falseis intentional). (gh #154 / KNA-144)Improved
kb_record_eventtool definition. The docstring now discloses behavior (durable, non-destructive audit append; requires write capability), documents both event_type values (gate_bypass,gate_degraded) and all required parameters, and gives explicit usage guidance on when to call vs. when to usekb_gate_check,kb_consult,kb_audit, orkb_complianceinstead. (gh #153 / KNA-143)
Fixed
Closed the remaining release verification gaps. Every PyPI publishing path now fails closed and verifies remote file hashes. Candidate provenance validates its source, candidate tag, and image digest before publication. Stable promotion derives its version from the requested complete candidate and verifies that version against the candidate source commit. OKF freshness reports only upstream fixture changes rather than unrelated repository commits.
Corrected exact SHA CI readiness for conditional jobs. Completed skipped jobs are now non-blocking only when they are not required. Missing, pending, failed, neutral, or skipped required checks still fail closed. This lets the release gate accept a fully green pull request whose publishing workflow intentionally skips upload and manual release paths.
Hardened read tools against present SQLite indexes without tables.
kb_search,kb_get,kb_list,kb_outline, onboarding status, and cleanup planning now degrade to empty results or empty health instead of surfacing rawsqlite3.Errorfailures. The Glama Docker sandbox now serves the bundledexample-bundlecorpus so catalog introspection starts with a populated, safe index. (gh #156 / KNA-142)
0.5.0 - 2026-07-12
Added
Version-immutability release guard (
scripts/check_version_free.py). A published version (PyPI, the ghcr:vX.Y.Zimage tag, or a GitHub release/tag) is immutable, so the release process now refuses to republish an existing version. The guard runs as the first step oftag-release.yml'sdecidejob (before the tag is pushed, so a duplicate never leaves an orphaned public tag) and in PR CI (to catch a duplicate declared version before merge). A legitimate reconcile (local tagvX.Y.Zalready at HEAD) is allowed and skips the registry lookups; a registry outage fails closed. The operator can override a stuck registry withKB_BYPASS_VERSION_CHECK=1. (gh #148 / KNA-67)First-class reverse-proxy Host allowlist (
KB_PUBLIC_HOSTNAMES). New config knob mapped onto fastmcp's Host-header allowlist when the HTTP app is built, so operators configure data-olympus directly instead of reaching for the dependency'sFASTMCP_HTTP_ALLOWED_HOSTSenv var (both are honoured and merged). The server now emits a startup WARN when Host protection is on, it binds a non-loopback address, and no public hostname is allowed: the exact silent-breakage shape (readiness green, every proxied request 421) behind the 2026-07-09 kn-dev outage. A regression test boots the app with a configured public hostname and asserts the good Host reaches the route while a foreign Host is answered 421. (gh #139 / KNA-70)Prometheus
/metricsendpoint (optionalmetricsextra).prometheus-clientis an OPTIONAL dependency; with it installed,GET /metricsserves the standard exposition format (pending/push queue depths, frozen count, push-failure counter, staleness, live sessions, per-tool call counters, index-build duration + last-build timestamp). Without the extra the route returns501and every metric update inside the core background loops is a silent no-op, so a standard deployment is unaffected. (gh #69 / KNA-71)Cached update-available signal. The server now checks PyPI/GitHub for a newer published
data-olympusversion in a background task and surfaceslatest_version/update_availableinkb_healthandGET /api/v1/health. The health request path reads the cache only, and compact health includeslatest_versiononly whenupdate_availableis true. Air-gapped deployments can setKB_DISABLE_VERSION_CHECK=onto make zero outbound version-check calls. (gh #146 / KNA-68)Legacy corpus status migration. Status-less documents from pre-0.4.0 corpora now default to
activein the SQLite index whenKB_STATUS_AUTOFILL=on(the default), preserving in-force retrieval without mutating source markdown during index builds. Operators can persist the same default explicitly with the idempotentdata-olympus migrate status --applycommand. (gh #147 / KNA-69)
Fixed
kb_consultdemoted_writes CTA count. The maintenance CTA now counts only the session's live pending entries and is omitted once none remain, instead of reporting stale "N write(s) awaiting operator review" after every demoted entry had been resolved (the demoted_writes / pending_actions surface shipped since v0.4.0 via #131). (gh #137 / KNA-72)
0.4.2 - 2026-07-11
Added
data-olympus verifycommand. New CLI subcommand that runs pass/fail checks against a running instance (--target,--json,--checks,--token): liveness (/api/v1/health), readiness (/readyz), a search round-trip (/api/v1/search), and a deployment-tolerant enforcement-plane probe (/api/v1/gate/check). Exit codes:0all pass,4a check failed,1the target is unreachable. Used as the pre/post-release verification gate.Staged-promotion release pipeline (release engineering). A release candidate is published to a
X.Y.Z-rc.Nghcr channel (rc-publish.yml), deployed as a canary and verified, then promoted by re-tagging the verified digest tovX.Y.Zbyte-identically (tag-release.yml), with a moving-channel helper (set-channel.yml) and project-local runbooks under.rules/.Glama registry score support. Added the Glama score badge to the README and documented the claimed server listing, sandbox release settings, and score-maintenance checklist in
docs/glama.md.Release security-clearance gate.
scripts/security_alerts.pyfails a release when any Dependabot or CodeQL alert is open; the release planner clears every alert (fix or justified dismissal) and the cutter verifies zero open before building the release candidate.
Changed
Improved MCP tool schema metadata for registry introspection. MCP tools now expose meaningful titles, MCP annotations, and per-parameter JSON Schema descriptions so registries such as Glama can score the running server's tool definitions more accurately.
Fixed
Documented the fastmcp 3.4.3 host allowlist requirement behind proxies/ingress (docs/serving.md, docs/operations.md section 3.4). v0.4.1's fastmcp bump enables DNS-rebinding protection that answers 421 Misdirected Request for undeclared public hostnames while health probes keep passing; deployments behind a reverse proxy must set
FASTMCP_HTTP_ALLOWED_HOSTS. First-class handling tracked in #139.
0.4.1 - 2026-07-08
Changed
Dependency refresh from the post-0.4.0 Dependabot queue: runtime dependencies
fastmcp3.4.2 -> 3.4.3 andnumpy2.5.0 -> 2.5.1; dev dependencymypy2.1.0 -> 2.2.0 (strict run clean); CI actionsactions/upload-artifactv4 -> v7 andactions/download-artifactv4 -> v8 (exercised together on the packaging dry-run and by this release's own publish chain). Full test suite green on the combined set.
0.4.0 - 2026-07-08
Fixed
data-olympus initstamped a stale format version (found by the release-gate review): the scaffold's rootindex.mddeclaredspec_version: "0.1"while the shipped format is0.2. Corrected, with a drift test tying the scaffold constant to SPEC.md's header andexample-bundle/index.mdso future format bumps fail fast.Graph-exclusion source guard now applies the memory-inbox floor (found by the v0.4.0 release-gate review).
graph_excluded_ids_sqlchecked the superseding source's status class and validity window but notis_inbox, so a forged or legacymemory/inbox/document claimingstatus: activewithsupersedes: <real-rule>could retire a real rule fromin_force=trueretrieval,kb_consult, and the computedin_forceboolean. The source guard now requiressrc.is_inbox = 0, matching the full single-sourced in-force predicate; regression-tested with an inbox-source scenario.
Added
Write-path enforcement of mandatory
status(issue #114).statushas been a required frontmatter field (SPEC.md 4.2) and akb linterror since the format's0.1draft: this was already true of the lint layer and is not a change of required-field semantics. The gap this closes is at the write path:kb_propose_editnow rejects a postimage that creates a NEW document withoutstatus(rejected_invalid_document, reasonmissing_status), where previously the write path was more permissive thankb lintand let a brand-new status-less document through.Staged migration, not a hard break. Editing an EXISTING status-less document still requires no
status, so a legacy corpus already carrying status-less docs (tracked by the issue #113 maintenance ledger'sstatus_present_in_all_kb_entriesflag andmissing_statusfile list) is never locked out of incremental fixes. Those documents keep being served bykb_search/kb_getexactly as before; they are simply never in force (already true viaIN_FORCE_STATUSESmembership) and never surfaced bykb_consult.kb_propose_memoryis unaffected: every server-rendered memory already stampsstatus: proposed(issue #109).Reserved filenames (
index.md/log.md/template.md) and documents under the memory-inbox prefix are exempt, consistent with their existing exemptions from the write-path's other schema checks.No SPEC.md version bump: see SPEC.md section 10 for why this is a reference-implementation enforcement-timing change, not a format change.
See
docs/operations.mdsection 5.1 for the operator migration runbook (lint, read the ledger's missing-status list, fix, done) and the new integration test (tests/test_status_mandatory_migration.py) tying lint-error + still-served + never-in-force + ledger flag + thepending_actionsCTA together as one tested contract.
Governed-lane write protection (issue #112) -- BREAKING DEFAULT-BEHAVIOR CHANGE. "Agents can propose, only humans can promote." Behind
KB_GOVERNED_LANE_PROTECTION(default ON; set tooffto restore the exact pre-#112 behavior). No write is ever rejected by this feature -- affected writes land as a PENDING entry instead of auto-committing:Status clamp. Any non-operator-confirmed write (the auto-commit path of
kb_propose_memory/kb_propose_edit, andkb_bootstrap_projectfiles) whose postimage sets or changesstatusINTO the in-force class (active/accepted/approved, single-sourced fromformat.validate.IN_FORCE_STATUSES) is demoted to pending withdemotion_reason: "status_promotion". An operator-confirmed resolve of that entry commits it -- the human step IS the promotion.Governed-target edit demotion. Any
kb_propose_editwhose target document is CURRENTLY in force (the full composed predicate: status class AND validity window AND not-inbox AND not-graph-excluded, evaluated against the live index) is demoted withdemotion_reason: "governed_target", regardless of confidence. An expired or superseded-out target is NOT in force and so is not protected by this rule. The lookup FAILS CLOSED: an edit whose target's in-force state cannot be verified (no index, or an index read failure) is demoted with the distinctdemotion_reason: "governed_target_unverified"rather than auto-committed on the strength of a broken lookup. An authoritative in-worktree backstop additionally re-judges the target's CURRENT bytes on the refreshed commit base (inside the serialized commit section, after the hard gates), consulting nothing from the index (a stale graph-exclusion edge could otherwise remove protection the base's own bytes assert), so an in-force doc that exists in git but is not yet re-indexed is still demoted -- index lag cannot bypass the rule in either direction.Injection-pattern annotation (advisory only). Postimages are scanned for agent-directed injection patterns ("ignore previous instructions", exfiltration-shaped imperatives, base64-looking blobs, "do not tell the operator", ...); a match annotates the pending entry (
injection_suspect,injection_patterns: pattern names + line numbers, never the matched text) but never blocks or demotes by itself.Ordering: the issue #71 secret-scanning gate (and the content- validation / duplicate-id gate) always run BEFORE this feature's demotion decision -- a postimage that would be rejected outright is REJECTED, never silently demoted.
Feedback loop, so a demotion is never silent: every demotion response carries the
pending_id,demotion_reason, and anoperator_promptinstructing the model to inform the operator; a new read-onlykb_session_recap(source_session)MCP tool /GET /api/v1/session-recapREST route /kb session-recap SOURCE_SESSIONCLI subcommand reports N committed / M demoted-to-pending / K rejected for a session;kb_consultsurfaces ademoted_writesitem in itspending_actionsenvelope when the calling session has open demotions; andbin/kb-session-recap-hookis a ready-to-wire SessionEnd/Stop hook script (documented for Claude Code / Codex) that prints the recap one-liner automatically.CLI review gate:
bin/kb proposenever flows a demoted (demotion_reason) or secret-flagged (matching_pattern) proposal into its same-command interactive resolve (which defaults to accept without a TTY); it prints the operator prompt and requires an explicit laterkb resolve. The MCPkb_session_recaptool requires an authenticated principal when auth is configured, matching the REST route.Unaffected: the maintenance-ledger system write path (issue #113) and the operator pending-resolve path (which IS the promotion).
Provenance surfacing + consult hardening (issue #109). Revised decision: no
authority_state/allowed_useenum (rejected as a parallel vocabulary that could drift fromstatus); the real gaps are fixed at the root instead:Memory stamping. Server-rendered memory files (
kb_propose_memory) now carrytype: memoryandstatus: proposedin frontmatter (neither was stamped before), so the EXISTING status filter, status rerank, and compact deviation-only status emission all apply to an agent-written memory with zero new code paths. Promotion out ofproposedhappens at operator review time, not here.Memory-inbox in-force floor. A document under the memory-inbox prefix (
KB_MEMORY_INBOX_PREFIX, defaultmemory/inbox/) is now NEVER in force, regardless of claimed status. It covers both a legacy inbox file and forged frontmatter on an agent-written memory. Implemented as a newis_inboxcolumn derived once at index-build time (format.validate.is_inbox_path/memory_inbox_prefix, the single source also consulted by the write path) and composed into the existing single-sourcedis_in_forcepredicate and SQL fragments (index.pyschema version bumped 10 -> 11), not a forked predicate.Computed
in_force: boolon verbosekb_getand verbosekb_searchhits, derived from the single-sourced predicate (status class AND validity window AND not-inbox AND, composing the issue #110 slice-2 graph rule, not-graph-excluded). Never stored in frontmatter: it is a serving-layer-only derivation (see SPEC.md's new "runtime envelope" note). Compact responses emit it deviation-only (in_force: falseonly when the doc is NOT in force; an in-force doc's compact shape is byte-for-byte unchanged): the compactstatus/freshnessfields reflect raw frontmatter, so a memory-inbox doc with a forgedstatus: activewould otherwise read as an ordinary current rule with no correcting signal. The existing deviation-onlystatus/freshnessemissions stay as they are.evidence: list[str]optional parameter onkb_propose_memoryandkb_propose_edit(max 10 items, 500 chars each). For a memory proposal it is rendered into frontmatter via the sameyaml.safe_dumppath astags(so a secret-shaped evidence item is caught by the existing full-postimage secret scan, no separate scan needed); for an edit proposal (whose postimage is caller-supplied verbatim, with no template) it is validated and redacted the same wayreasonalready is. In both cases it is persisted in pending meta, echoed on the propose/commit audit events, and surfaced bykb_list_pending.kb_list_pendingsurfaces provenance it already persisted:source_session,reason, and the newevidencewere already written into pending meta at enqueue time but never returned; they are now on thePendingEntrymodel (Nonewhen absent, e.g. a memory proposal has noreason).
Maintenance ledger: committed corpus-state audit +
pending_actionsCTA surface (issue #113). A frontmatter-only markdown doc (defaulttooling/maintenance-ledger.md,KB_MAINTENANCE_LEDGER_PATH) is now computed at every index build: whetherstatusis present on every indexed document (except reserved filenames), plus a capped list (50 pathsa total count) of the ones missing it (the migration vehicle for making
statusmandatory), and, consuming issue #107 validity data, documents that recently expired or are expiring soon within configurable windows (KB_MAINTENANCE_RECENTLY_EXPIRED_DAYS/KB_MAINTENANCE_EXPIRING_SOON_DAYS, both default 30 days), each capped the same way. When the computed state changes since the last committed copy, the ledger is committed through the same serialized write/commit machinery every other write uses (system agent identitydata-olympus-system, a normalmaintenance_ledgeraudit event); a commit failure is logged/audited and never breaks index refresh or serving. The ledger doc is excluded from its own audit and always carries valid concept frontmatter, so it lints clean on its own merits rather than relying on reserved-filename semantics. The same computed state drives a newpending_actionsfield (a list of{kind, message, count}items) onkb_consultandkb_healthresponses, deliberately neverkb_search, present only while the corpus is dirty; both tool descriptions now instruct the model to surface open items to the operator and act on them only with operator confirmation.kb health's plain-text CLI summary displays any open items. Silencing is automatic: remediate, the next index build flips the flag, the next ledger commit records it, andpending_actionsdisappears with no manual acking. Seedocs/operations.md§5.
Validity/freshness frontmatter metadata with hard expiry semantics (issue #107, format
0.2). Concept documents may now declare an optional nestedvalidityobject:valid_from,valid_until,last_verified,recheck_by(ISO date or datetime, normalized to a date at index/lint time), and free-textverification_source. Indexed as columns on the docs table (schema version bumped 9 -> 10, on top of the lifecycle-edges bump below). See SPEC.md section 4.2.data-olympus init <dir>bundle scaffold command (issue #66). Creates a new knowledge bundle: the tier directories (--tiers, defaultuniversal, tech-stacks,projects,decisions,workflows,tooling), a rootindex.mdcarrying thespec_version/okf_versionfrontmatter, atemplate.mdauthoring scaffold, and one example concept document per SPEC-supportedtype(standard,decision,workflow,project,memory,reference), including a realsuperseded/superseded_by/supersedespair andapplies_whentrigger metadata. The generated bundle passesdata-olympus lintwith zero errors and zero warnings and builds cleanly underdata-olympus index. Refuses to scaffold into a non-empty directory (no--forcein this slice).Typed lifecycle relationships: parsed, indexed, and lint-validated (issue #110, slice 1).
supersedes(scalar ID or list of IDs, normalized to a list at parse time so both shapes the ADR importer emits lint clean),superseded_by(scalar ID), and a newcontradictsfield (scalar ID or list of IDs, normalized to a list the same way assupersedes; unresolved-conflict evidence that never affects retrieval ranking) are now parsed intoParsedDocand extracted into a newedgestable (source_id,rel,target_id) at index-build time (schema version bumped 8 -> 9).kb lintgained a cross-file pass (an in-memory id map over the discovered bundle, no database) that reports: errors for a malformed field shape, self-supersession, or a supersession cycle of any length; warnings for a dangling target id, an asymmetric supersedes/superseded_by pair, a path-shaped target value (concept ids are the only stable target, never paths),superseded_byset on an in-force document,status: supersededwith nosuperseded_by, and an in-forcecontradictspair.SPEC.mdsection 4.2 documents the normalized shapes and the full lint severity list; the "not validated today" note is removed. Slice 2 (a separate change) will consume the edges table for in-force graph exclusion,kb_get/kb_searchsurfacing, and a health counter.Supersession edges become executable retrieval policy (issue #110, slice 2).
in_force=truenow ADDITIONALLY excludes any document that is the TARGET of asupersedesedge whose SOURCE document is itself in force (the full status-class-AND-validity-window predicate, not merelystatus: superseded): adraft, expired, or already-retired document can never retire another document just by naming it insupersedes.Behavior change: a document superseded only via an edge (its own
statuswas never flipped) is now excluded fromin_force=trueresults, even though its own status class would otherwise qualify it. It remains visible in a default (non-in_force)kb_search, same as anupcomingdocument. This closes the "forgotten status flip" gap.A mutually-supersessive in-force cycle (already a
kb lintERROR at parse time) is NOT special-cased: every member independently satisfies the exclusion rule, so ALL members of the cycle are excluded fromin_force=true.A dangling edge (source or target id with no corresponding document) excludes nothing and is not counted; the exclusion query joins
edgestodocson both ends, never trusting either raw id.kb_consultnow queries within_force=true(previously it ran a plain default search), so it never returns a not-yet-in-force, retired, or graph-excluded document as a "governing rule". The unconditional not-expired exclusion already gave this guarantee for expired docs; this closes the same gap for status/graph exclusion.New
graph_excluded_docshealth counter (kb_health//api/v1/health, alongsidemalformed_frontmatter/malformed_validity): the count of documents currently excluded by this rule, evaluated LIVE at health-read time against the current date from the same SQL definition (format.validate.graph_excluded_ids_sql) the retrieval-time filter uses, so the counter can never drift from the filter it reports on, even across a date boundary where an in-force source's validity window opens or closes between index rebuilds. Exposed on the Index asgraph_excluded_count(today=...)(injectable date for deterministic tests; health reads the real wall clock, bounded by the short health cache).Retirement is explainable.
kb_get(regardless of in-force/graph- exclusion status, same as it already ignores expiry) gainssuperseded_by: the sorted UNION of the document's own frontmattersuperseded_byclaim and any reversesupersedesedge naming it -- ONE consistent computed shape covering both the honest self-declared case and the forgotten-status-flip case.kb_getalso gainscontradicts(the document's own frontmatter list) and the computed reversecontradicted_by. All three are omitted from the compact response when empty.contradictsis annotation only: it has NO filtering or ranking effect anywhere in the retrieval path.Compact
kb_searchhits gain a deviation-onlysuperseded_by(the same computed union above), omitted when the document is not superseded; computed and attached to every hit regardless ofin_force, so a plain search result still explains why a hit is historically superseded.SPEC.mdsection 4.2 documents the graph-exclusion rule, the health counter, and the surfacing contract.
Secret-scanning gate on the write path (issue #71). Every commit path (
kb_propose_memory/kb_propose_editauto-commit,kb_resolve_pendingapprove, including a resolvededited_text, and onboarding bootstrap) now scans the final postimage for credential-shaped content (PEM private-key blocks, GitHub/Slack tokens, AWS access key ids, genericpassword=/secret=assignments with a non-placeholder value, and connection strings with an inline password) before anything is written to disk. A match rejects the write with a distinctrejected_secret_detectedstatus; nothing is committed and no pending entry is created on the auto-commit/bootstrap paths. Only the pattern name and an approximate line number are ever surfaced in the response, the audit event, or a log line, never the matched secret value. Operators can extend the built-in pattern set withKB_SECRET_SCAN_EXTRA_PATTERNS(comma-separated regexes; an invalid entry is logged and skipped, never crashes the server).kb_resolve_pendinggains an operator-onlyoverride_secret_scanflag to consciously commit a flagged postimage that is a confirmed false positive; the override is recorded on the resulting audit event, and no auto-commit or bootstrap path exposes it (kb resolve --override-secret-scanon the CLI). A low-confidence proposal containing a secret still enters pending, so the override workflow is not defeated, but its scan result is redacted to a pattern name in the propose response and tagged (secret_scan_flagged) on the entry sokb pendingsurfaces the warning without ever showing the matched value; the memory filename slug itself falls back to a neutral placeholder instead of embedding the flagged text. The scan runs before the content-validation gate on every commit path so an invalid-document rejection can never echo a credential value through its own error message. The gate also covers the fields around the postimage: a credential-shapedtarget_path(filename) is rejected on edit/bootstrap/resolve without echoing the path back, a flagged editreasonis replaced with a redacted note before reaching pending meta / push metadata / audit events, and a flagged memory tag is stored redacted in pending meta. Extra patterns (KB_SECRET_SCAN_EXTRA_PATTERNS) with the classic nested-quantifier ReDoS shape are rejected at load time alongside invalid regexes, and every accepted custom pattern executes through theregexengine (a new runtime dependency) with a hard 1-second match timeout so a catastrophic pattern cannot hang the single-writer write path.OKF profile document (issue #108).
docs/okf-profile.md: a field-by- field reference distinguishing stable governance extensions (id,type,status,tier,applies_when,supersedes/superseded_by/contradicts,validity) from runtime-only serving envelope fields (in_force,freshness,contradicted_by,pending_actions) and from experimental candidates tracked against open OKF ecosystem discussions (structuredrelationships:, version-keyed validity, a confidence/reliability axis). Documents the issue #109 decision to reject anauthority_state/allowed_useenum, and a short factual comparison againstinkxel/throughline,inkxel/dotKnowledge, anddynamicfeed/signed-okf. Cross-linked fromREADME.md,WHY.md, andSPEC.mdsection 11. Documentation only; no behavior change.
Changed
BEHAVIOR CHANGE:
kb_consultnow hard-filters retrieval to the in-force class (issue #109). Previouslykb_consultran an UNFILTERED search, so an unreviewed agent-written memory, a superseded/deprecated/rejected decision, a draft/proposed document, an expired or upcoming doc, or a memory-inbox file could all be handed back as "the" governing rule for an intent, the single highest-value gap this change closes.kb_consultnow passesin_force=trueinternally, so onlyactive/accepted/approveddocuments within their validity window and NOT under the memory inbox are ever returned as rules. Operational caveat: a document with nostatusfield at all (or a status outsideIN_FORCE_STATUSES) now stops appearing as a governing rule viakb_consult, where it previously did (the prior unfiltered search surfaced it same as any other hit).statusbecoming a mandatory field is tracked separately (issue #114); until a bundle finishes that migration, a status-less doc that WAS relied upon as an implicit rule viakb_consultwill needstatus: active(oraccepted/approved) added before it is retrievable there again. Plainkb_search/kb_getare unaffected (they still surface a status-less doc by default); this change is scoped to the enforcement-facingkb_consultretrieval path only.BEHAVIOR CHANGE: a document past its
validity.valid_untilnow leaves the DEFAULTkb_searchresult set, not justin_force=truequeries. Previously there was novalidityconcept at all, so nothing was ever excluded on this basis; a bundle that starts dating its documents will see expired ones silently drop out of ordinary search results (they are still reachable viakb_get(id),include_expired=true, or the newvalidity_state=expiredaudit facet). Rationale: unlike a superseded document, an expired one has no named successor to outrank it, so left visible it could surface as the top hit and incorrectly govern.A document with a future
valid_from("upcoming") is NOT excluded by default search, only byin_force=true; it is flaggedfreshness: "upcoming".A document past
recheck_by("stale") stays in force and visible; it is flaggedfreshness: "stale"(advisory only).kb_searchgainsinclude_expired(defaultfalse) and avalidity_statefacet ("expired","stale","expiring_within:N") for audit queries.kb_getby id always resolves regardless of expiry, returning the fullvalidityobject plus a computedfreshnessindicator.kb_consultnever returns an expired document (as of issue #109 below, via thein_force=truehard filter; previously via the default search path's own expired-exclusion).Compact search hits gain a deviation-only
freshnessfield (stale/expired/upcoming), omitted when fresh or absent.kb lintgains three new WARNING-only findings (never errors, since they are wall-clock-relative):recheck_byin the past;valid_untilin the past whilestatusis in the in-force class; a malformedvalidityvalue (treated as absent for indexing/search, fail open).kb_health/data-olympus reportgain amalformed_validitycounter mirroring the existingmalformed_frontmatterone.New
data-olympus validity-reportCLI subcommand lists expired and soon-to-expire documents by scanning a bundle directory directly.timestampis unaffected and remains content-change metadata only; SPEC.md now explicitly forbids deriving staleness fromtimestamporlast_modified.
Fixed
data-olympus indexsilently regenerated zero indexes for a bundle whose absolute path passes through a skip-named ancestor (found in companion review of theinitscaffold).regenerate_indexesmatched skip-directory names (.git,.venv,node_modules, ...) against the bundle's absolute path components instead of the bundle-relative ones, so a bundle located under e.g. a.venv/ornode_modules/parent produced "wrote 0 index.md file(s)" with no error. Skip matching is now relative to the bundle root, matchingdiscover_bundle_filesin the lint pipeline.
0.3.5 - 2026-07-06
Fixed
Ambiguous rejection reason for writes outside the deployment's indexed prefixes.
kb_propose_edit(andkb_propose_memory) rejected a well-formed, non-malicious path outsideKB_INDEXED_PREFIXESwith the same opaque reason string (traversal_or_excluded/not_md_or_excluded) used for an actual traversal or control-character attempt. A syntactically fine path outside the configured prefixes is an ordinary deployment-configuration fact, not a structural or security rejection - conflating the two sent an operator hunting for a nonexistent traversal bug during a company-knowledge KB audit session (operator/laptop.md, readable and indexed for search, came back rejected on write with no indication it was simply outside the deployment's writable-prefix allowlist).rejected_path_not_indexableresponses (and audit events) now carry a specific reason:structurally_invalid(empty/control-chars/absolute/traversal/excluded segment),not_markdown, ornot_in_indexed_prefixes.Investigated the same session's second report (a
rejected_stale_baseresolve appearing to "leak" its path lock) and confirmed it is not a bug:restore_resolvedeliberately keeps the path lock held after a gate rejection so the operator's still-pending proposal can't be clobbered by a concurrent write to the same path (documented behavior, hardened across multiple rounds of review).rejecting the stale entry (which does release the lock) and re-proposing fresh, exactly what that session did, is the correct and only intended recovery for a pending entry with a permanently-wrong base marker - there was nothing to fix.
0.3.4 - 2026-07-05
Fixed
Enforcement gate false-positives on prose that merely mentions a governed keyword, for tool calls with a known file path.
IntentClassifier.classifyscannedaction_diff(the raw Write/Edit content or Bash command text, up to 4000 chars) for free-text keywords likeauth,rls,tenant,secretin addition tointent, for every tool call. A scratch write whose text simply discussed one of those words as a topic - not an actual governed action - tripped the pre-tool gate regardless of the real (ungoverned) write target, and a freshkb_consultcall did not clear it because the consult's own classification (intent-only) and the gate's classification (intent + action_diff) disagreed on whether the action was governed. Keyword matching onaction_diffnow only applies when the tool call carries noaction_path(Bash commands and OpenCode'spatch, whose content is the only signal available); when anaction_pathis present (Write/Edit/MultiEdit), classification uses the path globs and dependency command patterns, unchanged, so this keeps full detection for real governed edits (a patch touchingpackage.json) without flagging unrelated prose in files the path globs don't consider governed. Found via a company-knowledge KB audit session that reproduced it live (a scratch.txtfile containing "RLS tenant boundaries auth trust model" was blocked with no governed path or command in sight); independently reviewed viacodex execbefore release.The audit's other finding (CX1: the pre-tool gate and the pre-commit
report --stagedgate resolving two different workspace keys) was already fixed in v0.2.0 (#64, worktree-invariant workspace key); no further change needed. It was still visible in that audit only because it predated the deployed kn-dev redeploy.
0.3.3 - 2026-07-05
Fixed
MCP streamable-HTTP session churn under long-lived clients. A persistent client (notably
opencode serve) that holds theGET /mcpSSE stream open was being forced to reconnect on a cycle, piling up abandoned transports on the server and leaking event listeners on the client until it degraded. Two causes addressed:The activity middleware now keeps a session non-idle for as long as its SSE stream is actually open (re-stamping every
KB_SESSION_TOUCH_INTERVAL_SEC, default 30s, clamped to a third of the idle window), so the reaper evicts a session only after its stream has genuinely closed. With that safety in place, the idle-reap default (KB_SESSION_IDLE_TIMEOUT_SEC) drops from 1800s to 300s, so an abandoned handshake is cleared in minutes instead of half an hour.The shipped ingress manifest (
deploy/k8s/ingress.yaml) now sets a longproxy-read-timeout/proxy-send-timeoutand disables buffering, so a proxy with a short default idle timeout (nginx: 60s) no longer closes the SSE stream and triggers the reconnect loop. Seedocs/serving.md.
0.3.2 - 2026-07-05
Fixed
gate/checkno longer self-DoSes multi-agent fleets with 429s. The enforcement hook callsPOST /api/v1/gate/check(andkb_gate_check) once per gated tool action for every agent, but it shared the write/consult quota (KB_RATE_LIMIT_PER_HOUR, default 100). Behind an ingress with no auth, all clients collapse to a single limiter bucket, so a few active agents exhausted the hourly quota in minutes and everything after got429 Too Many Requests. gate-check now has its own ceiling,KB_GATE_CHECK_RATE_LIMIT_PER_HOUR, defaulting to0(unthrottled, consistent with the read routes) since it is a mandatory per-action probe that does only cheap classification and no writes. Set a positive value for an explicit backstop.consultandonboarding/cleanup-planstay throttled byKB_RATE_LIMIT_PER_HOUR.
0.3.1 - 2026-07-05
Fixed
prepare-gitinitContainer is now idempotent across pod restarts. It staged the deploy key withcp /etc/git-key-mount /state/git-keythenchmod 0400. Because/stateis a PVC, on any pod restart the0400(unwritable) key already exists and the plaincpfails underset -e, leaving the pod stuck inInit:Error. The manifest nowrm -fs the staged key before copying (the/statedir is writable to uid 65534 viafsGroup). Only the first boot was ever exercised before; akubectl rollout restartor any pod recreation hit it.
0.3.0 - 2026-07-03
Added
Serving/ops hardening (WP3a). Production-readiness work on the serving path, container, and operator docs:
Readiness split from data staleness. New
GET /readyz(200 when the process is up and the index is loaded, independent of staleness) andGET /livez(always 200). The Kubernetes readiness probe now targets/readyzinstead of/api/v1/health, so a git-remote outage that makes the KB stale no longer ejects a single-replica pod from the Service and turns "reads are slightly old" into a hard 503./api/v1/healthkeeps its 503-on-degraded contract for thebin/kb --no-staleflag; alert on it rather than probing it.Audit-log rotation with chain continuity (
KB_AUDIT_MAX_BYTES). Size-based rotation that carries the tamper-evident hash chain across files (the first event of a new segment links to the last hash of the previous one), soverifyvalidates across the rotation boundary. The read path can include rotated segments for--sincequeries and bounds the reverse scan. Off by default (single-file, backward compatible; old single-file logs still verify).Proxy-header configuration (
KB_TRUSTED_PROXIES). Enables uvicornproxy_headers+forwarded_allow_ipsso the rate limiter sees the real client IP behind an ingress instead of collapsing every client into the proxy address. Off by default (X-Forwarded-For ignored) so a direct client cannot spoof its address.Rootless container. The root+
gosuentrypoint phase is removed. Deploy-key staging and the first-boot/kb-mainclone move to a non-rootprepare-gitinitContainer; the main container and initContainer both run as uid 65534 withrunAsNonRoot: true, all capabilities dropped, and a read-only root FS, so the manifest passes the restricted Pod Security Standard. The base image is digest-pinned, the git SSH host is build-arg + runtime-env configurable (KB_SSH_KEYSCAN_HOST), Docker Compose binds127.0.0.1:8080, and the Ingress is commented out of the default kustomization (opt-in after auth is set) with adeploy/k8s/README.mdnote.malformed_frontmatterin health. The count of docs whose front-matter was present but malformed at the last index build (from WP2b) is surfaced on the health payload. It is a warning signal and deliberately does not flipdegraded.Operations runbook. New
docs/operations.md(linked from README anddocs/serving.md) covering backup (git remote plus the audit chain / pending proposals / unpushed commits it does not cover), upgrade (image bump, taxonomy compatibility, index rebuilds), recovery playbooks (degraded/fetch-failed, history rewrite, frozen and rebase-conflict-demoted push entries, orphaned locks), and the health/readiness/alerting model.
Write-pipeline integrity core (epic #72). This wave makes the write-safety claims in
SPEC.mdsection 8 anddocs/serving.mdactually true; each item ships with regression tests, including two integration tests that drive a real second clone against a shared bare remote:Write serialization. A process-wide write serializer wraps the write →
git add→ commit → enqueue critical section, and a per-path advisory lock (now shared between the auto-commit path and the pending queue) prevents an auto-commit from racing a concurrent write or landing on a path with a pending proposal in flight (whose later approval would clobber it). The auto-commit path previously took no lock, so concurrentkb_propose_*calls in one session could interleave and one thread's commit could sweep another's staged file.Non-fast-forward push recovery. A push rejected non-FF (a second overlapping session moved
origin/main) is now classified distinctly from a network failure: the push loop fetches + rebases the session branch ontoorigin/mainand retries. On a rebase conflict the commit is demoted to a pending entry for operator resolution (with apush_conflict_demotedaudit event and the queue entry removed) instead of retrying identically forever. Pure contention (a repeated non-FF race) is retried in-line for a bounded number of passes and then demoted the same way, so it never silently freezes.CAS also enforces
base_commit. When the base marker is a specific commit (not theHEADsentinel), the blob the target had at that commit must equal the current blob, closing the bypass where a caller supplied onlybase_commit. If the base cannot be refreshed ontoorigin/main(remote unreachable / rebase conflict) while an enforceable marker was supplied, the write is rejectedrejected_stale_baserather than committed against a stale base.CAS enforcement.
base_commit/base_blob_sha/target_file_hashwere accepted and stored but never checked. They are now enforced at commit time on both the auto-commit and resolve paths: a supplied base marker that does not match the current content on the worktree's refreshed base is rejectedrejected_stale_basewithout committing. No marker → behavior unchanged.Content-validation gate. No validation ran on the write path, so malformed YAML, a forged/duplicate
id, or an invalid enum value committed and pushed toorigin/main, and a duplicateidbroke every subsequent index rebuild (one bad write, persistent degraded state). Every postimage is now format-validated plus checked for a duplicateid, using the EFFECTIVE id the rebuild assigns (explicit frontmatteridOR the path-derived id) against BOTH the live index and the session worktree's committed tree, and including reserved files, before commit; failures are rejectedrejected_invalid_documentwith machine-readable errors. Rendered memories pass the gate as a cheap self-check. The bootstrap path now commits its whole file bundle through the same serialized, per-path-locked, validated, reset-on-failure write path as the single-file writes (it previously wrote and committed directly, unserialized and ungated).Atomic pending claim.
approve/rejectwere get-then-remove, so two concurrent resolves of the same id both committed. Resolution now claims the entry viaos.renamebefore reading (test-and-set); the loser getsalready_resolved/ not-found. Orphaned path locks (a crash between lock create and entry write) are reclaimed by the pending GC loop. The gated resolve path holds the claim + path lock across the CAS/validation gates and restores the pending entry if a gate rejects, so an operator's proposal is never consumed without a commit.Post-commit enqueue is recoverable and truthful. A push-queue enqueue failure after a successful commit no longer turns a made commit into an exception path (which would drop the bootstrap in-flight guard and the resolve claim). Instead an in-process recovery re-enqueue is attempted so the live push loop still drains it, and the response
push_stateis truthful:queuedonly when the entry landed, elseenqueue_failed_recovery_pending(the durable commit on the session branch is republished by startupinit_recovery).Unique memory filenames.
<date>-<slug>.mdcollided for same-day, same-slug memories and the second auto-commit silently overwrote the first; a short session/body-derived uniquifier is now appended.Pending expiry audit. Expiring a >24h pending entry now emits a
pending_expired/auto_rejectedaudit event instead of rejecting silently.Ordering of side effects. The commit message and all gates are now evaluated before the file is written and
git add-ed; on any post-add failure the worktree is hard-reset so no staged leftover is swept into the next commit.Git identity in the shipped image. The Docker entrypoint (and the server
main()as a fallback) now set a defaultGIT_AUTHOR_*/GIT_COMMITTER_*identity (overridable viaKB_GIT_AUTHOR_NAME/KB_GIT_AUTHOR_EMAIL) so a commit inside a fresh container no longer fails with "who are you".
PyPI distribution +
data-olympus setupwizard (issue #70). The package is now installable from PyPI (uvx data-olympus,uv tool install data-olympus), published by the release chain via GitHub Actions Trusted Publishing (OIDC, no API token). A new.github/workflows/publish-pypi-reusable.ymlbuilds the sdist + wheel, runstwine check --strictand a wheel smoke test, and uploads throughpypa/gh-action-pypi-publishto thepypienvironment;tag-release.ymlcalls it off the same decided tag as the container image, and.github/workflows/publish-pypi.ymladds a PR-time dry run (build + twine check, no upload) plus a manual-tag fallback. The publish path is inert until the operator completes the one-time pypi.org publisher setup documented indocs/releases/pypi-trusted-publishing.md. The wheel now ships the enforcement machinery (bin/kb-enforce-hook,_kb_enforce.py, the OpenCode gate plugin) as package data so the wizard works from a clean install.data-olympus setup(newsrc/data_olympus/setup_wizard.py+ CLI subcommand): an idempotent, first-run/update wizard that probes the server endpoint (/api/v1/health), detects installed agents (Claude Code, Codex, Gemini, OpenCode), writes each agent's MCP registration with timestamped backups (preferringclaude mcp add/codex mcp addwhen the CLI is present, merging documented config files otherwise), offers enforcement-hook install via the existing provider registry, and prints a doctor summary (endpoint reachability, agents wired, hooks installed, installed vs latest version with offline-tolerant PyPI/GitHub lookup).data-olympus setup --checkis a fully read-only summary and update-check path.data_olympus.__version__now reads from the installed distribution metadata instead of a hand-maintained literal, so it cannot drift from the packaging version the release chain tags on.data-olympus importcommand to migrate an existing agent-rule corpus into a governed draft bundle (issue #67). Supports six source kinds via--kind:Flat rule files (
claude-md/agents-md/gemini-md/cursorrules): heuristic splitting into candidate concepts (markdown headings first, then blank-line-separated bullet clusters for heading-less files). Each becomes a draft with stamped frontmatter (generated<prefix>-NNNid,type: standard,status: draft, tier/category from flags, title from heading, description from the first sentence, tags heuristically from content). The original text is preserved verbatim as the body.adr(adr-toolsdoc/adr/NNNN-title.mddirectories): maps number+title toADR-NNNN/title, parses the## Statussection intosupersedes/superseded_byreferences, preserves the parsed adr-tools status in a non-activatingsource_statusfield,type: decision.okfbundles: normalizes alias field names into the profile, fills missing required fields with draft-safe defaults, and reports every inference.Everything lands as
status: draft; nothing is committed to git and nothing auto-activates (imported ADRs keep their original status insource_statusfor the reviewer). Duplicate ids are refused. The command writes files into the output dir and prints a human-readable and (with--json) machine-readable report: files created, sections skipped as too short, inferences made, and "needs human review" items. It runs the existing lint machinery over the output (lint-clean on the happy path) and points atkb_cleanup_planfor dedup when importing into an existing KB. Re-running into the same output dir is refused unless--forceis given (no silent duplication or clobber). Type/status/tier vocabularies are single-sourced fromformat/validate.py.
Specified
applies_whenin SPEC.md (WP4c, 0.3.0).applies_whenis the highest-weight indexed field inIndex.search(tied withtitle, ahead ofdescriptionand body) and one of the three discriminating columns for theabstainsignal gate, but was previously implemented and used in the benchmark story without ever being documented. SPEC.md section 4.2 now lists it as a recommended field with authoring guidance (short verb phrases in an agent's own vocabulary) and a worked example;docs/adoption.mdgained a matching authoring section. Documented honestly: unliketags, a malformedapplies_whenproduces nokb lintwarning today.Upgraded
example-bundle/to demonstrate the format's remaining differentiators (WP4c, 0.3.0): a real supersession pair (STD-U-003/STD-U-004,status: superseded/supersedes/superseded_by), amemorydocument and areferencedocument (the two concept types the bundle previously lacked), andapplies_whenon the standards most likely to be hit mid-task.example-bundle/index.mdand the new directoryindex.mdfiles were updated to match;uv run data-olympus lint example-bundleremains zero-error.Quickstart lifecycle-aware retrieval demo (WP4c, 0.3.0).
docs/quickstart.mdgained a verified section showing default search (soft status rerank: the activeSTD-U-004outranks the supersededSTD-U-003it replaced, both still returned) versusin_force=true(hard filter:STD-U-003excluded from the result set entirely), against the upgraded example bundle.CI doc-consistency guard (WP4c, 0.3.0):
scripts/check_doc_consistency.py, wired as a newdoc-consistency-guardCI job. Fails when thetype/status/tierenum lists restated in SPEC.md ordocs/adoption.mddrift fromdata_olympus.format.validate's canonicalTYPES/STATUSES/TIERS, or when SPEC.md's reserved-filename list drifts fromvalidate.RESERVED. Checks every restatement of an enum in a file (SPEC.md statestype/statustwice; both are now checked, not just the first), tolerant of reordering, rewrapped lines, and an Oxford "or".Cheap OKF minimal-field structural check (
tests/test_okf_minimal_fields.py, WP4c, 0.3.0): asserts every example-bundle concept doc has non-emptyid/typeand the bundle-rootindex.mddeclaresokf_version. This is a structural floor, not a conformance test; the module docstring states exactly what it does and does not prove.Benchmark re-cut with honest baselines and regenerated public numbers (epic #75, WP2c). Methodology fixes so the benchmark measures retrieval, not string-echo, and so every attribution is defensible:
Status-aware BM25 baseline (
bm25-status-aware): the same BM25 ranker that also readsstatusfrontmatter and skips superseded/deprecated docs. It isolates "the win is having governance metadata" (serves-stale 0.000, like data-olympus) from "the engine is a better ranker".Honest ranking for the strawman baselines.
whole-dumpno longer scored on recall@k/NDCG/MRR (it does not rank;ranks = False, reported only on token cost / precision / order-free Contains-Gold);grep-readnow ranks by query-term match count instead of alphabetical file order.De-leaked synthetic corpus. Lifecycle answer-vocabulary ("previous", "current", "(old)"/"(current)") removed from doc bodies and titles; the lifecycle signal now lives only in
status+ the supersedes chain, and old/new pairs are lexically identical. Shared distractor vocabulary added. Remaining known leak (theexacttopic-word echo) is documented on purpose.serves_stalemetric (headline lifecycle metric): fraction of supersession-topic queries where the retired doc reached the payload at all (tiebreak-independent), replacing the tiebreak-sensitive staleness rate as the honest signal.Bootstrap confidence intervals (
metrics.bootstrap_mean_ci, seeded and deterministic): every reported mean now carries a 95% percentile-bootstrap CI.Normalized token policy: token cost reported both as-shipped and under a normalized policy (top-1 full doc per method), so the token comparison separates retrieval from response-payload convention.
Grown governance strata for non-degenerate CIs: 30 governing topics, 10 supersession pairs, 31 distractor topics (trigger_covered n=30, negative n=31, supersession n=10, paraphrase_uncovered n=74 after enforcing FTS-token disjointness from gold triggers).
Committed real-corpus result:
benchmarks/real_corpus_eval.py --lexical-onlyover the committedexample-bundle(18 hand-authored docs, 9 paraphrase queries) writes a reproducible non-templated number tobenchmarks/real_corpus/example_bundle_result.json.Docs-drift guard:
docs/comparison.mdandWHY.mdbenchmark tables are generated from the committed result JSONs bybenchmarks/docs_tables.pybetween<!-- BENCH:* -->markers;scripts/check_benchmark_docs.py(wired into CI) fails the build if any quoted number drifts.All committed results (
benchmarks/results/,governance_results/, the embeddings ablation, the real-corpus result) regenerated against the hardened engine. With the B1in_forcefix, data-olympus exact recall rises from the 0.858 artifact to 1.000 and ALL recall to 0.582 (edging BM25's 0.572).
Deployable
in_forceandabstainmodes onkb_search(issue #68, epic #75).kb_search(MCP tool andGET /api/v1/search) gains two parameters:in_force: boolHARD-filters results to the in-force status class (active/accepted/approved) before ranking, excluding superseded/deprecated docs entirely rather than soft-downranking them. The class is defined once informat.validate.IN_FORCE_STATUSESand shared by the status reranker and this filter;Index.searchgained anin_forceparameter (the single-statusstatusfilter is unchanged and composes with it). The filter also applies to the dense (embedding) candidate source.abstain: boolapplies a signal gate: a query that matches no discriminating column (title/tags/applies_when) returns no hits withabstained: trueandabstain_reason: "no_signal_match"(distinct from an ordinary empty result). The gate logic is single-sourced indata_olympus.search_gate; the benchmark ablation now imports it. Fixes benchmark bug B1:benchmarks/methods/data_olympus.pyswitched from thestatus="active"filter (which silently droppedacceptedgold decision docs) toin_force=True. Committed benchmark numbers,report.md, anddocs/comparison.mdare intentionally NOT re-cut here (a later package does).
Actionable gate deny message: the BLOCKED text (and the
consult_requiredverdictreason) now includes the exact workspace key, the session id (the one parameter an agent cannot guess), and a copy-pasteablekb_consult(workspace=..., source_session=..., intent=...)call.GateCheckResponsegainedsession_idandworkspaceecho fields so MCP callers see the gate key.Write-pipeline visibility (issue #72, Wave 0).
kb_healthand/healthnow report the live pending-queue and push-queue sizes (previously hardwired to zero because the counters were static attributes that were never updated), plus a newpush_queue_frozencount so a stuck write path is observable.Startup push recovery. On boot the server scans every per-session worktree and re-enqueues any commit reachable from its HEAD but not from
origin/main, recovering commits orphaned by a crash betweengit commitand the push-queue enqueue. Already-queued shas are skipped, so recovery cannot double-enqueue.Periodic worktree GC. A background task honors
KB_WORKTREE_IDLE_SECto remove idle per-session worktrees (deferring any with unpushed commits) so KB checkouts no longer accumulate one-per-session forever. GC also deletes the session'skb-session/<safe_id>branch, so a returning session can create its worktree again instead of hitting a fatal "branch already exists" error.
Changed
BREAKING (default response shape): token-compact read-tool responses (#65). The read tools
kb_search,kb_get,kb_list,kb_outline, andkb_healthnow return a token-compact representation by default, because their consumer is almost always an LLM paying for every token. Measured against the example-bundle with a real tokenizer (tiktoken cl100k; reproduce withpython -m benchmarks.token_compact --tokenizer tiktoken) this saves ~37% of tokens onkb_search, ~42% onkb_list, ~41% onkb_health, and ~6-7% onkb_get(which keeps its full body and provenance); 26.1% aggregate. What changed in the default shape:kb_search: each hit is{id, title, snippet}plusstatusonly when the hit is not currently in force (e.g.superseded/deprecated) andtypewhen set. Thequeryecho, the per-hitpath, and the raw bm25scoreare dropped; snippets are capped at 160 chars. Array order still conveys rank; to read a hit in full, callkb_get(id).kb_get: keeps the fullcontent_markdownbody (unchanged) plussource_commit/last_modifiedprovenance, and trims the envelope:path,git_remote_url, andlast_modified_sourceare dropped, and emptystatus/type/applies_when/descriptionare omitted.kb_list: drops per-entrypath; omits a nullcategory.kb_health: keeps the core snapshot and omits diagnostic fields that are null/empty (a no-error steady state no longer emits a run of nulls).kb_outline: already lean; its shape is unchanged.
Opt out with
verbose=true(a REST query parameter, or theverboseargument on each MCP tool), which restores the exact pre-#65 JSON shape byte-for-byte. The bundledkbCLI requestsverbose=trueautomatically, so its output is unaffected. Any first-party or third-party consumer that parsed the dropped fields (query, per-hitpath/score, the full health envelope) must either adopt the compact shape or passverbose=true. A committed measurement harness (benchmarks/token_compact.py) reproduces the per-tool token table under either tokenizer (python -m benchmarks.token_compactdefaults to the dependency-freesimplesplitter; add--tokenizer tiktokenfor the cl100k numbers quoted above), and a tokenizer-based regression test (tests/test_token_compact_budget.py, with both asimplebudget and atiktokenaggregate-savings guard) fails loudly if a future change re-bloats the compact default.MCP/REST write-surface parity (deferred WP0b items). The MCP
kb_resolve_pendingtool now applies theKB_MAX_POSTIMAGE_BYTESedited_textcap (REST already did), and the MCPkb_consult/kb_gate_check/kb_cleanup_plantools now apply the shared rate limiter consistently with their REST counterparts.BREAKING (auth): A
KB_AUTH_PRINCIPALSentry that omits an explicitcapabilitieslist now defaults to least privilege (read,propose) instead of all capabilities. Previously such an entry silently receivedresolveandauto_commit, letting an agent approve its own proposals and skip operator review. Action required: any deployment relying on the old implicit full-capability default must now list the capabilities it needs explicitly, e.g.{"name": "operator-agent", "token": "...", "capabilities": ["read", "propose", "resolve", "auto_commit", "bootstrap", "record_event"]}. The singleKB_AUTH_TOKENoperator principal is unaffected and still receives all capabilities.Search pipeline hardening (WP2b, epic #75): false-positive reduction, dense-channel fixes, and index/query performance.
Penalized expansion backfill (finding a). Query expansion (synonym #38 + co-occurrence #40) previously folded derived terms into the single FTS5
MATCH; bm25 has no positional preference, so a doc matching only a synonym got full idf weight and could outrank a doc matching the user's actual term.Index.searchnow matches the user's own terms in a PRIMARY pass and the expansion terms in a SEPARATE penalized backfill pass whose hits can only rank BELOW the worst primary hit. The "expansion is down-weighted" claim in thequery_expansion/cooccurrencedocstrings is now actually true.Rank-class invariant (finding d).
SearchHitgained arank_class(RANK_CLASS_PRIMARY/RANK_CLASS_BACKFILL); the status and hybrid rerankers now sort by(rank_class, score). This makes the documented "backfill hits are never reordered above primaries" invariant genuinely true (the previous score-only "strictly worse" floor could be lifted by an active-status boost or the hybrid re-normalisation).Co-occurrence defaults hardened (finding b):
min_count2->3,min_pmi0.0->0.1, plus a corpus-size floor (KB_COOCCURRENCE_MIN_DOCS, default 50) that auto-disables the table on small corpora where PMI is noise, and a per-doc unique-token cap (KB_COOCCURRENCE_MAX_DOC_TOKENS, default 400) that bounds the O(n^2) pair counting so a multi-thousand-word doc is no longer a build-time memory cliff.Gated trigram index (finding c): the
fts_trigramsecondary table (a full second tokenized copy of the corpus, ~2-3x index-size / build-time tax) is now built and populated ONLY when the trigram fallback is enabled (KB_TRIGRAM_MODE=on), not unconditionally.Dense-channel intent vocabulary (finding f): the text embedded at build time now includes
applies_whenandtags(the curated intent phrases a paraphrase query embeds near), not just title/description/body.
Enforcement gate policy: the gate now clears only on an explicit consultation, not on any recent consult (behavioral change). Previously the gate meant "an HTTP call to
/consulthappened recently in this session", so the per-agent installers' per-prompt auto-consults (Claude/CodexUserPromptSubmit, GeminiBeforeAgent) kept the ledger perpetually fresh and the gate only fired during autonomous stretches longer than the TTL. Consults now carry atrigger: an explicit consult (a deliberatekb_consultcall, the default, and any old client that omits the field) clears the gate, while a prompt_hook consult (the installer auto-consults, now marked as such) is recorded for audit/compliance and injects rules but never clears the gate. A prompt-hook consult never downgrades a still-fresh explicit consult. Deployments that relied on the per-prompt auto-clear will now see the gate require an explicitkb_consultbefore governed edits. Backward compatible for old clients (a bare consult with notriggercounts as explicit). Seedocs/enforcement.md.Frozen push-queue entries (those that hit
max_attempts) are now skipped by the retry loop instead of being retried every interval forever; the freeze is logged once at WARN and surfaced viapush_queue_frozenin health. Seedocs/serving.mdfor the operator unfreeze procedure.The push-retry loop now drains the push queue in a thread executor (like the git-pull loop) rather than on the event loop, and
GitOps.pushtakes a bounded timeout (default 60s), so a hung git remote can no longer block the loop that answers the readiness probe. A push timeout is classified as a retryable failure.Quickstart and adoption docs lead with the
uvx data-olympus/uv tool installinstall path and the setup wizard (issue #70). The adoption guide's per-agent wiring section is corrected to the current registration surfaces (Claude Code viaclaude mcp add, not a hand-edited~/.claude/settings.json; Codex viacodex mcp add; Gemini's"type": "http"; OpenCode'smcp-remotewrapper).
Scope note: this wave makes write-path failures visible and recovers orphaned commits. It does not fix the non-fast-forward publication stall itself (rebase-retry redesign); that is tracked separately as Wave 1.
Fixed
Release delivery for 0.3.0. Two release-pipeline bugs surfaced on the first 0.3.0 run and are fixed so the tag, image, and PyPI package publish together: the Docker image build now copies everything
pyproject.tomlreferences into the build context before the project-installinguv sync(README.md,LICENSE,NOTICE, and thebin/machinery force-included into the wheel); and the PyPI publish runs inline intag-release.ymlrather than through a reusable workflow, which PyPI trusted publishing does not reliably match through.Stale auto-commit path locks are reclaimed. A hard kill while an auto-commit held a per-path advisory lock left the lock on the
/statevolume with no in-process holder to release it, wedging that path (rejected_path_lock_busy) until manual cleanup. Locks now record anowner_kind; crash-orphaned auto-commit locks are reclaimed unconditionally at server startup (a fresh process holds none) and, while running, once older thanKB_AUTO_COMMIT_LOCK_TTL_SEC(default 600s, far above a seconds-long commit critical section). The periodic reclaim runs under the same process-wide write serializer thatpath_lockacquire/release runs under, so a stalled holder that resumes cannot free the path (and let a successor re-acquire it) mid-reclaim; the delete is additionally inode + timestamp ownership-checked as defense in depth. Pre-fix legacy auto-commit locks (noowner_kind,auto-commit:pending_idprefix) are recognised too, so an upgrade on a persistent/statevolume frees any old wedged path. Pending-proposal locks are never TTL-reclaimed (they legitimately live until the operator resolves or the entry expires).Search pipeline hardening (WP2b, epic #75), bug + perf fixes:
Index.searchcould return MORE thanlimithits when embeddings were configured but no reranker was set (finding h): the dense union widened the pool but truncation lived only inside the reranker branch. Truncation tolimitis now unconditional.Vectors are batch-fetched once per build and cached as an in-memory id->vector matrix (finding g). The hybrid reranker previously opened one SQLite connection per candidate hit via
get_vector, anddense_candidatesre-read + re-deserialized the wholedoc_vectorstable on every query. The cache is keyed by the db file's (size, mtime) so the atomic build swap invalidates it transparently.Index.buildnow runs a singlegit log --name-onlypass for every file's last-commit time instead of onegit logsubprocess per file, and reads + parses each markdown file exactly once (previously three times: two parses plus a separate content read) (finding i). Per-file(last_modified, source)metadata is byte-for-byte identical.Malformed front matter is no longer silently swallowed (finding j): a doc whose front-matter block is present but is invalid YAML (so its
status/supersedesare dropped, quietly disabling staleness protection) is now logged at WARN per doc, counted, and surfaced viaIndex.malformed_frontmatter_countand themalformed_frontmatterfield inhealth(). (Wiring this count into the/healthdegraded signal is a tracked follow-up.)
Onboarding pipeline (
tools_onboarding.py, issue WP3b). Four coherence and atomicity fixes, each with regression tests:partialis no longer a dead end.kb_bootstrap_projectaccepted onlyabsentand rejectedpartialwithrejected_already_onboarded, contradicting the playbook that told the agent to bootstrap onabsentORpartial. It now proceeds onpartial, narrowing the request to only the exactmissing_filespaths under this workspace/component so an already-committed file is never overwritten and a basename collision cannot smuggle in a write to a different project or component; if nothing supplied fills a gap it rejects truthfully rather than committing an empty change. The playbook text now matches this behaviour.Double-bootstrap race closed. A committed bootstrap is invisible to the index until it is pushed and re-pulled, so a second call in that convergence window passed the
absentre-check and double-committed. A short-lived, self-expiring in-flight marker (onboarding_inflight.py) is now claimed on the committed path and rejects a concurrent bootstrap for the same workspace/component asrejected_already_in_progress. The playbook now instructs waiting forkb_onboarding_statusto reportonboardedbefore runningkb_cleanup_plan.Pending bundle is now atomic. In the low-confidence path a
PathLockBusyErrorwas silently swallowed per file, so a partially-enqueued bundle returnedpending_confirmationwith only the subset that fit. Lock-busy is now treated like queue-full: the whole bundle rolls back (zero orphan pending entries or held locks) and rejects asrejected_path_locked. Every entry of a bundle is stamped with a sharedbundle_idin its metadata (the seam for a future bundle-aware resolve UX).Safe remote-URL injection confirmed. The
git_remote_urlfront-matter injection parses and re-serializes YAML withyaml.safe_dump(a newline-laden URL cannot forge keys) and its presence check is key-aware, so a URL mentioned only in the body no longer false-positives and suppresses injection. Regression tests lock both behaviours in.
OpenCode gate (
data-olympus-gate.ts): resolves the workspace to the main git worktree basename (matching the key every other surface records consults under) instead of the raw absolute path, which could never match; and passes the tool arguments (command/patch/content) asaction_diff, so bash and patch actions are actually classified instead of the gate seeing empty everything and allowing.Enforcement hook (
bin/kb-enforce-hook): a nullsession_idnow renders as an empty string instead of the literal"None"(which became a bogus ledger key); the critical-path pre-tool gate now uses tight--max-time 2 --connect-timeout 1timeouts; and the pre-tool payload is parsed once (base64-framed) instead of spawningpython3per field.Anchored the installer tool matchers (
^(...)$) soBashno longer substring-matchesBashOutput(and likewise for the Codex and Gemini matchers).kb enforce doctornow also verifies the managed marker/version in the live settings file and that the hook dispatcher exists and is executable, and warns (and fails) when the dispatcher resolves inside a.worktrees/or.claude/worktrees/checkout, which dangles after pruning and silently fails open.Docs:
docs/enforcement.mdnow states that Codex DOES gate Bash (matching the installer) and documents the explicit-consult gate policy honestly.scripts/run-local.shno longer deletes an arbitrary user-supplied$1path. It previously ranrm -rf "$KB_DIR"unconditionally, so passing your own bundle's path as the first argument would silently delete it. The script now refuses to touch a pre-existing directory that is not its own default demo path and does not carry a marker file it created itself, printing the correct way to serve your own bundle (directdata-olympus-mcpinvocation withKB_MAIN_PATH, perdocs/quickstart.mdsection 6) instead. The no-argument default demo flow is unchanged.data-olympus indexanddata-olympus visualizeno longer treattemplate.mdas a concept document. Both modules maintained their own{"index.md", "log.md"}reserved-filename set, drifted fromformat/validate.py'sRESERVED(used by lint and validation), so a directory containing only atemplate.mdwas wrongly treated as holding concept docs:indexgenerated anindex.mdfor it and listedtemplate.mdas a concept entry, andvisualizecounted it as a graph node. Both now import the sameRESERVEDset as the rest of the format tooling, soindex.md,log.md, andtemplate.mdare consistently reserved everywhere.
Security
Hardened the write and enforcement surface (issue #74). Ten fixes, each with regression tests:
Request body caps. The resolve, consult, gate/check, and audit/event REST routes read the body with an uncapped
request.json(); they now go through the sameKB_MAX_BODY_BYTESstreaming cap as propose/bootstrap and return 413 on oversize input.resolve.edited_textcap. Operator-suppliededited_texton approve became the committed postimage with no size check; it is now bounded byKB_MAX_POSTIMAGE_BYTESand rejected with a distinctrejected_edited_text_too_largestatus, leaving the pending entry in place.YAML frontmatter injection. Memory frontmatter was string-concatenated, so a
tags/agent_identityvalue containing a newline or]could forge reserved keys (id/status/supersedes); a forged duplicateidbreaks every index rebuild. Frontmatter is now serialized withyaml.safe_dump, and the size cap counts the full rendered postimage (frontmatter + body), not just the body.Backslash / control-char path bypass.
decisions\x.mdvalidated asdecisions/x.mdbut wrote a literal root-level file outside every indexed prefix and invisible toKB_WRITE_BLOCK_PATHS. Path normalization now returns the canonical form and every downstream operation (classification, blocklist, join,git add, pending record) uses it; control characters (newline, CR, tab, NUL) in a target path are rejected. The same canonical-path handling and safe YAML frontmatter (with the size cap applied after remote-URL injection) now also cover the onboarding bootstrap path.Enforcement-plane auth + rate limiting.
/consult,/gate/check, and/onboarding/cleanup-planwere anonymous-allowed even when auth was configured and were unthrottled. They now require an authenticated principal when auth is configured (no-auth deployments are unchanged) and are subject to the shared rate limiter.kb_cleanup_planis added to the MCP auth-required tool set so the MCP enforcement plane matches REST.Viewer HTML injection. A doc body containing
</script>could break out of the embedded<script>block and run arbitrary JS;</is now escaped to<\/. Fixed a substitution-ordering bug where a body containing the literal__DISPLAY_NAME__was mangled (both placeholders are now substituted in a single pass).Search limit clamp.
kb_searchclamped only the upper bound, solimit=-1reached SQLite asLIMIT -1(unlimited full-corpus dump); it is now clamped to 1..100.Actionable status codes. Malformed
since/limitquery params on/auditand/compliancenow return 400 instead of an opaque 500, and resolve of an unknown/expiredpending_idreturns 404 (with path-traversal-shaped ids rejected).Rate-limiter hygiene. The sliding-window limiter now evicts empty bucket keys (bounding memory under varying identities) and guards its read-modify-write with a lock (correct under the threadpool the REST handlers run on).
Documentation
Reworded OKF-compatibility claims for honesty (WP4c, 0.3.0).
README.md,SPEC.md,WHY.md, anddocs/comparison.mdasserted "OKF-compatible" / "any OKF consumer can read a data-olympus bundle unchanged" / "every data-olympus bundle is a valid OKF bundle" with zero executable backing: no test runs a real OKF reference consumer/producer against a data-olympus bundle. The strongest claims are now worded as design intent ("designed to be readable by OKF consumers") backed by the shared structure that does exist by construction (directory layout, frontmatter conventions, reserved filenames, link model), with formal conformance testing tracked in issue #82. A cheap, non-behavioral structural floor (non-emptyid/typeon every example-bundle concept doc,okf_versionon the bundle-root index) was added astests/test_okf_minimal_fields.py; its docstring is explicit that this proves frontmatter shape, not that any OKF tool can read the bundle.Corrected several stale or inaccurate claims ahead of 0.3.0:
docs/adoption.mdnow describes the actualKB_MAIN_PATH-ignoring behaviour ofscripts/run-local.sh(pointing to the direct server invocation instead), and its frontmatter schema section andsupersedes/superseded_bysemantics now matchsrc/data_olympus/format/validate.pyandSPEC.md(concept IDs, not bundle-relative paths).SPEC.mdno longer promiseskb lintwarnings for missingsupersedes/superseded_by/owneror broken links, neither of which is implemented; its reserved-filename list in section 9 now includestemplate.md, and itsokf_versionexample matches whatexample-bundle/index.mdactually ships ("0.1").README.mddrops the stale "pre-release (v0.1)" status line and adds the Python 3.13+ requirement to the quickstart, plus links toSECURITY.mdandbenchmarks/README.md.CONTRIBUTING.md's documenteddata-olympus lintoutput now includes the(N linted)suffix the CLI actually prints.CHANGELOG.mdgains the previously missing[0.1.1]section (the tag was released 2026-06-24 without a changelog entry) and its compare link.docs/comparison.md's RAG section and "Honest weaknesses" no longer claim there is no embedding/semantic retrieval at all; they now describe the optional local-embedding hybrid (off by default) shipped in 0.2.0.example-bundle/index.mdno longer claims to demonstrate "all supported concept types" (it has nomemory,reference, orsupersededexample documents).
0.2.0 - 2026-07-03
Added
Content-free real-corpus retrieval eval harness (
benchmarks/real_corpus_eval.pybenchmarks/real_corpus_eval.md). Point it at your own KB directory and a labeled query set to measure what the optional local-embedding hybrid (issue #42) adds over the lexical stack (FTS + synonym + co-occurrence expansion) on your corpus; it prints only aggregate metrics (recall@k, MRR, recovered / regressed counts) and never document text, so it is safe to run over a private KB. The writeup includes a worked private-corpus example and an honest reading positioning embeddings as opt-in rather than default-on.
Automated, human-gated release pipeline (conformant with the STD-U-810 versioning standard). A daily
data-olympus-release-cutterroutine computes the SemVer bump from Conventional Commits and opens a release PR; merging that PR cuts an annotatedvX.Y.Ztag, a GitHub Release, and the container image via CI. New helpersscripts/compute_release.py,scripts/should_tag.py,scripts/lint_pr_title.py(a PR-title Conventional Commit check enforced by.github/workflows/pr-title-lint.yml), andscripts/compute-version.sh(preview version plus invariant check). Newtag-release.ymlcuts the tag on apyproject.tomlversion bump, builds the image via a reusablerelease-image-reusable.yml(which also stamps OCI version and revision labels), then publishes the GitHub Release. Process is documented in.rules/versioning.mdand.rules/release-routine.md. No tags are ever cut unattended.Optional local-embedding hybrid ranking (issue #42). Real paraphrase / synonymy handling via a LOCAL ONNX MiniLM-class model (no external API, no query-time network), blended with BM25. OFF by default (
KB_EMBEDDINGS_MODE, default off): with the feature off and theembeddingsextra uninstalled, nothing imports the embedding stack and the lexical product is byte-for-byte unchanged. When enabled, each doc is embedded atIndex.buildtime into a newdoc_vectorstable (schema v8, float32 blobs) written into the same tmp DB and swapped atomically, so vectors rebuild atomically with the rest of the index. A hybrid reranker embeds the query once and blends NORMALISED bm25 with cosine over the candidate hits' stored vectors, then re-sorts. It composes as the INNER layer under the exact-id/exact-tag short-circuit (so an exact id/tag still wins) and wraps the status prior (so an active doc still outranks the superseded one it replaced); when disabled the reranker stack is exactly today's. If enabled but the extra/model is unavailable, startup fails LOUDLY with an actionable message rather than silently reverting to lexical-only. Config:KB_EMBEDDINGS_MODE(on/off, default off),KB_EMBEDDINGS_MODEL(defaultBAAI/bge-small-en-v1.5, 384-dim),KB_EMBEDDINGS_WEIGHT(cosine fraction of the blend in [0, 1], default 0.35). The extra pulls infastembed(which bundlesonnxruntime; no torch); the default model is a ~33 MB quantised ONNX file fetched once at enable time and cached locally. Build cost: one batched local embed pass over the corpus atIndex.build(seconds for a small KB, no network at query time); storage cost is 384 x 4 bytes (~1.5 KB) per doc indoc_vectors. Install withuv sync --extra embeddings. When enabled,Index.searchnow adds a semantic candidate SOURCE (not just a reranker over the FTS pool): the top dense neighbours by query-doc cosine are unioned into the FTS candidate pool before the hybrid blend, so a paraphrase with ZERO lexical overlap (e.g. querying "car" against a corpus that only says "automobile") is retrieved where bm25 alone returned nothing. A minimum-cosine threshold guards abstention: an out-of-scope query whose nearest neighbour is only weakly similar pulls in nothing. A dense-only candidate (absent from the FTS results) is scored at the pool's worst (neutral-floor) bm25 so the blend ranks it by its cosine component. Two newIndexknobs (dense candidate count, default 10; minimum cosine, default 0.5). With embeddings OFF (the default)searchis byte-for-byte pure FTS. Embeddings settings (KB_EMBEDDINGS_MODE/MODEL/WEIGHT) are now read from env ONLY inload_configand threaded into theIndex(and its embedder) viaConfig; the build and query paths no longer re-read the environment, so a programmatic caller'sConfigvalues are honoured.Trigram fuzzy-match fallback for typos and partial identifiers (issue #41). A secondary FTS5 table (
fts_trigram,trigramtokenizer, schema v7) is built into the same tmp DB and swapped atomically with the primary FTS index, so it rebuilds atomically. At query time it is used only as a FALLBACK: the primaryporter unicode61FTS query runs first and, only when it returns at or below a small threshold of hits, a trigram match (an OR of the query's own quoted trigrams, so no FTS operator injection) backfills the results. Trigram hits are APPENDED after the primary hits and scored strictly worse than any primary hit, so an exact/primary match is never diluted or reordered by a fuzzy hit, and a query with good primary hits is unaffected. A query shorter than 3 chars safely no-ops the fallback. Opt-in and off by default (existing search behaviour is unchanged):KB_TRIGRAM_MODE(on/off, default off) andKB_TRIGRAM_FALLBACK_THRESHOLD(default 3).Corpus co-occurrence query expansion (embedding-free semantics, issue #40). At index-build time the indexer learns, per term, the top-k terms it most strongly co-occurs with across documents (pointwise mutual information at document granularity) into a bounded
related_termstable (schema v6) built into the same tmp DB and swapped atomically with the FTS index. At query time aquery_expanderappends a term's related terms, down-weighted by appending them after the originals so BM25 still favours typed terms, bounded to <= 32 terms. It composes WITH the synonym expander (issue #38) rather than replacing it: synonyms run first, then co-occurrence broadens the synonym-expanded set (compose_expanders). Config knobs:KB_COOCCURRENCE_MODE(on/off, default on),KB_COOCCURRENCE_K(default 5),KB_COOCCURRENCE_MIN_COUNT(default 2),KB_COOCCURRENCE_MIN_PMI(default 0.0). Stopword-like and short tokens are skipped so the table stays focused and build cost negligible.Search now short-circuits exact-id and exact-tag queries: a single-token query that is a document id (e.g.
STD-U-002, or a path-derived id) is surfaced as the top hit via a direct lookup even when it is absent from the bm25 results, and a single-token exact-tag query lifts docs carrying that tag ahead of the rest. Implemented as a composablereranker(issue #39); the match stage and_build_match_exprare untouched.kb_searchandkb_getnow support and returnstatusandtype, making the documented "filter by status / tier / type" capability real (index schema v4).Retrieval now indexes
applies_whentrigger metadata anddescriptionwith column-weighted ranking, improving coding-intent to governing-rule matching (index schema v5).kb_getreturns both fields.Status-aware search ranking (enabled by default):
kb_searchreranks hits by a status prior on top of BM25, so an in-force doc (active,accepted,approved) outranks asuperseded/deprecated/rejectedone that matches the same query, and drafts do not beat live guidance. Status matching is case-insensitive; unknown or empty statuses stay neutral and are never dropped. This changes result ordering for queries that hit both a live and a retired doc. A deployment overrides the built-in status->weight map viaKB_STATUS_WEIGHTS(a JSON object of{status: weight}, negative boosts, positive penalizes).Enforcement core: data-olympus can now act as a gated, mandatory consultation proxy for code/architectural decisions, not only an advisory KB. New MCP tools
kb_consult,kb_gate_check,kb_complianceand REST endpoints/api/v1/consult,/api/v1/gate/check,/api/v1/compliance, backed by a shared heuristic intent classifier and an in-memory consultation ledger.kb enforce install|uninstall|status|doctorCLI plus akb-enforce-hookdispatcher install an idempotent, reversible Claude Code enforcement shim (SessionStart / UserPromptSubmit / PreToolUse / Stop). Fail-open by default (KB_ENFORCE_FAIL_MODE), consultation freshness viaKB_CONSULT_TTL_SEC.Cross-agent enforcement providers:
kb enforce install --agent codex|gemini|opencode|copilot-cli|copilot-ideand--all. Codex and Gemini get hard PreToolUse/BeforeTool gates (merging into existing hooks), OpenCode gets atool.execute.beforeplugin, and Copilot CLI/IDE get a soft instructions-file + MCP provider. Antigravity is a documented-unsupported stub. Thekb-enforce-hookdispatcher gained a--dialect geminioutput mode.Detection floor for un-hookable agents:
kb enforce report(anddata-olympus report) correlates governed git commits against the consult audit and lists changes with no consultation on record. An opt-in repo-scoped git provider (kb enforce install --agent git) installs a post-commit warning hook, or a pre-commit blocking hook with--block. Reuses the existing audit endpoint; no server change.Streamable-http session idle reaper bounds transport accumulation (issue #43). A background loop terminates sessions with no request activity for longer than
KB_SESSION_IDLE_TIMEOUT_SEC(default 1800s / 30 min;0disables reaping and keeps observability only), scanning everyKB_SESSION_REAP_INTERVAL_SEC(default 60s).kb_healthand/api/v1/healthgained alive_sessionsfield reporting the current live transport count (nulluntil the HTTP app is serving); a value that only climbs signals leaked sessions.Synonym / acronym query expansion (default-on). Before the FTS MATCH is built, the search pipeline rewrites the query term list through a curated, bidirectional synonym map, so a short-form query (
k8s,rls) also reaches documents that only use the long form (kubernetes,row level security) and vice versa. Adjacent query tokens are scanned as n-grams so multi-word canonical keys match from a long-form query. Expansion is bounded (32 terms) and de-duplicated, originals are ranked first, and it is configurable viaKB_SYNONYMS(extra/override groups) andKB_SYNONYMS_MODE(mergedefault /replace/off). This is curated lexical expansion, not semantic (embedding) retrieval.Enforcement hardening and observability: gate_bypass and gate_degraded events are now recorded (via
data-olympus report --emit-events/ the git warn hook, and the pre-tool hook on a reachable-degraded gate), sokb_compliancesurfaces them. The gate receivesaction_diffand the classifier uses word-boundary matching plus dependency-install command signals (Codex and Claude now gate the Bash tool). Newkb enforce install --mode off|soft|hard, ledger persistence viaKB_LEDGER_PATH, a friendly PATH hint forkb enforce report, and a CI guard requiring a changelog entry for functional changes.Guided onboarding: MCP prompts
onboard,onboard_project, andonboard_componentwalk an agent through bootstrapping a new workspace or component, backed by a single-sourced playbook (render_playbook). A read-onlykb_cleanup_planMCP tool plusPOST /api/v1/onboarding/cleanup-planclassify local repo docs against KB content and propose thin-pointer replacements for duplicates.GET /api/v1/onboarding/playbookandkb onboard playbookexpose the same script to agents without native MCP prompt support.Composable search pipeline:
Index.search()now runs expand-query / match / re-rank stages with pluggablequery_expanderandrerankerhooks, so ranking and query-expansion features compose without rewriting the core query. When a reranker is installed the query is matched against a wider BM25 candidate pool (over-fetched) and the reranked result is truncated back to the requested limit.Read-only replica serving mode (
KB_READ_ONLY, issue #44) for horizontal read scaling. When set, the server registers only the read tools and read REST routes; the write/enforcement-write tools and routes (propose/resolve/bootstrap/consult/gate/record-event plus the observability GETs/api/v1/pending,/api/v1/audit,/api/v1/audit/verify,/api/v1/compliance) are not registered and return 404, and the write pipeline (worktrees/push queue/pending) is never initialised, so a replica is never a git writer. A newdeploy/k8s/read-replica/overlay (Deployment, Service, ConfigMap, NetworkPolicy, kustomization) runs N interchangeable read pods with per-pod ephemeral clone + index scratch and references a separate read-only git deploy key Secret (data-olympus-mcp-readonly). The replica logs an unambiguous "starting in READ-ONLY mode; write pipeline disabled" line at startup.
Fixed
Enforcement consult gates now agree on a worktree-invariant workspace key. The pre-tool gate and the pre-commit
data-olympus report --stagedgate derived the workspace differently (the hook from the detector or raw cwd, the report fromPath.cwd().name), so a singlekb_consultcould not clear both gates from a linked git worktree. Both now resolve to the main worktree's basename (the first non-bare entry ofgit worktree list, correct for separate-git-dir and bare-repo layouts):report's default--workspace, and the hook'sresolve_workspace, which now tries git first so it never disagrees with the report even whenKB_WORKSPACES_ROOTresolves the linked worktree. A newkb-enforce-hook resolve-workspace [dir]prints the resolved key.data-olympus lint(anddiscover_bundle_files) no longer discovers zero files when the bundle sits under an ancestor directory named like a skip-dir (for example a checkout under.worktrees/,.git/, ornode_modules/). Skip-dir matching now applies only to path components inside the bundle, not the absolute ancestor path.Intermittent
503 Service Unavailablefrom the single-replica MCP under load. The readiness probe was too aggressive (timeoutSeconds: 1) while every REST/enforcement handler ran synchronous work on the one asyncio event loop, so a burst stalled the probe and the only pod was ejected from the Service (nginx then served a 503 with an empty upstream, leaving no application-log trace). Fixes: relax the readiness probe (5s timeout, 5 failures) and raise the CPU limit to 2; offload blocking handler work to the anyio worker pool while serving/api/v1/healthinline (off that pool) from a short-TTL cache ofIndex.health(); add locks to the audit log and consultation ledger for safe concurrent access.The consultation ledger grew without bound (every
(session, workspace)pair, the whole file rewritten on every consult). It now evicts entries past the consult TTL, enforces a hard max-entries cap, and bounds an oversized persisted ledger on load rather than holding it all in memory until the first prune.data-olympus-mcp --helpnow prints usage and exits 0 instead of crashing withNotADirectoryError: KB root not a directory: /kb-main. Argument parsing runs before config loading, so the documented quickstart command works on first run.Git sync failures are now visible instead of masquerading as a fresh no-change.
kb_health//api/v1/healthexposelast_git_fetch_status(changed/no_change/no_remote/fetch_failed/ff_failed),last_git_fetch_error,last_git_fetch_at,last_successful_refresh_at, andremote_head_sha. A fetch or fast-forward failure no longer advances the freshness marker, so staleness climbs and health degrades rather than reporting the KB as up to date against a broken or diverged remote. A deployment with no remote (read-only) is classifiedno_remoteand stays healthy.Write routes return structured errors instead of opaque 500s. In read-only mode (
KB_REMOTE_URLunset) the write pipeline is disabled, andPOST /api/v1/propose/*,/resolve/{id}, and/onboarding/bootstrapnow return a503 {"error":"write_pipeline_disabled"}(and the MCP write tools return{"status":"write_pipeline_disabled"}) instead of crashing on an internal assertion. Missing-field validation was extended to/api/v1/consult,/api/v1/gate/check, and/api/v1/resolve/{id}, so a request that omits a required field gets an actionable400rather than aKeyError-driven 500.
Security
Hardening from the companion security review:
Audit chain forgery fixed (blocker):
verify()now tolerates unhashed legacy lines only as a prefix before the chain starts; any unhashed line after the first hashed event breaks verification, so an appended legacy-shaped record can no longer forge an event while keeping/audit/verifygreen.Path containment tightened:
safe_join_under_root()now also requires the resolved path to equal the lexical target, rejecting an in-tree symlink that redirects an allowedtarget_pathto a different in-root file (which would decouple classification/blocklist/audit from where bytes land).Body cap is real for chunked clients: the REST body limit is enforced by streaming the request and returning
413past the cap, instead of trustingContent-Length.MCP observability tools gated:
kb_list_pending,kb_audit,kb_consult,kb_gate_check, andkb_compliancerequire an authenticated principal over MCP when auth is configured, matching the REST gating.Aggregate caps: onboarding bootstrap rejects over
KB_MAX_BOOTSTRAP_FILES(default 50), andKB_PENDING_QUEUE_CAPis now enforced in the pending queue.Bootstrap confidence parse:
/api/v1/onboarding/bootstrapreturns a clean400on non-numeric confidence instead of a 500.Compliance route gated:
GET /api/v1/compliancenow requires an authenticated principal when auth is configured, like the other observability routes (it was the one REST route still open).Atomic bootstrap: a low-confidence bootstrap that would overflow the pending queue is rejected up front (and rolls back on a capacity race), so it never leaves a partial set of pending entries.
Observability routes are gated when auth is configured. With
KB_AUTH_TOKENset,GET /api/v1/pending,/api/v1/audit, and/api/v1/audit/verifynow require an authenticated principal (they leak target paths and agent identities); they stay open when no auth is configured.bin/kb pendingandkb auditsend the bearer header automatically.Deploy hardening for the sample Kubernetes manifests: the StatefulSet pins an immutable image tag (was
data-olympus:latest), drops all Linux capabilities except those the root entrypoint needs before itgosu-drops to uid 65534, forbids privilege escalation, sets aRuntimeDefaultseccomp profile, and runs a read-only root filesystem (writes go to the PVCs and a/tmpemptyDir). A new default-denyNetworkPolicyrestricts ingress to the ingress controller and same-namespace clients, and the ingress gained cert-manager/TLS guidance. The release workflow no longer moves thelatestchannel on manualedgebuilds, solatestalways means the last promoted release.The audit log is now tamper-evident. Each appended event carries an
event_id, theprev_hashof the previous event, and its ownhashover the canonical body (SHA-256, or keyed HMAC-SHA256 whenKB_AUDIT_HMAC_KEYis set). Any later edit, deletion, or reordering breaks the chain. A newGET /api/v1/audit/verifyroute andkb audit --verifyrecompute and report the first broken line. Legacy unhashed lines are tolerated, so enabling the feature on an existing log does not retroactively flag it.Resource limits bound write-side abuse. New configurable caps reject oversized proposals before any disk side effect:
KB_MAX_TEXT_BYTES(memory text, default 256 KiB),KB_MAX_POSTIMAGE_BYTES(edit/bootstrap file, default 1 MiB), andKB_MAX_BODY_BYTES(REST request body viaContent-Length, default 2 MiB, returns 413). The sliding-window rate limiter gained an optional per-IP cap (KB_RATE_LIMIT_PER_IP_PER_HOUR, default 0 = disabled) so a client cannot multiply its quota by varyingagent_identity.Identity + capability authorization unifies MCP/REST write auth, per-agent policy, and a confidence clamp. A
Principalis resolved from theAuthorization: Bearerheader against a registry built fromKB_AUTH_TOKEN(a full-capabilityoperator) and an optionalKB_AUTH_PRINCIPALSJSON list of per-agent tokens with explicit capabilities (read,propose,auto_commit,resolve,bootstrap,record_event).MCP write tools are now authenticated. A FastMCP middleware enforces the same capabilities on
kb_propose_*,kb_resolve_pending,kb_bootstrap_project, andkb_record_eventthat the REST layer enforces, closing the gap where MCP write tools bypassedKB_AUTH_TOKEN.Confidence is clamped for non-privileged callers. A principal lacking the
auto_commitcapability has its proposals parked as pending regardless of the client-asserted confidence, so a caller can no longer self-assertconfidence: 1.0to skip operator review. REST returns 401 for anonymous and 403 for an authenticated principal missing a capability.When no auth is configured (no token, no principals) every caller is the fully-trusted local principal, preserving the prior trusted-local behavior.
Path containment is now enforced uniformly across every write path. A new shared
safe_join_under_root()guard rejects any proposed write whose resolved location escapes the per-session worktree (symlink, traversal, or absolute path). This closes a memory-proposal symlink-escape where a malicious KB commit could plantmemory/inboxas a symlink and cause a high-confidence proposal to write a file outside the worktree beforegit addaborted. The edit, resolve, and onboarding-bootstrap paths now use the same guard. Regression tests cover the helper directly and each write path.Cleanup-plan input validation is now centralized in
kb_cleanup_plan_fnitself, so bothPOST /api/v1/onboarding/cleanup-planand the MCPkb_cleanup_plantool are protected identically (previously only the REST route validated, and the MCP tool called the function directly, unvalidated). The shared function now rejects a non-listlocal_files, a non-object entry, a non-stringpath, and a non-stringcontent(including an explicitcontent: null, which used to slip through.get()and crash later). It also enforces a per-filemax_content_bytescap, an aggregatemax_filescap, and requiresjaccard_thresholdto be a finite number in[0.0, 1.0](rejectingnan,inf, and out-of-range values). REST and MCP callers pass the existingKB_MAX_BOOTSTRAP_FILES/KB_MAX_POSTIMAGE_BYTEScaps through to the shared function.
Changed
uv run mypy srcis now green and runs as a CI gate (addedtypes-PyYAML, fixed the source type errors). Type regressions insrc/now fail CI;tests/typing is intentionally out of scope.The path-to-
(tier, category)taxonomy now ships a deployment-neutral default and is configurable at deploy time, with no code change:KB_TAXONOMY_PATH(a JSON file of[prefix, tier, category]triples that replaces the default table),KB_INDEXED_PREFIXES(comma-separated writable top-level prefixes that replace the default set), andKB_MEMORY_INBOX_PREFIX(directory new memory proposals are written under, defaultmemory/inbox/).tech-stacks/<stack>/is now classified dynamically asstack:<stack>rather than from a fixed allow-list. Deployments that relied on the previous built-in directory names must set these variables to preserve prior classification and writable paths.
Fixed
The enforcement hook now reports the correct per-agent identity to the consult audit. Previously every agent (Claude, Codex, Gemini) was recorded as
claude-code, so thekb_complianceper-agent view was wrong for Codex and Gemini. Thekb-enforce-hookdispatcher gained an--agentflag and each provider threads its own identity.data-olympus lintno longer false-greens when its file discovery matches nothing. The summary now reports how many concept files were actually linted (e.g.0 errors across 0 files (9 linted)), and the command exits non-zero when a bundle has no concept files to lint, so a broken or over-broad skip walk surfaces as a red CI gate instead of a silent pass. Reserved files (index.md,log.md,template.md) are exempt from the concept schema and so are excluded from the linted count, preventing a bundle that kept only its generated indexes from passing the guard. File discovery is now exposed asdiscover_bundle_filesfor direct testing.
0.1.1 - 2026-06-24
Added
data-olympus lintskips repo-meta directories and root-level meta files.CI builds and publishes a multi-arch (amd64 + arm64) container image to GHCR on release.
Fixed
Bumped
fastmcpto 3.x to close Dependabot-reported CVEs.Fixed the broken OKF link (now points to the knowledge-catalog repository).
0.1.0 - 2026-06-24
Added
Format (OKF-compatible profile)
Governance-grade knowledge-base format as a conformant extension of the Open Knowledge Format.
Controlled frontmatter fields:
id(stable slug),type(decision, standard, workflow, project, component),status(draft, proposed, accepted, deprecated, superseded),tier(T1-T4 plus freeform), andsupersedeschain for tracing decision history.Bundle layout:
decisions/,universal/,tech-stacks/,projects/,workflows/,log.md, plus per-prefixindex.mdgenerated by theindexcommand.SPEC.md: format specification covering bundle layout, frontmatter schema, reserved filenames, and serving contracts.
MCP server (single-writer, streamable HTTP)
Streamable-HTTP MCP server (
data-olympus-mcp) backed by FastMCP.Read tools:
kb_search,kb_get,kb_list,kb_health,kb_outline.Write pipeline:
kb_propose_memory,kb_propose_edit,kb_resolve_pendingwith pending-confirmation flow, path blocklist (KB_WRITE_BLOCK_TIERS,KB_WRITE_BLOCK_PATHS), and structural write rules.Onboarding tools:
kb_onboarding_status,kb_bootstrap_project.Audit tools:
kb_auditwith JSONL event log.Single-replica advisory locking and durable push queue to prevent concurrent write races.
Per-session worktree isolation for in-flight proposed edits.
Health endpoint with degraded/healthy state.
Optional bearer-token authentication for the write routes via
KB_AUTH_TOKEN. When set,POST /api/v1/propose/memory|propose/edit|resolve/{id}|onboarding/bootstraprequireAuthorization: Bearer <token>(constant-time compared withhmac.compare_digest); a missing or wrong token returns401. Read routes stay open. When unset (the default), there is no auth. Thebin/kbCLI sends the header automatically (via a curl config FD, never in argv) whenKB_AUTH_TOKENis set. There is otherwise no built-in auth, so operators should still deploy behind a trusted network or authenticating reverse proxy. SeeSECURITY.md.
data-olympus CLI
data-olympus lint <bundle>: validates bundle conformance against the format spec; exits non-zero on errors (warnings are informational only).data-olympus index <bundle>: regeneratesindex.mdfiles for each prefix in the bundle.data-olympus visualize <bundle>: produces a self-contained Cytoscape-backed HTML graph of document relationships and cross-links (adapted from the OKF visualizer). Bundle markdown is sanitized with DOMPurify and the CDN scripts are pinned with SubResource Integrity (SRI) hashes.
kb bash REST client (bin/kb)
REST client over the MCP server's HTTP API.
Subcommands:
kb search,kb get,kb list,kb health,kb outline.Write subcommands:
kb propose memory,kb propose edit,kb resolve,kb pending,kb audit.Onboarding subcommands:
kb onboard status,kb onboard project,kb onboard component,kb onboard rename,kb onboarding-check.Workspace auto-detection via
bin/_kb_detect_workspace.sh(resolves workspace and component fromgit remoteof the current working directory).
Example bundle
example-bundle/: a multi-tier example with decisions (ADR-001, ADR-002), universal standards, a backend-nestjs tech-stack stub, an acme-app project stub, a workflow, and alog.md.Pre-generated
index.mdfiles andviz.htmlgraph for the example bundle.
Deploy
deploy/docker/: Dockerfile,compose.yaml, andentrypoint.shfor local Docker deployment.deploy/k8s/: Kustomize manifests (StatefulSet, Service, Ingress, ConfigMap, Namespace) for Kubernetes deployment.deploy/k8s/secret.template.yaml: template (not real credentials) for the SOPS-encrypted Secret.
Documentation
docs/quickstart.md: verified local-run procedure with curl andkbCLI examples.docs/serving.md: single-replica serving model, read-only replicas, and git pull loop.docs/adoption.md: bring-your-own-KB guide (author, lint, index, serve, wire an agent).docs/comparison.md: how data-olympus relates to OKF, enterprise catalogs, markdown KB tools, agent-context conventions, RAG, and ADR tooling.
512 files · 486 not served here
- .gitattributes (not served here)
- .github/CODEOWNERS (not served here)
- .github/ISSUE_TEMPLATE/bug_report.yml (not served here)
- .github/ISSUE_TEMPLATE/config.yml (not served here)
- .github/ISSUE_TEMPLATE/release-scope.yml (not served here)
- .github/ISSUE_TEMPLATE/spec_proposal.yml (not served here)
- .github/PULL_REQUEST_TEMPLATE.md (not served here)
- .github/copilot-instructions.md
- .github/dependabot.yml (not served here)
- .github/labels.yml (not served here)
- .github/workflows/ci.yaml (not served here)
- .github/workflows/hotfix-cut.yml (not served here)
- .github/workflows/maintenance-inventory.yml (not served here)
- .github/workflows/okf-pin-freshness.yml (not served here)
- .github/workflows/package-dry-run.yml (not served here)
- .github/workflows/pr-title-lint.yml (not served here)
- .github/workflows/promote-release.yml (not served here)
- .github/workflows/publish-python-build.yml (not served here)
- .github/workflows/rc-build.yml (not served here)
- .github/workflows/rc-publish-stage.yml (not served here)
- .github/workflows/recut-release-branch.yml (not served here)
- .gitignore (not served here)
- .prose-lintignore (not served here)
- .rules/changelog-per-release.md (not served here)
- .rules/release-planning.md (not served here)
- .rules/release-rollback.md (not served here)
- .rules/release-routine.md (not served here)
- .rules/versioning.md (not served here)
- AGENTS.md
- ATTRIBUTION.md
- CHANGELOG.md
- CLAUDE.md
- CODE_OF_CONDUCT.md
- CONTRIBUTING.md
- LICENSE
- NOTICE
- README.md
- ROADMAP.md
- SECURITY.md
- SPEC.md
- WHY.md
- benchmarks/README.md (not served here)
- benchmarks/__init__.py (not served here)
- benchmarks/ablate.py (not served here)
- benchmarks/chunking.py (not served here)
- benchmarks/corpus/decisions/BENCH-AUTH-TOKENS-1.md (not served here)
- benchmarks/corpus/decisions/BENCH-AUTH-TOKENS-3.md (not served here)
- benchmarks/corpus/decisions/BENCH-AUTH-TOKENS-5.md (not served here)
- benchmarks/corpus/decisions/BENCH-AUTH-TOKENS-6.md (not served here)
- benchmarks/corpus/decisions/BENCH-AUTH-TOKENS-7.md (not served here)
- benchmarks/corpus/decisions/BENCH-AUTH-TOKENS-8.md (not served here)
- benchmarks/corpus/decisions/BENCH-AUTH-TOKENS.md (not served here)
- benchmarks/corpus/decisions/BENCH-CONFIG-RELOADING-1.md (not served here)
- benchmarks/corpus/decisions/BENCH-CONFIG-RELOADING-2.md (not served here)
- benchmarks/corpus/decisions/BENCH-CONFIG-RELOADING-3.md (not served here)
- benchmarks/corpus/decisions/BENCH-CONFIG-RELOADING-4.md (not served here)
- benchmarks/corpus/decisions/BENCH-CONFIG-RELOADING-5.md (not served here)
- benchmarks/corpus/decisions/BENCH-CONFIG-RELOADING-7.md (not served here)
- benchmarks/corpus/decisions/BENCH-CONFIG-RELOADING.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-1.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-2.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-3.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-4.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-5.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-6.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-7.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY-8.md (not served here)
- benchmarks/corpus/decisions/BENCH-IDEMPOTENCY.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING-1.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING-2.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING-3.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING-4.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING-5.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING-6.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING-7.md (not served here)
- benchmarks/corpus/decisions/BENCH-MESSAGE-ORDERING.md (not served here)
- benchmarks/corpus/decisions/BENCH-NEW-AUTH-TOKENS-2.md (not served here)
- benchmarks/corpus/decisions/BENCH-NEW-AUTH-TOKENS-4.md (not served here)
- benchmarks/corpus/decisions/BENCH-NEW-CONFIG-RELOADING-6.md (not served here)
- benchmarks/corpus/decisions/BENCH-NEW-MESSAGE-ORDERING-8.md (not served here)
- benchmarks/corpus/decisions/BENCH-NEW-SAGA-ORCHESTRATION-1.md (not served here)
- benchmarks/corpus/decisions/BENCH-NEW-SAGA-ORCHESTRATION-4.md (not served here)
- benchmarks/corpus/decisions/BENCH-NEW-SAGA-ORCHESTRATION-6.md (not served here)
- benchmarks/corpus/decisions/BENCH-OLD-AUTH-TOKENS-2.md (not served here)
- benchmarks/corpus/decisions/BENCH-OLD-AUTH-TOKENS-4.md (not served here)
- benchmarks/corpus/decisions/BENCH-OLD-CONFIG-RELOADING-6.md (not served here)
- benchmarks/corpus/decisions/BENCH-OLD-MESSAGE-ORDERING-8.md (not served here)
- benchmarks/corpus/decisions/BENCH-OLD-SAGA-ORCHESTRATION-1.md (not served here)
- benchmarks/corpus/decisions/BENCH-OLD-SAGA-ORCHESTRATION-4.md (not served here)
- benchmarks/corpus/decisions/BENCH-OLD-SAGA-ORCHESTRATION-6.md (not served here)
- benchmarks/corpus/decisions/BENCH-SAGA-ORCHESTRATION-2.md (not served here)
- benchmarks/corpus/decisions/BENCH-SAGA-ORCHESTRATION-3.md (not served here)
- benchmarks/corpus/decisions/BENCH-SAGA-ORCHESTRATION-5.md (not served here)
- benchmarks/corpus/decisions/BENCH-SAGA-ORCHESTRATION-7.md (not served here)
- benchmarks/corpus/decisions/BENCH-SAGA-ORCHESTRATION.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-BLUE-GREEN-DEPLOYS-1.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-BLUE-GREEN-DEPLOYS-3.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-BLUE-GREEN-DEPLOYS-4.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-BLUE-GREEN-DEPLOYS-5.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-BLUE-GREEN-DEPLOYS-6.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-BLUE-GREEN-DEPLOYS-7.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-BLUE-GREEN-DEPLOYS.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-FEATURE-FLAGS-1.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-FEATURE-FLAGS-3.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-FEATURE-FLAGS-4.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-FEATURE-FLAGS-5.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-FEATURE-FLAGS-7.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-FEATURE-FLAGS-8.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-FEATURE-FLAGS.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-GRACEFUL-SHUTDOWN-1.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-GRACEFUL-SHUTDOWN-2.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-GRACEFUL-SHUTDOWN-3.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-GRACEFUL-SHUTDOWN-4.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-GRACEFUL-SHUTDOWN-6.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-GRACEFUL-SHUTDOWN-7.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-GRACEFUL-SHUTDOWN.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-NEW-BLUE-GREEN-DEPLOYS-2.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-NEW-FEATURE-FLAGS-2.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-NEW-FEATURE-FLAGS-6.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-NEW-GRACEFUL-SHUTDOWN-5.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-OLD-BLUE-GREEN-DEPLOYS-2.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-OLD-FEATURE-FLAGS-2.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-OLD-FEATURE-FLAGS-6.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-OLD-GRACEFUL-SHUTDOWN-5.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-1.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-2.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-3.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-4.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-5.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-6.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-7.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION-8.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-PAGINATION.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-1.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-2.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-3.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-4.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-5.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-6.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-7.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING-8.md (not served here)
- benchmarks/corpus/projects/example-project/BENCH-SECRETS-HANDLING.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS-1.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS-2.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS-3.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS-4.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS-5.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS-7.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS-8.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-CIRCUIT-BREAKERS.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-DEAD-LETTER-QUEUES-1.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-DEAD-LETTER-QUEUES-2.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-DEAD-LETTER-QUEUES-4.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-DEAD-LETTER-QUEUES-5.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-DEAD-LETTER-QUEUES.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-HEALTH-CHECKS-1.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-HEALTH-CHECKS-2.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-HEALTH-CHECKS-3.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-HEALTH-CHECKS-4.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-HEALTH-CHECKS-5.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-HEALTH-CHECKS-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-HEALTH-CHECKS.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-INPUT-VALIDATION-1.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-INPUT-VALIDATION-2.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-INPUT-VALIDATION-3.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-INPUT-VALIDATION-4.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-INPUT-VALIDATION-7.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-INPUT-VALIDATION-8.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-INPUT-VALIDATION.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-CIRCUIT-BREAKERS-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-DEAD-LETTER-QUEUES-3.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-DEAD-LETTER-QUEUES-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-DEAD-LETTER-QUEUES-7.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-HEALTH-CHECKS-7.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-INPUT-VALIDATION-5.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-INPUT-VALIDATION-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-NEW-RATE-LIMITING.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-CIRCUIT-BREAKERS-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-DEAD-LETTER-QUEUES-3.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-DEAD-LETTER-QUEUES-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-DEAD-LETTER-QUEUES-7.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-HEALTH-CHECKS-7.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-INPUT-VALIDATION-5.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-INPUT-VALIDATION-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-OLD-RATE-LIMITING.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-1.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-2.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-3.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-4.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-5.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-6.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-7.md (not served here)
- benchmarks/corpus/projects/example-project/components/api/BENCH-RATE-LIMITING-8.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-1.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-2.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-3.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-4.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-5.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-6.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-7.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE-8.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-BACKPRESSURE.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS-1.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS-2.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS-3.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS-4.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS-5.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS-6.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS-7.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CANARY-ROLLOUTS.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CONNECTION-POOLING-2.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CONNECTION-POOLING-4.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CONNECTION-POOLING-5.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CONNECTION-POOLING-6.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CONNECTION-POOLING-8.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-CONNECTION-POOLING.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-NEW-CONNECTION-POOLING-1.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-NEW-CONNECTION-POOLING-3.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-NEW-CONNECTION-POOLING-7.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-NEW-RETRIES-7.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-NEW-TRACING-5.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-NEW-TRACING.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-OLD-CONNECTION-POOLING-1.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-OLD-CONNECTION-POOLING-3.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-OLD-CONNECTION-POOLING-7.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-OLD-RETRIES-7.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-OLD-TRACING-5.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-OLD-TRACING.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES-1.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES-2.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES-3.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES-4.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES-5.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES-6.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES-8.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-RETRIES.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-TRACING-1.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-TRACING-2.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-TRACING-3.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-TRACING-4.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-TRACING-6.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-TRACING-7.md (not served here)
- benchmarks/corpus/tech-stacks/backend-nestjs/BENCH-TRACING-8.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-1.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-2.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-3.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-4.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-5.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-6.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-7.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING-8.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-CACHING.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-DATABASE-MIGRATIONS-2.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-DATABASE-MIGRATIONS-3.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-DATABASE-MIGRATIONS-4.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-DATABASE-MIGRATIONS-5.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-DATABASE-MIGRATIONS-8.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-DATABASE-MIGRATIONS.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-ERROR-BUDGETS-1.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-ERROR-BUDGETS-2.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-ERROR-BUDGETS-5.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-ERROR-BUDGETS-6.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-ERROR-BUDGETS-7.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-ERROR-BUDGETS.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING-1.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING-2.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING-3.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING-4.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING-6.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING-7.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING-8.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-LOGGING.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-DATABASE-MIGRATIONS-1.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-DATABASE-MIGRATIONS-6.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-DATABASE-MIGRATIONS-7.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-ERROR-BUDGETS-3.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-ERROR-BUDGETS-4.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-LOGGING-5.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-SCHEMA-EVOLUTION-3.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-NEW-SCHEMA-EVOLUTION-8.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-DATABASE-MIGRATIONS-1.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-DATABASE-MIGRATIONS-6.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-DATABASE-MIGRATIONS-7.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-ERROR-BUDGETS-3.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-ERROR-BUDGETS-4.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-LOGGING-5.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-SCHEMA-EVOLUTION-3.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-OLD-SCHEMA-EVOLUTION-8.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-SCHEMA-EVOLUTION-1.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-SCHEMA-EVOLUTION-2.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-SCHEMA-EVOLUTION-4.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-SCHEMA-EVOLUTION-5.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-SCHEMA-EVOLUTION-6.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-SCHEMA-EVOLUTION-7.md (not served here)
- benchmarks/corpus/universal/foundation/BENCH-SCHEMA-EVOLUTION.md (not served here)
- benchmarks/corpus_gen.py (not served here)
- benchmarks/corpus_model.py (not served here)
- benchmarks/docs_tables.py (not served here)
- benchmarks/generate_artifacts.py (not served here)
- benchmarks/generate_embeddings_ablation.py (not served here)
- benchmarks/generate_governance_artifacts.py (not served here)
- benchmarks/governance/projects/example-project/GOV_BLUE_GREEN_DEPLOYS.md (not served here)
- benchmarks/governance/projects/example-project/GOV_CIRCUIT_BREAKERS.md (not served here)
- benchmarks/governance/projects/example-project/GOV_DEAD_LETTER_QUEUES.md (not served here)
- benchmarks/governance/projects/example-project/GOV_IDEMPOTENCY.md (not served here)
- benchmarks/governance/projects/example-project/GOV_MODULE_STRUCTURE.md (not served here)
- benchmarks/governance/projects/example-project/GOV_NEW_AUTH_TOKENS.md (not served here)
- benchmarks/governance/projects/example-project/GOV_NEW_PAGINATION.md (not served here)
- benchmarks/governance/projects/example-project/GOV_OLD_AUTH_TOKENS.md (not served here)
- benchmarks/governance/projects/example-project/GOV_OLD_PAGINATION.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_CACHING_STRATEGY.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_LOGGING.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_MESSAGE_ORDERING.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_NEW_RATE_LIMITING.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_NEW_SAGA_ORCHESTRATION.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_NEW_SECRETS_HANDLING.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_OLD_RATE_LIMITING.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_OLD_SAGA_ORCHESTRATION.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_OLD_SECRETS_HANDLING.md (not served here)
- benchmarks/governance/projects/example-project/components/api/GOV_TRACING.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_AUDIT_LOGGING.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_BACKPRESSURE.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_FEATURE_FLAGS.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_FORCE_PUSH.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_HEALTH_CHECKS.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_INPUT_VALIDATION.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_NEW_CANARY_ROLLOUTS.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_NEW_SCHEMA_EVOLUTION.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_OLD_CANARY_ROLLOUTS.md (not served here)
- benchmarks/governance/tech-stacks/backend/GOV_OLD_SCHEMA_EVOLUTION.md (not served here)
- benchmarks/governance/universal/foundation/GOV_API_VERSIONING.md (not served here)
- benchmarks/governance/universal/foundation/GOV_CONNECTION_POOLING.md (not served here)
- benchmarks/governance/universal/foundation/GOV_DATABASE_MIGRATIONS.md (not served here)
- benchmarks/governance/universal/foundation/GOV_ERROR_BUDGETS.md (not served here)
- benchmarks/governance/universal/foundation/GOV_NEW_CONFIG_MANAGEMENT.md (not served here)
- benchmarks/governance/universal/foundation/GOV_NEW_EXCEL_EXPORT.md (not served here)
- benchmarks/governance/universal/foundation/GOV_NEW_GRACEFUL_SHUTDOWN.md (not served here)
- benchmarks/governance/universal/foundation/GOV_OLD_CONFIG_MANAGEMENT.md (not served here)
- benchmarks/governance/universal/foundation/GOV_OLD_EXCEL_EXPORT.md (not served here)
- benchmarks/governance/universal/foundation/GOV_OLD_GRACEFUL_SHUTDOWN.md (not served here)
- benchmarks/governance/universal/foundation/GOV_RETRY_POLICY.md (not served here)
- benchmarks/governance_corpus.py (not served here)
- benchmarks/governance_queries.py (not served here)
- benchmarks/governance_queries.yaml (not served here)
- benchmarks/governance_results/ablation.json (not served here)
- benchmarks/governance_results/ablation.md (not served here)
- benchmarks/governance_results/embeddings/ablation.json (not served here)
- benchmarks/governance_results/embeddings/ablation.md (not served here)
- benchmarks/methods/__init__.py (not served here)
- benchmarks/methods/base.py (not served here)
- benchmarks/methods/bm25.py (not served here)
- benchmarks/methods/data_olympus.py (not served here)
- benchmarks/methods/grep_read.py (not served here)
- benchmarks/methods/vector_rag.py (not served here)
- benchmarks/methods/whole_dump.py (not served here)
- benchmarks/metrics.py (not served here)
- benchmarks/queries.yaml (not served here)
- benchmarks/query_gen.py (not served here)
- benchmarks/real_corpus/example_bundle_queries.json (not served here)
- benchmarks/real_corpus/example_bundle_result.json (not served here)
- benchmarks/real_corpus_eval.md (not served here)
- benchmarks/real_corpus_eval.py (not served here)
- benchmarks/receipt.py (not served here)
- benchmarks/results/receipt.json (not served here)
- benchmarks/results/report.md (not served here)
- benchmarks/results/results.json (not served here)
- benchmarks/run.py (not served here)
- benchmarks/token_compact.py (not served here)
- benchmarks/tokenizer.py (not served here)
- bin/_kb_detect_workspace.sh (not served here)
- bin/_kb_enforce.py (not served here)
- bin/_kb_fallback.py (not served here)
- bin/kb (not served here)
- bin/kb-enforce-hook (not served here)
- bin/kb-session-recap-hook (not served here)
- bin/opencode/data-olympus-gate.ts (not served here)
- deploy/docker/Dockerfile (not served here)
- deploy/docker/compose.yaml (not served here)
- deploy/docker/entrypoint.sh (not served here)
- deploy/k8s/README.md (not served here)
- deploy/k8s/configmap.yaml (not served here)
- deploy/k8s/ingress.yaml (not served here)
- deploy/k8s/kustomization.yaml (not served here)
- deploy/k8s/namespace.yaml (not served here)
- deploy/k8s/networkpolicy.yaml (not served here)
- deploy/k8s/read-replica/README.md (not served here)
- deploy/k8s/read-replica/configmap.yaml (not served here)
- deploy/k8s/read-replica/deployment.yaml (not served here)
- deploy/k8s/read-replica/kustomization.yaml (not served here)
- deploy/k8s/read-replica/networkpolicy.yaml (not served here)
- deploy/k8s/read-replica/secret.template.yaml (not served here)
- deploy/k8s/read-replica/service.yaml (not served here)
- deploy/k8s/secret.template.yaml (not served here)
- deploy/k8s/service.yaml (not served here)
- deploy/k8s/statefulset.yaml (not served here)
- docs/adoption.md
- docs/comparison.md
- docs/enforcement.md
- docs/glama.md
- docs/maintenance.md
- docs/mcp-registry.md
- docs/okf-profile.md
- docs/operations.md
- docs/quickstart.md
- docs/releases/README.md (not served here)
- docs/releases/adoption-cut-runbook.md (not served here)
- docs/releases/branch-recut.md (not served here)
- docs/releases/placeholder-version.md (not served here)
- docs/releases/pypi-trusted-publishing.md (not served here)
- docs/releases/rc-build.md (not served here)
- docs/releases/rc-publication-stage.md (not served here)
- docs/releases/release-scope.md (not served here)
- docs/releases/repository-settings.md (not served here)
- docs/releases/reviewed-promotion.md (not served here)
- docs/releases/sbom.md (not served here)
- docs/releases/std-u-821-amendment-1.3.md (not served here)
- docs/releases/std-u-821-amendment-1.5.md (not served here)
- docs/releases/v0.10.0.md (not served here)
- docs/releases/v0.11.0.md (not served here)
- docs/releases/v0.2.0.md (not served here)
- docs/releases/v0.3.0.md (not served here)
- docs/releases/v0.3.1.md (not served here)
- docs/releases/v0.3.2.md (not served here)
- docs/releases/v0.3.3.md (not served here)
- docs/releases/v0.3.4.md (not served here)
- docs/releases/v0.3.5.md (not served here)
- docs/releases/v0.4.0.md (not served here)
- docs/releases/v0.4.1.md (not served here)
- docs/releases/v0.4.2.md (not served here)
- docs/releases/v0.5.0.md (not served here)
- docs/releases/v0.6.0-expanded-release-design.md (not served here)
- docs/releases/v0.6.0.md (not served here)
- docs/releases/v0.7.0.md (not served here)
- docs/releases/v0.7.1.md (not served here)
- docs/releases/v0.7.2.md (not served here)
- docs/releases/v0.7.3.md (not served here)
- docs/releases/v0.8.0.md (not served here)
- docs/releases/v0.8.1.md (not served here)
- docs/releases/v0.8.2.md (not served here)
- docs/releases/v0.9.0.md (not served here)
- docs/releases/vulnerability-gate.md (not served here)
- docs/serving.md
- example-bundle/decisions/ADR-001-use-data-olympus.md (not served here)
- example-bundle/decisions/ADR-002-single-writer-serving.md (not served here)
- example-bundle/decisions/index.md (not served here)
- example-bundle/index.md (not served here)
- example-bundle/log.md (not served here)
- example-bundle/memory/accepted/2026-06-20-nestjs-module-naming-collision.md (not served here)
- example-bundle/memory/accepted/index.md (not served here)
- example-bundle/memory/index.md (not served here)
- example-bundle/projects/acme-app/README.md (not served here)
- example-bundle/projects/acme-app/components/api/README.md (not served here)
- example-bundle/projects/acme-app/components/api/index.md (not served here)
- example-bundle/projects/acme-app/index.md (not served here)
- example-bundle/reference/index.md (not served here)
- example-bundle/reference/rest-api-endpoints.md (not served here)
- example-bundle/tech-stacks/backend-nestjs/STD-BN-001-module-structure.md (not served here)
- example-bundle/tech-stacks/backend-nestjs/index.md (not served here)
- example-bundle/universal/foundation/STD-U-001-writing-style.md (not served here)
- example-bundle/universal/foundation/STD-U-002-code-review.md (not served here)
- example-bundle/universal/foundation/STD-U-003-commit-message-format.md (not served here)
- example-bundle/universal/foundation/STD-U-004-commit-message-format.md (not served here)
- example-bundle/universal/foundation/STD-U-702-telegram-cron-routing.md (not served here)
- example-bundle/universal/foundation/index.md (not served here)
- example-bundle/universal/security/STD-U-601-secrets.md (not served here)
- example-bundle/universal/security/index.md (not served here)
- example-bundle/viz.html (not served here)
- example-bundle/workflows/WF-001-review-flow.md (not served here)
- example-bundle/workflows/index.md (not served here)
- glama.json (not served here)
- pyproject.toml (not served here)
- release/adoption-lines.json (not served here)
- release/exceptions.json (not served here)
- scripts/__init__.py (not served here)
- scripts/adoption_ratification.py (not served here)
- scripts/check_benchmark_docs.py (not served here)
- scripts/check_changelog.py (not served here)
- scripts/check_doc_consistency.py (not served here)
- scripts/check_version_free.py (not served here)
- scripts/ci_status.py (not served here)
- scripts/compute-version.sh (not served here)
- scripts/compute_release.py (not served here)
- scripts/deployed_digest.py (not served here)
- scripts/lint_pr_title.py (not served here)
- scripts/maintenance_inventory.py (not served here)
- scripts/okf_conformance.py (not served here)
- scripts/placeholder_version.py (not served here)
- scripts/prose_lint.py (not served here)
- scripts/rc_decide.py (not served here)
- scripts/rc_verify_and_publish.py (not served here)
- scripts/release_artifacts.py (not served here)
- scripts/release_readiness.py (not served here)
- scripts/release_record.py (not served here)
- scripts/run-local.sh (not served here)
- scripts/sbom_evidence.py (not served here)
- scripts/sdlc_recut.py (not served here)
- scripts/sdlc_version.py (not served here)
- scripts/security_alerts.py (not served here)
- scripts/should_tag.py (not served here)
- scripts/smoke_installed_wheel.py (not served here)
- scripts/version_free.py (not served here)
- scripts/vulnerability_gate.py (not served here)
- server.json
- src/data_olympus/__init__.py (not served here)
- src/data_olympus/_vendor/__init__.py (not served here)
- src/data_olympus/_vendor/prose_lint.py (not served here)
- src/data_olympus/audit_log.py (not served here)
- src/data_olympus/audit_trailers.py (not served here)
- src/data_olympus/auth.py (not served here)
- src/data_olympus/capture.py (not served here)
- src/data_olympus/cli/__init__.py (not served here)
- tests/okf/LICENSE.upstream.md
Advantages
- Local-first: the memory stays in files or a database on your own machine (a rule-derived fact from the brain’s files, evidence: README.md)
- Works with 14 MCP clients (a rule-derived fact from the brain’s files, evidence: README.md)
- Plain markdown memory that you can read and edit yourself (a rule-derived fact from the brain’s files, evidence: README.md)
- No API key is named in its files (a rule-derived fact from the brain’s files, evidence: README.md)
- Open licence (Apache-2.0) (a rule-derived fact from the brain’s files)
Limitations
- No ZIP download: install it from its source (a rule-derived fact from the brain’s files)
Security
Risk level: Low risk
This brain runs
Agent instructions that run or fetch something (5)
AGENTS.md:`uv run pytest` on its own is not equivalent.AGENTS.md:`tests/test_server_cli.py` and `tests/test_smoke.py` spawn a subprocess with `sys.executable` and read installed distribution metadata, so without the editable install they fail with `No module named data_olympus.server` and a stale version assertion.AGENTS.md:Anything that gates on `uv run pytest` without the editable install (a release routine, a scheduled job) will block on a false failure.AGENTS.md:Run the linter before the tests.tests/importer-fixtures/.cursorrules:Run the type checker in CI and treat type errors as build failures rather than warnings.
- Low risk: network-instructions (AGENTS.md:75)
- Low risk: scripts-present (scripts/check_benchmark_docs.py)
- Low risk: scripts-present (scripts/okf_conformance.py)
Attribution and licence
- Created by
- Knaisoma
- Declared authors
- knaisoma (declared in pyproject.toml)
- Publisher
- Knaisoma (Official Knaisoma project)
- Source
- knaisoma/data-olympus · commit 6d46033
- Licence
- Apache License 2.0 (files served here)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
AutoGineer is not affiliated with or endorsed by Knaisoma.
Install from source
486 of its files are not served here, so there is no ZIP. knaisoma/data-olympus at 6d46033