Skip to content

v2.5.0

Choose a tag to compare

@imattau imattau released this 02 Aug 01:14
· 100 commits to master since this release

Added

  • Activation model (adaptive memory). A new two-tier relevance layer turns
    Polypack from a passive data store into an adaptive memory system:
    • Durable activation. Optional NodeActivation (score, importance,
      reinforcementCount, lastMeaningfulActivation) on every node, persisted
      through the existing snapshot/WAL and adapters (MessagePack carries the new
      field, so stores remain forward/backward compatible).
    • Core PolyGraph primitives. reinforceNode/reinforceNodeSafe
      (durable reinforcement, decay-corrected, re-anchored, emits a new
      activation_updated change event with delta/reason),
      getActivation/getActivationState (lazy decay as a pure function of
      elapsed time from the anchor, so replicas converge), topActivated
      (working-memory primitive), and decay (materialize fresh values).
    • @0xx0lostcause0xx0/polypack/activation subpath. ActivationEngine
      composing the adaptive layer: transient (never-synced) attention
      (bumpAttention/attentionOf/effective), spreading activation
      (spread with per-hop attenuation and edge-type filtering), semantic
      region scoring (pulse, zero-similarity nodes never seed the region),
      self-maintaining reinforcement (absorb), and a live workingMemory
      view. mergeActivation merges total-state records (max, idempotent).
    • Query integration. whereActivated and orderByActivation on both
      GraphQuery and PersistedGraphQuery; GraphQuery falls back from the
      native executor when activation filters are present.
    • React. useWorkingMemory — a live view of the most-activated nodes;
      pass an optional ActivationEngine to rank by durable score plus
      transient attention instead of the graph's durable-only ranking.
    • Sync semantics. Activation is synced as derived statistical state,
      not ordinary CRUD: deltas accumulate via a new activationUpdate op
      (coalesced per node and gated by SyncClientOptions.activationSyncThreshold,
      default 0.05) while full-node payloads max-merge on arrival, and an absent
      incoming activation never wipes locally learned state. The SyncClient
      applies deltas synchronously for loaded nodes so the echo-suppression window
      stays intact.
    • examples/activation.ts — a Nostr-style adaptive-memory scenario.
    • Rust port (polypack-core + polypack-graph). The activation model is
      implemented natively in Rust, byte-for-byte compatible with the TypeScript
      reference: NodeActivation on the core Node (persisted through the
      snapshot/WAL — the Rust node_to_msg/msg_to_node codecs now always emit
      the activation key, matching TS's explicit-nil encoding, with updated
      byte-compat hex fixtures and a new activation round-trip test);
      polypack_core::activation (decay/reinforce/merge math); Graph
      primitives (reinforce_node/reinforce_node_safe emitting a new
      ActivationUpdated event, get_activation/get_activation_state,
      top_activated, decay); where_activated/order_by_activation on
      GraphQuery and PersistedGraphQuery; and an ActivationEngine with
      spread/pulse/absorb/working-memory.
    • Python port (polypack-db). The pure-Python PolyGraph/GraphQuery
      layer carries activation through node plumbing and the store, gains
      reinforce_node/get_activation/top_activated/decay and
      where_activated/order_by_activation (activation filters bypass the
      native query executor), and ships an ActivationEngine, merge_activation,
      and decay_factor. reinforce_node_safe is an alias — the Python graph
      has no hot-cache eviction.
    • Native bindings (polypack-node/polypack-native). Pure activation
      helpers exposed through NAPI and wrapped in @0xx0lostcause0xx0/polypack-native:
      decayFactor, mergeActivation, reinforceActivation, activationScoreOf,
      with a parity test. The TS activation engine itself stays pure TypeScript.

Changed

  • PolyNode/SerializedNode gain an optional activation field;
    GraphChangeEvent gains the activation_updated type (additive, non-breaking).
  • updateNode accepts an optional fourth activation argument for sync-merge
    application.
  • The Rust snapshot/WAL wire format now carries the activation node field
    (always emitted, nil when absent, matching TypeScript) — old stores without
    the field still decode.

Fixed

  • SyncClient.disconnect() now flushes pending operations (including
    coalesced autoFlush: false activation deltas) before tearing down,
    instead of silently discarding whatever hadn't been sent yet.
  • HNSWIndex now validates M/Mmax0/efConstruction/efSearch are
    positive integers and throws RangeError on invalid config, instead of
    silently building a degenerate graph that returns bad query results.
  • HNSWIndex.update() — not part of VectorIndexLike and functionally
    identical to add() — is now @deprecated and delegates to add()
    instead of duplicating its logic.
  • BinaryStoreAdapter.close() now flips its closed state synchronously
    before enqueueing shutdown work, closing a race where a write issued
    concurrently with close() could pass its open-check and land after
    the compaction step completed.
  • Python PolyGraph.close_store() (and the with PolyGraph.open(...)
    context manager, which calls it) now saves before closing the store,
    instead of silently discarding mutations made since the last explicit
    save() — matching Rust's Graph::close, which already flushes first.
  • Rust HnswIndex::new now validates m/mmax0/ef_construction/
    ef_search are at least 1 and returns Err(InvalidArgument) instead of
    either building an index whose queries silently return no results
    (ef_search == 0) or panicking on the second insert (ef_construction == 0). Propagated through Graph::open, the N-API NativeHnswIndex
    constructor, and the PyO3 HnswIndex constructor (all now fallible).
  • Removed two stale Rust doc comments in polypack-graph::Graph claiming
    remove_node and hot-cache eviction were unimplemented stubs that would
    panic; both are fully implemented and tested.
  • polypack-graph now re-exports polypack_core::query::Direction, which
    GraphQuery/PersistedGraphQuery's traverse/join require as a
    parameter but previously couldn't be named without an explicit
    polypack-core dependency.
  • The Python wheel/sdist (maturin) no longer bundles __pycache__/.pyc
    build-environment leakage.
  • @0xx0lostcause0xx0/polypack-native's files field now excludes
    dist/*.node, so a locally-built platform binary left in dist/ can't
    accidentally ship in the root npm package — only the per-platform
    optionalDependencies packages should carry a binary.

Full changelog: https://github.com/imattau/polypack/blob/master/CHANGELOG.md#250---2026-08-02