Domain-neutral graph-aware layer for context graphs: deterministic node identity, spine and overlay grammar, spine-edit operations with effective-view projection, supersession resolution, idempotent emission/extension, and provenance-by-declaration.
Project description
cjm-context-graph-layer
Install
pip install cjm_context_graph_layer
Project Structure
nbs/
├── declare.ipynb # Provenance-by-declaration: host logic stays readable Python in the workflow core and DECLARES its provenance contributions as a `Derivation` event node (+ DERIVED_FROM input edges, PRODUCED output edges). This recovers audit completeness without the substrate executing host logic (pass-2 Thread 4's false-dichotomy resolution). The substrate stays untouched: declarations read composition/job ids from the outside.
├── edits.ipynb # The spine-edit operation vocabulary (`prune` / `replace_text` / `boundary_shift`) + supersession resolution + the effective-view projection. These are generic operations on any NEXT-chained text spine; correction workflows carry them in overlay-node payloads, and the projection interprets them at read time (migrates correction-core C11/C16 onto the layer).
├── grammar.ipynb # The domain-neutral context-graph grammar: spine relations (NEXT / PART_OF / STARTS_WITH, recurring fractally at every layer), overlay relations (SUPERSEDES / DERIVED_FROM / PRODUCED), root kinds, and the standardized attribution fields.
├── identity.ipynb # Deterministic node/edge identity: UUIDv5 over canonical identity tuples (stage-5 ratified rule: a node's id derives from what makes it THE same node across re-derivation, never from its correctable content).
└── ops.ipynb # Queue-touching layer operations: the shared `graph_task` helper (task channel), idempotent emission (emit-if-absent + verify-if-present), and `extend_graph` — the one primitive every graph-extending workflow commits through. Deterministic ids (see `identity`) make idempotency a presence check instead of a search.
Total: 5 notebooks
Module Dependencies
graph LR
declare["declare<br/>declare"]
edits["edits<br/>edits"]
grammar["grammar<br/>grammar"]
identity["identity<br/>identity"]
ops["ops<br/>ops"]
declare --> grammar
grammar --> identity
2 cross-module dependencies detected
CLI Reference
No CLI commands found in this project.
Module Overview
Detailed documentation for each module in the project:
declare (declare.ipynb)
Provenance-by-declaration: host logic stays readable Python in the workflow core and DECLARES its provenance contributions as a
Derivationevent node (+ DERIVED_FROM input edges, PRODUCED output edges). This recovers audit completeness without the substrate executing host logic (pass-2 Thread 4’s false-dichotomy resolution). The substrate stays untouched: declarations read composition/job ids from the outside.
Import
from cjm_context_graph_layer.declare import (
Derivation,
derivation_to_graph
)
Functions
def derivation_to_graph(
d: Derivation, # The declared event
derivation_id: Optional[str] = None, # Explicit node id; None = generated (events are asserted, not re-derivable)
) -> Tuple[Dict[str, Any], List[Dict[str, Any]]]: # (event node wire dict, edges)
"""
Materialize a declaration as one event node + DERIVED_FROM / PRODUCED edges.
The event node gets a GENERATED id (asserted/decision class — the
FLIP-TRIGGER-protected kind); its edges are deterministic per
(event, anchor, relation).
"""
Classes
@dataclass
class Derivation:
"""
One host-logic transformation event, declared for the audit trail.
Coarse-grained by design: the adopter passes the ids that anchor the event
(e.g. the Transcript nodes consumed + the Source whose spine was produced),
not every fine-grained output (per-node provenance already rides each
node's SourceRefs — duplicating it here would re-create topology, the
Thread-2 no-derived_from rule).
"""
actor: str # Who ran it (e.g. "host:cjm-transcript-decomp-core")
method: str # The transformation (e.g. "alignment-fold/v1")
input_ids: List[str] = field(...) # Graph node ids consumed
output_ids: List[str] = field(...) # Graph node ids produced (coarse anchors)
asserted_at: Optional[float] # Unix timestamp; None = now at to_graph time
composition_id: Optional[str] # Substrate composition run id, if any
job_ids: List[str] = field(...) # Member job ids, if any
properties: Dict[str, Any] = field(...) # Extra event properties
edits (edits.ipynb)
The spine-edit operation vocabulary (
prune/replace_text/boundary_shift) + supersession resolution + the effective-view projection. These are generic operations on any NEXT-chained text spine; correction workflows carry them in overlay-node payloads, and the projection interprets them at read time (migrates correction-core C11/C16 onto the layer).
Import
from cjm_context_graph_layer.edits import (
EDIT_OPS,
SpineEditError,
SpineUnit,
SpineEdit,
resolve_active,
project_effective_spine
)
Functions
def resolve_active(
edit_ids: Iterable[str], # Candidate overlay node ids
supersedes_pairs: Iterable[Tuple[str, str]], # (superseder_id, superseded_id) SUPERSEDES edges
) -> Set[str]: # Active (non-superseded) ids
"""
Resolve the active set under append-only supersession.
An id is superseded iff it is the TARGET of any SUPERSEDES edge — chains
resolve naturally (C supersedes B supersedes A leaves only C active), and
nothing is ever mutated (the C16 semantics, now layer-owned).
"""
def project_effective_spine(
units: List[SpineUnit], # Ordered layer-0 spine (immutable input)
edits: List[SpineEdit], # ACTIVE edits to apply (resolve supersession first)
) -> List[SpineUnit]: # New effective spine (input never mutated)
"""
Project the effective view: layer-0 + active edits, resolved at read time.
Edits apply in (created_at, edit_id) order over the evolving text state, so
later decisions see earlier ones' effects and replace_text latest-wins
emerges from ordering. Prunes drop positions at the end (a boundary_shift
or replace recorded before a later prune still applies cleanly).
boundary_shift is STRICT: if the current text no longer carries the moved
text verbatim at the boundary, the projection fails loudly rather than
guessing (SpineEditError).
"""
Classes
class SpineEditError(ValueError):
"A spine edit could not be validated or applied (loud, never silent)."
@dataclass
class SpineUnit:
"Minimal projection unit: one spine position with its effective text."
id: str # Layer-0 segment node id
text: str # Effective text at this position
@dataclass
class SpineEdit:
"""
One spine-edit decision, as carried in an overlay node's payload.
`op` semantics:
- `prune`: drop `targets` from the effective view (payload unused).
- `replace_text`: payload `{"text": ...}` replaces each target's text.
- `boundary_shift`: payload `{"boundary_after": <left segment id>,
"text": <moved text>, "direction": "push"|"pull"}` moves text across the
boundary between two adjacent FIXED positions (push = from the end of the
left unit to the start of the right; pull = the mirror). 1:1 alignment is
maintained continuously — count and positions never change.
"""
edit_id: str # Carrying overlay node id (supersession anchor)
op: str # One of EDIT_OPS
targets: List[str] = field(...) # Layer-0 segment node ids the edit applies to
payload: Dict[str, Any] = field(...) # Op-specific payload (see above)
created_at: float = 0.0 # Decision timestamp (application order + latest-wins tiebreak)
def to_dict(self) -> Dict[str, Any]: # Payload-ready dict
"""Serialize for carriage in an overlay node payload."""
return {"edit_id": self.edit_id, "op": self.op, "targets": list(self.targets),
"Serialize for carriage in an overlay node payload."
def from_dict(cls, d: Dict[str, Any]) -> "SpineEdit": # Reconstructed edit
"Reconstruct from a payload dict."
Variables
EDIT_OPS
grammar (grammar.ipynb)
The domain-neutral context-graph grammar: spine relations (NEXT / PART_OF / STARTS_WITH, recurring fractally at every layer), overlay relations (SUPERSEDES / DERIVED_FROM / PRODUCED), root kinds, and the standardized attribution fields.
Import
from cjm_context_graph_layer.grammar import (
ROOT_KINDS,
SpineRelations,
OverlayRelations,
attribution,
make_edge,
spine_edges,
grouped_spine_edges
)
Functions
def attribution(
actor: str, # Who asserted/produced this (e.g. "human", "agent:claude", "capability:whisper")
method: Optional[str] = None, # How (e.g. "transcribe", "alignment-fold/v1")
asserted_at: Optional[float] = None, # Unix timestamp; None = now
) -> Dict[str, Any]: # Standardized attribution property dict
"""
Standardized attribution fields for derived/asserted nodes.
Every derivation/assertion carries the same three fields, so audit reads
are uniform across workflows (P13's hand-rolled Connection attribution
graduated into the grammar).
"""
def make_edge(
source_id: str, # Edge source node id
target_id: str, # Edge target node id
relation_type: str, # Relation type (SpineRelations / OverlayRelations / domain)
properties: Optional[Dict[str, Any]] = None, # Optional edge properties (e.g. {"role": "foreshadow"})
edge_id: Optional[str] = None, # Explicit id; None = deterministic from the triple
) -> Dict[str, Any]: # Edge wire dict
"Build an edge wire dict with a deterministic id by default."
def spine_edges(
parent_id: str, # Parent node id
child_ids: List[str], # Ordered child node ids
) -> List[Dict[str, Any]]: # Edge wire dicts
"""
The uniform spine pattern at any layer: PART_OF child->parent for each
child + NEXT chain among children + STARTS_WITH parent->first child.
"""
def grouped_spine_edges(
groups: List[Tuple[str, List[str]]], # (parent id, ordered child ids) per group, groups in spine order
) -> List[Dict[str, Any]]: # Edge wire dicts
"""
Spine edges for a fine layer grouped under coarse parents.
PART_OF goes to the OWNING parent; STARTS_WITH per parent -> its first
child (the coarse-seam jump anchor); the NEXT chain is GLOBAL across group
boundaries — fine continuity crosses coarse boundaries (agent span reads).
"""
Classes
class SpineRelations:
"""
Structural spine relations, reused fractally at every layer
(Source -> AudioSegment -> Segment today; series -> episode tomorrow).
"""
def all(cls) -> list: # All spine relation types
"All spine relation types."
class OverlayRelations:
"""
Overlay/derivation relations — the trust grammar shared by every
workflow's graph extensions.
"""
def all(cls) -> list: # All overlay relation types
"All overlay relation types."
Variables
ROOT_KINDS
identity (identity.ipynb)
Deterministic node/edge identity: UUIDv5 over canonical identity tuples (stage-5 ratified rule: a node’s id derives from what makes it THE same node across re-derivation, never from its correctable content).
Import
from cjm_context_graph_layer.identity import (
LAYER_ID_NAMESPACE,
IDENTITY_SEPARATOR,
canonical_part,
derive_node_id,
derive_edge_id
)
Functions
def canonical_part(
value: Union[str, int, float], # One identity-tuple part
) -> str: # Canonical string form used inside the UUIDv5 name
"""
Render one identity-tuple part canonically.
Floats use `repr` (shortest round-trip — identical floats from the same
deterministic computation render identically); ints use `str`; strings pass
through. Anything else (including bool, whose int-ness is ambiguous) is
rejected loudly: identity inputs must be deliberate.
"""
def derive_node_id(
kind: str, # Node kind discriminator (e.g. "source", "audio-segment")
*parts: Union[str, int, float], # The identity tuple (positional, order-significant)
) -> str: # Deterministic UUID string (UUIDv5)
"""
Derive a deterministic node id from a kind + identity tuple.
Same kind + same parts always yields the same id, across processes and
re-derivations — re-derived graphs reproduce their node ids, so cross-graph
references survive a rebuild (the G3a fix made structural). Content hashes
belong in SourceRefs, NOT here: identity is position/provenance, never the
correctable content.
"""
def derive_edge_id(
source_id: str, # Edge source node id
target_id: str, # Edge target node id
relation_type: str, # Relation type (e.g. "NEXT")
) -> str: # Deterministic UUID string
"""
Derive a deterministic edge id from (source, target, relation).
Layer-0 structural edges are unique per (source, target, relation), so the
triple IS the identity — re-derivation reproduces edge ids the same way it
reproduces node ids.
"""
Variables
LAYER_ID_NAMESPACE
IDENTITY_SEPARATOR = '\x1f'
ops (ops.ipynb)
Queue-touching layer operations: the shared
graph_taskhelper (task channel), idempotent emission (emit-if-absent + verify-if-present), andextend_graph— the one primitive every graph-extending workflow commits through. Deterministic ids (seeidentity) make idempotency a presence check instead of a search.
Import
from cjm_context_graph_layer.ops import (
GRAPH_TASK,
graph_task,
GraphIntegrityError,
node_identity_mismatch,
partition_by_presence,
ExtendResult,
extend_graph
)
Functions
async def graph_task(
queue: JobQueue, # Started job queue
graph_id: str, # Graph-storage capability instance id
method: str, # Adapter method (e.g. "query_nodes", "add_nodes")
**kwargs, # Typed-method kwargs (wire dicts ok; the in-worker adapter normalizes)
) -> Any: # Typed task result (wire-decoded host-side)
"""
Invoke a graph-storage adapter method through the queue's task channel.
THE shared copy: decomp-core and correction-core's per-core helpers migrate
onto this one (graph ops stay on the queue path for telemetry/cancellation
per D7/Thread-5 lock 5).
"""
def _source_hashes(sources: Optional[List[Any]]) -> Set[str]:
"""Content-hash set from a node's sources (typed SourceRefs or wire dicts)."""
out: Set[str] = set()
"Content-hash set from a node's sources (typed SourceRefs or wire dicts)."
def node_identity_mismatch(
existing: Any, # Existing node (typed GraphNode or wire dict)
new: Dict[str, Any], # New node wire dict being emitted
) -> Optional[str]: # Mismatch description, or None when compatible
"Verify-if-present check: label + sources content-hash set must match."
def partition_by_presence(
items: List[Dict[str, Any]], # Wire dicts carrying "id"
existing_ids: Set[str], # Ids already present in the graph
) -> Tuple[List[Dict[str, Any]], List[Dict[str, Any]]]: # (absent, present)
"Split wire dicts into absent (to add) and present (to verify)."
async def extend_graph(
queue: JobQueue, # Started job queue
graph_id: str, # Graph-storage capability id
nodes: List[Dict[str, Any]], # Node wire dicts (deterministic ids for layer-0; generated for decisions)
edges: List[Dict[str, Any]], # Edge wire dicts
) -> ExtendResult: # Counts + created ids
"""
Idempotently extend the graph: emit-if-absent + verify-if-present.
Deterministic ids make idempotency a batched presence check (2 reads + at
most 2 writes per call — the C17 lesson applied to the write path): nodes
already present are verified against the new emission (label + provenance
content hashes) and a mismatch raises `GraphIntegrityError` LOUDLY; absent
nodes/edges are added. Cache-hit re-emission therefore collides into a
verified no-op (stress item 4), and a re-derived spine reproduces — never
duplicates — its layer-0 (stress item 1).
"""
Classes
class GraphIntegrityError(RuntimeError):
"""
An emitted node collided with an existing node of different identity content.
Raised by verify-if-present: same deterministic id but mismatched label or
provenance content hashes means the identity tuple and the content have
diverged — never overwrite silently.
"""
@dataclass
class ExtendResult:
"Outcome of one idempotent extend_graph commit."
nodes_added: int = 0 # Nodes newly created
nodes_verified: int = 0 # Nodes already present, identity-verified
edges_added: int = 0 # Edges newly created
edges_existing: int = 0 # Edges already present (skipped)
added_node_ids: List[str] = field(...) # Ids of created nodes
added_edge_ids: List[str] = field(...) # Ids of created edges
Variables
GRAPH_TASK = 'graph-storage' # The graph-storage adapter task (explicit task channel, stage 4)
Project details
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file cjm_context_graph_layer-0.0.5.tar.gz.
File metadata
- Download URL: cjm_context_graph_layer-0.0.5.tar.gz
- Upload date:
- Size: 24.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0c5acd4c6321e5f25c280806573f506e9a414164caaf886e4037b1d9cf9c6106
|
|
| MD5 |
11fc2c254f2b0969da7d6bdfc16d38f9
|
|
| BLAKE2b-256 |
3359b6e280d131a9f88bef9e15365748a8b1285a8be84c5806bccd2c2e69856b
|
File details
Details for the file cjm_context_graph_layer-0.0.5-py3-none-any.whl.
File metadata
- Download URL: cjm_context_graph_layer-0.0.5-py3-none-any.whl
- Upload date:
- Size: 24.3 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.13
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e31da775eb7d61a3830e6e8038a031e051b330fe8d0d82886a7e8590baccbb82
|
|
| MD5 |
b46a3c8b78475e6dd343be93dfcc6ca6
|
|
| BLAKE2b-256 |
75ae3a19a4b6a795b903ffce6fce938913f056fa0b31d39c31ebfc7d8537c032
|