Repository navigation
AKMON v0.4.0
AKMON 0.4.0 follows 0.3.0. It keeps one copy of the graph's attribute data
instead of four (one test database went from 29.6 GB to 16.3 GB). It reworks
how AI agents read a graph: exact record ids rank first, a missing name is
reported as missing, the instructions name only the records a deployment has,
groups can be ranked, and every read of an outside system can be reused,
totalled and computed over without asking it again. It keeps a
source's other records publishing when one link cannot be resolved, rebuilds a
view only when its inputs changed, and keeps the scheduled publisher from
stalling behind a long sync. Upgrading applies three database migrations, one
of which deletes the old attribute copies: back up first, and read "Upgrading
from 0.3.0". AKMON's Python package and command are named okg, so every
command below starts with okg.
Get it
- Python package: the wheel
okg-0.4.0-py3-none-any.whlis attached to
this release. Install it withuv tool installorpip install, then
follow the quickstart (docs/start/quickstart.md). - Containers and Kubernetes: the Install section at the end lists the
runtime and PostgreSQL images and the Helm chart, each with its digest. The
PostgreSQL image is unchanged from 0.3.0.
What's new
Storage
- One copy of the attribute data. Before, every publish wrote the
attribute values of each record into three stored copies besides the graph
itself, checked them against each other, and refused some publishes until a
repair ran. Reads now come only from the graph's versioned records plus a
small per-version overlay, and publish no longer writes the copies. A
migration deletes them (see Upgrading). On a 29.6 GB test copy the
database went to 16.3 GB, and the next publish was correct. - Generations made by a fast-forward, a history compaction or a rollover
keep their overlays and are read normally. Before, the next publish could
lose them.
Reading the graph: what agents get
AKMON's MCP server is how an AI agent reads a graph. This release changes what
an agent finds, how much it has to read to find it, and how much it can trust
a number it is given. The server still lists eight tools: inspect, search,
expand, filter, map, aggregate, query and run.
Finding the right record
- An exact record id ranks first. Searching for one id, such as a report
number likeABC-12-345, lists the record that carries it first and marks it
as an exact match. A record whose id differs only in case or separators comes
next, marked as a variant. A record type can name the fields that hold its
ids. Before, such a record could rank fourth. - A search for one code name (with an underscore, a dot or CamelCase) finds
the record bearing that name through the attribute index instead of reading
every text that repeats it. - Search says plainly when no record has a name. When an agent searches
for one exact name or id that no record has, the reply names what was
checked (names, ids and text, in any spelling) and lists ids with similar
names; a lookup of a missing record id says "no record has id X". Before,
the reply said only "rows 0 of count unknown", and agents guessed. The line
never appears under access rules or with live data sources, nor for a
one-word name. On graphs above about 150,000 records it needs the index that
okg migratebuilds (see Upgrading).
Reading less to get the answer
- The instructions name only the record kinds a deployment has.
doc:
text records are mentioned only on deployments that declare a document
source, andfile:andcommit:ids andsource_filelookups only on
deployments that declare a git source. Before, every deployment was told to
use them, and the reads failed. A source module of your own that wraps
DocCorpusSourcemust setmcp.doc_text_nodes: true; one that wraps a git
source must setmcp.file_nodes: trueand, for history,mcp.commit_nodes: true. - The dataset overview keeps the graph's four largest sources. The short
overview an agent reads first used to hide the largest sources behind a
"+N more sources" line once the server offeredrun, becauserun's
example took their room. It now names the same four sources with or withoutrun. When
therunexample does not fit beside them, the example is left out and one
line says where the step format is described. inspecton a code function points the agent at just the function's
lines in the file's document, not the whole file.- Agents can read how a record changed.
inspecttakes two new targets:
node_history(each version of a record in a time window) and
subtype_diff(the records of one type that were added, removed or changed
between two generations). Each takes one call instead of one read per
generation and passes the same per-reader access filter as other reads. - Python code chunks start at their first non-blank line, so a cited
start_lineis the line the definition is on (a decorated function starts at
its decorator). Existing graphs keep the old chunks until each file next
changes; the first edit to a file re-embeds most of its chunks.
Exact numbers instead of arithmetic by hand
aggregateranks groups.order_byon a metric (for example
"max(attrs.size) desc") ranks every group beforelimitandoffsetcut
the page, on stored records, on live results and insiderun. Each group
gets its rank, and the reply says whether all groups were ranked or only
those from a partial read. Replies withoutorder_byare unchanged.- Every live read returns a handle. A read of an outside system through
filter,map,aggregateorexpandnow returns a handle: a short id for
the rows the server keeps for the caller. The next call passes it asinput
and rereads those rows without asking the outside system again. An
aggregate's groups get a handle too, so they can be filtered and sorted.
Kept rows last 10 minutes; a session keeps up to 64 sets. A full server drops
the busiest session's oldest set instead of refusing, and another session
cannot read the rows. Before, rows were kept only after more than 20 outside
calls or a cut reply, for 5 minutes, 4 per caller. - One handle covers a run of one-at-a-time calls. When an agent asks an
outside system one value at a time, from the third call the reply names one
handle for all the answers and two ready calls that total or rank them, with
no new outside call. mapadds computed columns. An agent can have the server compute a
value for each row of a live result, such asfill = used / total, then
filter, sort or total by it. Stored (non-live) records come in a later
release.- Totals over a cut-short answer say they are short. When an outside system
sends only part of an answer, a count says it "is at least the value shown",
and a sum, minimum or maximum says it is over the rows received only. Before,
such a total read like a whole total.
Clear refusals
- A failed read of an outside system says so, and "nothing matched" is an
answer. When a live read fails, the reply says so on its first line and is
marked as an error, with the remedy. Before, a failure could sit inside a
normal-looking reply, and an agent could take it for an answer. When the
outside system answers that nothing matched or that a name was not found,
the reply is now zero rows with the system's own words, not an error. In a
one-value-at-a-time call, a value with no match gives zero rows for that value
and the values that did answer keep their rows. A real failure still fails
the whole call. - A word search over whole records no longer claims "complete" for text it
never read. When the default search profile lists a document's text chunks
but not the document,searchwithmethod: "grep"andwithin: "record"
read no documents and answered "0 of 0, complete", though a search by text
unit found them. It now reads the documents those chunks belong to and says
so. A filter that names a chunk type next to other types is refused with two
fixes that both run; before, it returned only the other types and said
everything was shown. Chunks nested inside chunks are not searched. - Kept live rows are checked on every call. A later read of kept rows
checks the reader's permissions again, as the first read does. Amapor
filterthat names a field the kept rows lack now fetches it, or is refused
asfield_not_fetched; before, it returned empty values with no error. An
inputsent as a JSON string is read like the object form; before,
aggregateon a live type returned zero groups without an error. - A refused
mapkeeps the rows it fetched. When amapthat adds
computed columns must first fetch missing fields and is then refused, the
fetched rows are kept under a handle and the refusal names it, so the
corrected retry asks the outside system for nothing. Before, the rows were
thrown away and a retry repeated the same outside calls, up to 1,500. The
handle counts toward the caller's own limit and can push out the caller's
oldest handle, never another caller's. - Calling a tool the server does not offer is refused as
tool_not_served,
with the tool name and the list of tools it serves. Before, it looked like a
crash. aggregatewithinputset to the textnullis refused as a bad
argument that namesinput. Before, it failed with an internal error.
Ingest and publish
- A link to a record that is not there yet no longer refuses the run. When
one source points at a record another source has not written, or has since
removed, the pointing source keeps its other records and the link waits in a
hidden table until the record exists.okg doctor --check deferred-edges
reports waiting links, andokg runtime deferred-edgeslists them by source
and type. A run that defers too many links is still refused. - The scheduled publisher no longer waits forever for a sync. It used to
skip every publish while any source sync was queued or running, so one long
or stuck sync stopped publishing (one deployment skipped 1,280 of 1,294
publishes in 14 days). It now waits only until its last publish is older than
publisher.max_source_sync_yield_secondsindeployment.yaml(default 600,
that is 10 minutes), then publishes beside the sync, and still
refuses while a source run is writing. The health check counts only real
publishes, and a new check lists stuck syncs (source_sync_stuck). - Held repairs say what changed. After a failed ingest leaves a hold on
sources that were renamed or removed, the refusal names each changed source.
okg migrate --deploymentand a worker restart lift a hold themselves, only
when that is safe. - Views are reused only on proof. A view's output used to be copied forward
to a new generation without proof that its inputs were unchanged, so a view
could show old data under the new generation's label. AKMON now reuses a
view's output only when its definition, its graph reads and the catalog
version are unchanged since the previous published generation of the branch,
and the view has no input outside the graph. Every other view is rebuilt. A
rebuild with the same inline content is stored as a reference to the earlier
output. Each view manifest records where its content came from (origin) and
how current its inputs are.okg view listand the portal mark a view that
views_registry.yamlno longer lists asretired. - Declare a view's reads outside the graph. A view that declares no graph
reads is assumed to read any graph type, so it is rebuilt whenever the graph
changed. A view whosebuild_corpusreads anything outside the graph must
list it inexternal_inputsinviews_registry.yaml:publish_time,
source_runsorlive:<relation>. A view that does not is carried forward
while the graph is unchanged and can show stale content. - Faster, steadier publishes. A publish that changes a large share of the
graph recounts its summary tables in one pass (a bulk load of 233k records
and 326k links went from 4.0 s and 4.9 s to 1.5 s and 1.1 s in those two
stages), and the type-count update joins the changed links instead of
filtering with a long list (a lookup that ran past 1,200 s took 1.2 s). The
post-publish index rebuild retries up to three times when an old snapshot is
open, instead of skipping until the next publish.okg migrategoes on
within about a second when another index change holds the index lock. The
publish report says why a cycle rebuilt the whole attribute read model and
times each sub-step. - Opt-in code text (workspace-graph profile). A new
code_textsource
stores the full text of each function so a search for a word inside a
function body finds it. It ships switched off, because the text costs about
20 times the code size in the database; a size cap refuses an oversize corpus
whole. - New module for reissued documents.
document_versionsand its helper
DocumentVersionFactsgive sources a standard shape for documents that are
issued again and must keep every issue. No built-in source uses it yet.
Operating
- Health is judged by the profile a deployment declares. A deployment can
setreadiness.profiletodogfood,ciorstandard; one that declares
nothing is judged asstandard.okg dogfood status(behind the chat banner
and the console) uses it, and--max-source-age-secondsdefaults to the
profile's limit (dogfood 1 hour, ci 6 hours, standard 14 days). Before, every
deployment was judged by the strict team-workspace rules, so a plain install
with a published, readable graph showed a red "unavailable" banner. - Repeating
--includeor--excludeadds values.okg ingest --include a --include bnow ingests both. Before, only the last was kept without a
message. The same applies to--includeonokg provision,okg deployment upandokg up. - Chat tool names use one separator. For deployment
my-graph, Open WebUI
now offersokg_my_graph_searchinstead ofokg-my-graph_search.okg chat syncmoves an existing site to the new names on its next run. - Profile source defaults can name
${deployment_dir},${manifest_dir},
${source_registry_dir},${repo_root}or${environment_data_root};
before,okg install --profilerefused them as undefined. - Postgres gets 2 GB of shared memory. Containers started by
okg environmentservices now setshm_size: 2gbwhen the deployment declares no
database.shm_size. At Docker's 64 MB default, a forced-full publish could
stop with a DiskFull error. - Read-only status checks take no Git lock, so a commit in the inspected
repository no longer fails while AKMON checks whether it is dirty. - Issues opened on the public akmon repository are copied to the project's
private tracker, and the public issue closes when the tracked copy
closes. Before, maintainers copied them by hand and nothing closed them. The
copy marks the reporter's text as untrusted. Nothing from the private
tracker is written back to the public issue except a short fixed reply.
Breaking changes
These change how an existing installation behaves. A new install is not
affected.
- The upgrade deletes the old attribute copies. Migration 0046 drops the
tablesokg.attribute_versions,okg.attribute_effective_viewand
okg.attribute_effective_view_intervals, their integrity-receipt tables and
functions, the copy-recovery journal, and the drift-check table and the four
drift columns ofokg.declared_attr_indexesand
okg.declared_attr_index_state.okg up,okg migrate --applyand every
command that migrates a deployment (okg deployment up,okg provision,
okg init) apply it with no separate confirmation. A 0.3.0 server or worker
still running against the database fails once it has run, and so does
anything outside AKMON that reads those tables. To go back to 0.3.0
afterwards, restore a backup taken before the upgrade. - Six commands that served the old copies are gone:
okg db repair-effective-attribute-intervals,okg db inspect-effective-attribute-recovery,okg db prepare-effective-attribute-recovery,okg db prune-effective-intervals,
okg db backfill-effective-interval-raw-attrsandokg db attr-index verify. A script that calls one now fails with an unknown-command error. - Settings that tuned the old copies are gone and now have no effect:
OKG_IVM_INTERVAL_VIEW,OKG_IVM_DIRTY_MATERIALIZE,
OKG_IVM_INTERVAL_RECONCILE_EVERY,OKG_EFFECTIVE_VIEW_RETENTION_GENERATIONS,
OKG_EFFECTIVE_INTERVAL_PRUNE_BATCH,
OKG_ATTRIBUTE_EFFECTIVE_VIEW_EMPTY_OVERLAY_FAST_PATH,
OKG_ATTR_HASH_PRELOCK,OKG_COUNT_STAMP_RECOVERY_AUTHORIZATIONand
OKG_DECLARED_ATTR_DRIFT_SLICES. Remove them from your environment. - An unknown key in a
views_registry.yamlentry is now an error. It used
to be ignored.okg deployment lintand the upgrade preflight refuse the
registry and name the entry and the key, so a misspeltreads_subtypescan no
longer leave a view undeclared. Remove or correct the key before upgrading. - Repeated
--includeand--excludeadd values (see What's new). A script
that relied on "last one wins" now selects more sources. - Chat tool names change on
okg chat sync(see What's new). Prompts that
name the old tools must change at the same time. Chat-enabled deployment
names with_,.or capitals are now refused. - A call on kept live rows can now be refused (
field_not_fetched, or the
permission refusal on a repeated read) where it used to return empty values or
succeed. - Deployments that declare no
readiness.profileare judged asstandard
byokg dogfood status, not by the team-workspace rules. A team workspace
that relied on the strict rules must declarereadiness.profile: dogfood
(or get it from an environment'sreadiness.policy). A value other than
dogfood,ciorstandard, or one that disagrees with the environment
policy, makes the statusunavailable.
Upgrading from 0.3.0
- Run
okg deployment linton every deployment and fix any
views_registry.yamlit refuses (see Breaking changes). - Take a backup you can restore.
okg uptakes and checks one itself before
it migrates; it needspg_dumpandpg_restoreat your database server's
major version or newer (17 for the bundled image) on your PATH. If you
migrate withokg migrate --deployment <deployment> --applyinstead, take
your own backup first: migration 0046 deletes data that 0.3.0 needs. - Stop every 0.3.0 process that uses this database:
okg mcp-serve, a worker
on another machine, chat servers.okg upstops only the runtime worker it
finds. A 0.3.0 reader fails once 0046 has run, and an open read can make the
migration wait and time out. - Install the 0.4.0 wheel, then run
okg up <deployment> --planand
okg up <deployment>. It applies three migrations in order:- 0044 adds two small tables that mark which generations have their
overlays written, and fills them from the existing data. - 0045 records the publisher as a participant in the run-outcome
tables; it changes only a table comment. - 0046 deletes the attribute copies (see Breaking changes). The space
is freed at once; on a large graph expect several GB back.
okg migratealso builds a new index on record ids (CREATE INDEX CONCURRENTLY, so reads and publishes go on; about 3 s for 250,000
records). Until it exists,okg doctorwarnsgraph_read_index_missing
and search on large graphs does not say that a name is absent.
The first publish after the upgrade rebuilds each branch's overlay ranges
and takes longer than usual. An old generation whose overlays were never
recorded cannot be read pinned (overlay_generation_unmaterialized).
- 0044 adds two small tables that mark which generations have their
- Only for a deployment kept in a Git checkout: a backup check record
(okg deployment backup verify) written by 0.3.0, or an interruptedokg up
resumed with 0.4.0, is refused once. Rerunokg deployment backup verify, or
start a freshokg up. A deployment created byokg installhas no such
record:okg deployment backup verifyrefuses it
(standalone_upgrade_unsupported), andokg upchecks its own backup. - If you run Postgres through
okg environmentservices,okg environment readyreports the installed services as stale. Runokg environment service install --applyand thenokg environment service restart --apply: Docker
Compose recreates the Postgres container once, a short restart of the
database, and the data volume is kept. - If you use
okg chat, runokg chat syncand change any prompt that names
the old tool names in the same step.
Known issues
-
Views that read outside the graph and declare nothing are rebuilt on every
publish that changes the graph, so such a deployment publishes more slowly
by their build time, and is carried forward (possibly stale) on a publish with
no graph change. Declare theirexternal_inputsto fix both. -
Deployments that set
consistency.dangling_edges: prunestill drop links
to records that are not there yet at publish by default. Set
consistency.defer_dangling_edges: trueto have them wait instead. -
Searching
ABC12345does not find a record stored asABC-12-345(a
query with fewer separators than the stored id). -
A sort with a direction on a missing field of kept live rows is refused
rather than fetched. A refusedfilteroraggregatethat had fetched
fields keeps no rows, so a retry fetches again (a refusedmapthat adds
computed columns keeps them). Steps over arun's kept rows do not take
computed columns yet. -
Codex is reported as connected on a machine without Codex.
okg install
creates the folder~/.codex, andokg agent connectthen takes it as a
sign that Codex is installed. Check thatcodexruns before relying on it. -
Removing a source does not remove its records. If you take a source out
of the source registry, the records it produced stay in the graph and in
search results after the next publish. -
Some
runhints and pages with live steps are off. When arunis
pinned to an older graph version, its live steps are still read now, while
the reply names the older version. The hint to send one live step on its
own adds a graph version, so following it is refused. Page 2 of a live step
insiderunreads the outside system again, so rows that change between
pages can be skipped or repeated. -
A fresh install can fail on its first ingest. When the code-structure
source runs before the repository checkout exists, it fails withthe given reference name 'refs/heads/.invalid' is not validandokg ingestexits 1.
Runokg ingesta second time; it passes. -
code_graphalone does not load. Composing only thecode_graphmodule
fails the catalog load without saying why. It needs the modules
agent_sessions,quality_evidence,ciandopenspec; compose all five. -
Queued ingest gives no note about the other sources. When the runtime is
on andokg ingestqueues the work, a failed source still gets the new reason
but not the note saying what became of the other sources. The suggested
command that leaves a source out also needs--inlinethere. -
Some rare secret forms are still shown. The masking added in 0.3.0 does not
cover signed-URL values after#or;, URL-encoded forms, JSON-style"sig"
values,X-Goog-Signature,apikey, OAuthcode=, andauth: value. -
Graph content from Git and GitHub sources is not masked with the 0.3.0
rules. That masking applies to messages and bug reports. Text stored in
the graph from thegithub,github_actions,pactand Git-history sources
can still hold signed-URL, cookie,auth=and token values that those rules
would hide. -
Install output can print a default password. When a database answer is
written as${VARIABLE:-default}, the install output shows the default
value as written. Keep a real password out of the default, or read it from
the environment with no default. -
Adding one source to an existing profile install means a full reinstall.
okg deployment add-sourcecovers local folders, GitHub and manifest
sources. For another source of a bundled profile, run
okg install --force --yes, which takes 3 to 10 minutes. -
Code parsing on a slow network. On first use, code parsing downloads
about 500 MB of language grammars, and the download stops after about a
minute. On a slow connection, download them in advance; see
docs/runbooks/troubleshooting/first-run.md. -
The quickstart was followed on podman. The Docker commands in it are the
same commands, but they were not run on a Docker host for this release. -
One missing merge commit stops the whole GitHub source. With
commit_reposet, a merged pull request whose merge commit the git source
does not hold (a shallow clone, or a branch it does not read) makes the graph
refuse the whole GitHub batch on every sync. Sync the git source first, or
clearcommit_repo. -
Exact-name search skips plain words. Only a name that looks like code
(an underscore, a dot, or camel case such asgetUser) gets the exact-name
route. A one-word name such asSerializerstill returns the pages it did
before, not its definition first. Under enforced access rules, an
exact-completeness search for a code-shaped name returns no count. -
The background worker is off by default. To keep a graph current
without runningokg ingestyourself, setruntime.enabled: truein the
deployment'sdeployment.yaml, runokg runtime bootstrap, and keep
okg runtime workerrunning; it checks each source every 5 minutes. With
the worker on,okg ingestwaits for it.okg doctorcan report red for
the first few minutes after the first worker starts. On Linux,okg up
refuses a graph that has run a worker unless the worker service is
installed (okg dogfood service install --deployment <deployment> --adapter systemd --apply) andokg upruns as root. -
Agents and a password kept out of the command line. If you installed
with--postgres-dsn '${OKG_DSN}'(single quotes, sookgreads the
address from the environment), agents cannot see your shell's
environment: copy the address into the deployment's.okg/local.env
first.okg agent connectprints the exact command. -
Publishing a large code source takes about 30 minutes, and other
updates of the same graph wait for it. On a graph of about 785,000
records it measured 1,720 to 1,780 s. No data or search results are lost. -
The viewer's read pages need a token only when you ask for one, or when the
deployment is sensitive. Started the default way on a deployment that is not
sensitive,python -m okg.vizand the operator console (okg ui) show graph
data to anyone who can reach their port, and on a shared machine every
account can reach127.0.0.1. Start them with--auth-token-env VARIABLE_NAMEto require the token in that variable for the read API
(/viz/api/);okg uithen requires it for pages too.okg uilistens on
loopback only.python -m okg.vizrefuses a network address unless both this
token andOKG_VIZ_OPERATOR_TOKENare set. Operator controls always need
OKG_VIZ_OPERATOR_TOKEN. -
With access rules on, search can still hint at hidden text. Keyword
search now applies the reader's rules before its read limits, which closes
the case where many hidden matches changed the shape of the answer. Servers
where each request names its reader,greplimits, relevance scores, search
by meaning and records with no read class are not covered yet, so
a reader there can still learn that a word exists somewhere they cannot read. If the existence of a term must stay secret, keep that data
in a separate deployment rather than relying on access rules alone. -
--verboseis ignored by two install paths. Profiles with their own
installer (academic-researcher) and--templateinstalls keep their old,
longer output.
Install
Everything below was built from the commit this tag names. The tags v0.4.0 (images) and 0.4.0 (chart) are never moved or overwritten; pin a digest to be certain.
| Artifact | Reference | Digest |
|---|---|---|
| Runtime image | ghcr.io/mitdbg/akmon:v0.4.0 |
sha256:c8a74986e50950bffe0496af0b47fc6dec2272f45d02d5431ffedaa953e40431 |
| PostgreSQL image | ghcr.io/mitdbg/akmon-postgres:v0.4.0 |
sha256:cc7ea2921411dd30f1e7c98fe2bf089415050fa8ca10e73566ade3b393390f62 |
| Helm chart | oci://ghcr.io/mitdbg/charts/akmon version 0.4.0 |
sha256:4670e69846f2d709fcf409df015b6746c81c7a96506424203a8d3a1cdfe79b28 |
helm install okg oci://ghcr.io/mitdbg/charts/akmon --version 0.4.0 -f values.yaml
The chart's appVersion is v0.4.0: with images.okg.tag and digest empty it runs ghcr.io/mitdbg/akmon:v0.4.0, and its PostgreSQL image is ghcr.io/mitdbg/akmon-postgres:v0.4.0.