Skip to content

Releases

0.0.41

Choose a tag to compare

@b10x-bot b10x-bot released this 20 Oct 23:59
f2b9ec6

Added

  • !MergeEntity {absorbed, into, reason, evidence} applies (story:merge-entity-applies, design
    § 111, ADR 0018): it joins a duplicate node to the node it duplicates — of one type, or of types
    related through parents — in a revision of its own. The absorbed node leaves the head into a
    supersession record naming into, as a SupersedeNode retires a node, and every reference to it
    is rewritten in place: each edge at it keeps its id and runs to into, an edge joining the two is
    retired into the record, and one duplicating an edge into holds, with equal properties,
    collapses onto it; each assertion about it, naming it as its object or in a NodeRef inside its
    value, names into, every other byte unchanged; each NodeRef naming it in a node's or an edge's
    properties names into; and a property only it holds moves to into. The record's merge
    lineage — under superseded in ekr snapshot — says what moved. The absorbed node reads as a
    superseded node does; /changes lists the merge as a NodeMerged change, counted in
    nodes_merged, with into in target. Applied under every validation profile. Refused by name:
    merge-into-itself, unresolved-node, node-superseded, merge-without-reason,
    merge-without-evidence, merge-evidence-unknown, merge-types-unrelated,
    merge-undeclared-property, merge-property-conflict, merge-duplicate-edge-conflict,
    merge-self-relation (an active relation assertion joining the two nodes, either way: retract
    it first), edge-endpoint-type, wrong-type, edge-cardinality, property-cardinality and
    superseded-node-named-in-transaction. The bare {absorbed, into} shape is refused as before,
    unsupported-operation, under every profile, so retained rejections replay; a store that never
    merges keeps its roots and checkpoint bytes. ekr compare compares a merged node's lineage in
    its superseded category. The SDK builds the bare shape with EntityMerge::bare, reads
    SnapshotSupersededNode::merge and ChangeKind::NodeMerged, and Resolver::observe drops every
    cached answer naming a node a committed merge absorbs.

Changed

  • Breaking: the SDK's EntityMerge::new(absorbed, into, reason, evidence) takes the reason
    and the evidence, no longer only the two nodes, and builds the shape that applies; a caller that
    wrote EntityMerge::new(absorbed, into) for the bare P1 shape writes EntityMerge::bare(absorbed, into), which every profile still refuses.
  • Breaking: EntityMerge is no longer Copy, in the SDK (ekr_sdk) and in the kernel
    (ekr_kernel): each gained reason: Option<String> and evidence: Option<BTreeSet<EvidenceId>>.
    A caller that copied one clones it.

0.0.40

Choose a tag to compare

@b10x-bot b10x-bot released this 20 Oct 09:49
efe5365

Fixed

  • SupersedeNode review fixes (story:supersede-node-review-fixes). describe_node /1 of a live
    node no longer lists or counts, under referencing, an assertion whose subject is a superseded
    node or an edge retired with one, and the overview's roles date no node from one, as its
    timeline already did. Resolver::observe drops every cached answer naming a node a committed
    SupersedeNode retires. ekr apply-extraction and ekr_sdk::extraction::apply check a fact
    against the type of the node its key resolves to: a key superseded by a node of another type
    takes the kept node's properties and edge ends, a fact the kept type does not admit is refused
    before anything is written — no node is created for it — and a refusal names the supersession
    only where that end caused it. A validation and a sample look retired edges up in one index of
    the supersession records instead of scanning them per lookup; no verdict changes. Documents:
    design § 110 and graph.yaml say an edge between two nodes superseded
    together is held in its source's record; docs/cli.md says only ekr view answers the refusal
    with 404; views.yaml says overview totals count every held assertion while projections and the
    timeline omit those about superseded or retired subjects.

0.0.39

Choose a tag to compare

@b10x-bot b10x-bot released this 20 Oct 00:55
67bd555

Added

  • !SupersedeNode {node, by, reason, evidence} retires a duplicate node in favour of the node
    that replaces it, in a revision of its own (story:supersede-node, design § 110). The node and
    every edge at it leave the head and are kept in a supersession record — the node and its edges
    as they were, by, the revision, the reason and the evidence — which ekr snapshot lists under
    superseded; assertions about it are not changed, and every earlier revision still reads it.
    Every head read omits it. ekr resolve of its type and one of its aliases answers by, even when
    by is of another type, and those aliases stay taken (alias-already-exists). A read of its id
    at the head — /node/<id> in either format, /expand, the MCP describe_node and expand, the
    session's — is refused as ekr.views.NodeSuperseded, naming by; /changes lists the
    supersession as a NodeSuperseded change, with the node kept in target. Refused by name:
    supersede-unknown-node, node-already-superseded, supersede-into-itself,
    supersede-by-unknown-node, supersede-by-superseded-node, supersession-without-reason,
    supersession-without-evidence, supersession-evidence-unknown and
    superseded-node-named-in-transaction; a later operation naming a superseded node or a retired
    edge is node-superseded, naming by. Applied under every validation profile. The SDK builds
    it (NodeSupersession) and reads SnapshotGraph::superseded and ChangeKind::NodeSuperseded.
    A store that never supersedes keeps its roots and checkpoint bytes. A read that reaches an
    assertion naming a superseded node names its pointer: subject_superseded_by and
    object_superseded_by in ekr sample and ekr explain, object_superseded_by on every rendered
    assertion (SDK SampledFact, ViewAssertion, SnapshotAssertion). /timeline of a superseded
    subject is ekr.views.NodeSuperseded; ekr compare compares the records as a superseded
    category, listed only where either store holds one. ekr apply-extraction replaces a value
    under a superseded key on the node kept, and a fact refused after resolving through a
    supersession to a node of another type names it.

0.0.38

Choose a tag to compare

@b10x-bot b10x-bot released this 17 Oct 20:17
2dd0940

Added

  • ekr mcp-http and ekr view take --metrics, which serves GET /metrics in the Prometheus
    text exposition format, and --request-log <path> (- for standard error), which writes one
    ekr.request-log/1 JSON line per request. Both count each read at their dispatch by its wire
    name and outcome under one set of names: ekr_read_requests_total{tool,outcome},
    ekr_read_duration_seconds{tool}, ekr_read_result_bytes{tool}, ekr_read_in_flight,
    ekr_store_head_revision and ekr_store_query_errors_total{tool}, so one read through either
    server moves the same series. A log line carries the request's X-Request-Id and
    traceparent when they are well formed, generated ones otherwise, and never an argument, a
    result, document content or a credential. /metrics is admitted as every other path is and
    answers 404 without the option (ekr.views.ReaderMetricName, ekr.views.RequestLogV1,
    ekr.cli.ReaderTelemetryOptions).
  • ekr.node-matches/3, a search that ranks a whole name or alias first whatever its case
    (story:search-match-kinds). Under ekr.node-matches/2 a case-exact match comes before a
    case-folded one, so a node whose whole name is the text in another case ranked below every node
    holding the text case-exactly inside a longer name or alias, and a reader of the first few
    matches missed it. /3 matches the nodes /2 matches, with the same type, from and to, and
    names each match's kind — WholeName, WholeAlias, Prefix, Substring or Words — and
    folded, whether it matched only after lowercasing. Matches rank by kind, then case-exact
    before case-folded, then (with a window) newest first, degree and id; meta.whole_total
    counts the whole matches before limit. ekr view's /search and the session verb search
    answer it with format=ekr.node-matches/3 (--format), and the SDK reads it as
    Reader::search_v3, NodeMatchesV3. ekr.node-matches/1 and /2 answer the bytes they did.

Changed

  • The search tool of ekr mcp answers ekr.node-matches/3 when no format is named (it
    answered /2); name ekr.node-matches/2 or /1 for the earlier documents.

0.0.37

Choose a tag to compare

@b10x-bot b10x-bot released this 17 Oct 06:35
2f233f8

Fixed

  • ekr stage begin stages a store whose seed or commit carries thousands of evidence payloads on
    PostgreSQL (#93). The begin's copy published each occurrence as one provider group, and
    PostgreSQL takes one advisory lock per stream of a group, so a seed of 8,192 payloads was
    refused stage-copy-failed, "out of shared memory", on a stock-sized lock table. A copied
    occurrence that would carry more than 32 streams now has its payloads stored ahead of it in
    groups of at most 32 (ekr.store.CopyGroupStreams), with their class and stored_at, and its
    own group binds only the objects it appends, so no transaction of the copy takes more than 34
    advisory locks (32 streams, the publication gate and the group's own). ekr seed,
    ekr commit and ekr migrate are unchanged: a migration still writes each copied seed or
    commit as one transaction, into a destination another migration may also be writing, and
    docs/cli.md names the lock-table setting that bounds all three.
  • ekr stage list on a PostgreSQL store answered [], or left out a stage, while any other
    transaction on the server was open, although the stage's begin had committed and
    ekr stage show found it (#94). A begin now records the stage in the store's stage index,
    ekr.store.StageIndexed, in the same atomic write as its StageBegun, and the listing reads
    that index as a stream; stages begun before this release are still found through the log.

0.0.36

Choose a tag to compare

@b10x-bot b10x-bot released this 16 Oct 21:22
48e35b1

Added

  • ekr.node-detail/2, one node bounded for an agent's context (ekr-mcp-fixes-20261015 #1, #10,
    #11). ekr.node-detail/1 lists every assertion and edge of the node, so a node with thousands
    of edges answered megabytes. /2 answers the record with its type's name; same_name, the
    other nodes sharing its name or an alias, compared lowercased; its property values grouped by
    property and named, newest first, a String over 2,000 bytes cut on a character boundary and
    marked truncated; and its neighbours grouped by edge type and direction, each group named
    and counted, listing at most limit (10 by default, up to 500) by degree, each with the newest
    Relation assertion between the two. edge_type (an id or a name) and after page one edge
    type's groups. An ekr.node-detail/2 request for a node type or edge type id is refused as
    ekr.views.NodeIsAType, naming the type; /1 still answers ekr.views.NodeNotFound for it.
    ekr view serves it at /node/<id>?format=ekr.node-detail/2[&limit=L][&edge_type=E][&after=A]
    and the session's describe with --format ekr.node-detail/2; both keep answering /1 by
    default, byte for byte as before.

  • A search format, ekr.node-matches/2, served by ekr view's /search and the session verb
    search when asked for (format=ekr.node-matches/2, --format ekr.node-matches/2) and by the
    MCP tool search by default. A text of two or more words also matches a name or an alias
    holding every word in any order, as the new tier Words (widget router finds
    Widget-Router); within a tier a node whose whole name or alias is the text comes first (eu
    before a busier prod-eu-central-9); every match carries type_name and, when the node has
    one, its timeline time. type (a node type's id or name) lists only that type's nodes, an
    empty text listing them all, and from and to (milliseconds or YYYY-MM-DD) only nodes
    whose time falls in the window, newest first. A type naming no node type is refused as
    ekr.views.NodeTypeNotFound. ekr.node-matches/1 is unchanged and stays the default of
    ekr view and the session verb; the SDK's NodeMatches keeps reading /1.

  • The MCP tool search takes text as query or q as well; naming two of them is refused.

  • Named formats, so an answer names the types and properties it cites and needs no ontology to
    read: ekr.graph-overview/3 (each node_types and edge_types entry with its name, and
    meta.committed_at, when the revision read was committed), ekr.graph-slice/2 (each node and
    edge with its type_name), ekr.graph-timeline/2 and ekr.graph-changes/2 (with names, an
    object mapping every node type, edge type and property id the document holds to its name),
    ekr.explanation/3 (ekr explain's document with names) and ekr.resolution/1 (ekr resolve's outcome under outcome, with names). ekr view serves them when asked for
    (format= on /overview, /expand, /timeline and /changes), so do the session verbs
    (--format), ekr explain --format ekr.explanation/3 and ekr resolve --format ekr.resolution/1. Without a format each answers what it answered before; the SDK keeps
    reading the earlier formats.

  • ekr.explanation/3 answers the evidence the chain cites, within a whole-answer cap. --scope cited (its default) keeps only the Evidence links the chain's assertions and attachments cite
    and counts the rest as omitted_evidence, where ekr.explanation/2 lists every evidence entry
    of the transactions on the chain (247 for an assertion citing one, as measured); --scope all
    keeps them all. With --documents it carries no proposal record and no base64 payload beside
    a text, and the printed answer is at most 48 KiB: the longest evidence text is halved, and
    marked truncated with offset and record_length, until it fits. --scope needs --format ekr.explanation/3; ekr explain without a format prints ekr.explanation/2 unchanged.

  • The MCP tools take a few arguments under a second name: describe_node's node as id or
    node_id and its edge_type as edge, expand's seeds as seed, node or nodes (and one id where a list was
    required), explain's assertion as id or assertion_id, and timeline's subject as
    node. Each tool's inputSchema lists them. An alias answers exactly what the argument's own
    name answers; naming one argument twice is -32602. ekr view is unchanged.

Changed

  • The MCP tool describe_node answers ekr.node-detail/2 by default, byte for byte what
    ekr view serves for the same request with the format named, and takes limit, edge_type
    and after; full: true answers the whole ekr.node-detail/1 as before.
  • The MCP tool search answers ekr.node-matches/2 unless format names
    ekr.node-matches/1: an agent reading tier sees Words, and each match gains type_name.
    For one request with the format named, its answer is byte for byte what ekr view serves.
  • The MCP tools overview, expand, timeline, changes_since, explain and resolve answer
    the named formats unless format names the earlier one (ekr.graph-overview/2,
    ekr.graph-slice/1, ekr.graph-timeline/1, ekr.graph-changes/1, ekr.explanation/2,
    outcome); explain answers scope cited unless scope names all. An agent reading
    resolve reads the outcome under outcome. For one request with the format (and for
    explain the scope) named, each answer is byte for byte what ekr view, the session verb,
    ekr explain or ekr resolve answers.
  • The MCP tools answer smaller default pages, since an agent reads a tool result whole: expand
    takes edges as 200 (was 5,000) and changes_since takes limit as 100 (was 500) when the
    call names none. The answer is what ekr view and the session verb answer with that bound
    named; their own defaults are unchanged. A page ends at whichever of its limits comes first,
    and the describe_node and expand descriptions say how to ask for the next page.
  • changes_since, GET /changes and the session's changes derive each revision's changes
    from its transaction document once while the server keeps its indexes: a second request over
    the same revisions parses no transaction document, and one after a commit parses only the
    revisions it added. Answers are unchanged. ekr_views::Index::transaction_documents_parsed
    counts the documents parsed.
  • ekr mcp-http answers initialize, ping, tools/list, notifications and every transport
    refusal on the request's own connection, never behind the store's queue: a client's handshake
    no longer waits for another client's tool call or for the store's first open, and is answered
    (with the same bytes) before the store is admitted. Only tools/call and /readyz wait for
    the store.

0.0.35

Choose a tag to compare

@b10x-bot b10x-bot released this 16 Oct 17:26
ab88fdb

Fixed

  • ekr stage begin whose copy into the stage's tenant does not land no longer answers
    publication outcome is unknown, which named no stage although the begin had recorded one. It
    abandons the stage and empties its tenant as far as each lands and refuses with the new named
    refusal stage-copy-failed (exit 2), naming the stage, whether it is Abandoned or still
    Begun, whether its tenant may hold part of the copy, and the copy's own error;
    ekr stage abandon <stage id> finishes what the begin could not. The store's head and history
    do not move.
  • On PostgreSQL a write whose outcome the provider cannot tell is tried again only while less
    than pool.transaction_timeout_ms has passed since its first attempt, at most 16 times as
    before, so a write that runs into that deadline answers within about two deadlines instead of
    sixteen. A stage begin of a large store needs pool.transaction_timeout_ms (with
    statement_timeout_ms and lock_timeout_ms) long enough for its largest copy group; SQLite and
    File stores retry as before. docs/cli.md describes both.

0.0.34

Choose a tag to compare

@b10x-bot b10x-bot released this 15 Oct 01:09
c128476

Added

  • ekr compare --other <store> says whether two stores hold the same knowledge, on any providers,
    each at its head or at --at and --other-at: the ekr.store-comparison/1 document, equal or
    not, and per category (types, properties, nodes, edges, assertions, evidence) the counts on each
    side and the first differing ids with the fields that differ. Commit times and transaction ids
    are not compared, so two stores built from the same documents at different times compare equal.
    It writes to neither store. docs/cli.md describes it.

  • A schema change can cite the evidence that introduced it. !CiteEvidence <evidence id> beside
    the schema changes of a transaction names evidence the store retains, or evidence an
    !AddEvidence of the same transaction adds, and that !AddEvidence no longer makes the
    transaction mixed-schema-transaction; any other data operation still does. The schema version
    the commit makes records every cited id, and ekr ontology (with or without --at) prints them
    as schema_version_evidence. Under validation profiles v2 and v3 only; v1 refuses the kind as
    unsupported-operation. A citation is refused as unresolved-evidence,
    citation-without-schema-change, duplicate-citation or evidence-set-mismatch (the cited id
    belongs in transaction.evidence). ekr operations lists seventeen kinds. A version that cites
    nothing encodes as before, so no existing ontology root moves; a store holding a citing version
    is refused by earlier binaries. The projection (ekr.graph-projection/1, what ekr view
    serves at /projection) carries a version's citations as evidence on its schema.versions
    entry, omitted when it cites none, so a projection without a citation is unchanged; the viewer
    page does not display them yet. In ekr-sdk, read::Ontology carries
    schema_version_evidence, and TransactionBuilder::build refuses a citation without a schema
    change as DocumentError::CitationWithoutSchemaChange, the kernel's
    citation-without-schema-change, rather than MixedSchemaTransaction.

Changed

  • One-shot ekr snapshot and ekr ontology answer from the retained replay checkpoint, which
    serves as a persisted read model, when the kernel verifies it for the revision read. They then
    load the revision's record, the seed envelope and every evidence payload, and decode the
    record with the checkpoint. They replay nothing and no longer read, hash or decode every record of the history
    (design § 108). Answers are byte for byte what a complete read prints. A model that does not
    verify is ignored, and the read answers as before.

    • Still served by a complete read, as before: a snapshot of a head past the checkpoint's
      revision (up to four commits made one process each, § 99.5.1), a snapshot --at any revision
      other than the checkpoint's, a schema changed since the checkpoint, any head read after a
      proposal or validation no checkpoint pointer covers yet, --full-replay, and every read after
      the first on a long-lived handle (ekr session).
    • A retained record damaged after the checkpoint pointer that covers it was written is no longer
      refused by an ekr snapshot or ekr ontology served from the model, which does not load it.
      This is what ekr head already did since § 99.2. The next read that loads the record refuses
      it: any write verb, ekr explain, ekr transactions, or any verb under --full-replay.
      A damaged or absent seed envelope or evidence payload is still refused by both verbs: the
      model loads and verifies each one a complete read requires (design § 108.2 item 7).
  • overview answers ekr.graph-overview/2 and takes sections, any of ontology, schema,
    roles and timeline, on GET /overview (sections=ontology,schema), the MCP tool
    ("sections": ["ontology"]) and the session verb (--sections ontology,schema). Named none, the
    answer is the counts, the type counts and the top nodes only, so its size no longer grows with
    the ontology's properties, the schema's versions or the revisions committed. Named all four, it
    is ekr.graph-overview/1's content under the new format name. A section not named is absent,
    never empty. A client that reads the ontology, schema, roles or timeline from overview now
    names them; the viewer page and the SDK's Reader::overview name all four. The SDK's
    Overview holds ontology, schema, roles and timeline as Options, and
    Reader::overview_sections names the sections to read.

0.0.33

Choose a tag to compare

@b10x-bot b10x-bot released this 08 Oct 06:00
cb8efa9

Added

  • A run can be staged: ekr stage begin copies the store at its head into a stage of its own and
    prints the stage id; --stage <id> or EKR_STAGE=<id> joins every verb that opens an existing
    store to that stage; ekr stage publish <id> --expect-head <revision> appends the stage's
    revisions to the store in one append group, or refuses by name and changes nothing; ekr stage abandon <id> drops the stage; ekr stage list lists a store's stages. After a publication or
    an abandonment the stage's tenant holds nothing. A retried publish returns the first one's
    result and appends nothing twice. Stages run on SQLite and PostgreSQL; the File provider
    refuses them. docs/cli.md lists the verbs and their refusals.
  • A PostgreSQL store's inventory is read under one provider capture, so a stage can be begun from
    a PostgreSQL store. ekr migrate still refuses a PostgreSQL source.

Changed

  • Every store opener refuses a tenant containing ekr.stage: as stage-tenant-reserved; that
    marker names stage tenants only.
  • A stage publication is elected through a new retained format,
    ekr.publication-preparation/4. A store holding one is refused by earlier binaries as
    inventory-preparation-unreadable.
  • The Eventlog crates move from 0.5.0 to 0.8.1. The File provider's appends and in-window reads
    no longer grow with the journal's size.

0.0.32

Choose a tag to compare

@b10x-bot b10x-bot released this 07 Oct 02:12
c3d9da3

Changed

  • ekr explain --documents and the MCP explain tool with documents: true answer at most
    64 KiB of each evidence record, centred on the cited text (the first string value of an
    assertion on the chain that the record holds), instead of the whole record. A link that answers
    less than its whole record carries offset, record_length and truncated: true; a record
    within the bound is answered whole and unchanged. Proposal records and commit receipts are
    still answered whole.

Added

  • ekr explain --documents --offset N --limit N, and offset and limit on the MCP explain
    tool, read any byte range of each evidence record. Offsets are raw byte offsets: a range that
    cuts a character answers payload without text. Steps of limit from offset 0 to
    record_length reassemble the record byte for byte. Both refuse without documents, and a
    limit of 0 is refused.
  • An ekr.postgres/1 configuration takes an optional password_file holding exactly
    {"password": "..."}, the document a saved PostgreSQL connection hands a launched program, so
    the connection file and the configuration directory hold no secret. An absolute path such as
    /proc/self/fd/3 is used as written. A connection file that already carries a password, a
    document with another field or a non-string password, and a document over 64 KiB are refused
    as postgres-configuration without echoing the value.

Fixed

  • A running ekr mcp, ekr view or ekr session no longer answers a discarded revision after
    its SQLite store is restored to an older snapshot through SQLite's online backup and a new
    commit reuses the revision number. Their indexes and /projection and /roles answers are
    kept under the revision's identity — its revision id and root — and each read drops one the
    store no longer holds, so search, overview, describe_node, expand, timeline and
    changes_since answer the new revision. While anything is kept, a read also reads the store's
    retained transaction records. ekr_views::LoadedRevisionEntry carries each revision's
    revision_id and root; ekr_views::RevisionIdentity and ekr_views::Lineage are new.