v2.5.0
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
PolyGraphprimitives.reinforceNode/reinforceNodeSafe
(durable reinforcement, decay-corrected, re-anchored, emits a new
activation_updatedchange event withdelta/reason),
getActivation/getActivationState(lazy decay as a pure function of
elapsed time from the anchor, so replicas converge),topActivated
(working-memory primitive), anddecay(materialize fresh values). @0xx0lostcause0xx0/polypack/activationsubpath.ActivationEngine
composing the adaptive layer: transient (never-synced) attention
(bumpAttention/attentionOf/effective), spreading activation
(spreadwith per-hop attenuation and edge-type filtering), semantic
region scoring (pulse, zero-similarity nodes never seed the region),
self-maintaining reinforcement (absorb), and a liveworkingMemory
view.mergeActivationmerges total-state records (max, idempotent).- Query integration.
whereActivatedandorderByActivationon both
GraphQueryandPersistedGraphQuery;GraphQueryfalls back from the
native executor when activation filters are present. - React.
useWorkingMemory— a live view of the most-activated nodes;
pass an optionalActivationEngineto 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 newactivationUpdateop
(coalesced per node and gated bySyncClientOptions.activationSyncThreshold,
default 0.05) while full-node payloads max-merge on arrival, and an absent
incoming activation never wipes locally learned state. TheSyncClient
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:NodeActivationon the coreNode(persisted through the
snapshot/WAL — the Rustnode_to_msg/msg_to_nodecodecs now always emit
theactivationkey, 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_safeemitting a new
ActivationUpdatedevent,get_activation/get_activation_state,
top_activated,decay);where_activated/order_by_activationon
GraphQueryandPersistedGraphQuery; and anActivationEnginewith
spread/pulse/absorb/working-memory. - Python port (
polypack-db). The pure-PythonPolyGraph/GraphQuery
layer carriesactivationthrough node plumbing and the store, gains
reinforce_node/get_activation/top_activated/decayand
where_activated/order_by_activation(activation filters bypass the
native query executor), and ships anActivationEngine,merge_activation,
anddecay_factor.reinforce_node_safeis 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.
- Durable activation. Optional
Changed
PolyNode/SerializedNodegain an optionalactivationfield;
GraphChangeEventgains theactivation_updatedtype (additive, non-breaking).updateNodeaccepts an optional fourthactivationargument for sync-merge
application.- The Rust snapshot/WAL wire format now carries the
activationnode field
(always emitted, nil when absent, matching TypeScript) — old stores without
the field still decode.
Fixed
SyncClient.disconnect()now flushes pending operations (including
coalescedautoFlush: falseactivation deltas) before tearing down,
instead of silently discarding whatever hadn't been sent yet.HNSWIndexnow validatesM/Mmax0/efConstruction/efSearchare
positive integers and throwsRangeErroron invalid config, instead of
silently building a degenerate graph that returns bad query results.HNSWIndex.update()— not part ofVectorIndexLikeand functionally
identical toadd()— is now@deprecatedand delegates toadd()
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 withclose()could pass its open-check and land after
the compaction step completed.- Python
PolyGraph.close_store()(and thewith 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'sGraph::close, which already flushes first. - Rust
HnswIndex::newnow validatesm/mmax0/ef_construction/
ef_searchare at least 1 and returnsErr(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 throughGraph::open, the N-APINativeHnswIndex
constructor, and the PyO3HnswIndexconstructor (all now fallible). - Removed two stale Rust doc comments in
polypack-graph::Graphclaiming
remove_nodeand hot-cache eviction were unimplemented stubs that would
panic; both are fully implemented and tested. polypack-graphnow re-exportspolypack_core::query::Direction, which
GraphQuery/PersistedGraphQuery'straverse/joinrequire as a
parameter but previously couldn't be named without an explicit
polypack-coredependency.- The Python wheel/sdist (
maturin) no longer bundles__pycache__/.pyc
build-environment leakage. @0xx0lostcause0xx0/polypack-native'sfilesfield now excludes
dist/*.node, so a locally-built platform binary left indist/can't
accidentally ship in the root npm package — only the per-platform
optionalDependenciespackages should carry a binary.
Full changelog: https://github.com/imattau/polypack/blob/master/CHANGELOG.md#250---2026-08-02