graph

package
v0.3.0-alpha.3 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: May 10, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// User-driven actions (api/ cluster).
	ActionCapture                = "capture"
	ActionClassify               = "classify"
	ActionUpdate                 = "update"
	ActionResolve                = "resolve"
	ActionDelete                 = "delete"
	ActionLink                   = "link"
	ActionUnlink                 = "unlink"
	ActionMerge                  = "merge"
	ActionRevert                 = "revert"
	ActionIngest                 = "ingest"
	ActionImportCSV              = "import_csv"
	ActionReembed                = "reembed"
	ActionRepair                 = "repair"
	ActionSessionCreate          = "session_create"
	ActionSessionCommit          = "session_commit"
	ActionSessionArchive         = "session_archive"
	ActionCollectionCreate       = "collection_create"
	ActionCollectionAdd          = "collection_add"
	ActionCollectionRemove       = "collection_remove"
	ActionCollectionUpdate       = "collection_update"
	ActionCollectionMove         = "collection_move"
	ActionCollectionRename       = "collection_rename"
	ActionCollectionRetire       = "collection_retire"
	ActionCollectionUnretire     = "collection_unretire"
	ActionCollectionSchemaUpdate = "collection_schema_update"
	ActionCollectionMigrate      = "collection_migrate"
	ActionCurationStuckReset     = "curation_stuck_reset"

	// Curation cycle actions (autonomous + deterministic passes).
	ActionCurationClassify           = "curation:classify"
	ActionCurationSummary            = "curation:summary"
	ActionCurationLink               = "curation:link" // orphan linking (related_to edges)
	ActionCurationSupersede          = "curation:supersede"
	ActionCurationContradictionCheck = "curation:contradiction_check" // covers contradict + no_contradict outcomes
	ActionCurationConceptEmerge      = "curation:concept_emerge"      // new concept + instance_of edges
	ActionCurationConceptEnrich      = "curation:concept_enrich"      // concept node enrichment (LLM synthesis or deterministic alias-merge / evidence-count update)
	ActionCurationSectionLink        = "curation:section_link"
	ActionCurationObservationExtract = "curation:observation_extract"
	ActionCurationLifecycle          = "curation:lifecycle"      // valid_until set on stale ephemeral/temporal records
	ActionCurationQualityRepair      = "curation:quality_repair" // deterministic content_short fix
	ActionCurationGC                 = "curation:gc"             // hard-delete debris
	ActionCurationSelfHeal           = "curation:self_heal"      // self-heal repair_method/repair_input_hash recovery (curation/self_heal.go)
)

Action kind canonical strings. Every emit site references one of these constants; the saveactions lint enforces it. Two namespaces:

  • User-driven verbs (no prefix) -- one action per api/ operation. These are the kinds an operator sees in `gramaton_log` for their own write activity.
  • Curation cycle verbs (`curation:` prefix) -- emitted by the autonomous + deterministic curation passes. Filterable separately so an operator asking "what did curation touch this week" doesn't have to scrape commit messages.

Adding a new kind: declare the constant here, reference it at the emit site. Renaming a kind is a breaking change for any persisted commit that carries the old string (commit.Actions is on-disk state); avoid unless there's a real reason.

View Source
const DefaultCacheCapacity = 10000

DefaultCacheCapacity is the default maximum number of nodes held in the in-memory cache. Nodes beyond this limit are evicted LRU. Set to 0 for unlimited (all accessed nodes stay cached). Dirty nodes are never evicted.

View Source
const DefaultEdgeCacheCapacity = 10000

DefaultEdgeCacheCapacity is the default max edges in the LRU cache.

Variables

View Source
var ErrNotFound = errors.New("not found")

ErrNotFound is returned when a node or edge does not exist.

Functions

func ChunkText

func ChunkText(text string, thresholdTokens, chunkTokens, overlapTokens int) []string

ChunkText splits text into overlapping chunks based on a token budget. Uses a simple approximation of 1 token per 4 characters. Returns nil if the text is at or below the threshold, or if chunkTokens is non-positive (callers misconfiguring chunkSize=0 previously could trigger an infinite loop when nextStart didn't advance past start).

func IsStructuralEdge

func IsStructuralEdge(edgeType string) bool

IsStructuralEdge returns true for edge types that represent structural relationships rather than semantic ones. Structural edges are excluded from semantic edge counts and graph-based scoring.

func MarshalEdge

func MarshalEdge(e *Edge) ([]byte, error)

MarshalEdge encodes an edge to a deterministic byte representation. Format: [id][source_id][target_id][type][weight:8][properties_bytes]

func MarshalNode

func MarshalNode(n *Node) ([]byte, error)

MarshalNode encodes a node to a deterministic byte representation. Format: [id_length:4][id][properties_bytes]

func NodeHashInCommit

func NodeHashInCommit(s *storage.Store, commitHash, nodeID string) (string, bool, error)

NodeHashInCommit returns the content hash for a specific node ID in a commit. Uses prolly tree lookup for v1 commits (O(log N)).

func NodeIDsInCommit

func NodeIDsInCommit(s *storage.Store, commitHash string) ([]string, error)

NodeIDsInCommit returns all node IDs in a commit without loading the full graph. Uses the prolly tree for v1 commits, falls back to reading all chunks for v0 commits.

Types

type ActivationConfig

type ActivationConfig struct {
	BaseAmount        float64
	AttenuationFactor float64
}

ActivationConfig controls spreading activation behavior.

type BboltEdgeStore

type BboltEdgeStore struct {
	// contains filtered or unexported fields
}

BboltEdgeStore is a disk-backed EdgeStore using bbolt (D25).

Bucket layout:

edges      -> edge_id -> serialized Edge (MarshalEdge format)
adj:out    -> node_id -> encoded edge ID list
adj:in     -> node_id -> encoded edge ID list
adj:type   -> edge_type -> encoded edge ID list

An LRU cache holds recently accessed edges to avoid repeated bbolt reads for hot paths (graph traversal neighborhoods).

Concurrency: Put/Delete take no *bolt.Tx (open their own Update); PutTx/DeleteTx accept the caller's tx + an optional *EdgeBatch adjacency cache. Removes the stashed-pointer race class.

func NewBboltEdgeStore

func NewBboltEdgeStore(db *bolt.DB, cacheCapacity int) (*BboltEdgeStore, error)

NewBboltEdgeStore opens or creates a bbolt-backed edge store.

func (*BboltEdgeStore) ByType

func (s *BboltEdgeStore) ByType(edgeType string) []*Edge

func (*BboltEdgeStore) Clear

func (s *BboltEdgeStore) Clear()

Clear deletes every edge bucket and empties the in-memory cache.

Caller is expected to hold the engine write lock so no concurrent Get/Put hits the cache during the swap. Even so, the cache is emptied in place via reset() instead of pointer-reassigning the cache field -- the latter would race with Get callers that snapshot the field before the swap.

func (*BboltEdgeStore) Count

func (s *BboltEdgeStore) Count() int

func (*BboltEdgeStore) Delete

func (s *BboltEdgeStore) Delete(id string)

Delete removes an edge via its own tx.

func (*BboltEdgeStore) DeleteTx

func (s *BboltEdgeStore) DeleteTx(tx *bolt.Tx, batch *EdgeBatch, id string)

DeleteTx removes an edge via the caller's tx + optional *EdgeBatch.

func (*BboltEdgeStore) FlushBatchTx

func (s *BboltEdgeStore) FlushBatchTx(tx *bolt.Tx, batch *EdgeBatch)

FlushBatchTx writes the *EdgeBatch's cached adjacency lists back to bbolt via tx. Safe with nil batch (no-op).

func (*BboltEdgeStore) ForEach

func (s *BboltEdgeStore) ForEach(fn func(e *Edge))

func (*BboltEdgeStore) From

func (s *BboltEdgeStore) From(nodeID string) []*Edge

func (*BboltEdgeStore) Get

func (s *BboltEdgeStore) Get(id string) (*Edge, bool)

func (*BboltEdgeStore) Put

func (s *BboltEdgeStore) Put(e *Edge)

Put stores an edge via its own bbolt Update.

func (*BboltEdgeStore) PutTx

func (s *BboltEdgeStore) PutTx(tx *bolt.Tx, batch *EdgeBatch, e *Edge)

PutTx stores an edge via the caller's tx + optional *EdgeBatch.

func (*BboltEdgeStore) To

func (s *BboltEdgeStore) To(nodeID string) []*Edge

type Commit

type Commit struct {
	Version      int       `json:"version"`
	Hash         string    `json:"hash"`
	Parent       string    `json:"parent,omitempty"`
	Timestamp    time.Time `json:"timestamp"`
	Message      string    `json:"message"`
	NodeTreeRoot string    `json:"node_tree_root,omitempty"`
	EdgeTreeRoot string    `json:"edge_tree_root,omitempty"`
	// Persisted index roots (content-addressed chunks).
	// Omitempty ensures backward compatibility with older commits.
	BM25Root       string `json:"bm25_root,omitempty"`        // legacy single-index (read as bm25Full)
	BM25FullRoot   string `json:"bm25_full_root,omitempty"`   // content_full BM25 index
	BM25MediumRoot string `json:"bm25_medium_root,omitempty"` // content_medium BM25 index
	BM25ShortRoot  string `json:"bm25_short_root,omitempty"`  // content_short BM25 index
	VecRoot        string `json:"vec_root,omitempty"`
	PropRoot       string `json:"prop_root,omitempty"`
	EdgeAdjRoot    string `json:"edge_adj_root,omitempty"`
	// NodeHashes/EdgeHashes retained for reading v0 commits only.
	NodeHashes []string `json:"node_hashes,omitempty"`
	EdgeHashes []string `json:"edge_hashes,omitempty"`
	// Structured per-commit actions (D3). Optional; old commits
	// deserialize with Actions == nil. Omitempty on write keeps
	// pre-D3 consumers readable and bounds commit JSON size.
	Actions []CommitAction `json:"actions,omitempty"`
}

Commit is an immutable snapshot of the graph state.

func LoadCommitMeta

func LoadCommitMeta(s *storage.Store, commitHash string) (*Commit, error)

LoadCommitMeta reads a commit's JSON from storage without mutating graph state. Used by callers that only need commit-level fields (parent, timestamp, index roots) -- chain traversal, timestamp backfill, history walkers -- and want to avoid the prolly-tree load that full (*Graph).Load performs.

type CommitAction

type CommitAction struct {
	Kind     string `json:"kind"`                // canonical Action* constant (see below)
	RecordID string `json:"record_id,omitempty"` // target record when action is record-scoped
	Field    string `json:"field,omitempty"`     // target property when action is field-scoped
}

CommitAction is a structured descriptor of an intent-level change carried by a commit. Unlike the free-form Commit.Message, actions are addressable per-record and filterable by Kind (D3). A single commit may carry many actions (e.g. a curation cycle touching multiple records, a batch collection add, a migration sweep).

Emission is loose: callers populate the slice at each `Save` site, the server trusts the contents, and a future CI lint enforces coverage. The field is omitempty so existing commits serialize unchanged and old-binary reads of new commits fall back to Message-based filtering.

type DiffResult

type DiffResult struct {
	// Added: entries in the new commit but not the old (key=node/edge ID, value=content hash).
	Added []storage.ProllyEntry `json:"added,omitempty"`
	// Removed: entries in the old commit but not the new.
	Removed []storage.ProllyEntry `json:"removed,omitempty"`
}

DiffResult describes the structural differences between two commits.

func DiffCommits

func DiffCommits(s *storage.Store, oldCommit, newCommit *Commit) (DiffResult, error)

DiffCommits computes the structural difference between two commits using prolly tree diff. For v1 commits, this efficiently skips unchanged subtrees. Falls back to flat list comparison for v0.

A nil oldCommit means "no prior state" -- every entry in newCommit is reported as Added. Callers diffing against chain-root (empty Since) rely on this.

type Edge

type Edge struct {
	ID         string
	SourceID   string
	TargetID   string
	Type       string
	Weight     float64
	Properties Properties
}

Edge is a directed relationship between two nodes. Edges are first-class objects with their own ID, type, weight, and optional properties.

func UnmarshalEdge

func UnmarshalEdge(data []byte) (*Edge, error)

UnmarshalEdge decodes an edge from bytes.

type EdgeBatch

type EdgeBatch struct {
	// contains filtered or unexported fields
}

EdgeBatch bundles the in-batch adjacency cache. addToEdgeIDList decoded, linear-scanned, sorted, and re-encoded the full edge ID list for each single-item write -- O(K log K) per edge with K being the node's current degree. Bulk-loading a node with K edges was O(K^2 log K). These maps buffer decoded adjacency lists per bucket; FlushBatchTx flushes each dirty key once.

Pass via PutTx/DeleteTx and flush via FlushBatchTx when done. A nil *EdgeBatch disables caching and falls back to per-call encode/ decode.

func NewEdgeBatch

func NewEdgeBatch() *EdgeBatch

NewEdgeBatch creates an empty adjacency cache for use with a single shared bbolt transaction. The caller flushes via FlushBatchTx.

type EdgeStore

type EdgeStore interface {
	// Put stores an edge and updates adjacency indexes via its own tx.
	Put(e *Edge)
	// PutTx stores an edge via the caller's tx + optional *EdgeBatch.
	// In-memory impls ignore tx and batch.
	PutTx(tx *bolt.Tx, batch *EdgeBatch, e *Edge)
	// Get retrieves an edge by ID.
	Get(id string) (*Edge, bool)
	// Delete removes an edge and updates adjacency indexes via its own tx.
	Delete(id string)
	// DeleteTx removes an edge via the caller's tx + optional *EdgeBatch.
	DeleteTx(tx *bolt.Tx, batch *EdgeBatch, id string)
	// From returns all outbound edges from a node.
	From(nodeID string) []*Edge
	// To returns all inbound edges to a node.
	To(nodeID string) []*Edge
	// ByType returns all edges of the given type.
	ByType(edgeType string) []*Edge
	// Count returns the total number of edges.
	Count() int
	// ForEach iterates all edges in unspecified order.
	ForEach(fn func(e *Edge))
	// Clear removes all edges and adjacency data. Used during Load
	// to reset state before repopulating from the prolly tree.
	Clear()
}

EdgeStore abstracts edge storage and adjacency index operations. The Graph delegates edge CRUD and traversal queries to this interface.

Implementations: MemoryEdgeStore (in-memory maps, current default), and BboltEdgeStore (bbolt-backed).

type Graph

type Graph struct {
	// contains filtered or unexported fields
}

Graph is a property graph engine backed by a content-addressed store. Nodes are lazily loaded from a prolly tree on first access and cached in memory. Edges are loaded at startup (MemoryEdgeStore) or on demand (BboltEdgeStore).

The graph tracks which nodes and edges have been modified since the last save (dirty tracking), enabling incremental persistence. The nodeHashes/edgeHashes maps cache content hashes from the previous save, so only dirty items need re-marshaling.

The graph is not thread-safe. The server layer handles write serialization.

func New

func New() *Graph

New creates an empty graph with the default cache capacity.

func NewWithCapacity

func NewWithCapacity(cacheCapacity int, opts ...GraphOption) *Graph

NewWithCapacity creates a graph with a specific node cache capacity. 0 means unlimited.

func (*Graph) AddEdge

func (g *Graph) AddEdge(sourceID, targetID, edgeType string, weight float64, props Properties) (*Edge, error)

AddEdge creates a new edge between two existing nodes via the edge store's own transaction. Both source and target nodes must exist. Weight should be in [0.0, 1.0]. Properties are cloned on creation.

func (*Graph) AddEdgeTx

func (g *Graph) AddEdgeTx(tx *bolt.Tx, batch *EdgeBatch, sourceID, targetID, edgeType string, weight float64, props Properties) (*Edge, error)

AddEdgeTx is AddEdge via the caller's bbolt transaction + optional *EdgeBatch cache. When tx and batch are both nil, falls back to the edge store opening its own Update (non-batched path). When non-nil, writes go through the bbolt-backed store's PutTx so the shared tx is used and the adjacency cache amortizes re-encode cost. In-memory edge stores ignore tx/batch. See D40.

func (*Graph) AddNode

func (g *Graph) AddNode(props Properties) *Node

AddNode creates a new node with the given properties and returns it. The node is assigned a new ULID. Properties are cloned on creation.

func (*Graph) AddNodeWithIDForTest

func (g *Graph) AddNodeWithIDForTest(id string, props Properties) *Node

AddNodeWithIDForTest creates a node with a caller-chosen ID. Exists only to let tests construct the record-deleted-then-recreated-with- same-ID scenario (RC-4 regression) that no user-facing API path produces. Do not call from production code -- the ID collision risk defeats the ULID invariant.

func (*Graph) AllNodeIDs

func (g *Graph) AllNodeIDs() []string

AllNodeIDs returns all node IDs in the graph. Order is not guaranteed. In lazy mode, iterates the prolly tree for IDs.

Prefer NodeIterator for new code -- it avoids the intermediate slice.

func (*Graph) ClearDirty

func (g *Graph) ClearDirty()

ClearDirty resets all dirty tracking state. Called after a successful save.

func (*Graph) DeleteEdge

func (g *Graph) DeleteEdge(id string) error

DeleteEdge removes an edge and updates all indexes.

func (*Graph) DeleteNode

func (g *Graph) DeleteNode(id string) error

DeleteNode removes a node and all its inbound and outbound edges (cascading deletion). Returns ErrNotFound if the node doesn't exist. Lazily loads the node if needed.

func (*Graph) EdgeCount

func (g *Graph) EdgeCount() int

EdgeCount returns the number of edges in the graph.

func (*Graph) EdgesByType

func (g *Graph) EdgesByType(edgeType string) []*Edge

EdgesByType returns all edges of the given type.

func (*Graph) EdgesFrom

func (g *Graph) EdgesFrom(nodeID string) []*Edge

EdgesFrom returns all outbound edges from a node.

func (*Graph) EdgesTo

func (g *Graph) EdgesTo(nodeID string) []*Edge

EdgesTo returns all inbound edges to a node.

func (*Graph) ForEachEdge

func (g *Graph) ForEachEdge(fn func(*Edge))

ForEachEdge iterates every edge in the store in unspecified order, calling fn for each. Avoids the per-node EdgesFrom/EdgesTo round trips when callers genuinely need every edge (e.g. Validate).

func (*Graph) GetEdge

func (g *Graph) GetEdge(id string) (*Edge, bool)

GetEdge returns the edge with the given ID, or nil and false if not found.

func (*Graph) GetNode

func (g *Graph) GetNode(id string) (*Node, bool)

GetNode returns the node with the given ID, or nil and false if not found. When the graph has a backing store (after Load), cache misses trigger a lazy load from the prolly tree. Accessed nodes are promoted in the LRU; eviction may remove a clean (non-dirty) node from the cache.

Lazy-load I/O happens outside the cache lock so concurrent cache hits aren't blocked on disk reads. After the load, we re-check under the lock in case another goroutine raced us to populate the same entry.

func (*Graph) IsDirty

func (g *Graph) IsDirty() bool

IsDirty reports whether any nodes or edges have been modified since the last save.

func (*Graph) IsStructuralChild

func (g *Graph) IsStructuralChild(id string) bool

IsStructuralChild returns true if a node has an outbound structural edge (chunk_of or section_of), meaning it is a chunk or section of another record.

func (*Graph) Load

func (g *Graph) Load(s *storage.Store, commitHash string) (*Commit, error)

Load restores graph state from a commit. For v1 commits, nodes are NOT loaded eagerly -- they are lazily loaded from the prolly tree on first access via GetNode. Edges are fully loaded since they're lightweight and needed for adjacency indexes.

Handles both v0 (flat hash lists) and v1 (prolly tree) commit formats. v0 commits still load everything eagerly since they lack prolly tree support for single-key lookup.

func (*Graph) MarshalEdgeAdjacency

func (g *Graph) MarshalEdgeAdjacency() ([]byte, error)

MarshalEdgeAdjacency serializes the three edge adjacency maps (outEdges, inEdges, typeEdges) into a binary format. This allows EdgesFrom/EdgesTo to work at startup without loading all edges.

func (*Graph) NodeCount

func (g *Graph) NodeCount() int

NodeCount returns the number of nodes in the graph. In lazy mode, this returns the count from the prolly tree (which includes nodes not yet loaded into the cache).

func (*Graph) NodeIDSet

func (g *Graph) NodeIDSet() map[string]struct{}

NodeIDSet returns a set of all node IDs without loading node data. Uses the in-memory nodeHashes map (populated at Load time) so this is O(n) in IDs only, no disk I/O.

func (*Graph) NodeIterator

func (g *Graph) NodeIterator() NodeIterator

NodeIterator returns an iterator over all nodes in the graph. In lazy mode, iterates prolly tree entries and loads each node on demand. In eager mode (no backing store), iterates the in-memory map.

func (*Graph) PrepareCommit

func (g *Graph) PrepareCommit(s *storage.Store, parent string, message string, actions []CommitAction, pCfg ...storage.ProllyConfig) (*Commit, error)

PrepareCommit persists all dirty/new graph state (nodes, edges, prolly trees) into the store and returns a *Commit with NodeTreeRoot and EdgeTreeRoot populated. The commit chunk itself is NOT yet written -- the caller may set additional fields (e.g. engine-managed index roots) on the returned commit, then call WriteCommit to persist.

PrepareCommit advances g.lastNodeTreeRoot and g.lastEdgeTreeRoot so subsequent saves apply incremental updates against the freshly persisted trees. ClearDirty runs in WriteCommit, after the commit chunk lands -- not here -- so dirty state is preserved if the caller fails between Prepare and Write.

func (*Graph) RecordAccess

func (g *Graph) RecordAccess(nodeID string, now time.Time, cfg ActivationConfig)

RecordAccess updates a node's access metadata and spreads activation to its one-hop neighbors. Call this when a node is returned to a consumer (search result, inspect, etc.).

Direct access: increments access_count, sets last_accessed to now. Neighbor activation: for each edge from the accessed node, adds base_amount * edge_weight * attenuation_factor to the neighbor's activation_boost.

Lookups go through GetNode so the call works correctly in lazy mode (evicted neighbors are loaded back; previously they would silently be skipped). Mutations of n.Properties assume the engine write lock is held by the caller -- the engine convention for any code path that modifies node state.

func (*Graph) RemoveEdgeProperty

func (g *Graph) RemoveEdgeProperty(id, key string) error

RemoveEdgeProperty removes a property from an edge.

func (*Graph) RemoveNodeProperty

func (g *Graph) RemoveNodeProperty(id, key string) error

RemoveNodeProperty removes a property from a node. No error if the property doesn't exist. Lazily loads the node if needed.

func (*Graph) Save

func (g *Graph) Save(s *storage.Store, parent string, message string, pCfg ...storage.ProllyConfig) (*Commit, error)

Save persists the current graph state as a commit to the store. Uses dirty tracking to only marshal modified nodes/edges. The full entry maps (nodeHashes/edgeHashes) are rebuilt for the prolly tree, but unchanged chunks deduplicate via content-addressing.

Equivalent to SaveWithActions with no D3 action descriptors. Kept for source-compatibility with pre-Phase-3 tests and callers that don't carry structured actions.

func (*Graph) SaveWithActions

func (g *Graph) SaveWithActions(s *storage.Store, parent string, message string, actions []CommitAction, pCfg ...storage.ProllyConfig) (*Commit, error)

SaveWithActions is Save extended with the optional D3 structured action descriptor list. Empty slice / nil = no structured actions (commit is filterable by Message prefix only). Callers that emit actions (api/ cluster, future curation/ cluster) invoke this directly; engine.Save routes here via its variadic actions arg.

SaveWithActions is the single-call form -- it composes PrepareCommit and WriteCommit with no fields set in between. Callers (e.g. core engine) that need to attach engine-managed index roots before the commit chunk lands call PrepareCommit + WriteCommit directly to avoid a per-save orphan commit chunk.

func (*Graph) SemanticEdgeCount

func (g *Graph) SemanticEdgeCount(id string) int

SemanticEdgeCount returns the total edge count (in + out) excluding structural edges (chunk_of, section_of).

func (*Graph) SetEdgeProperty

func (g *Graph) SetEdgeProperty(id, key string, val Property) error

SetEdgeProperty sets a single property on an edge.

func (*Graph) SetEdgeWeight

func (g *Graph) SetEdgeWeight(id string, weight float64) error

SetEdgeWeight updates an edge's weight.

func (*Graph) SetNodeProperty

func (g *Graph) SetNodeProperty(id, key string, val Property) error

SetNodeProperty sets a single property on a node. Creates the property if absent, overwrites if present. Lazily loads the node if needed.

func (*Graph) Traverse

func (g *Graph) Traverse(startID string, opts TraverseOptions) Subgraph

Traverse performs a breadth-first traversal from the given node, returning all reachable nodes and edges within the given depth. Follows both outbound and inbound edges.

func (*Graph) UnmarshalEdgeAdjacency

func (g *Graph) UnmarshalEdgeAdjacency(data []byte) error

UnmarshalEdgeAdjacency restores the edge adjacency maps from binary data. Clears existing adjacency state.

func (*Graph) WriteCommit

func (g *Graph) WriteCommit(s *storage.Store, c *Commit) (*Commit, error)

WriteCommit serializes c, writes it as a content-addressed chunk, sets c.Hash, and clears dirty tracking on success. Pair with PrepareCommit -- typical flow is PrepareCommit, populate any engine-managed fields (index roots), then WriteCommit. Calling WriteCommit twice on the same commit writes two chunks and orphans the first; the engine path writes once.

type GraphOption

type GraphOption func(*Graph)

GraphOption configures graph creation.

func WithEdgeStore

func WithEdgeStore(es EdgeStore) GraphOption

WithEdgeStore injects an external EdgeStore. If not provided, a MemoryEdgeStore is used (the default for backward compatibility).

type MemoryEdgeStore

type MemoryEdgeStore struct {
	// contains filtered or unexported fields
}

MemoryEdgeStore is an in-memory EdgeStore using Go maps.

func NewMemoryEdgeStore

func NewMemoryEdgeStore() *MemoryEdgeStore

NewMemoryEdgeStore creates an empty in-memory edge store.

func (*MemoryEdgeStore) ByType

func (s *MemoryEdgeStore) ByType(edgeType string) []*Edge

func (*MemoryEdgeStore) Clear

func (s *MemoryEdgeStore) Clear()

func (*MemoryEdgeStore) Count

func (s *MemoryEdgeStore) Count() int

func (*MemoryEdgeStore) Delete

func (s *MemoryEdgeStore) Delete(id string)

func (*MemoryEdgeStore) DeleteTx

func (s *MemoryEdgeStore) DeleteTx(_ *bolt.Tx, _ *EdgeBatch, id string)

DeleteTx mirrors Delete; the in-memory impl ignores tx and batch.

func (*MemoryEdgeStore) ForEach

func (s *MemoryEdgeStore) ForEach(fn func(e *Edge))

func (*MemoryEdgeStore) From

func (s *MemoryEdgeStore) From(nodeID string) []*Edge

func (*MemoryEdgeStore) Get

func (s *MemoryEdgeStore) Get(id string) (*Edge, bool)

func (*MemoryEdgeStore) InEdgeIDs

func (s *MemoryEdgeStore) InEdgeIDs() map[string]map[string]struct{}

InEdgeIDs returns the raw set of edge IDs to a target node.

func (*MemoryEdgeStore) OutEdgeIDs

func (s *MemoryEdgeStore) OutEdgeIDs() map[string]map[string]struct{}

OutEdgeIDs returns the raw set of edge IDs from a source node. Used by MarshalEdgeAdjacency and other internal operations that need the adjacency index directly.

func (*MemoryEdgeStore) Put

func (s *MemoryEdgeStore) Put(e *Edge)

func (*MemoryEdgeStore) PutTx

func (s *MemoryEdgeStore) PutTx(_ *bolt.Tx, _ *EdgeBatch, e *Edge)

PutTx mirrors Put; the in-memory impl ignores tx and batch.

func (*MemoryEdgeStore) To

func (s *MemoryEdgeStore) To(nodeID string) []*Edge

func (*MemoryEdgeStore) TypeEdgeIDs

func (s *MemoryEdgeStore) TypeEdgeIDs() map[string]map[string]struct{}

TypeEdgeIDs returns the raw set of edge IDs by type.

type Node

type Node struct {
	ID         string
	Properties Properties
}

Node is a vertex in the property graph. It has a stable ULID and a set of typed properties. The graph engine treats all nodes identically -- "types" like knowledge record, concept node, and chunk node are conventions enforced by higher layers through property values.

func UnmarshalNode

func UnmarshalNode(data []byte) (*Node, error)

UnmarshalNode decodes a node from bytes.

type NodeIterator

type NodeIterator interface {
	Next() bool
	Node() *Node
	Close()
}

NodeIterator pages through nodes without full materialization. The current in-memory implementation wraps a slice. A future lazy implementation will cursor through the prolly tree.

Usage:

it := g.NodeIterator()
defer it.Close()
for it.Next() {
    n := it.Node()
    // use n
}

type NodeReader

type NodeReader interface {
	GetNode(id string) (*Node, bool)
	NodeIterator() NodeIterator
	NodeIDSet() map[string]struct{}
	NodeCount() int
	EdgeCount() int
	EdgesFrom(nodeID string) []*Edge
	EdgesTo(nodeID string) []*Edge
	IsStructuralChild(id string) bool
	SemanticEdgeCount(id string) int
}

NodeReader is the read interface for graph access. All read-side consumers should accept this interface rather than *Graph directly.

The graph loads nodes lazily from the prolly tree with LRU caching. Callers that go through NodeReader work with any implementation unchanged.

type Properties

type Properties map[string]Property

Properties is a map of named properties. A key is either present with a value or absent. No nulls.

func UnmarshalProperties

func UnmarshalProperties(data []byte) (Properties, error)

UnmarshalProperties decodes a Properties map from bytes.

func (Properties) Clone

func (ps Properties) Clone() Properties

Clone returns a deep copy.

func (Properties) GetBool

func (ps Properties) GetBool(key string) (bool, bool)

func (Properties) GetBytes

func (ps Properties) GetBytes(key string) ([]byte, bool)

func (Properties) GetFloat64

func (ps Properties) GetFloat64(key string) (float64, bool)

func (Properties) GetInt64

func (ps Properties) GetInt64(key string) (int64, bool)

func (Properties) GetString

func (ps Properties) GetString(key string) (string, bool)

func (Properties) GetStringList

func (ps Properties) GetStringList(key string) ([]string, bool)

func (Properties) GetTimestamp

func (ps Properties) GetTimestamp(key string) (time.Time, bool)

func (Properties) GetVector

func (ps Properties) GetVector(key string) ([]float32, bool)

func (Properties) MarshalBinary

func (ps Properties) MarshalBinary() ([]byte, error)

MarshalBinary encodes a Properties map to a deterministic byte representation. Keys are sorted lexicographically for deterministic output (required for content-addressed storage).

type Property

type Property struct {
	Type PropertyType
	// contains filtered or unexported fields
}

Property is a typed value. Exactly one of the typed fields is valid, determined by Type. This is a value type -- copy freely.

func BoolProperty

func BoolProperty(v bool) Property

func BytesProperty

func BytesProperty(v []byte) Property

func Float64Property

func Float64Property(v float64) Property

func Int64Property

func Int64Property(v int64) Property

func StringListProperty

func StringListProperty(v []string) Property

func StringProperty

func StringProperty(v string) Property

func TimestampProperty

func TimestampProperty(v time.Time) Property

func UnmarshalProperty

func UnmarshalProperty(data []byte) (Property, error)

UnmarshalProperty decodes a Property from bytes produced by MarshalBinary.

func VectorProperty

func VectorProperty(v []float32) Property

func (Property) Bool

func (p Property) Bool() bool

func (Property) Bytes

func (p Property) Bytes() []byte

func (Property) Compare

func (p Property) Compare(other Property) int

Compare returns -1, 0, or 1 for ordered types (String, Float64, Int64, Timestamp). Panics for unordered types (Bool, Vector, StringList, Bytes) or if the types don't match.

func (Property) Equal

func (p Property) Equal(other Property) bool

Equal reports whether two properties have the same type and value.

func (Property) Float64

func (p Property) Float64() float64

func (Property) FormatValue

func (p Property) FormatValue() string

FormatValue returns a human-readable string representation of the property value.

func (Property) Int64

func (p Property) Int64() int64

func (Property) MarshalBinary

func (p Property) MarshalBinary() ([]byte, error)

MarshalBinary encodes a Property to a deterministic byte representation. Format: [type_byte] [type-specific payload]

func (Property) String

func (p Property) String() string

String implements fmt.Stringer with a human-readable rendering of the property value. Never panics. Use StringValue for the typed accessor that asserts Type == TypeString.

func (Property) StringList

func (p Property) StringList() []string

func (Property) StringValue

func (p Property) StringValue() string

StringValue returns the underlying string. Panics if Type != TypeString. Use Properties.GetString for a safe accessor that returns (value, ok).

String (without Value) is the fmt.Stringer implementation and never panics; reaching for the panicking variant from a fmt/log site is the bug this rename was designed to prevent.

func (Property) Timestamp

func (p Property) Timestamp() time.Time

func (Property) Vector

func (p Property) Vector() []float32

type PropertyType

type PropertyType uint8

PropertyType identifies the type of a property value.

const (
	TypeString     PropertyType = 1
	TypeFloat64    PropertyType = 2
	TypeInt64      PropertyType = 3
	TypeBool       PropertyType = 4
	TypeTimestamp  PropertyType = 5
	TypeVector     PropertyType = 6
	TypeStringList PropertyType = 7
	TypeBytes      PropertyType = 8
)

func (PropertyType) String

func (t PropertyType) String() string

type Section

type Section struct {
	Heading string // section heading (empty if split by paragraphs)
	Text    string // full section content including heading line
}

Section is a semantically coherent portion of a document, split at structural boundaries (headings, numbered sections, paragraphs).

func SplitSections

func SplitSections(text string, minChars, maxChars int) []Section

SplitSections splits text into semantically coherent sections based on structural cues. Detection priority: markdown headings, numbered sections, HTML headings, paragraph breaks. Sections are merged or sub-split to stay within [minChars, maxChars].

Returns nil if text is shorter than minChars (no splitting needed).

type Subgraph

type Subgraph struct {
	Nodes []SubgraphNode `json:"nodes"`
	Edges []SubgraphEdge `json:"edges"`
}

Subgraph is the result of a graph traversal.

type SubgraphEdge

type SubgraphEdge struct {
	Source string  `json:"source"`
	Target string  `json:"target"`
	Type   string  `json:"type"`
	Weight float64 `json:"weight"`
}

SubgraphEdge is an edge in a traversal result.

type SubgraphNode

type SubgraphNode struct {
	ID           string   `json:"id"`
	Keywords     []string `json:"keywords,omitempty"`
	SummaryShort string   `json:"summary_short,omitempty"`
}

SubgraphNode is a node in a traversal result.

type TSIndex

type TSIndex struct {
	// contains filtered or unexported fields
}

TSIndex is a bbolt-backed index mapping commit timestamps to commit hashes (D7). Used by temporal queries that need to find commits by wall-clock time instead of walking the parent chain from HEAD.

Bucket layout:

commit_timestamps      -> tsKey(timestamp, hash[:12]) -> full commit hash
commit_timestamps_meta -> reserved for future sentinels (unused in Phase 1)

The key encodes time as 8-byte big-endian unix nanoseconds + '#' + up-to-12 char hash prefix. Big-endian byte order guarantees that lexicographic key order equals chronological order, so bbolt cursor Seek gives O(log N) snap-to-prior lookups and range scans. RFC3339Nano string keys were considered and rejected because Go drops trailing zero fractional digits, which breaks the lexicographic==chronological invariant.

Concurrency: Put opens its own bbolt Update tx; PutTx accepts the caller's tx. CommitAt / CommitsBetween always open their own View; no tx argument.

func NewTSIndex

func NewTSIndex(db *bolt.DB) (*TSIndex, error)

NewTSIndex opens or creates the timestamp index buckets.

func (*TSIndex) CommitAt

func (idx *TSIndex) CommitAt(t time.Time) (string, bool)

CommitAt returns the full hash of the commit at or strictly before t (snap-to-prior semantics, following Fluree's as-of-date contract). Returns ("", false) when the bucket is empty or t is before the earliest indexed commit.

func (*TSIndex) CommitBefore

func (idx *TSIndex) CommitBefore(t time.Time) (string, bool)

CommitBefore returns the full hash of the latest commit STRICTLY before t. Unlike CommitAt, a commit at exactly t is NOT returned. Useful for diff since-boundaries: a diff window "since X" should include commits at X, so sinceCommit must be before X.

func (*TSIndex) CommitsBetween

func (idx *TSIndex) CommitsBetween(start, end time.Time) []string

CommitsBetween returns the full hashes of commits with timestamps in [start, end] (inclusive both ends), in chronological order. Returns nil for an empty range (end < start) or no matches.

func (*TSIndex) Count

func (idx *TSIndex) Count() int

Count returns the number of entries in the timestamp index. Useful for diagnostics and the `gramaton migrate` progress log.

func (*TSIndex) Put

func (idx *TSIndex) Put(c *Commit) error

Put writes a commit's index entry via its own tx. Idempotent: a repeat put for the same commit overwrites with identical value.

func (*TSIndex) PutTx

func (idx *TSIndex) PutTx(tx *bolt.Tx, c *Commit)

PutTx writes a commit's index entry via the caller's tx.

type TraverseOptions

type TraverseOptions struct {
	MaxDepth      int
	EdgeTypes     []string // nil means all types
	MinEdgeWeight float64
}

TraverseOptions controls graph traversal behavior.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL