Skip to content

AKMON v0.4.0

Choose a tag to compare

@github-actions github-actions released this 27 Oct 16:13
· 3 commits to main since this release

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.whl is attached to
    this release. Install it with uv tool install or pip 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 like ABC-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 migrate builds (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, and file: and commit: ids and source_file lookups 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
    DocCorpusSource must set mcp.doc_text_nodes: true; one that wraps a git
    source must set mcp.file_nodes: true and, 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 offered run, because run's
    example took their room. It now names the same four sources with or without run. When
    the run example does not fit beside them, the example is left out and one
    line says where the step format is described.
  • inspect on 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. inspect takes 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_line is 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

  • aggregate ranks groups. order_by on a metric (for example
    "max(attrs.size) desc") ranks every group before limit and offset cut
    the page, on stored records, on live results and inside run. Each group
    gets its rank, and the reply says whether all groups were ranked or only
    those from a partial read. Replies without order_by are unchanged.
  • Every live read returns a handle. A read of an outside system through
    filter, map, aggregate or expand now returns a handle: a short id for
    the rows the server keeps for the caller. The next call passes it as input
    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.
  • map adds computed columns. An agent can have the server compute a
    value for each row of a live result, such as fill = 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, search with method: "grep" and within: "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. A map or
    filter that names a field the kept rows lack now fetches it, or is refused
    as field_not_fetched; before, it returned empty values with no error. An
    input sent as a JSON string is read like the object form; before,
    aggregate on a live type returned zero groups without an error.
  • A refused map keeps the rows it fetched. When a map that 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.
  • aggregate with input set to the text null is refused as a bad
    argument that names input. 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, and okg runtime deferred-edges lists 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_seconds in deployment.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 --deployment and 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 list and the portal mark a view that
    views_registry.yaml no longer lists as retired.
  • 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 whose build_corpus reads anything outside the graph must
    list it in external_inputs in views_registry.yaml: publish_time,
    source_runs or live:<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 migrate goes 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_text source
    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_versions and its helper
    DocumentVersionFacts give 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
    set readiness.profile to dogfood, ci or standard; one that declares
    nothing is judged as standard. okg dogfood status (behind the chat banner
    and the console) uses it, and --max-source-age-seconds defaults 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 --include or --exclude adds values. okg ingest --include a --include b now ingests both. Before, only the last was kept without a
    message. The same applies to --include on okg provision, okg deployment up and okg up.
  • Chat tool names use one separator. For deployment my-graph, Open WebUI
    now offers okg_my_graph_search instead of okg-my-graph_search. okg chat sync moves 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 --profile refused them as undefined.
  • Postgres gets 2 GB of shared memory. Containers started by okg environment services now set shm_size: 2gb when 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
    tables okg.attribute_versions, okg.attribute_effective_view and
    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 of okg.declared_attr_indexes and
    okg.declared_attr_index_state. okg up, okg migrate --apply and 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-attrs and okg 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_AUTHORIZATION and
    OKG_DECLARED_ATTR_DRIFT_SLICES. Remove them from your environment.
  • An unknown key in a views_registry.yaml entry is now an error. It used
    to be ignored. okg deployment lint and the upgrade preflight refuse the
    registry and name the entry and the key, so a misspelt reads_subtypes can no
    longer leave a view undeclared. Remove or correct the key before upgrading.
  • Repeated --include and --exclude add 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.profile are judged as standard
    by okg dogfood status, not by the team-workspace rules. A team workspace
    that relied on the strict rules must declare readiness.profile: dogfood
    (or get it from an environment's readiness.policy). A value other than
    dogfood, ci or standard, or one that disagrees with the environment
    policy, makes the status unavailable.

Upgrading from 0.3.0

  1. Run okg deployment lint on every deployment and fix any
    views_registry.yaml it refuses (see Breaking changes).
  2. Take a backup you can restore. okg up takes and checks one itself before
    it migrates; it needs pg_dump and pg_restore at your database server's
    major version or newer (17 for the bundled image) on your PATH. If you
    migrate with okg migrate --deployment <deployment> --apply instead, take
    your own backup first: migration 0046 deletes data that 0.3.0 needs.
  3. Stop every 0.3.0 process that uses this database: okg mcp-serve, a worker
    on another machine, chat servers. okg up stops 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.
  4. Install the 0.4.0 wheel, then run okg up <deployment> --plan and
    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 migrate also 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 doctor warns graph_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).
  5. Only for a deployment kept in a Git checkout: a backup check record
    (okg deployment backup verify) written by 0.3.0, or an interrupted okg up
    resumed with 0.4.0, is refused once. Rerun okg deployment backup verify, or
    start a fresh okg up. A deployment created by okg install has no such
    record: okg deployment backup verify refuses it
    (standalone_upgrade_unsupported), and okg up checks its own backup.
  6. If you run Postgres through okg environment services, okg environment ready reports the installed services as stale. Run okg environment service install --apply and then okg environment service restart --apply: Docker
    Compose recreates the Postgres container once, a short restart of the
    database, and the data volume is kept.
  7. If you use okg chat, run okg chat sync and 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 their external_inputs to fix both.

  • Deployments that set consistency.dangling_edges: prune still drop links
    to records that are not there yet
    at publish by default. Set
    consistency.defer_dangling_edges: true to have them wait instead.

  • Searching ABC12345 does not find a record stored as ABC-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 refused filter or aggregate that had fetched
    fields keeps no rows, so a retry fetches again (a refused map that adds
    computed columns keeps them). Steps over a run'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, and okg agent connect then takes it as a
    sign that Codex is installed. Check that codex runs 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 run hints and pages with live steps are off. When a run is
    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
    inside run reads 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 with the given reference name 'refs/heads/.invalid' is not valid and okg ingest exits 1.
    Run okg ingest a second time; it passes.

  • code_graph alone does not load. Composing only the code_graph module
    fails the catalog load without saying why. It needs the modules
    agent_sessions, quality_evidence, ci and openspec; compose all five.

  • Queued ingest gives no note about the other sources. When the runtime is
    on and okg ingest queues 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 --inline there.

  • 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, OAuth code=, and auth: 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 the github, github_actions, pact and 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-source covers 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_repo set, 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
    clear commit_repo.

  • Exact-name search skips plain words. Only a name that looks like code
    (an underscore, a dot, or camel case such as getUser) gets the exact-name
    route. A one-word name such as Serializer still 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 running okg ingest yourself, set runtime.enabled: true in the
    deployment's deployment.yaml, run okg runtime bootstrap, and keep
    okg runtime worker running; it checks each source every 5 minutes. With
    the worker on, okg ingest waits for it. okg doctor can 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) and okg up runs as root.

  • Agents and a password kept out of the command line. If you installed
    with --postgres-dsn '${OKG_DSN}' (single quotes, so okg reads 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 connect prints 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.viz and the operator console (okg ui) show graph
    data to anyone who can reach their port, and on a shared machine every
    account can reach 127.0.0.1. Start them with --auth-token-env VARIABLE_NAME to require the token in that variable for the read API
    (/viz/api/); okg ui then requires it for pages too. okg ui listens on
    loopback only. python -m okg.viz refuses a network address unless both this
    token and OKG_VIZ_OPERATOR_TOKEN are 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, grep limits, 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.

  • --verbose is ignored by two install paths. Profiles with their own
    installer (academic-researcher) and --template installs 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.