ContextGraph OS

A provenance-first, time-aware, decision-trace substrate for auditable AI agents.

What is ContextGraph OS?

ContextGraph OS provides the foundational infrastructure for building AI agent systems where every action, decision, and piece of knowledge is fully traceable and auditable. It's designed for organizations that need:

  • Complete audit trails for AI agent decisions
  • Provenance tracking for all data and claims
  • Temporal awareness with explicit validity periods
  • Policy enforcement with deny-takes-precedence semantics
  • Compliance support for regulatory requirements (GDPR, SOC2, etc.)

Core Principles

1. Provenance First

Every claim in the system requires a source. No orphan data. This ensures you can always trace back to where information came from.

// Every claim has provenance
await client.addClaim({
  subjectId: entityId,
  predicate: 'status',
  value: 'active',
  // Provenance is automatically tracked
});

// Verify the chain
const verification = await client.verifyProvenance();
console.log(`Chain valid: ${verification.value.valid}`);

2. Time as First-Class Citizen

All data is temporally qualified with explicit validity periods. Query data as it was at any point in time.

await client.addClaim({
  subjectId: entityId,
  predicate: 'temperature',
  value: 72,
  context: {
    validFrom: createTimestamp('2024-01-01'),
    validUntil: createTimestamp('2024-12-31'),
  },
});

3. Decisions as Data

Agent decisions are tracked with full lifecycle and audit trails. Every decision goes through a defined state machine.

// Record a decision
const decision = await client.recordDecision({
  type: 'workflow_step',
  title: 'Deploy to production',
  proposedBy: agentId,
  riskLevel: 'high',
});

// Approve it
await client.approveDecision(decision.value.data.id, approverId);

4. Context Filtering

Query data by time, jurisdiction, scope, and confidence level.

const context = await client.assembleContext(entityId, {
  asOf: createTimestamp('2024-06-15'),
  jurisdiction: 'EU',
  minConfidence: 0.8,
});

5. Policy Enforcement

Deny-takes-precedence evaluation with approval workflows.

await client.createPolicy({
  name: 'Restrict PII Access',
  effect: 'deny',
  subjects: ['*'],
  actions: ['read'],
  resources: ['pii/*'],
  priority: 100,
});

Package Overview

ContextGraph OS is organized into layered packages:

LayerPackagesPurpose
Foundationcore, storage, ontologyTypes, storage, schemas
Knowledgeckg, provenance, retrievalKnowledge graph, provenance, context
Governancedtg, policy, exceptions, rbac, complianceDecisions, policies, access control
Agentagent, executionAgent registry, action execution
Advancedviz, reasoning, recommendations, telemetryVisualization, inference, insights
Integrationsdk, api, cli, webhooksAPIs, CLI tools

Quick Example

import { ContextGraph } from '@contextgraph/sdk';

// Create a client
const result = await ContextGraph.create();
const client = result.value;

// Create an entity
const person = await client.createEntity({
  type: 'person',
  name: 'Alice',
  properties: { department: 'Engineering' },
});

// Add claims with provenance
await client.addClaim({
  subjectId: person.value.data.id,
  predicate: 'has_skill',
  value: 'TypeScript',
});

// Create an agent
const agent = await client.createAgent({
  name: 'assistant',
  description: 'Research assistant',
});

// Execute actions with audit trail
await client.execute({
  agentId: agent.value.data.id,
  action: 'read',
  resourceType: 'document',
  resourceId: 'doc_123',
});

// Get audit trail
const audit = await client.getAuditTrail({ limit: 10 });

Next Steps

Quick Start

Get up and running with ContextGraph OS in 5 minutes.

Prerequisites

  • Node.js 18+
  • pnpm (recommended) or npm

Installation

# Clone the repository
git clone https://github.com/akz4ol/contextgraph-os.git
cd contextgraph-os

# Install dependencies
pnpm install

# Build all packages
pnpm -r build

# Run tests to verify
pnpm -r test

Your First ContextGraph Application

Create a new file demo.ts:

import { ContextGraph, createScope, createConfidence } from '@contextgraph/sdk';
import { ok } from '@contextgraph/core';

async function main() {
  // 1. Create the ContextGraph client
  const result = await ContextGraph.create();
  if (!result.ok) {
    console.error('Failed to create client:', result.error);
    return;
  }
  const client = result.value;

  console.log('ContextGraph client created!');

  // 2. Create an entity
  const personResult = await client.createEntity({
    type: 'person',
    name: 'Alice',
    properties: {
      department: 'Engineering',
      level: 'senior'
    },
  });

  if (!personResult.ok) {
    console.error('Failed to create entity:', personResult.error);
    return;
  }

  const person = personResult.value;
  console.log(`Created person: ${person.data.name} (${person.data.id})`);

  // 3. Add claims with context
  await client.addClaim({
    subjectId: person.data.id,
    predicate: 'has_skill',
    value: 'TypeScript',
    context: {
      scope: createScope('professional'),
      confidence: createConfidence(0.95),
    },
  });

  await client.addClaim({
    subjectId: person.data.id,
    predicate: 'has_skill',
    value: 'Python',
    context: {
      scope: createScope('professional'),
      confidence: createConfidence(0.85),
    },
  });

  console.log('Added skill claims');

  // 4. Query claims
  const claimsResult = await client.getClaims(person.data.id);
  if (claimsResult.ok) {
    console.log('\nClaims for Alice:');
    for (const claim of claimsResult.value) {
      console.log(`  - ${claim.data.predicate}: ${claim.data.value}`);
    }
  }

  // 5. Create an agent
  const agentResult = await client.createAgent({
    name: 'assistant',
    description: 'A helpful research assistant',
  });

  if (!agentResult.ok) {
    console.error('Failed to create agent:', agentResult.error);
    return;
  }

  const agent = agentResult.value;
  console.log(`\nCreated agent: ${agent.data.name}`);

  // 6. Register an action handler
  client.registerHandler('analyze', 'document', async (action) => {
    console.log(`  [Handler] Analyzing document: ${action.resourceId}`);
    return ok({
      analyzed: true,
      wordCount: 1500,
      sentiment: 'positive'
    });
  });

  // 7. Execute an action
  const execResult = await client.execute({
    agentId: agent.data.id,
    action: 'analyze',
    resourceType: 'document',
    resourceId: 'doc_quarterly_report',
    parameters: { depth: 'full' },
  });

  if (execResult.ok) {
    console.log('\nAction executed successfully!');
  }

  // 8. Record a decision
  const decisionResult = await client.recordDecision({
    type: 'analysis_review',
    title: 'Review quarterly report analysis',
    proposedBy: agent.data.id,
    riskLevel: 'low',
  });

  if (decisionResult.ok) {
    console.log(`\nDecision recorded: ${decisionResult.value.data.title}`);
  }

  // 9. Verify provenance chain
  const verifyResult = await client.verifyProvenance();
  if (verifyResult.ok) {
    console.log(`\nProvenance chain valid: ${verifyResult.value.valid}`);
    console.log(`Entries verified: ${verifyResult.value.entriesVerified}`);
  }

  // 10. Get system statistics
  const stats = await client.getStats();
  if (stats.ok) {
    console.log('\nSystem Statistics:');
    console.log(`  Entities: ${stats.value.entities}`);
    console.log(`  Claims: ${stats.value.claims}`);
    console.log(`  Agents: ${stats.value.agents}`);
    console.log(`  Decisions: ${stats.value.decisions}`);
  }
}

main().catch(console.error);

Run the Demo

# Using ts-node
npx ts-node demo.ts

# Or compile and run
npx tsc demo.ts && node demo.js

Expected Output

ContextGraph client created!
Created person: Alice (ent_abc123...)
Added skill claims

Claims for Alice:
  - has_skill: TypeScript
  - has_skill: Python

Created agent: assistant
  [Handler] Analyzing document: doc_quarterly_report

Action executed successfully!

Decision recorded: Review quarterly report analysis

Provenance chain valid: true
Entries verified: 5

System Statistics:
  Entities: 1
  Claims: 2
  Agents: 1
  Decisions: 1

Using the CLI

ContextGraph includes a powerful CLI for exploration:

# View system statistics
npx contextgraph stats

# List entities
npx contextgraph entities

# Inspect an entity
npx contextgraph entity <entity-id> --with-claims

# Start interactive REPL
npx contextgraph repl

Using the REST API

Start the API server:

npx contextgraph-api

Then use it via HTTP:

# Create an entity
curl -X POST http://localhost:3000/api/v1/entities \
  -H "Content-Type: application/json" \
  -d '{"type": "person", "name": "Bob"}'

# List entities
curl http://localhost:3000/api/v1/entities

# Get statistics
curl http://localhost:3000/api/v1/stats

Next Steps

Installation

Requirements

  • Node.js: 18.0.0 or higher
  • Package Manager: pnpm (recommended), npm, or yarn
  • TypeScript: 5.0+ (for development)

Installation Methods

# Clone the repository
git clone https://github.com/akz4ol/contextgraph-os.git
cd contextgraph-os

# Install dependencies
pnpm install

# Build all packages
pnpm -r build

# Verify installation
pnpm -r test

Individual Packages

Install only the packages you need:

# Core SDK (includes most functionality)
pnpm add @contextgraph/sdk

# Or install individual packages
pnpm add @contextgraph/core
pnpm add @contextgraph/storage
pnpm add @contextgraph/ckg
pnpm add @contextgraph/provenance

Package Dependencies

Here's how packages depend on each other:

@contextgraph/core (no dependencies)
    └── @contextgraph/storage
        └── @contextgraph/ontology
            ├── @contextgraph/ckg
            ├── @contextgraph/provenance
            └── @contextgraph/dtg
                ├── @contextgraph/policy
                ├── @contextgraph/agent
                └── @contextgraph/execution
                    └── @contextgraph/sdk

Configuration

TypeScript Configuration

Add to your tsconfig.json:

{
  "compilerOptions": {
    "target": "ES2022",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "declaration": true
  }
}

Storage Configuration

ContextGraph supports multiple storage backends:

In-Memory (Default)

import { ContextGraph } from '@contextgraph/sdk';

const client = await ContextGraph.create({
  storage: { type: 'memory' }
});

SQLite

const client = await ContextGraph.create({
  storage: {
    type: 'sqlite',
    path: './contextgraph.db'
  }
});

Development Setup

For contributing or modifying ContextGraph:

# Clone and setup
git clone https://github.com/akz4ol/contextgraph-os.git
cd contextgraph-os
pnpm install

# Build in watch mode
pnpm -r build:watch

# Run tests in watch mode
pnpm -r test:watch

# Run specific package tests
pnpm --filter @contextgraph/sdk test

Docker Setup (Coming Soon)

FROM node:18-alpine

WORKDIR /app
COPY package.json pnpm-lock.yaml ./
RUN npm install -g pnpm && pnpm install

COPY . .
RUN pnpm -r build

EXPOSE 3000
CMD ["npx", "contextgraph-api"]

Troubleshooting

Build Errors

If you encounter TypeScript errors:

# Clean and rebuild
pnpm -r clean
pnpm -r build

Module Resolution Issues

Ensure your project uses ES modules:

// package.json
{
  "type": "module"
}

Missing Dependencies

# Reinstall all dependencies
rm -rf node_modules
pnpm install

Next Steps

First Steps

This guide walks you through the fundamental concepts of ContextGraph OS with hands-on examples.

Understanding the Result Pattern

ContextGraph uses a Result pattern for error handling instead of exceptions:

import { ok, err, type Result } from '@contextgraph/core';

// All operations return Result<T, Error>
const result = await client.createEntity({...});

if (result.ok) {
  // Success - access result.value
  console.log(result.value.data.id);
} else {
  // Error - access result.error
  console.error(result.error.message);
}

// Or use helper methods
const entity = result.unwrapOr(defaultEntity);

Creating Entities

Entities are the fundamental data objects in ContextGraph:

// Create a person entity
const person = await client.createEntity({
  type: 'person',
  name: 'Alice Smith',
  properties: {
    email: 'alice@example.com',
    department: 'Engineering',
  },
});

// Create a project entity
const project = await client.createEntity({
  type: 'project',
  name: 'ContextGraph OS',
  properties: {
    status: 'active',
    priority: 'high',
  },
});

Adding Claims

Claims are statements about entities with temporal context:

import { createScope, createConfidence, createTimestamp } from '@contextgraph/sdk';

// Basic claim
await client.addClaim({
  subjectId: person.data.id,
  predicate: 'works_on',
  value: project.data.id,
});

// Claim with full context
await client.addClaim({
  subjectId: person.data.id,
  predicate: 'role',
  value: 'Tech Lead',
  context: {
    scope: createScope('work'),
    confidence: createConfidence(1.0),
    validFrom: createTimestamp('2024-01-01'),
    validUntil: null, // Still valid
  },
});

// Claim about another claim (meta-claims)
await client.addClaim({
  subjectId: person.data.id,
  predicate: 'has_certification',
  value: 'AWS Solutions Architect',
  context: {
    scope: createScope('professional'),
    confidence: createConfidence(0.95),
    validFrom: createTimestamp('2023-06-15'),
    validUntil: createTimestamp('2026-06-15'),
  },
});

Querying Data

Get Claims for an Entity

const claims = await client.getClaims(person.data.id);

if (claims.ok) {
  for (const claim of claims.value) {
    console.log(`${claim.data.predicate}: ${claim.data.value}`);
  }
}

Filter by Context

// Get claims valid at a specific time
const historicalClaims = await client.getClaims(person.data.id, {
  asOf: createTimestamp('2024-06-01'),
});

// Get claims with minimum confidence
const confidentClaims = await client.getClaims(person.data.id, {
  minConfidence: 0.9,
});

Working with Agents

Agents represent autonomous actors in the system:

// Create an agent
const agent = await client.createAgent({
  name: 'data-processor',
  description: 'Processes incoming data files',
  capabilities: ['read', 'transform', 'write'],
});

// Register action handlers
client.registerHandler('transform', 'csv', async (action) => {
  const { resourceId, parameters } = action;
  // Transform the CSV file
  return ok({
    transformed: true,
    rowCount: 1000
  });
});

// Execute actions
const result = await client.execute({
  agentId: agent.data.id,
  action: 'transform',
  resourceType: 'csv',
  resourceId: 'data/input.csv',
  parameters: {
    outputFormat: 'json',
    validate: true
  },
});

Recording Decisions

Track important decisions with full audit trails:

// Record a decision
const decision = await client.recordDecision({
  type: 'deployment',
  title: 'Deploy v2.0.0 to production',
  description: 'Release new version with performance improvements',
  proposedBy: agent.data.id,
  riskLevel: 'medium',
});

// Approve the decision
await client.approveDecision(
  decision.value.data.id,
  approverAgentId,
  'Approved after review'
);

// Or reject it
await client.rejectDecision(
  decision.value.data.id,
  approverAgentId,
  'Needs more testing'
);

Creating Policies

Define access control policies:

// Deny policy for sensitive data
await client.createPolicy({
  name: 'Protect PII',
  version: '1.0.0',
  description: 'Restrict access to personally identifiable information',
  effect: 'deny',
  subjects: ['*'],
  actions: ['read', 'export'],
  resources: ['pii/*', 'sensitive/*'],
  conditions: [
    {
      field: 'agent.clearance',
      operator: 'less_than',
      value: 'confidential',
    },
  ],
  priority: 100, // Higher = evaluated first
});

// Allow policy for specific roles
await client.createPolicy({
  name: 'Admin Read Access',
  version: '1.0.0',
  effect: 'allow',
  subjects: ['role:admin'],
  actions: ['read'],
  resources: ['*'],
  priority: 50,
});

Verifying Provenance

Ensure data integrity:

// Verify the entire provenance chain
const verification = await client.verifyProvenance();

console.log(`Chain valid: ${verification.value.valid}`);
console.log(`Entries: ${verification.value.entriesVerified}`);
console.log(`Broken links: ${verification.value.brokenLinks}`);
console.log(`Invalid hashes: ${verification.value.invalidHashes}`);

Event Handling

React to system events:

// Subscribe to events
client.on('entity:created', (event) => {
  console.log('New entity:', event.data.name);
});

client.on('claim:added', (event) => {
  console.log('New claim:', event.data.predicate);
});

client.on('decision:approved', (event) => {
  console.log('Decision approved:', event.data.title);
});

client.on('execution:completed', (event) => {
  console.log('Action completed:', event.data.action);
});

Getting Statistics

Monitor your system:

const stats = await client.getStats();

console.log(`
System Statistics:
  Entities: ${stats.value.entities}
  Claims: ${stats.value.claims}
  Agents: ${stats.value.agents}
  Decisions: ${stats.value.decisions}
  Policies: ${stats.value.policies}
  Provenance entries: ${stats.value.provenanceEntries}
`);

Next Steps

Architecture Overview

ContextGraph OS is designed as a layered architecture where each layer builds upon the capabilities of the layers below it.

System Architecture

┌─────────────────────────────────────────────────────────────────┐
│                        ContextGraph OS                          │
├─────────────────────────────────────────────────────────────────┤
│  Interface Layer                                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │
│  │  REST API   │  │     CLI     │  │  Webhooks   │             │
│  └─────────────┘  └─────────────┘  └─────────────┘             │
├─────────────────────────────────────────────────────────────────┤
│  SDK Layer                                                      │
│  ┌─────────────────────────────────────────────────┐           │
│  │                      SDK                         │           │
│  │  (Unified API for all ContextGraph operations)  │           │
│  └─────────────────────────────────────────────────┘           │
├─────────────────────────────────────────────────────────────────┤
│  Advanced Capabilities Layer                                    │
│  ┌───────────┐ ┌───────────┐ ┌───────────────┐ ┌───────────┐  │
│  │    Viz    │ │ Reasoning │ │Recommendations│ │ Telemetry │  │
│  └───────────┘ └───────────┘ └───────────────┘ └───────────┘  │
├─────────────────────────────────────────────────────────────────┤
│  Execution Layer                                                │
│  ┌─────────────┐  ┌─────────────┐                              │
│  │  Executor   │  │  Handlers   │                              │
│  └─────────────┘  └─────────────┘                              │
├─────────────────────────────────────────────────────────────────┤
│  Agent Layer                                                    │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │
│  │   Agents    │  │ Capabilities│  │ Hierarchies │             │
│  └─────────────┘  └─────────────┘  └─────────────┘             │
├─────────────────────────────────────────────────────────────────┤
│  Governance Layer                                               │
│  ┌───────────┐ ┌───────────┐ ┌───────────┐ ┌─────────────────┐│
│  │  Policy   │ │Exceptions │ │   RBAC    │ │   Compliance    ││
│  └───────────┘ └───────────┘ └───────────┘ └─────────────────┘│
│  ┌─────────────────────────────────────────────────┐           │
│  │              Decision Trace Graph               │           │
│  └─────────────────────────────────────────────────┘           │
├─────────────────────────────────────────────────────────────────┤
│  Knowledge Layer                                                │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │
│  │     CKG     │  │ Provenance  │  │  Retrieval  │             │
│  └─────────────┘  └─────────────┘  └─────────────┘             │
├─────────────────────────────────────────────────────────────────┤
│  Foundation Layer                                               │
│  ┌─────────────┐  ┌─────────────┐  ┌─────────────┐             │
│  │    Core     │  │   Storage   │  │  Ontology   │             │
│  └─────────────┘  └─────────────┘  └─────────────┘             │
└─────────────────────────────────────────────────────────────────┘

Layer Descriptions

Foundation Layer

The base layer providing essential primitives:

PackagePurpose
coreBranded types, Result pattern, time utilities, error types
storageAbstract storage interface with SQLite and in-memory implementations
ontologySchema definitions, versioning, validation, code generation

Knowledge Layer

Manages all knowledge and provenance:

PackagePurpose
ckgContextual Knowledge Graph - entities and claims with temporal context
provenanceImmutable ledger tracking all data origins with hash chain verification
retrievalContext assembly with temporal, scope, and confidence filtering

Governance Layer

Handles policies, decisions, and access control:

PackagePurpose
dtgDecision Trace Graph - tracks decisions through their lifecycle
policyPolicy ledger with deny-takes-precedence evaluation
exceptionsException requests and approvals for policy overrides
rbacRole-based access control with permission inheritance
complianceAudit reports, GDPR features, compliance tracking

Agent Layer

Manages autonomous agents and their capabilities:

PackagePurpose
agentAgent registry, capabilities, and problem-space graphs
executionAction execution framework with policy enforcement

Advanced Capabilities Layer

Specialized features for analysis and insight:

PackagePurpose
vizVisualization in DOT, Mermaid, D3.js, and SVG formats
reasoningSemantic reasoning, inference rules, contradiction detection
recommendationsDecision recommendations based on precedents
telemetryOpenTelemetry-compatible tracing, metrics, logging

Interface Layer

External interfaces for system access:

PackagePurpose
sdkUnified TypeScript SDK for all operations
apiREST API with authentication and rate limiting
cliCommand-line tools and interactive REPL
webhooksEvent notifications via HTTP callbacks

Data Flow

Claim Creation Flow

User/Agent Request
       │
       ▼
   ┌───────┐
   │  SDK  │
   └───┬───┘
       │
       ▼
┌─────────────┐     ┌─────────────┐
│   Policy    │────▶│   Enforce   │
│  Evaluation │     │  Decision   │
└─────────────┘     └──────┬──────┘
                           │
       ┌───────────────────┴───────────────────┐
       ▼                                       ▼
┌─────────────┐                         ┌─────────────┐
│     CKG     │                         │ Provenance  │
│ Store Claim │                         │   Record    │
└─────────────┘                         └─────────────┘
       │                                       │
       └───────────────────┬───────────────────┘
                           ▼
                    ┌─────────────┐
                    │   Storage   │
                    └─────────────┘

Decision Lifecycle

┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│ PROPOSED │────▶│ APPROVED │────▶│ EXECUTED │────▶│COMPLETED │
└──────────┘     └────┬─────┘     └──────────┘     └──────────┘
      │               │
      │               ▼
      │          ┌──────────┐
      └─────────▶│ REJECTED │
                 └──────────┘

Key Design Patterns

1. Branded Types

All identifiers are branded types for type safety:

type EntityId = string & { readonly __brand: 'EntityId' };
type ClaimId = string & { readonly __brand: 'ClaimId' };
type AgentId = string & { readonly __brand: 'AgentId' };

2. Result Pattern

All operations return Result<T, Error> instead of throwing:

type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

3. Temporal Context

All data includes temporal qualifications:

interface TemporalContext {
  validFrom: Timestamp;
  validUntil: Timestamp | null;
  observedAt: Timestamp;
}

4. Provenance Chain

Every data mutation creates a provenance entry with hash linking:

interface ProvenanceEntry {
  id: ProvenanceId;
  hash: string;
  previousHash: string | null;
  type: ProvenanceType;
  data: unknown;
  timestamp: Timestamp;
}

Storage Architecture

Abstract Interface

interface StorageInterface {
  get<T>(key: string): Promise<Result<T | null>>;
  set<T>(key: string, value: T): Promise<Result<void>>;
  delete(key: string): Promise<Result<void>>;
  list<T>(prefix: string): Promise<Result<T[]>>;
  query<T>(query: Query): Promise<Result<T[]>>;
}

Implementations

  • InMemoryStorage: Fast, ephemeral, great for testing
  • SQLiteStorage: Persistent, ACID-compliant, production-ready

Extension Points

Custom Storage

Implement StorageInterface for custom backends:

class RedisStorage implements StorageInterface {
  // Implementation
}

Custom Action Handlers

Register handlers for any action type:

client.registerHandler('custom', 'resource', async (action) => {
  // Handle the action
  return ok(result);
});

Custom Policy Conditions

Extend policy conditions:

{
  field: 'custom.property',
  operator: 'custom_op',
  value: 'expected'
}

Next Steps

Provenance First

Provenance is the cornerstone of ContextGraph OS. Every piece of data in the system has a traceable origin, ensuring complete auditability and trust.

What is Provenance?

Provenance answers the question: "Where did this data come from?"

In ContextGraph, provenance tracking means:

  • Every claim has a documented source
  • Every change is recorded in an immutable ledger
  • The entire history can be verified cryptographically
  • No orphan data exists in the system

The Provenance Ledger

The provenance ledger is an append-only, hash-chained log of all data mutations:

interface ProvenanceEntry {
  id: ProvenanceId;
  hash: string;           // SHA-256 hash of this entry
  previousHash: string;   // Link to previous entry
  type: ProvenanceType;   // claim_created, entity_created, etc.
  subjectId: string;      // What this is about
  data: unknown;          // Entry-specific data
  source: ProvenanceSource;
  timestamp: Timestamp;
  agentId?: AgentId;      // Who created this
}

type ProvenanceType =
  | 'claim_created'
  | 'claim_revoked'
  | 'entity_created'
  | 'entity_updated'
  | 'decision_recorded'
  | 'policy_created'
  | 'execution_logged';

Hash Chain Verification

Each entry contains a hash of its contents and a reference to the previous entry's hash, creating an unbreakable chain:

Entry 1          Entry 2          Entry 3
┌─────────┐      ┌─────────┐      ┌─────────┐
│ hash: A │◄─────│prevHash:A│◄─────│prevHash:B│
│ prev: ∅ │      │ hash: B │      │ hash: C │
│ data... │      │ data... │      │ data... │
└─────────┘      └─────────┘      └─────────┘

To verify integrity:

const verification = await client.verifyProvenance();

if (verification.value.valid) {
  console.log('Chain integrity verified');
  console.log(`Entries: ${verification.value.entriesVerified}`);
} else {
  console.log('Chain corrupted!');
  console.log(`Broken links: ${verification.value.brokenLinks}`);
  console.log(`Invalid hashes: ${verification.value.invalidHashes}`);
}

Provenance Sources

Every entry has a source describing its origin:

interface ProvenanceSource {
  type: 'agent' | 'user' | 'system' | 'external' | 'inference';
  id: string;
  method?: string;
  metadata?: Record<string, unknown>;
}

// Example sources
const agentSource = {
  type: 'agent',
  id: 'agt_processor',
  method: 'data_extraction'
};

const externalSource = {
  type: 'external',
  id: 'weather_api',
  method: 'GET /current',
  metadata: { apiVersion: '2.0' }
};

const inferenceSource = {
  type: 'inference',
  id: 'reasoning_engine',
  method: 'transitive_closure',
  metadata: { confidence: 0.95 }
};

Automatic Provenance Tracking

When using the SDK, provenance is tracked automatically:

// When you add a claim...
await client.addClaim({
  subjectId: entityId,
  predicate: 'status',
  value: 'active',
});

// A provenance entry is automatically created:
// {
//   type: 'claim_created',
//   subjectId: entityId,
//   data: { predicate: 'status', value: 'active' },
//   source: { type: 'system', id: 'sdk' },
//   hash: '...',
//   previousHash: '...'
// }

Querying Provenance

Get Entry by ID

const entry = await ledger.get(provenanceId);

Query by Subject

const entries = await ledger.query({
  subjectId: entityId,
  limit: 100
});

Query by Type

const claimEntries = await ledger.query({
  type: 'claim_created',
  from: startTimestamp,
  to: endTimestamp
});

Query by Agent

const agentActions = await ledger.query({
  agentId: agentId,
  limit: 50
});

Provenance for Compliance

Provenance records support compliance requirements:

Audit Trails

// Get complete audit trail for an entity
const audit = await client.getAuditTrail({
  entityId: personId,
  format: 'detailed'
});

// Export for compliance review
const report = await compliance.generateAuditReport({
  from: '2024-01-01',
  to: '2024-12-31',
  format: 'pdf'
});

GDPR Support

// Find all data related to a person (data subject)
const subjectData = await compliance.getDataSubjectData(personId);

// Export for data portability
const exportData = await compliance.exportDataSubjectData(personId);

// Right to erasure (with provenance of deletion)
await compliance.deleteDataSubjectData(personId, {
  reason: 'GDPR Article 17 request',
  requestId: 'gdpr_req_123'
});

Best Practices

1. Always Provide Source Context

// Good - includes context
await ledger.record({
  type: 'claim_created',
  source: {
    type: 'external',
    id: 'crm_system',
    method: 'sync',
    metadata: { syncId: 'sync_456' }
  },
  // ...
});

// Avoid - missing context
await ledger.record({
  type: 'claim_created',
  source: { type: 'system', id: 'unknown' },
  // ...
});

2. Use Meaningful Agent IDs

// Good - descriptive agent
const agent = await client.createAgent({
  name: 'invoice-processor',
  description: 'Processes incoming invoices from suppliers'
});

// Avoid - generic agent
const agent = await client.createAgent({
  name: 'agent1',
  description: 'Does stuff'
});

3. Verify Regularly

// In your health checks
async function healthCheck() {
  const verification = await client.verifyProvenance();
  if (!verification.value.valid) {
    alertOps('Provenance chain integrity failure');
  }
}

Next Steps

Temporal Data

Time is a first-class citizen in ContextGraph OS. All data is temporally qualified, allowing you to query the state of your knowledge graph at any point in time.

Why Temporal Data?

Traditional databases answer: "What is the current state?"

ContextGraph answers:

  • "What is the current state?"
  • "What was the state on June 15, 2024?"
  • "When did this value change?"
  • "What will be the state next month?" (for claims with future validity)

Temporal Concepts

Timestamps

All times in ContextGraph use branded timestamps:

import { createTimestamp, Timestamp } from '@contextgraph/core';

// Create a timestamp
const now = createTimestamp();
const specific = createTimestamp('2024-06-15T10:30:00Z');

// Timestamps are ISO 8601 strings with branding
type Timestamp = string & { readonly __brand: 'Timestamp' };

Validity Periods

Claims have explicit validity periods:

interface TemporalContext {
  validFrom: Timestamp;      // When this becomes true
  validUntil: Timestamp | null;  // When this stops being true (null = forever)
  observedAt: Timestamp;     // When this was recorded
}

Example: Employee Role Changes

// Alice joins as Junior Developer on Jan 1
await client.addClaim({
  subjectId: aliceId,
  predicate: 'role',
  value: 'Junior Developer',
  context: {
    validFrom: createTimestamp('2024-01-01'),
    validUntil: createTimestamp('2024-06-30'),
  }
});

// Alice becomes Senior Developer on July 1
await client.addClaim({
  subjectId: aliceId,
  predicate: 'role',
  value: 'Senior Developer',
  context: {
    validFrom: createTimestamp('2024-07-01'),
    validUntil: null, // Current role
  }
});

Point-in-Time Queries

Query the knowledge graph as it was at any point:

// What was Alice's role in March 2024?
const marchClaims = await client.getClaims(aliceId, {
  asOf: createTimestamp('2024-03-15'),
  predicate: 'role'
});
// Returns: 'Junior Developer'

// What is Alice's role now?
const currentClaims = await client.getClaims(aliceId, {
  predicate: 'role'
});
// Returns: 'Senior Developer'

Temporal Query Operators

As Of (Point in Time)

// State at a specific moment
const claims = await ckg.query({
  subjectId: entityId,
  asOf: createTimestamp('2024-06-15T12:00:00Z')
});

Between (Time Range)

// All claims that were valid during Q2 2024
const q2Claims = await ckg.query({
  subjectId: entityId,
  validDuring: {
    from: createTimestamp('2024-04-01'),
    to: createTimestamp('2024-06-30')
  }
});

Active Only

// Only claims currently valid
const activeClaims = await ckg.query({
  subjectId: entityId,
  activeOnly: true
});

Temporal Patterns

Superseding Claims

When new information supersedes old:

// Original claim
const original = await client.addClaim({
  subjectId: productId,
  predicate: 'price',
  value: 99.99,
  context: {
    validFrom: createTimestamp('2024-01-01'),
    validUntil: null
  }
});

// Update: new price takes effect
await client.addClaim({
  subjectId: productId,
  predicate: 'price',
  value: 89.99,
  context: {
    validFrom: createTimestamp('2024-06-01'),
    validUntil: null
  }
});

// Mark old claim as ended
await client.updateClaimValidity(original.value.data.id, {
  validUntil: createTimestamp('2024-05-31')
});

Scheduled Changes

Claims that become valid in the future:

// Price change scheduled for next month
await client.addClaim({
  subjectId: productId,
  predicate: 'price',
  value: 79.99,
  context: {
    validFrom: createTimestamp('2024-07-01'),  // Future date
    validUntil: null
  }
});

// Query for July will return the scheduled price
const julyClaims = await client.getClaims(productId, {
  asOf: createTimestamp('2024-07-15'),
  predicate: 'price'
});
// Returns: 79.99

Retroactive Corrections

When you discover past data was wrong:

// We discovered the Q1 data was incorrect
// Record the correction with proper timestamps
await client.addClaim({
  subjectId: reportId,
  predicate: 'revenue',
  value: 1500000,  // Corrected value
  context: {
    validFrom: createTimestamp('2024-01-01'),
    validUntil: createTimestamp('2024-03-31'),
    // observedAt will be now, showing when we learned this
  }
});

// The original claim stays in provenance for audit

Time Utilities

Comparing Timestamps

import { isAfter, isBefore, isBetween } from '@contextgraph/core';

if (isAfter(timestamp1, timestamp2)) {
  // timestamp1 is later
}

if (isBetween(check, start, end)) {
  // check is within the range
}

Duration Calculations

import { duration, addDuration } from '@contextgraph/core';

// Calculate duration
const age = duration(startTime, endTime);
console.log(`${age.days} days, ${age.hours} hours`);

// Add duration to timestamp
const futureTime = addDuration(now, { days: 30 });

Temporal Indexes

For efficient temporal queries, ContextGraph maintains temporal indexes:

// These queries are optimized:

// Point-in-time lookup
await ckg.query({ subjectId, asOf: timestamp });

// Range query
await ckg.query({ validFrom: start, validTo: end });

// Active claims
await ckg.query({ activeOnly: true });

Best Practices

1. Always Specify Validity

// Good - explicit validity
await client.addClaim({
  subjectId: id,
  predicate: 'status',
  value: 'active',
  context: {
    validFrom: createTimestamp(),
    validUntil: null
  }
});

// Avoid - relying on defaults
await client.addClaim({
  subjectId: id,
  predicate: 'status',
  value: 'active'
});

2. Use Appropriate Granularity

// For daily values, use date precision
const dailyTimestamp = createTimestamp('2024-06-15');

// For real-time data, use full precision
const preciseTimestamp = createTimestamp(); // Includes milliseconds

3. Handle Time Zones Properly

// Always use UTC for storage
const utcTime = createTimestamp('2024-06-15T10:00:00Z');

// Convert for display in user's timezone
const userTime = formatInTimezone(utcTime, 'America/New_York');

Next Steps

Decisions as Data

In ContextGraph OS, decisions are first-class data objects with full lifecycle tracking. Every significant decision made by agents or users is recorded, tracked, and auditable.

Why Track Decisions?

AI agents make countless decisions. Without tracking:

  • You can't explain why something happened
  • You can't audit agent behavior
  • You can't learn from past decisions
  • You can't ensure compliance

ContextGraph solves this by treating decisions as data.

Decision Structure

interface Decision {
  id: DecisionId;
  type: DecisionType;
  title: string;
  description?: string;
  status: DecisionStatus;
  riskLevel: RiskLevel;
  proposedBy: AgentId;
  proposedAt: Timestamp;
  approvedBy?: AgentId;
  approvedAt?: Timestamp;
  rejectedBy?: AgentId;
  rejectedAt?: Timestamp;
  executedAt?: Timestamp;
  completedAt?: Timestamp;
  context: DecisionContext;
  outcome?: DecisionOutcome;
}

type DecisionStatus =
  | 'proposed'
  | 'approved'
  | 'rejected'
  | 'executed'
  | 'completed'
  | 'failed';

type RiskLevel = 'low' | 'medium' | 'high' | 'critical';

Decision Lifecycle

┌──────────┐
│ PROPOSED │ ─────────────────────────────┐
└────┬─────┘                              │
     │                                    │
     ▼                                    ▼
┌──────────┐                        ┌──────────┐
│ APPROVED │                        │ REJECTED │
└────┬─────┘                        └──────────┘
     │
     ▼
┌──────────┐
│ EXECUTED │
└────┬─────┘
     │
     ├──────────────────┐
     ▼                  ▼
┌──────────┐      ┌──────────┐
│COMPLETED │      │  FAILED  │
└──────────┘      └──────────┘

Recording Decisions

Basic Decision

const decision = await client.recordDecision({
  type: 'data_processing',
  title: 'Process customer data batch',
  proposedBy: agentId,
  riskLevel: 'low',
});

Decision with Context

const decision = await client.recordDecision({
  type: 'deployment',
  title: 'Deploy version 2.0.0 to production',
  description: 'Release includes new auth system and performance improvements',
  proposedBy: agentId,
  riskLevel: 'high',
  context: {
    environment: 'production',
    affectedServices: ['api', 'web', 'worker'],
    rollbackPlan: 'Revert to v1.9.5',
    estimatedDowntime: '0 minutes',
  },
});

Approving and Rejecting

Approval

await client.approveDecision(
  decisionId,
  approverId,
  'Approved after security review'
);

Rejection

await client.rejectDecision(
  decisionId,
  reviewerId,
  'Needs additional testing before deployment'
);

Conditional Approval

// Check conditions before approving
const decision = await dtg.get(decisionId);

if (decision.value.data.riskLevel === 'critical') {
  // Critical decisions need multiple approvers
  const approvals = await dtg.getApprovals(decisionId);
  if (approvals.length < 2) {
    throw new Error('Critical decisions require 2 approvals');
  }
}

await client.approveDecision(decisionId, approverId);

Executing Decisions

Once approved, decisions can be executed:

// Mark as executed
await dtg.updateStatus(decisionId, 'executed', {
  executedAt: createTimestamp(),
  executor: agentId,
});

// Perform the actual work
const result = await performDeployment(decision.context);

// Mark as completed or failed
if (result.success) {
  await dtg.updateStatus(decisionId, 'completed', {
    completedAt: createTimestamp(),
    outcome: {
      success: true,
      metrics: result.metrics,
    },
  });
} else {
  await dtg.updateStatus(decisionId, 'failed', {
    completedAt: createTimestamp(),
    outcome: {
      success: false,
      error: result.error,
    },
  });
}

Querying Decisions

Get Pending Decisions

const pending = await client.getPendingDecisions();

for (const decision of pending.value) {
  console.log(`${decision.data.title} - ${decision.data.riskLevel}`);
}

Query by Status

const approved = await dtg.queryDecisions({
  status: 'approved',
  from: startOfMonth,
  to: endOfMonth,
});

Query by Agent

const agentDecisions = await dtg.queryDecisions({
  proposedBy: agentId,
  limit: 50,
});

Query by Risk Level

const highRisk = await dtg.queryDecisions({
  riskLevel: ['high', 'critical'],
  status: 'proposed',
});

Decision Types

Define decision types for your domain:

type DecisionType =
  // Operational
  | 'deployment'
  | 'rollback'
  | 'scaling'
  | 'maintenance'

  // Data
  | 'data_processing'
  | 'data_deletion'
  | 'data_export'

  // Access
  | 'access_grant'
  | 'access_revoke'

  // Business
  | 'approval'
  | 'exception'
  | 'escalation';

Risk Assessment

Risk Levels

// Low: Routine, easily reversible
{ riskLevel: 'low' }

// Medium: Some impact, recoverable
{ riskLevel: 'medium' }

// High: Significant impact, careful execution needed
{ riskLevel: 'high' }

// Critical: Major impact, requires multiple approvals
{ riskLevel: 'critical' }

Automatic Risk Escalation

import { createRecommendationEngine } from '@contextgraph/recommendations';

const engine = createRecommendationEngine(dtg, storage);

// Check risk before executing
const assessment = await engine.assessRisk({
  action: 'delete',
  entityType: 'User',
  resource: '/api/users/bulk',
  attributes: { count: 1000 },
});

if (assessment.riskLevel === 'high') {
  // Escalate for review
  await decision.escalate('Bulk deletion requires manager approval');
}

Decision Recommendations

Use historical decisions to inform new ones:

const recommendation = await engine.recommend({
  action: 'deploy',
  entityType: 'Service',
  resource: 'api-gateway',
  attributes: {
    environment: 'production',
    changeType: 'configuration',
  },
});

console.log(`Recommendation: ${recommendation.action}`);
console.log(`Confidence: ${recommendation.confidence}`);
console.log(`Based on ${recommendation.precedents.length} similar decisions`);

Audit Integration

Decisions integrate with the audit system:

// Get full decision history
const history = await dtg.getDecisionHistory(decisionId);

// Includes all state changes with timestamps and actors
for (const event of history) {
  console.log(`${event.timestamp}: ${event.previousStatus} → ${event.newStatus}`);
  console.log(`  By: ${event.actor}`);
  console.log(`  Reason: ${event.reason}`);
}

Best Practices

1. Be Specific About Risk

// Good - clear risk assessment
{
  riskLevel: 'high',
  context: {
    reason: 'Affects production database',
    mitigations: ['Backup taken', 'Rollback script ready']
  }
}

// Avoid - vague risk
{ riskLevel: 'medium' }  // Why medium?

2. Always Record Outcomes

// Good - complete outcome
await dtg.updateStatus(id, 'completed', {
  outcome: {
    success: true,
    duration: 45000,
    affectedRecords: 1523,
    metrics: { cpu: '12%', memory: '45%' }
  }
});

// Avoid - missing outcome
await dtg.updateStatus(id, 'completed');
// Reference related decisions
await client.recordDecision({
  type: 'rollback',
  title: 'Rollback v2.0.0 deployment',
  context: {
    relatedDecisionId: originalDeploymentId,
    reason: 'Performance degradation detected'
  }
});

Next Steps

Context Filtering

ContextGraph enables sophisticated filtering of data based on temporal, jurisdictional, scope, and confidence contexts. This allows you to get exactly the right data for your use case.

Context Dimensions

1. Temporal Context

Filter by time - when data is/was valid:

// Get claims valid at a specific time
const claims = await ckg.query({
  subjectId: entityId,
  asOf: createTimestamp('2024-06-15'),
});

// Get claims valid during a range
const rangeClaims = await ckg.query({
  subjectId: entityId,
  validFrom: createTimestamp('2024-01-01'),
  validTo: createTimestamp('2024-12-31'),
});

2. Jurisdictional Context

Filter by legal/regulatory jurisdiction:

import { createJurisdiction } from '@contextgraph/core';

// Create jurisdiction-specific claims
await client.addClaim({
  subjectId: userId,
  predicate: 'data_retention_period',
  value: '7 years',
  context: {
    jurisdiction: createJurisdiction('EU'),
  },
});

await client.addClaim({
  subjectId: userId,
  predicate: 'data_retention_period',
  value: '5 years',
  context: {
    jurisdiction: createJurisdiction('US'),
  },
});

// Query for specific jurisdiction
const euClaims = await ckg.query({
  subjectId: userId,
  jurisdiction: 'EU',
});

3. Scope Context

Filter by domain or application scope:

import { createScope } from '@contextgraph/core';

// Work-related claims
await client.addClaim({
  subjectId: personId,
  predicate: 'phone',
  value: '+1-555-WORK',
  context: {
    scope: createScope('work'),
  },
});

// Personal claims
await client.addClaim({
  subjectId: personId,
  predicate: 'phone',
  value: '+1-555-HOME',
  context: {
    scope: createScope('personal'),
  },
});

// Query by scope
const workContacts = await ckg.query({
  subjectId: personId,
  scope: 'work',
});

4. Confidence Context

Filter by confidence level:

import { createConfidence } from '@contextgraph/core';

// High confidence claim (verified)
await client.addClaim({
  subjectId: entityId,
  predicate: 'verified_email',
  value: 'alice@example.com',
  context: {
    confidence: createConfidence(1.0),
  },
});

// Lower confidence claim (inferred)
await client.addClaim({
  subjectId: entityId,
  predicate: 'likely_industry',
  value: 'technology',
  context: {
    confidence: createConfidence(0.75),
  },
});

// Query only high-confidence claims
const verifiedClaims = await ckg.query({
  subjectId: entityId,
  minConfidence: 0.9,
});

Context Assembly

The Retrieval package provides powerful context assembly:

import { ContextAssembler } from '@contextgraph/retrieval';

const assembler = new ContextAssembler(ckg, provenance, storage);

// Assemble full context for an entity
const context = await assembler.assemble(entityId, {
  // Temporal filter
  asOf: createTimestamp('2024-06-15'),

  // Jurisdictional filter
  jurisdiction: 'EU',

  // Scope filter
  scope: 'work',

  // Confidence filter
  minConfidence: 0.8,

  // Include related entities
  depth: 2,

  // Include provenance
  includeProvenance: true,
});

// Result includes:
// - Matching claims
// - Related entities (up to depth 2)
// - Provenance records
// - Applied filters

Filter Combinations

Combine multiple filters for precise queries:

// Complex query: EU jurisdiction, work scope,
// high confidence, as of last month
const context = await assembler.assemble(entityId, {
  asOf: createTimestamp('2024-05-31'),
  jurisdiction: 'EU',
  scope: 'work',
  minConfidence: 0.85,
});

Context Inheritance

Claims can inherit context from their entities:

// Entity with default context
const entity = await client.createEntity({
  type: 'contract',
  name: 'Service Agreement',
  properties: {
    defaultJurisdiction: 'US-DE',
    defaultScope: 'legal',
  },
});

// Claims inherit entity context unless overridden
await client.addClaim({
  subjectId: entity.data.id,
  predicate: 'effective_date',
  value: '2024-01-01',
  // Inherits jurisdiction: US-DE, scope: legal
});

// Override specific context
await client.addClaim({
  subjectId: entity.data.id,
  predicate: 'eu_compliance_date',
  value: '2024-06-01',
  context: {
    jurisdiction: createJurisdiction('EU'),  // Override
    // Inherits scope: legal
  },
});

Query Operators

Equality

{ jurisdiction: 'EU' }  // Exact match

Multiple Values

{ jurisdiction: ['EU', 'UK'] }  // Either EU or UK

Range (for confidence)

{ minConfidence: 0.8 }  // At least 0.8
{ maxConfidence: 0.95 }  // At most 0.95
{ minConfidence: 0.8, maxConfidence: 0.95 }  // Between

Negation

{ excludeScope: ['test', 'development'] }  // Not test or development

Context-Aware Policies

Policies can reference context:

await client.createPolicy({
  name: 'GDPR Data Access',
  effect: 'deny',
  subjects: ['*'],
  actions: ['export'],
  resources: ['personal_data/*'],
  conditions: [
    {
      field: 'context.jurisdiction',
      operator: 'not_equals',
      value: 'EU',
    },
  ],
});

SDK Integration

The SDK provides convenient methods:

// Get claims with context
const claims = await client.getClaims(entityId, {
  asOf: timestamp,
  jurisdiction: 'EU',
  scope: 'work',
  minConfidence: 0.8,
});

// Assemble context
const context = await client.assembleContext(entityId, {
  jurisdiction: 'US',
  scope: 'compliance',
  includeRelated: true,
  depth: 2,
});

Performance Considerations

Indexed Fields

These context fields are indexed for fast queries:

  • validFrom / validUntil
  • jurisdiction
  • scope
  • confidence

Query Optimization

// Efficient: Uses indexes
const claims = await ckg.query({
  subjectId: entityId,
  jurisdiction: 'EU',
  minConfidence: 0.9,
});

// Less efficient: Full scan needed
const claims = await ckg.query({
  predicate: 'custom_field',
  // No subjectId, no indexed fields
});

Best Practices

1. Be Explicit About Context

// Good - clear context
await client.addClaim({
  subjectId: id,
  predicate: 'price',
  value: 99.99,
  context: {
    jurisdiction: createJurisdiction('US'),
    scope: createScope('retail'),
    confidence: createConfidence(1.0),
    validFrom: createTimestamp(),
  },
});

2. Use Appropriate Granularity

// Jurisdiction examples
'US'      // Country level
'US-CA'   // State level
'US-CA-LA'  // City level

// Scope examples
'work'
'work:hr'
'work:hr:benefits'

3. Document Context Semantics

// In your codebase, document what contexts mean
const SCOPES = {
  'work': 'Professional/employment context',
  'personal': 'Personal/private context',
  'public': 'Publicly accessible information',
  'internal': 'Internal company use only',
};

Next Steps

Policy Enforcement

ContextGraph OS includes a powerful policy engine for controlling access and enforcing business rules. Policies follow a deny-takes-precedence model for security.

Policy Structure

interface Policy {
  id: PolicyId;
  name: string;
  version: string;
  description?: string;
  effect: 'allow' | 'deny';
  subjects: string[];      // Who this applies to
  actions: string[];       // What actions
  resources: string[];     // Which resources
  conditions?: Condition[];
  priority: number;        // Higher = evaluated first
  validFrom?: Timestamp;
  validUntil?: Timestamp;
}

Creating Policies

Basic Allow Policy

await client.createPolicy({
  name: 'Read Access for Analysts',
  version: '1.0.0',
  effect: 'allow',
  subjects: ['role:analyst'],
  actions: ['read'],
  resources: ['reports/*', 'dashboards/*'],
  priority: 50,
});

Basic Deny Policy

await client.createPolicy({
  name: 'Block PII Access',
  version: '1.0.0',
  effect: 'deny',
  subjects: ['*'],
  actions: ['read', 'export'],
  resources: ['pii/*', 'sensitive/*'],
  priority: 100,
});

Conditional Policy

await client.createPolicy({
  name: 'Time-Based Access',
  version: '1.0.0',
  effect: 'allow',
  subjects: ['role:contractor'],
  actions: ['*'],
  resources: ['*'],
  conditions: [
    {
      field: 'time.hour',
      operator: 'between',
      value: [9, 17],
    },
    {
      field: 'time.dayOfWeek',
      operator: 'in',
      value: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
    },
  ],
  priority: 40,
});

Deny-Takes-Precedence

When multiple policies match, deny always wins:

// This allows read access to reports
await client.createPolicy({
  name: 'Allow Reports',
  effect: 'allow',
  subjects: ['role:analyst'],
  actions: ['read'],
  resources: ['reports/*'],
  priority: 50,
});

// This denies access to confidential reports
await client.createPolicy({
  name: 'Block Confidential',
  effect: 'deny',
  subjects: ['*'],
  actions: ['read'],
  resources: ['reports/confidential/*'],
  priority: 100,
});

// Result: Analysts can read reports/* except reports/confidential/*

Policy Evaluation

Automatic Enforcement

Policies are evaluated automatically during execution:

// Policies are checked before execution
const result = await client.execute({
  agentId: agentId,
  action: 'read',
  resourceType: 'report',
  resourceId: 'confidential/q4-financials',
});

if (!result.ok && result.error.code === 'POLICY_DENIED') {
  console.log('Access denied by policy');
}

Manual Evaluation

import { PolicyEvaluator } from '@contextgraph/policy';

const evaluator = new PolicyEvaluator(policyLedger, storage);

const decision = await evaluator.evaluate({
  subject: 'agent:data-processor',
  action: 'export',
  resource: 'pii/customer-emails',
  context: {
    time: new Date(),
    jurisdiction: 'EU',
  },
});

console.log(`Effect: ${decision.effect}`);  // 'allow' or 'deny'
console.log(`Matched policies: ${decision.matchedPolicies.length}`);

Condition Operators

Comparison

{ field: 'risk.level', operator: 'equals', value: 'high' }
{ field: 'data.count', operator: 'greater_than', value: 100 }
{ field: 'confidence', operator: 'less_than_or_equals', value: 0.5 }

Collection

{ field: 'role', operator: 'in', value: ['admin', 'superuser'] }
{ field: 'action', operator: 'not_in', value: ['delete', 'purge'] }

String Matching

{ field: 'resource', operator: 'starts_with', value: '/api/v1/' }
{ field: 'resource', operator: 'ends_with', value: '.json' }
{ field: 'resource', operator: 'contains', value: 'admin' }
{ field: 'resource', operator: 'matches', value: '^/api/v[0-9]+/' }

Temporal

{ field: 'time.hour', operator: 'between', value: [9, 17] }
{ field: 'date', operator: 'after', value: '2024-01-01' }

Existence

{ field: 'approval.id', operator: 'exists' }
{ field: 'override.reason', operator: 'not_exists' }

Policy Templates

Use built-in templates for common patterns:

import { PolicyTemplateManager } from '@contextgraph/policy';

const templates = new PolicyTemplateManager(storage);

// Read-only template
await templates.instantiate('read-only', {
  subjects: ['role:viewer'],
  resources: ['dashboards/*'],
});

// PII protection template
await templates.instantiate('pii-protection', {
  piiResources: ['customers/*', 'users/*'],
  allowedRoles: ['role:privacy-officer'],
});

// Rate limiting template
await templates.instantiate('rate-limit', {
  subjects: ['*'],
  maxRequests: 100,
  windowSeconds: 60,
});

Available Templates

TemplateDescription
read-onlyAllow only read operations
pii-protectionBlock access to PII unless authorized
approval-requiredRequire approval for actions
rate-limitLimit request rate
time-basedRestrict by time of day
jurisdictionRestrict by jurisdiction

Policy Simulation

Test policies before deployment:

import { PolicySimulator } from '@contextgraph/policy';

const simulator = new PolicySimulator(policyLedger, storage);

// Simulate a request
const result = await simulator.simulate({
  subject: 'agent:new-processor',
  action: 'delete',
  resource: 'data/customer-records',
  context: { time: new Date() },
});

console.log(`Would be: ${result.effect}`);
console.log(`Matching policies:`);
for (const policy of result.matchedPolicies) {
  console.log(`  - ${policy.name} (${policy.effect})`);
}

// Bulk simulation
const scenarios = [
  { subject: 'role:admin', action: 'delete', resource: 'data/*' },
  { subject: 'role:analyst', action: 'read', resource: 'reports/*' },
  { subject: 'role:guest', action: 'write', resource: 'comments/*' },
];

const results = await simulator.simulateMany(scenarios);

Exception Handling

Request policy exceptions:

import { ExceptionManager } from '@contextgraph/exceptions';

const exceptions = new ExceptionManager(dtg, policyLedger, storage);

// Request an exception
const request = await exceptions.request({
  policyId: policyId,
  reason: 'Need temporary access for audit',
  requestedBy: agentId,
  duration: { hours: 24 },
  scope: {
    action: 'read',
    resources: ['sensitive/audit-data'],
  },
});

// Approve the exception
await exceptions.approve(request.id, approverId, 'Approved for audit period');

// The exception is now active and will be considered during evaluation

RBAC Integration

Combine with Role-Based Access Control:

import { RBACManager } from '@contextgraph/rbac';

const rbac = new RBACManager(storage);

// Define roles
await rbac.createRole({
  name: 'data-analyst',
  permissions: ['read:reports', 'read:dashboards', 'export:csv'],
  inherits: ['viewer'],
});

// Assign roles
await rbac.assignRole(agentId, 'data-analyst');

// Policies can reference roles
await client.createPolicy({
  name: 'Analyst Access',
  effect: 'allow',
  subjects: ['role:data-analyst'],
  actions: ['read', 'export'],
  resources: ['analytics/*'],
  priority: 50,
});

Best Practices

1. Use Specific Subjects

// Good - specific subjects
{ subjects: ['role:admin', 'role:security-officer'] }

// Avoid - too broad
{ subjects: ['*'] }  // Only for deny policies

2. Set Appropriate Priorities

// Security policies: High priority (100+)
{ name: 'Block Dangerous Actions', priority: 150 }

// Business rules: Medium priority (50-99)
{ name: 'Department Access', priority: 60 }

// Default policies: Low priority (1-49)
{ name: 'Default Allow', priority: 10 }

3. Version Your Policies

{
  name: 'Data Access Policy',
  version: '2.1.0',  // Semantic versioning
  description: 'Updated to include new data categories',
}

4. Document Conditions

{
  conditions: [
    {
      field: 'data.classification',
      operator: 'not_in',
      value: ['top-secret', 'classified'],
      // _comment: 'Only allow unclassified data'
    },
  ],
}

Next Steps

@contextgraph/core

The foundational package providing branded types, Result pattern, time utilities, and error types used throughout ContextGraph OS.

Installation

pnpm add @contextgraph/core

Branded Types

All identifiers use branded types for compile-time safety:

// Branded type definitions
type EntityId = string & { readonly __brand: 'EntityId' };
type ClaimId = string & { readonly __brand: 'ClaimId' };
type AgentId = string & { readonly __brand: 'AgentId' };
type DecisionId = string & { readonly __brand: 'DecisionId' };
type PolicyId = string & { readonly __brand: 'PolicyId' };
type ProvenanceId = string & { readonly __brand: 'ProvenanceId' };
type Timestamp = string & { readonly __brand: 'Timestamp' };

Creating IDs

import {
  createEntityId,
  createClaimId,
  createAgentId,
  createTimestamp,
} from '@contextgraph/core';

const entityId = createEntityId();      // ent_abc123...
const claimId = createClaimId();        // clm_def456...
const agentId = createAgentId();        // agt_ghi789...
const timestamp = createTimestamp();    // 2024-01-15T10:30:00.000Z

Result Pattern

All operations return Result<T, Error> instead of throwing exceptions:

import { ok, err, type Result } from '@contextgraph/core';

// Creating results
function divide(a: number, b: number): Result<number> {
  if (b === 0) {
    return err(new Error('Division by zero'));
  }
  return ok(a / b);
}

// Using results
const result = divide(10, 2);

if (result.ok) {
  console.log(result.value); // 5
} else {
  console.error(result.error.message);
}

Result Utilities

import { isOk, isErr, unwrapOr, map, flatMap } from '@contextgraph/core';

// Type guards
if (isOk(result)) {
  console.log(result.value);
}

// Default value
const value = unwrapOr(result, 0);

// Transform success value
const mapped = map(result, x => x * 2);

// Chain operations
const chained = flatMap(result1, val =>
  otherOperation(val)
);

Time Utilities

Creating Timestamps

import { createTimestamp } from '@contextgraph/core';

// Current time
const now = createTimestamp();

// Specific time
const specific = createTimestamp('2024-06-15T10:30:00Z');

// From Date
const fromDate = createTimestamp(new Date());

Time Comparisons

import { isAfter, isBefore, isBetween } from '@contextgraph/core';

if (isAfter(timestamp1, timestamp2)) {
  // timestamp1 is later than timestamp2
}

if (isBetween(check, start, end)) {
  // check is within [start, end]
}

Duration Utilities

import { duration, addDuration, subtractDuration } from '@contextgraph/core';

// Calculate duration
const elapsed = duration(start, end);
// { days: 5, hours: 2, minutes: 30, seconds: 0, ms: 0 }

// Add duration
const future = addDuration(now, { days: 7 });

// Subtract duration
const past = subtractDuration(now, { hours: 24 });

Context Types

Scope

import { createScope, type Scope } from '@contextgraph/core';

const workScope = createScope('work');
const personalScope = createScope('personal');
const scope = createScope('work:hr:benefits'); // Hierarchical

Jurisdiction

import { createJurisdiction, type Jurisdiction } from '@contextgraph/core';

const usJurisdiction = createJurisdiction('US');
const euJurisdiction = createJurisdiction('EU');
const stateJurisdiction = createJurisdiction('US-CA');

Confidence

import { createConfidence, type Confidence } from '@contextgraph/core';

const highConfidence = createConfidence(0.95);  // 95%
const mediumConfidence = createConfidence(0.7);
const lowConfidence = createConfidence(0.5);

// Confidence must be between 0 and 1
createConfidence(1.5); // Throws error

Error Types

ContextGraphError

import { ContextGraphError, ErrorCode } from '@contextgraph/core';

throw new ContextGraphError(
  ErrorCode.NOT_FOUND,
  'Entity not found',
  { entityId }
);

Error Codes

CodeDescription
NOT_FOUNDResource not found
ALREADY_EXISTSResource already exists
VALIDATION_ERRORValidation failed
PERMISSION_DENIEDInsufficient permissions
POLICY_DENIEDPolicy blocked the operation
STORAGE_ERRORStorage operation failed
INTEGRITY_ERRORData integrity violation
TIMEOUTOperation timed out

Type Guards

import {
  isEntityId,
  isClaimId,
  isAgentId,
  isTimestamp,
} from '@contextgraph/core';

if (isEntityId(value)) {
  // TypeScript knows value is EntityId
}

Constants

import {
  MAX_STRING_LENGTH,
  MAX_PROPERTIES,
  DEFAULT_PAGE_SIZE,
  MAX_PAGE_SIZE,
} from '@contextgraph/core';

Type Exports

export type {
  EntityId,
  ClaimId,
  AgentId,
  DecisionId,
  PolicyId,
  ProvenanceId,
  Timestamp,
  Scope,
  Jurisdiction,
  Confidence,
  Result,
};

@contextgraph/storage

Storage abstraction layer with SQLite and in-memory implementations.

Installation

pnpm add @contextgraph/storage

Storage Interface

All storage backends implement the StorageInterface:

interface StorageInterface {
  // Basic operations
  get<T>(key: string): Promise<Result<T | null>>;
  set<T>(key: string, value: T): Promise<Result<void>>;
  delete(key: string): Promise<Result<void>>;
  has(key: string): Promise<Result<boolean>>;

  // Batch operations
  getMany<T>(keys: string[]): Promise<Result<Map<string, T>>>;
  setMany<T>(entries: Map<string, T>): Promise<Result<void>>;
  deleteMany(keys: string[]): Promise<Result<void>>;

  // List and query
  list<T>(prefix: string, options?: ListOptions): Promise<Result<T[]>>;
  query<T>(query: Query): Promise<Result<T[]>>;
  count(prefix: string): Promise<Result<number>>;

  // Lifecycle
  initialize(): Promise<Result<void>>;
  close(): Promise<Result<void>>;
  clear(): Promise<Result<void>>;
}

In-Memory Storage

Fast, ephemeral storage ideal for testing and development:

import { createInMemoryStorage } from '@contextgraph/storage';

const storage = createInMemoryStorage();
await storage.initialize();

// Use storage
await storage.set('key', { name: 'value' });
const result = await storage.get('key');

Features

  • Zero dependencies
  • Fast operations
  • Perfect for testing
  • Data lost on restart

SQLite Storage

Persistent storage for production use:

import { createSQLiteStorage } from '@contextgraph/storage';

const storage = createSQLiteStorage({
  path: './contextgraph.db',
  // Optional configuration
  wal: true,
  busyTimeout: 5000,
});

await storage.initialize();

Features

  • ACID compliance
  • Persistent storage
  • WAL mode support
  • Full-text search (planned)

Configuration

interface SQLiteStorageConfig {
  path: string;
  wal?: boolean;           // Enable WAL mode (default: true)
  busyTimeout?: number;    // Busy timeout in ms (default: 5000)
  pageSize?: number;       // Page size (default: 4096)
}

Queries

List with Prefix

// List all entities
const entities = await storage.list('entity:');

// List with options
const recent = await storage.list('entity:', {
  limit: 10,
  offset: 0,
  sortBy: 'createdAt',
  sortOrder: 'desc',
});

Query with Filters

const results = await storage.query({
  prefix: 'entity:',
  filters: [
    { field: 'type', operator: 'eq', value: 'person' },
    { field: 'createdAt', operator: 'gt', value: timestamp },
  ],
  limit: 50,
});

Query Operators

OperatorDescription
eqEquals
neNot equals
gtGreater than
gteGreater than or equal
ltLess than
lteLess than or equal
inIn array
ninNot in array
containsContains substring
startsWithStarts with
endsWithEnds with

Batch Operations

// Get multiple keys
const entries = await storage.getMany(['key1', 'key2', 'key3']);

// Set multiple entries
await storage.setMany(new Map([
  ['key1', value1],
  ['key2', value2],
]));

// Delete multiple keys
await storage.deleteMany(['key1', 'key2']);

Indexing

The storage layer supports indexes for optimized queries:

// Create index (SQLite)
await storage.createIndex('entity:type', {
  fields: ['type', 'createdAt'],
  unique: false,
});

Query Optimization

For frequent query patterns, use the query optimizer:

import { QueryOptimizer } from '@contextgraph/storage';

const optimizer = new QueryOptimizer(storage);

// Optimize query
const optimizedQuery = optimizer.optimize({
  prefix: 'entity:',
  filters: [...],
  limit: 100,
});

Custom Storage Backends

Implement StorageInterface for custom backends:

import type { StorageInterface, Result } from '@contextgraph/storage';

class RedisStorage implements StorageInterface {
  constructor(private client: RedisClient) {}

  async get<T>(key: string): Promise<Result<T | null>> {
    const value = await this.client.get(key);
    return ok(value ? JSON.parse(value) : null);
  }

  async set<T>(key: string, value: T): Promise<Result<void>> {
    await this.client.set(key, JSON.stringify(value));
    return ok(undefined);
  }

  // Implement remaining methods...
}

Storage Factory

Use the factory for flexible configuration:

import { createStorage } from '@contextgraph/storage';

// Memory
const memStorage = createStorage({ type: 'memory' });

// SQLite
const sqliteStorage = createStorage({
  type: 'sqlite',
  path: './data.db',
});

@contextgraph/ontology

Schema definitions, versioning, and validation for ContextGraph data structures.

Installation

pnpm add @contextgraph/ontology

Overview

The ontology package provides:

  • Schema definitions for entities and claims
  • Schema versioning and migration
  • Validation of data against schemas
  • Code generation from schemas

Defining Schemas

import { defineSchema, SchemaBuilder } from '@contextgraph/ontology';

const personSchema = defineSchema({
  name: 'person',
  version: '1.0.0',
  fields: {
    firstName: { type: 'string', required: true },
    lastName: { type: 'string', required: true },
    email: { type: 'string', format: 'email' },
    age: { type: 'number', min: 0, max: 150 },
    department: { type: 'string', enum: ['Engineering', 'Product', 'Sales'] },
  },
});

Validation

import { validate } from '@contextgraph/ontology';

const result = validate(personSchema, {
  firstName: 'Alice',
  lastName: 'Smith',
  email: 'alice@example.com',
  age: 30,
});

if (!result.valid) {
  console.log('Validation errors:', result.errors);
}

Schema Versioning

import { SchemaRegistry } from '@contextgraph/ontology';

const registry = new SchemaRegistry(storage);

// Register schema
await registry.register(personSchema);

// Get latest version
const latest = await registry.getLatest('person');

// Get specific version
const v1 = await registry.get('person', '1.0.0');

// List all versions
const versions = await registry.listVersions('person');

Schema Migration

import { createMigration } from '@contextgraph/ontology';

const migration = createMigration({
  from: '1.0.0',
  to: '2.0.0',
  schema: 'person',
  up: (data) => ({
    ...data,
    fullName: `${data.firstName} ${data.lastName}`,
  }),
  down: (data) => {
    const [firstName, lastName] = data.fullName.split(' ');
    return { ...data, firstName, lastName };
  },
});

await registry.registerMigration(migration);

Field Types

TypeDescription
stringText values
numberNumeric values
booleanTrue/false
arrayArray of items
objectNested object
dateISO 8601 date
timestampISO 8601 timestamp
entityRefReference to entity

Field Options

{
  type: 'string',
  required: true,
  default: 'default value',
  min: 1,              // min length for string
  max: 100,            // max length for string
  pattern: '^[A-Z]+$', // regex pattern
  format: 'email',     // built-in formats
  enum: ['a', 'b'],    // allowed values
}

Code Generation

Generate TypeScript types from schemas:

import { generateTypes } from '@contextgraph/ontology';

const code = generateTypes(personSchema);
// Outputs TypeScript interface

@contextgraph/ckg

Contextual Knowledge Graph - entities and claims with full temporal context.

Installation

pnpm add @contextgraph/ckg

Overview

The CKG (Contextual Knowledge Graph) stores:

  • Entities: Objects with types and properties
  • Claims: Statements about entities with temporal validity

Creating a CKG

import { CKG } from '@contextgraph/ckg';

const ckg = new CKG(storage, provenanceLedger);
await ckg.initialize();

Entity Operations

Create Entity

const result = await ckg.createEntity({
  type: 'person',
  name: 'Alice',
  properties: {
    email: 'alice@example.com',
    department: 'Engineering',
  },
});

const entity = result.value;
// { id: 'ent_abc123', type: 'person', name: 'Alice', ... }

Get Entity

const entity = await ckg.getEntity(entityId);

Update Entity

await ckg.updateEntity(entityId, {
  properties: {
    department: 'Product',
  },
});

Find Entities

const entities = await ckg.findEntitiesByType('person', {
  limit: 100,
  offset: 0,
});

Claim Operations

Add Claim

const claim = await ckg.addClaim({
  subjectId: entityId,
  predicate: 'works_on',
  objectValue: projectId,
  context: {
    scope: createScope('work'),
    confidence: createConfidence(1.0),
    validFrom: createTimestamp(),
    validUntil: null,
  },
});

Get Claims for Subject

const claims = await ckg.getClaimsForSubject(entityId);

Get Specific Claim

const claim = await ckg.getClaim(claimId);

Revoke Claim

await ckg.revokeClaim(claimId, 'Information superseded');

Temporal Queries

Point-in-Time

// Get claims valid at specific time
const claims = await ckg.getClaimsForSubject(entityId, {
  asOf: createTimestamp('2024-06-15'),
});

Time Range

const claims = await ckg.query({
  subjectId: entityId,
  validFrom: startTimestamp,
  validTo: endTimestamp,
});

Active Only

const activeClaims = await ckg.getClaimsForSubject(entityId, {
  activeOnly: true,
});

Context Filtering

// Filter by jurisdiction
const euClaims = await ckg.query({
  subjectId: entityId,
  jurisdiction: 'EU',
});

// Filter by scope
const workClaims = await ckg.query({
  subjectId: entityId,
  scope: 'work',
});

// Filter by confidence
const highConfidenceClaims = await ckg.query({
  subjectId: entityId,
  minConfidence: 0.9,
});

Claim Types

Claims can have different value types:

// String value
{ predicate: 'name', objectValue: 'Alice' }

// Number value
{ predicate: 'age', objectValue: 30 }

// Boolean value
{ predicate: 'isActive', objectValue: true }

// Reference to another entity
{ predicate: 'manages', objectId: otherEntityId }

Entity Interface

interface Entity {
  id: EntityId;
  type: string;
  name: string;
  properties: Record<string, unknown>;
  createdAt: Timestamp;
  updatedAt: Timestamp;
}

Claim Interface

interface Claim {
  id: ClaimId;
  subjectId: EntityId;
  predicate: string;
  objectValue?: unknown;
  objectId?: EntityId;
  context: ClaimContext;
  status: 'active' | 'revoked';
  createdAt: Timestamp;
  revokedAt?: Timestamp;
  revokedReason?: string;
}

interface ClaimContext {
  scope?: Scope;
  jurisdiction?: Jurisdiction;
  confidence?: Confidence;
  validFrom?: Timestamp;
  validUntil?: Timestamp | null;
}

Statistics

const stats = await ckg.getStats();

console.log(`Entities: ${stats.entityCount}`);
console.log(`Claims: ${stats.claimCount}`);
console.log(`Active claims: ${stats.activeClaimCount}`);

@contextgraph/provenance

Immutable provenance ledger with hash chain verification.

Installation

pnpm add @contextgraph/provenance

Overview

The provenance ledger tracks the origin of all data in ContextGraph:

  • Every mutation creates a provenance entry
  • Entries are hash-chained for integrity
  • The chain can be verified at any time

Creating the Ledger

import { ProvenanceLedger } from '@contextgraph/provenance';

const ledger = new ProvenanceLedger(storage);
await ledger.initialize();

Recording Provenance

Basic Record

const entry = await ledger.record({
  type: 'claim_created',
  subjectId: entityId,
  data: { predicate: 'status', value: 'active' },
  source: {
    type: 'agent',
    id: agentId,
    method: 'api_call',
  },
});

Entry Types

TypeDescription
claim_createdNew claim added
claim_revokedClaim revoked
entity_createdNew entity created
entity_updatedEntity properties updated
decision_recordedDecision recorded
decision_approvedDecision approved
decision_rejectedDecision rejected
execution_loggedAction executed
policy_createdPolicy created

Source Types

// Agent source
{ type: 'agent', id: 'agt_123', method: 'process_data' }

// User source
{ type: 'user', id: 'user_456', method: 'manual_entry' }

// System source
{ type: 'system', id: 'scheduler', method: 'batch_job' }

// External source
{ type: 'external', id: 'weather_api', method: 'sync' }

// Inference source
{ type: 'inference', id: 'reasoner', method: 'transitive_closure' }

Querying Provenance

Get by ID

const entry = await ledger.get(provenanceId);

Query by Subject

const entries = await ledger.query({
  subjectId: entityId,
  limit: 100,
});

Query by Type

const claimEntries = await ledger.query({
  type: 'claim_created',
  from: startTime,
  to: endTime,
});

Query by Agent

const agentActions = await ledger.query({
  agentId: agentId,
  limit: 50,
});

Chain Verification

Verify Entire Chain

const result = await ledger.verify();

console.log(`Valid: ${result.valid}`);
console.log(`Entries verified: ${result.entriesVerified}`);
console.log(`Broken links: ${result.brokenLinks}`);
console.log(`Invalid hashes: ${result.invalidHashes}`);

Verify Specific Range

const result = await ledger.verify({
  from: startId,
  to: endId,
});

Entry Structure

interface ProvenanceEntry {
  id: ProvenanceId;
  hash: string;              // SHA-256 hash
  previousHash: string | null; // Link to previous
  type: ProvenanceType;
  subjectId: string;
  data: unknown;
  source: ProvenanceSource;
  timestamp: Timestamp;
  agentId?: AgentId;
}

Hash Calculation

The hash includes:

const hashData = {
  previousHash,
  type,
  subjectId,
  data,
  source,
  timestamp,
};

const hash = sha256(JSON.stringify(hashData));

Statistics

const stats = await ledger.getStats();

console.log(`Total entries: ${stats.totalEntries}`);
console.log(`First entry: ${stats.firstEntryId}`);
console.log(`Last entry: ${stats.lastEntryId}`);

Integrity Monitoring

// Periodic verification
setInterval(async () => {
  const result = await ledger.verify();
  if (!result.valid) {
    alertOps('Provenance chain integrity failure');
  }
}, 60 * 60 * 1000); // Hourly

@contextgraph/retrieval

Context assembly with temporal and scope filtering.

Installation

pnpm add @contextgraph/retrieval

Overview

The retrieval package assembles context for entities by:

  • Gathering relevant claims
  • Filtering by temporal context
  • Filtering by jurisdiction and scope
  • Traversing relationships

Context Assembly

import { ContextAssembler } from '@contextgraph/retrieval';

const assembler = new ContextAssembler(ckg, provenance, storage);

const context = await assembler.assemble(entityId, {
  asOf: createTimestamp('2024-06-15'),
  jurisdiction: 'EU',
  scope: 'work',
  minConfidence: 0.8,
  depth: 2,
  includeProvenance: true,
});

Assembly Options

interface AssemblyOptions {
  // Temporal filter
  asOf?: Timestamp;

  // Jurisdictional filter
  jurisdiction?: string;

  // Scope filter
  scope?: string;

  // Confidence threshold
  minConfidence?: number;

  // Relationship traversal depth
  depth?: number;

  // Include provenance records
  includeProvenance?: boolean;

  // Include specific predicates only
  predicates?: string[];

  // Exclude specific predicates
  excludePredicates?: string[];

  // Maximum claims per entity
  maxClaims?: number;
}

Context Result

interface AssembledContext {
  // Primary entity
  entity: Entity;

  // Claims about the entity
  claims: Claim[];

  // Related entities (up to depth)
  relatedEntities: Map<EntityId, Entity>;

  // Claims about related entities
  relatedClaims: Map<EntityId, Claim[]>;

  // Provenance records (if requested)
  provenance?: ProvenanceEntry[];

  // Applied filters
  filters: {
    asOf?: Timestamp;
    jurisdiction?: string;
    scope?: string;
    minConfidence?: number;
  };

  // Assembly statistics
  stats: {
    claimCount: number;
    relatedEntityCount: number;
    maxDepthReached: number;
  };
}

Filtering Examples

Temporal

// Get context as of last quarter
const q3Context = await assembler.assemble(entityId, {
  asOf: createTimestamp('2024-09-30'),
});

Jurisdictional

// Get EU-compliant context
const euContext = await assembler.assemble(entityId, {
  jurisdiction: 'EU',
});

Confidence

// Get only high-confidence claims
const highConfidence = await assembler.assemble(entityId, {
  minConfidence: 0.9,
});

Relationship Traversal

// Traverse 3 levels of relationships
const deepContext = await assembler.assemble(entityId, {
  depth: 3,
});

// Access related entities
for (const [id, entity] of context.relatedEntities) {
  console.log(`Related: ${entity.name}`);
}

@contextgraph/dtg

Decision Trace Graph - tracks decisions with full lifecycle and audit trails.

Installation

pnpm add @contextgraph/dtg

Overview

The DTG tracks every significant decision through its lifecycle:

PROPOSED → APPROVED → EXECUTED → COMPLETED
              ↓           ↓
          REJECTED     FAILED

Creating the DTG

import { DecisionTraceGraph } from '@contextgraph/dtg';

const dtg = new DecisionTraceGraph(storage);
await dtg.initialize();

Recording Decisions

const decision = await dtg.record({
  type: 'deployment',
  title: 'Deploy v2.0.0 to production',
  description: 'Release new authentication system',
  proposedBy: agentId,
  riskLevel: 'high',
  context: {
    environment: 'production',
    services: ['api', 'web'],
  },
});

Decision Lifecycle

Approve

await dtg.approve(decisionId, approverId, 'Approved after security review');

Reject

await dtg.reject(decisionId, reviewerId, 'Needs more testing');

Execute

await dtg.execute(decisionId, {
  executedBy: agentId,
  executedAt: createTimestamp(),
});

Complete

await dtg.complete(decisionId, {
  success: true,
  outcome: {
    deploymentId: 'dep_123',
    duration: 45000,
  },
});

Fail

await dtg.fail(decisionId, {
  error: 'Database migration failed',
  failedAt: createTimestamp(),
});

Querying Decisions

Get by ID

const decision = await dtg.get(decisionId);

Query by Status

const pending = await dtg.queryDecisions({
  status: 'proposed',
  limit: 50,
});

const approved = await dtg.queryDecisions({
  status: 'approved',
  from: startTime,
  to: endTime,
});

Query by Agent

const agentDecisions = await dtg.queryDecisions({
  proposedBy: agentId,
});

Query by Risk Level

const highRisk = await dtg.queryDecisions({
  riskLevel: ['high', 'critical'],
  status: 'proposed',
});

Decision Interface

interface Decision {
  id: DecisionId;
  type: string;
  title: string;
  description?: string;
  status: DecisionStatus;
  riskLevel: RiskLevel;
  proposedBy: AgentId;
  proposedAt: Timestamp;
  approvedBy?: AgentId;
  approvedAt?: Timestamp;
  rejectedBy?: AgentId;
  rejectedAt?: Timestamp;
  executedAt?: Timestamp;
  completedAt?: Timestamp;
  context?: Record<string, unknown>;
  outcome?: Record<string, unknown>;
}

type DecisionStatus =
  | 'proposed'
  | 'approved'
  | 'rejected'
  | 'executed'
  | 'completed'
  | 'failed';

type RiskLevel = 'low' | 'medium' | 'high' | 'critical';

Decision History

Get the full history of a decision:

const history = await dtg.getHistory(decisionId);

for (const event of history) {
  console.log(`${event.timestamp}: ${event.previousStatus} → ${event.newStatus}`);
  console.log(`  By: ${event.actor}`);
  console.log(`  Reason: ${event.reason}`);
}

Statistics

const stats = await dtg.getStats();

console.log(`Total: ${stats.total}`);
console.log(`Proposed: ${stats.byStatus.proposed}`);
console.log(`Approved: ${stats.byStatus.approved}`);
console.log(`Completed: ${stats.byStatus.completed}`);

@contextgraph/policy

Policy ledger with rule evaluation and deny-takes-precedence semantics.

Installation

pnpm add @contextgraph/policy

Overview

The policy package provides:

  • Policy creation and management
  • Deny-takes-precedence evaluation
  • Condition-based rules
  • Policy templates and simulation

Creating Policies

import { PolicyLedger } from '@contextgraph/policy';

const ledger = new PolicyLedger(storage);
await ledger.initialize();

await ledger.create({
  name: 'Read Access for Analysts',
  version: '1.0.0',
  effect: 'allow',
  subjects: ['role:analyst'],
  actions: ['read'],
  resources: ['reports/*'],
  priority: 50,
});

Policy Evaluation

import { PolicyEvaluator } from '@contextgraph/policy';

const evaluator = new PolicyEvaluator(ledger, storage);

const decision = await evaluator.evaluate({
  subject: 'agent:data-processor',
  action: 'read',
  resource: 'reports/q4-2024',
  context: {
    time: new Date(),
    jurisdiction: 'US',
  },
});

console.log(`Effect: ${decision.effect}`);

Policy Structure

interface Policy {
  id: PolicyId;
  name: string;
  version: string;
  description?: string;
  effect: 'allow' | 'deny';
  subjects: string[];      // Who
  actions: string[];       // What
  resources: string[];     // Where
  conditions?: Condition[];
  priority: number;
  validFrom?: Timestamp;
  validUntil?: Timestamp;
}

Conditions

interface Condition {
  field: string;
  operator: ConditionOperator;
  value: unknown;
}

type ConditionOperator =
  | 'equals'
  | 'not_equals'
  | 'greater_than'
  | 'less_than'
  | 'in'
  | 'not_in'
  | 'contains'
  | 'starts_with'
  | 'ends_with'
  | 'matches'
  | 'exists'
  | 'between';

Policy Templates

import { PolicyTemplateManager } from '@contextgraph/policy';

const templates = new PolicyTemplateManager(storage);

// Use built-in templates
await templates.instantiate('read-only', {
  subjects: ['role:viewer'],
  resources: ['dashboards/*'],
});

await templates.instantiate('pii-protection', {
  piiResources: ['customers/*'],
  allowedRoles: ['role:privacy-officer'],
});

Policy Simulation

import { PolicySimulator } from '@contextgraph/policy';

const simulator = new PolicySimulator(ledger, storage);

const result = await simulator.simulate({
  subject: 'agent:new-processor',
  action: 'delete',
  resource: 'data/records',
});

console.log(`Would be: ${result.effect}`);
console.log(`Matched policies: ${result.matchedPolicies.length}`);

Priority System

  • Higher priority policies are evaluated first
  • Deny policies take precedence over allow at same priority
  • Recommended ranges:
    • Security policies: 100+
    • Business rules: 50-99
    • Default policies: 1-49

@contextgraph/exceptions

Exception requests and approvals for policy overrides.

Installation

pnpm add @contextgraph/exceptions

Overview

Handle cases where policies need to be temporarily bypassed:

  • Request exceptions to denied policies
  • Approval workflows
  • Scoped, time-limited exceptions
  • Full audit trail

Requesting Exceptions

import { ExceptionManager } from '@contextgraph/exceptions';

const exceptions = new ExceptionManager(dtg, policyLedger, storage);

const request = await exceptions.request({
  policyId: policyId,
  reason: 'Need temporary access for audit',
  requestedBy: agentId,
  duration: { hours: 24 },
  scope: {
    action: 'read',
    resources: ['sensitive/audit-data/*'],
  },
});

Approving Exceptions

await exceptions.approve(
  request.id,
  approverId,
  'Approved for audit period'
);

Exception Lifecycle

PENDING → APPROVED → ACTIVE → EXPIRED
            ↓
         REJECTED

Exception Structure

interface ExceptionRequest {
  id: string;
  policyId: PolicyId;
  reason: string;
  requestedBy: AgentId;
  requestedAt: Timestamp;
  duration: Duration;
  scope: ExceptionScope;
  status: ExceptionStatus;
  approvedBy?: AgentId;
  approvedAt?: Timestamp;
  expiresAt?: Timestamp;
}

Querying Exceptions

// Get active exceptions
const active = await exceptions.getActive();

// Get by policy
const policyExceptions = await exceptions.getByPolicy(policyId);

// Get by agent
const agentExceptions = await exceptions.getByAgent(agentId);

@contextgraph/rbac

Role-Based Access Control with built-in roles and permission checking.

Installation

pnpm add @contextgraph/rbac

Overview

Manage access control through roles:

  • Role definitions with permissions
  • Role inheritance
  • Permission checking
  • Built-in roles

Creating Roles

import { RBACManager } from '@contextgraph/rbac';

const rbac = new RBACManager(storage);
await rbac.initialize();

await rbac.createRole({
  name: 'data-analyst',
  description: 'Can read and analyze data',
  permissions: [
    'read:reports',
    'read:dashboards',
    'export:csv',
  ],
  inherits: ['viewer'],
});

Role Inheritance

// Viewer role
await rbac.createRole({
  name: 'viewer',
  permissions: ['read:public'],
});

// Analyst inherits viewer
await rbac.createRole({
  name: 'analyst',
  permissions: ['read:data', 'export:csv'],
  inherits: ['viewer'],
});

// Admin inherits analyst
await rbac.createRole({
  name: 'admin',
  permissions: ['write:*', 'delete:*'],
  inherits: ['analyst'],
});

Assigning Roles

// Assign role to agent
await rbac.assignRole(agentId, 'data-analyst');

// Get agent roles
const roles = await rbac.getRoles(agentId);

// Check role
const hasRole = await rbac.hasRole(agentId, 'data-analyst');

Permission Checking

// Check single permission
const canRead = await rbac.hasPermission(agentId, 'read:reports');

// Check any of multiple permissions
const canAccess = await rbac.hasAnyPermission(agentId, ['read:reports', 'read:dashboards']);

// Get all effective permissions
const permissions = await rbac.getEffectivePermissions(agentId);

Built-in Roles

RolePermissions
viewerRead public data
editorRead, write data
adminFull access
auditorRead audit logs

Permission Format

action:resource
action:resource:subresource
action:*
*:resource

Examples:

  • read:documents
  • write:reports:quarterly
  • delete:*
  • *:admin-panel

@contextgraph/compliance

Compliance reporting and GDPR features.

Installation

pnpm add @contextgraph/compliance

Overview

Support regulatory compliance:

  • Audit reports
  • Access reports
  • Decision reports
  • GDPR data subject rights

Audit Reports

import { ComplianceReporter } from '@contextgraph/compliance';

const reporter = new ComplianceReporter(ckg, dtg, provenance, storage);

const report = await reporter.generateAuditReport({
  from: startDate,
  to: endDate,
  format: 'json', // or 'pdf', 'csv'
  include: ['entities', 'claims', 'decisions', 'provenance'],
});

Access Reports

// Who accessed what
const accessReport = await reporter.generateAccessReport({
  entityId: entityId,
  from: startDate,
  to: endDate,
});

Decision Reports

// Decision analytics
const decisionReport = await reporter.generateDecisionReport({
  from: startDate,
  to: endDate,
  groupBy: 'agent', // or 'type', 'status'
});

GDPR Support

Data Subject Access

// Get all data for a person
const subjectData = await reporter.getDataSubjectData(personId);

Data Portability

// Export data in portable format
const exportData = await reporter.exportDataSubjectData(personId, {
  format: 'json',
  include: ['profile', 'claims', 'decisions'],
});

Right to Erasure

// Delete with provenance tracking
await reporter.deleteDataSubjectData(personId, {
  reason: 'GDPR Article 17 request',
  requestId: 'gdpr_req_123',
  retainAudit: true, // Keep audit trail
});
// Record consent
await reporter.recordConsent(personId, {
  purpose: 'marketing',
  granted: true,
  timestamp: createTimestamp(),
});

// Check consent
const hasConsent = await reporter.hasConsent(personId, 'marketing');

// Withdraw consent
await reporter.withdrawConsent(personId, 'marketing');

Report Formats

FormatDescription
jsonMachine-readable JSON
csvSpreadsheet-compatible
pdfHuman-readable document

Scheduling Reports

// Schedule daily compliance report
await reporter.schedule({
  type: 'audit',
  frequency: 'daily',
  recipients: ['compliance@example.com'],
  format: 'pdf',
});

@contextgraph/agent

Agent registry, capabilities, and hierarchies.

Installation

pnpm add @contextgraph/agent

Overview

The agent package manages:

  • Agent registration and lifecycle
  • Capability definitions
  • Agent hierarchies and delegation
  • Problem-space graphs

Creating Agents

import { AgentRegistry } from '@contextgraph/agent';

const registry = new AgentRegistry(storage);
await registry.initialize();

const agent = await registry.create({
  name: 'data-processor',
  description: 'Processes incoming data files',
  capabilities: ['read', 'transform', 'write'],
  metadata: {
    version: '1.0.0',
    maintainer: 'team@example.com',
  },
});

Agent Lifecycle

// Suspend an agent
await registry.suspend(agentId, 'Maintenance');

// Reactivate
await registry.reactivate(agentId);

// Revoke permanently
await registry.revoke(agentId, 'Security concern');

Agent Status

StatusDescription
activeAgent is operational
suspendedTemporarily disabled
revokedPermanently disabled

Capabilities

// Define capabilities
const capabilities = [
  'read:documents',
  'write:reports',
  'execute:workflows',
  'admin:users',
];

// Check capability
const hasCapability = await registry.hasCapability(agentId, 'read:documents');

Agent Hierarchies

import { AgentHierarchyManager } from '@contextgraph/agent';

const hierarchy = new AgentHierarchyManager(storage);

// Set parent-child relationship
await hierarchy.setParent(childAgentId, parentAgentId);

// Get children
const children = await hierarchy.getChildren(parentAgentId);

// Check if descendant
const isDescendant = await hierarchy.isDescendant(agentId, ancestorId);

Capability Delegation

// Delegate capabilities from parent to child
await hierarchy.delegate(parentId, childId, ['read:documents']);

// Get effective capabilities (inherited + own)
const effective = await hierarchy.getEffectiveCapabilities(agentId);

Agent Interface

interface Agent {
  id: AgentId;
  name: string;
  description?: string;
  status: AgentStatus;
  capabilities: string[];
  metadata?: Record<string, unknown>;
  parentId?: AgentId;
  createdAt: Timestamp;
  updatedAt: Timestamp;
}

Querying Agents

// Get by ID
const agent = await registry.get(agentId);

// Get by name
const agent = await registry.getByName('data-processor');

// List all active agents
const agents = await registry.list({ status: 'active' });

@contextgraph/execution

Agent execution framework with policy enforcement.

Installation

pnpm add @contextgraph/execution

Overview

The execution package provides:

  • Action execution framework
  • Handler registration
  • Policy enforcement
  • Audit logging

Creating the Executor

import { ActionExecutor } from '@contextgraph/execution';

const executor = new ActionExecutor(
  agentRegistry,
  policyEvaluator,
  provenanceLedger,
  storage
);
await executor.initialize();

Registering Handlers

executor.registerHandler('read', 'document', async (action, context) => {
  const content = await readDocument(action.resourceId);
  return ok({ content, size: content.length });
});

executor.registerHandler('transform', 'csv', async (action, context) => {
  const data = await readCSV(action.resourceId);
  const transformed = await transform(data, action.parameters);
  return ok({ rowCount: transformed.length });
});

Executing Actions

const result = await executor.execute({
  agentId: agentId,
  action: 'read',
  resourceType: 'document',
  resourceId: 'reports/q4-2024.pdf',
  parameters: {
    format: 'text',
  },
});

if (result.ok) {
  console.log('Execution result:', result.value.output);
} else {
  console.error('Execution failed:', result.error);
}

Action Structure

interface ActionRequest {
  agentId: AgentId;
  action: string;
  resourceType: string;
  resourceId: string;
  parameters?: Record<string, unknown>;
}

interface ActionResult {
  executionId: string;
  status: 'allowed' | 'denied' | 'error';
  output?: unknown;
  error?: Error;
  duration: number;
  timestamp: Timestamp;
}

Handler Context

executor.registerHandler('process', 'data', async (action, context) => {
  // Access context
  const agent = context.agent;           // Executing agent
  const policies = context.policies;     // Matched policies
  const timestamp = context.timestamp;   // Execution time

  // Perform work
  return ok({ processed: true });
});

Policy Enforcement

Actions are automatically checked against policies:

// If policy denies, execution is blocked
const result = await executor.execute({
  agentId: agentId,
  action: 'delete',
  resourceType: 'pii',
  resourceId: 'user/123',
});

if (!result.ok && result.error.code === 'POLICY_DENIED') {
  console.log('Access denied by policy');
}

Audit Logging

All executions are automatically logged to provenance:

// Query execution history
const history = await provenanceLedger.query({
  type: 'execution_logged',
  agentId: agentId,
  limit: 100,
});

Workflows

Chain multiple actions:

async function processWorkflow(agentId: AgentId) {
  // Step 1: Read data
  const readResult = await executor.execute({
    agentId,
    action: 'read',
    resourceType: 'csv',
    resourceId: 'input.csv',
  });

  if (!readResult.ok) return readResult;

  // Step 2: Transform
  const transformResult = await executor.execute({
    agentId,
    action: 'transform',
    resourceType: 'data',
    resourceId: 'temp',
    parameters: { data: readResult.value.output },
  });

  if (!transformResult.ok) return transformResult;

  // Step 3: Write output
  return executor.execute({
    agentId,
    action: 'write',
    resourceType: 'json',
    resourceId: 'output.json',
    parameters: { data: transformResult.value.output },
  });
}

@contextgraph/viz - Visualization Package

Visualization renderers for ContextGraph data structures. Generate visual representations of provenance chains, entity relationships, decision trees, and custom graphs in multiple formats.

Table of Contents

Overview

The visualization package provides multiple rendering backends to visualize ContextGraph data:

FormatUse CaseInteractiveDependencies
DOTGraphViz tools, CI/CDNoGraphviz
MermaidMarkdown docs, GitHubNoMermaid.js
D3.jsWeb dashboardsYesD3.js
SVGStandalone imagesNoNone

Installation

pnpm add @contextgraph/viz

Output Formats

DOT (Graphviz)

Standard graph description language. Use with Graphviz tools:

# Generate PNG
echo "$DOT_OUTPUT" | dot -Tpng -o graph.png

# Generate PDF
echo "$DOT_OUTPUT" | dot -Tpdf -o graph.pdf

# Generate SVG
echo "$DOT_OUTPUT" | dot -Tsvg -o graph.svg

Mermaid

Markdown-compatible diagrams. Renders in GitHub, GitLab, and documentation sites:

```mermaid
graph TB
  A[Entity] --> B[Claim]
  B --> C[Provenance]

### D3.js

JSON format for D3.js force-directed graphs:

```javascript
const data = JSON.parse(d3Output);
// Use with d3.forceSimulation()

SVG

Standalone SVG images with no external dependencies.

API Reference

visualizeProvenance

Visualize the provenance chain from a ProvenanceLedger.

async function visualizeProvenance(
  ledger: ProvenanceLedger,
  options?: Partial<VizOptions>
): Promise<Result<string>>

Parameters:

NameTypeDescription
ledgerProvenanceLedgerThe provenance ledger to visualize
optionsPartial<VizOptions>Visualization options

Returns: Result<string> - The rendered output string

Example:

import { visualizeProvenance } from '@contextgraph/viz';
import { ProvenanceLedger } from '@contextgraph/provenance';

const ledger = new ProvenanceLedger(storage);
await ledger.initialize();

// Record some provenance entries
await ledger.record({
  sourceType: 'human',
  actor: 'user_123',
  action: 'create',
  metadata: { entity: 'User profile' },
});

// Visualize as Mermaid diagram
const result = await visualizeProvenance(ledger, {
  format: 'mermaid',
  showTimestamps: true,
  colorScheme: 'default',
});

if (result.ok) {
  console.log(result.value);
  // Output:
  // graph TB
  //   prov_abc123["create<br/>2024-01-15"]
  //   prov_def456["update<br/>2024-01-16"]
  //   prov_abc123 -->|derives| prov_def456
}

visualizeEntities

Visualize entity relationships from the Contextual Knowledge Graph.

async function visualizeEntities(
  ckg: CKG,
  entityType: string,
  options?: Partial<VizOptions>
): Promise<Result<string>>

Parameters:

NameTypeDescription
ckgCKGThe Contextual Knowledge Graph
entityTypestringType of entities to visualize
optionsPartial<VizOptions>Visualization options

Example:

import { visualizeEntities } from '@contextgraph/viz';
import { CKG } from '@contextgraph/ckg';

const ckg = new CKG(storage, provenance);
await ckg.initialize();

// Visualize person entities
const result = await visualizeEntities(ckg, 'person', {
  format: 'dot',
  showLabels: true,
  direction: 'LR',
});

if (result.ok) {
  // Write to file for Graphviz
  fs.writeFileSync('entities.dot', result.value);
}

visualizeDecisions

Visualize the decision tree from a DecisionTraceGraph.

async function visualizeDecisions(
  dtg: DecisionTraceGraph,
  options?: Partial<VizOptions>
): Promise<Result<string>>

Parameters:

NameTypeDescription
dtgDecisionTraceGraphThe Decision Trace Graph
optionsPartial<VizOptions>Visualization options

Example:

import { visualizeDecisions } from '@contextgraph/viz';
import { DecisionTraceGraph } from '@contextgraph/dtg';

const dtg = new DecisionTraceGraph(storage);
await dtg.initialize();

// Visualize with status colors
const result = await visualizeDecisions(dtg, {
  format: 'svg',
  showTimestamps: true,
  colorScheme: 'default',
});

if (result.ok) {
  fs.writeFileSync('decisions.svg', result.value);
}

visualizeTimeline

Visualize a sequence of events on a timeline.

function visualizeTimeline(
  events: TimelineEvent[],
  options?: Partial<VizOptions>
): Result<string>

Parameters:

NameTypeDescription
eventsTimelineEvent[]Array of timeline events
optionsPartial<VizOptions>Visualization options

TimelineEvent Interface:

interface TimelineEvent {
  id: string;
  label: string;
  timestamp: number;
  type: 'entity' | 'claim' | 'decision' | 'provenance';
  metadata?: Record<string, unknown>;
}

Example:

import { visualizeTimeline } from '@contextgraph/viz';

const events: TimelineEvent[] = [
  { id: '1', label: 'User Created', timestamp: Date.now() - 3600000, type: 'entity' },
  { id: '2', label: 'Profile Updated', timestamp: Date.now() - 1800000, type: 'claim' },
  { id: '3', label: 'Access Granted', timestamp: Date.now(), type: 'decision' },
];

const result = visualizeTimeline(events, {
  format: 'mermaid',
  direction: 'LR', // Left to right for timeline
});

console.log(result.value);

visualizeGraph

Visualize custom graph data.

function visualizeGraph(
  data: GraphData,
  options?: Partial<VizOptions>
): Result<string>

GraphData Interface:

interface GraphData {
  nodes: GraphNode[];
  edges: GraphEdge[];
  title?: string;
  metadata?: Record<string, unknown>;
}

interface GraphNode {
  id: string;
  label: string;
  type: 'entity' | 'claim' | 'decision' | 'provenance' | 'agent';
  metadata?: Record<string, unknown>;
}

interface GraphEdge {
  from: string;
  to: string;
  type: string;
  label?: string;
  metadata?: Record<string, unknown>;
}

Example:

import { visualizeGraph } from '@contextgraph/viz';

const data: GraphData = {
  title: 'Knowledge Graph',
  nodes: [
    { id: 'alice', label: 'Alice', type: 'entity' },
    { id: 'bob', label: 'Bob', type: 'entity' },
    { id: 'project', label: 'Project X', type: 'entity' },
  ],
  edges: [
    { from: 'alice', to: 'project', type: 'works_on', label: 'lead' },
    { from: 'bob', to: 'project', type: 'works_on', label: 'contributor' },
    { from: 'alice', to: 'bob', type: 'manages' },
  ],
};

const result = visualizeGraph(data, { format: 'd3' });
console.log(result.value); // JSON for D3.js

Renderers

DOT/Graphviz

The DOT renderer generates Graphviz-compatible output.

import { renderDot } from '@contextgraph/viz';

const dot = renderDot(graphData, options);

Output Example:

digraph G {
  rankdir=TB;
  node [shape=ellipse, style=filled];

  "ent_123" [label="Alice", fillcolor="#4a90d9"];
  "ent_456" [label="Bob", fillcolor="#4a90d9"];

  "ent_123" -> "ent_456" [label="manages"];
}

Node Shapes by Type:

TypeShape
entityellipse
claimbox
decisiondiamond
provenancehexagon
agentdoubleoctagon

Mermaid

The Mermaid renderer generates markdown-compatible diagrams.

import { renderMermaid } from '@contextgraph/viz';

const mermaid = renderMermaid(graphData, options);

Output Example:

graph TB
  ent_123["Alice"]
  ent_456["Bob"]
  ent_123 -->|manages| ent_456

  classDef entity fill:#4a90d9,stroke:#333
  class ent_123,ent_456 entity

Color Schemes:

SchemeDescription
defaultStandard colors
darkDark theme
lightLight/pastel theme
colorblindColorblind-friendly palette

D3.js

The D3.js renderer outputs JSON for force-directed graphs.

import { renderD3, renderD3Config } from '@contextgraph/viz';

// Get graph data
const data = renderD3(graphData, options);

// Get recommended simulation config
const config = renderD3Config(options);

Output Structure:

interface D3GraphData {
  nodes: D3Node[];
  links: D3Link[];
  metadata?: {
    title?: string;
    nodeCount: number;
    linkCount: number;
    groups: string[];
  };
}

interface D3Node {
  id: string;
  label: string;
  group: string;
  metadata?: Record<string, unknown>;
}

interface D3Link {
  source: string;
  target: string;
  label?: string;
  type: string;
  value: number;
}

Integration Example:

<script src="https://d3js.org/d3.v7.min.js"></script>
<script>
const data = /* output from renderD3() */;
const config = /* output from renderD3Config() */;

const simulation = d3.forceSimulation(data.nodes)
  .force('link', d3.forceLink(data.links).id(d => d.id))
  .force('charge', d3.forceManyBody().strength(config.forces.charge.strength))
  .force('center', d3.forceCenter(width / 2, height / 2));
</script>

SVG

The SVG renderer generates standalone SVG images.

import { renderSvg } from '@contextgraph/viz';

const svg = renderSvg(graphData, options);
fs.writeFileSync('graph.svg', svg);

Features:

  • Automatic layout calculation (BFS-based)
  • Arrow markers for directed edges
  • Node styling based on type
  • Edge labels
  • Configurable color schemes

Configuration

VizOptions

interface VizOptions {
  /** Output format */
  format: 'dot' | 'mermaid' | 'd3' | 'svg';

  /** Show labels on nodes and edges */
  showLabels?: boolean;

  /** Show timestamps in labels */
  showTimestamps?: boolean;

  /** Maximum depth for traversal */
  maxDepth?: number;

  /** Color scheme */
  colorScheme?: 'default' | 'dark' | 'light' | 'colorblind';

  /** Graph direction */
  direction?: 'TB' | 'BT' | 'LR' | 'RL';

  /** Custom node styles by type */
  nodeStyles?: Record<string, NodeStyle>;

  /** Custom edge styles by type */
  edgeStyles?: Record<string, EdgeStyle>;
}

NodeStyle

interface NodeStyle {
  color?: string;
  fillColor?: string;
  shape?: 'circle' | 'rect' | 'diamond' | 'hexagon';
  size?: number;
}

EdgeStyle

interface EdgeStyle {
  color?: string;
  width?: number;
  style?: 'solid' | 'dashed' | 'dotted';
}

Default Options

const DEFAULT_OPTIONS: VizOptions = {
  format: 'mermaid',
  showLabels: true,
  showTimestamps: false,
  maxDepth: 10,
  colorScheme: 'default',
  direction: 'TB',
};

Examples

Provenance Audit Report

import { visualizeProvenance } from '@contextgraph/viz';
import { ProvenanceLedger } from '@contextgraph/provenance';

async function generateAuditReport(ledger: ProvenanceLedger) {
  // Generate Mermaid diagram for documentation
  const mermaidResult = await visualizeProvenance(ledger, {
    format: 'mermaid',
    showTimestamps: true,
    colorScheme: 'default',
  });

  // Generate SVG for PDF report
  const svgResult = await visualizeProvenance(ledger, {
    format: 'svg',
    showTimestamps: true,
    direction: 'TB',
  });

  return {
    markdown: `## Provenance Chain\n\n\`\`\`mermaid\n${mermaidResult.value}\n\`\`\``,
    svg: svgResult.value,
  };
}

Interactive Dashboard

import { visualizeGraph } from '@contextgraph/viz';

async function getDashboardData() {
  const graphData = await buildGraphFromDatabase();

  const result = visualizeGraph(graphData, {
    format: 'd3',
    colorScheme: 'dark',
  });

  return JSON.parse(result.value);
}

// In React/Vue component
const dashboardData = await getDashboardData();
// Use with react-force-graph or similar

Entity Relationship Diagram

import { visualizeEntities } from '@contextgraph/viz';

async function generateERD(ckg: CKG, entityType: string) {
  const result = await visualizeEntities(ckg, entityType, {
    format: 'dot',
    showLabels: true,
    direction: 'LR',
    nodeStyles: {
      entity: { fillColor: '#e3f2fd', shape: 'rect' },
    },
    edgeStyles: {
      references: { style: 'dashed', color: '#1976d2' },
    },
  });

  return result.value;
}

Integration Guide

With Graphviz

# Install Graphviz
brew install graphviz  # macOS
apt install graphviz   # Ubuntu

# Generate image from DOT output
node generate-dot.js | dot -Tpng -o output.png

With Mermaid CLI

# Install Mermaid CLI
npm install -g @mermaid-js/mermaid-cli

# Generate image from Mermaid output
node generate-mermaid.js > diagram.mmd
mmdc -i diagram.mmd -o output.png

With GitHub

Mermaid diagrams render automatically in GitHub:

# Knowledge Graph

```mermaid
graph TB
  A[User] -->|owns| B[Document]
  B -->|references| C[Entity]

### With React

```tsx
import { visualizeGraph } from '@contextgraph/viz';
import ForceGraph2D from 'react-force-graph-2d';

function GraphViewer({ graphData }) {
  const d3Data = useMemo(() => {
    const result = visualizeGraph(graphData, { format: 'd3' });
    return JSON.parse(result.value);
  }, [graphData]);

  return (
    <ForceGraph2D
      graphData={d3Data}
      nodeLabel="label"
      linkLabel="label"
    />
  );
}

With Express API

import express from 'express';
import { visualizeProvenance } from '@contextgraph/viz';

const app = express();

app.get('/api/provenance/diagram', async (req, res) => {
  const format = req.query.format || 'svg';

  const result = await visualizeProvenance(ledger, { format });

  if (!result.ok) {
    return res.status(500).json({ error: result.error.message });
  }

  const contentType = format === 'svg' ? 'image/svg+xml' :
                      format === 'd3' ? 'application/json' : 'text/plain';

  res.setHeader('Content-Type', contentType);
  res.send(result.value);
});

Supported Formats Summary

FeatureDOTMermaidD3.jsSVG
Provenance Chains
Entity Relationships
Decision Trees
Timelines
Custom Graphs
Color Schemes
Custom Styles
Interactive
Standalone

@contextgraph/reasoning - Semantic Reasoning Package

Semantic reasoning engine for ContextGraph with inference rules, relation types, and contradiction detection. Enables knowledge graph enrichment through logical inference and consistency validation.

Table of Contents

Overview

The reasoning package provides:

ComponentPurpose
RelationRegistryManage relation types (transitive, symmetric, inverse)
RuleEngineForward chaining inference with pattern matching
ReasonerHigh-level reasoning API with CKG integration
ContradictionDetectorDetect and resolve conflicting claims

Installation

pnpm add @contextgraph/reasoning

Core Concepts

Relations

Relations define how entities connect to each other. The reasoning engine supports three special relation types:

TypeDescriptionExample
TransitiveIf A→B and B→C, then A→CpartOf, locatedIn, ancestorOf
SymmetricIf A→B, then B→Aknows, siblingOf, collaboratesWith
InverseIf A→B via R, then B→A via R⁻¹parentOf/childOf, employs/employedBy

Inference Rules

Rules consist of conditions (patterns to match) and conclusions (facts to derive):

interface InferenceRule {
  id: string;
  name: string;
  description: string;
  conditions: RuleCondition[];
  conclusions: RuleConclusion[];
  priority: number;
  enabled: boolean;
}

Contradictions

Contradictions occur when claims conflict:

TypeDescriptionExample
Direct NegationSame predicate with true/falseisActive: true vs isActive: false
Mutual ExclusionIncompatible valuesstatus: active vs status: deleted
Temporal OverlapConflicting validity periodsTwo addresses valid at same time
CardinalityMultiple values for single-valued propertyTwo birthdates

API Reference

Reasoner

The main reasoning interface that integrates with CKG.

class Reasoner {
  constructor(
    ckg: CKG,
    storage: StorageInterface,
    config?: Partial<ReasonerConfig>
  );

  // Infer new facts for an entity
  async infer(entityId: EntityId, predicates?: string[]): Promise<Result<InferredFact[]>>;

  // Explain why a claim exists
  async explain(claimId: ClaimId): Promise<Result<Explanation>>;

  // Explain an inferred fact
  explainInference(inferredFact: InferredFact): Explanation;

  // Get all inferred facts
  getInferredFacts(): InferredFact[];

  // Clear inference cache
  clearInferences(): void;

  // Get statistics
  getStats(): ReasoningStats;

  // Register custom relation
  registerRelation(definition: RelationDefinition): void;

  // Register custom rule
  registerRule(rule: InferenceRule): void;

  // Access underlying components
  getRelations(): RelationRegistry;
  getRuleEngine(): RuleEngine;
}

ReasonerConfig

interface ReasonerConfig {
  /** Maximum inference iterations (default: 100) */
  maxIterations?: number;
  /** Maximum inferred facts (default: 10000) */
  maxFacts?: number;
  /** Minimum confidence threshold (default: 0.5) */
  minConfidence?: number;
  /** Whether to store inferred facts (default: false) */
  materialize?: boolean;
}

InferredFact

interface InferredFact {
  subjectId: EntityId;
  predicate: string;
  object: string | number | boolean;
  sourceClaims: ClaimId[];
  ruleId: string;
  confidence: number;
  inferredAt: number;
}

Explanation

interface Explanation {
  claimId?: ClaimId;
  inferredFact?: InferredFact;
  text: string;
  sources: Array<{ claimId: ClaimId; description: string }>;
  rules: Array<{ ruleId: string; ruleName: string }>;
  chain: string[];
}

Example:

import { createReasoner } from '@contextgraph/reasoning';
import { CKG } from '@contextgraph/ckg';

const ckg = new CKG(storage, provenance);
const reasoner = createReasoner(ckg, storage);

// Infer facts for an entity
const result = await reasoner.infer(entityId);
if (result.ok) {
  for (const fact of result.value) {
    console.log(`Inferred: ${fact.subjectId} ${fact.predicate} ${fact.object}`);
    console.log(`  Confidence: ${fact.confidence}`);
    console.log(`  Rule: ${fact.ruleId}`);
  }
}

// Explain a claim
const explanation = await reasoner.explain(claimId);
if (explanation.ok) {
  console.log(explanation.value.text);
  console.log('Reasoning chain:', explanation.value.chain.join(' → '));
}

RelationRegistry

Manages relation definitions and their properties.

class RelationRegistry {
  constructor();

  // Register a relation
  register(definition: RelationDefinition): void;

  // Check relation properties
  isTransitive(name: string): boolean;
  isSymmetric(name: string): boolean;

  // Get inverse relation
  getInverse(name: string): string | undefined;

  // Get relation definition
  get(name: string): RelationDefinition | undefined;

  // Get all relations
  getAll(): RelationDefinition[];

  // Number of registered relations
  get size(): number;
}

RelationDefinition

interface RelationDefinition {
  name: string;
  description?: string;
  transitive?: boolean;
  symmetric?: boolean;
  inverseName?: string;
  domain?: string[];    // Allowed subject types
  range?: string[];     // Allowed object types
}

Example:

import { createRelationRegistry } from '@contextgraph/reasoning';

const registry = createRelationRegistry();

// Register custom relation
registry.register({
  name: 'reportsTo',
  description: 'Employee reports to manager',
  transitive: true,
  inverseName: 'manages',
  domain: ['employee'],
  range: ['employee', 'manager'],
});

// Check properties
console.log(registry.isTransitive('reportsTo')); // true
console.log(registry.getInverse('reportsTo'));    // 'manages'

RuleEngine

Forward chaining inference engine with pattern matching.

class RuleEngine {
  constructor(rules?: RuleRegistry);

  // Find all matching rules
  findMatches(facts: Fact[]): RuleMatch[];

  // Apply a rule to generate new facts
  applyRule(rule: InferenceRule, bindings: VariableBinding[]): Fact[];

  // Run forward chaining until fixpoint
  forwardChain(
    initialFacts: Fact[],
    options?: { maxIterations?: number; maxFacts?: number }
  ): Result<{ facts: Fact[]; iterations: number; newFactsCount: number }>;

  // Access rule registry
  getRules(): RuleRegistry;
}

Fact

interface Fact {
  subject: string;
  predicate: string;
  object: string | number | boolean;
}

RuleMatch

interface RuleMatch {
  rule: InferenceRule;
  bindings: VariableBinding[];
}

Example:

import { createRuleEngine } from '@contextgraph/reasoning';

const engine = createRuleEngine();

const facts: Fact[] = [
  { subject: 'alice', predicate: 'parentOf', object: 'bob' },
  { subject: 'bob', predicate: 'parentOf', object: 'charlie' },
];

const result = engine.forwardChain(facts, { maxIterations: 10 });

if (result.ok) {
  console.log(`Derived ${result.value.newFactsCount} new facts`);
  console.log(`Total facts: ${result.value.facts.length}`);
  console.log(`Iterations: ${result.value.iterations}`);
}

ContradictionDetector

Detects and resolves contradictory claims.

class ContradictionDetector {
  constructor(ckg: CKG, storage: StorageInterface);

  // Detect contradictions for an entity
  async detectContradictions(entityId: EntityId): Promise<Result<Contradiction[]>>;

  // Detect all contradictions for an entity type
  async detectAllForType(entityType: string): Promise<Result<Contradiction[]>>;

  // Check consistency before adding a claim
  async checkConsistency(
    entityId: EntityId,
    predicate: string,
    value: unknown
  ): Promise<Result<{ consistent: boolean; conflicts: Contradiction[] }>>;

  // Resolve a contradiction
  resolveContradiction(
    contradictionId: string,
    strategy: ResolutionStrategy,
    claims: Array<{ id: ClaimId; createdAt: number; confidence: number }>
  ): Result<ResolutionResult>;

  // Get all detected contradictions
  getContradictions(): Contradiction[];

  // Add custom exclusion rule
  addExclusionRule(rule: MutualExclusionRule): void;

  // Add single-valued property rules
  addSingleValuedRule(rule: SingleValuedRule): void;
}

Contradiction

interface Contradiction {
  id: string;
  type: ContradictionType;
  entityId: EntityId;
  claimIds: ClaimId[];
  description: string;
  detectedAt: number;
  suggestedResolution?: ResolutionStrategy;
}

type ContradictionType =
  | 'direct_negation'
  | 'mutual_exclusion'
  | 'temporal_overlap'
  | 'cardinality'
  | 'type_mismatch';

ResolutionStrategy

type ResolutionStrategy =
  | 'latest_wins'        // Keep most recent claim
  | 'highest_confidence' // Keep highest confidence claim
  | 'manual_required'    // Requires human decision
  | 'keep_both'          // Accept contradiction
  | 'revoke_both';       // Remove all conflicting claims

ResolutionResult

interface ResolutionResult {
  contradictionId: string;
  strategy: ResolutionStrategy;
  keptClaims: ClaimId[];
  revokedClaims: ClaimId[];
  resolvedAt: number;
}

Example:

import { createContradictionDetector } from '@contextgraph/reasoning';

const detector = createContradictionDetector(ckg, storage);

// Detect contradictions
const result = await detector.detectContradictions(entityId);

if (result.ok) {
  for (const contradiction of result.value) {
    console.log(`Found ${contradiction.type}: ${contradiction.description}`);
    console.log(`  Claims: ${contradiction.claimIds.join(', ')}`);
    console.log(`  Suggested: ${contradiction.suggestedResolution}`);
  }
}

// Check before adding claim
const consistency = await detector.checkConsistency(
  entityId,
  'dateOfBirth',
  '1990-01-15'
);

if (!consistency.value.consistent) {
  console.log('Would create contradiction!');
  for (const conflict of consistency.value.conflicts) {
    console.log(`  Conflict with: ${conflict.claimIds.join(', ')}`);
  }
}

Built-in Relations

The registry includes common relations out of the box:

Transitive Relations

NameDescription
partOfPart-whole relationship
subclassOfType hierarchy
locatedInLocation hierarchy
reportsToOrganizational hierarchy
ancestorOfFamily hierarchy

Symmetric Relations

NameDescription
knowsPersonal acquaintance
collaboratesWithProfessional collaboration
relatedToGeneric relation
siblingOfFamily relation
marriedToMarital relation

Inverse Relations

RelationInverse
parentOfchildOf
employsemployedBy
ownsownedBy
managesmanagedBy
teachestaughtBy
createscreatedBy

Built-in Rules

Transitive Closure

{
  id: 'transitive-closure',
  name: 'Transitive Closure',
  description: 'If A relates to B and B relates to C via transitive relation, then A relates to C',
  conditions: [
    { subject: '?a', predicate: '?rel', object: '?b' },
    { subject: '?b', predicate: '?rel', object: '?c' },
  ],
  conclusions: [
    { subject: '?a', predicate: '?rel', object: '?c', confidenceMultiplier: 0.9 }
  ],
  priority: 100,
}

Symmetric Relation

{
  id: 'symmetric-relation',
  name: 'Symmetric Relation',
  description: 'If A relates to B via symmetric relation, then B relates to A',
  conditions: [
    { subject: '?a', predicate: '?rel', object: '?b' }
  ],
  conclusions: [
    { subject: '?b', predicate: '?rel', object: '?a', confidenceMultiplier: 1.0 }
  ],
  priority: 90,
}

Inverse Relation

{
  id: 'inverse-relation',
  name: 'Inverse Relation',
  description: 'If A relates to B, then B relates to A via inverse',
  conditions: [
    { subject: '?a', predicate: '?rel', object: '?b' }
  ],
  conclusions: [
    { subject: '?b', predicate: '?inverseRel', object: '?a', confidenceMultiplier: 1.0 }
  ],
  priority: 80,
}

Custom Rules

Create custom inference rules for domain-specific reasoning:

import { Reasoner } from '@contextgraph/reasoning';

const reasoner = createReasoner(ckg, storage);

// Rule: Team members know each other
reasoner.registerRule({
  id: 'team-knows',
  name: 'Team Members Know Each Other',
  description: 'If A and B are on the same team, they know each other',
  conditions: [
    { subject: '?a', predicate: 'memberOf', object: '?team' },
    { subject: '?b', predicate: 'memberOf', object: '?team' },
  ],
  conclusions: [
    { subject: '?a', predicate: 'knows', object: '?b', confidenceMultiplier: 0.8 }
  ],
  priority: 50,
  enabled: true,
});

// Rule: Manager hierarchy
reasoner.registerRule({
  id: 'skip-level-manager',
  name: 'Skip-Level Manager',
  description: 'If A manages B and B manages C, then A is skip-level manager of C',
  conditions: [
    { subject: '?a', predicate: 'manages', object: '?b' },
    { subject: '?b', predicate: 'manages', object: '?c' },
  ],
  conclusions: [
    { subject: '?a', predicate: 'skipLevelManages', object: '?c', confidenceMultiplier: 0.95 }
  ],
  priority: 60,
  enabled: true,
});

Pattern Variables

Rules use variables (prefixed with ?) for pattern matching:

VariableMatches
?a, ?b, ?cAny entity ID
?relAny predicate
?valueAny value

Confidence Multiplier

Each conclusion has a confidenceMultiplier that reduces confidence for inferred facts:

// Source claims have confidence 1.0
// After transitive inference (0.9 multiplier):
// Final confidence = 1.0 * 0.9 = 0.9

// After two transitive steps:
// Final confidence = 1.0 * 0.9 * 0.9 = 0.81

Contradiction Resolution

Default Single-Valued Properties

These properties can only have one value:

  • dateOfBirth
  • dateOfDeath
  • birthplace
  • gender
  • maritalStatus
  • nationality
  • ssn
  • taxId
  • email
  • primaryPhone

Default Mutual Exclusions

// Status values are mutually exclusive
{ predicate: 'status', exclusiveValues: ['active', 'inactive', 'suspended', 'deleted'] }

// Alive/dead are mutually exclusive
{ predicate: 'alive', exclusiveValues: [true, false] }

Adding Custom Rules

const detector = createContradictionDetector(ckg, storage);

// Add single-valued property
detector.addSingleValuedRule({
  predicates: ['primaryAddress', 'currentEmployer', 'spouseId'],
});

// Add mutual exclusion
detector.addExclusionRule({
  predicate: 'employmentStatus',
  exclusiveValues: ['employed', 'unemployed', 'retired', 'student'],
  description: 'Employment status values are mutually exclusive',
});

Resolution Workflow

// 1. Detect contradictions
const contradictions = await detector.detectContradictions(entityId);

// 2. For each contradiction, decide resolution
for (const contradiction of contradictions.value) {
  // Get the claims with their metadata
  const claims = await Promise.all(
    contradiction.claimIds.map(async (id) => {
      const claim = await ckg.getClaim(id);
      return {
        id,
        createdAt: claim.value?.data.createdAt ?? 0,
        confidence: 1.0, // or get from claim metadata
      };
    })
  );

  // Apply resolution strategy
  const resolution = detector.resolveContradiction(
    contradiction.id,
    'latest_wins',
    claims
  );

  if (resolution.ok) {
    // Revoke the claims that lost
    for (const claimId of resolution.value.revokedClaims) {
      await ckg.revokeClaim(claimId);
    }

    console.log(`Resolved: kept ${resolution.value.keptClaims.length} claims`);
  }
}

Examples

Organization Hierarchy Reasoning

import { createReasoner, createRelationRegistry } from '@contextgraph/reasoning';

async function reasonAboutOrganization(ckg: CKG) {
  const reasoner = createReasoner(ckg, storage);

  // Register organization-specific relations
  reasoner.registerRelation({
    name: 'reportsTo',
    transitive: true,
    inverseName: 'directReports',
  });

  // Add organization-specific rule
  reasoner.registerRule({
    id: 'same-department',
    name: 'Same Department Inference',
    description: 'People who report to same manager are in same department',
    conditions: [
      { subject: '?a', predicate: 'reportsTo', object: '?manager' },
      { subject: '?b', predicate: 'reportsTo', object: '?manager' },
    ],
    conclusions: [
      { subject: '?a', predicate: 'sameDepartmentAs', object: '?b', confidenceMultiplier: 0.85 }
    ],
    priority: 40,
    enabled: true,
  });

  // Run inference for an employee
  const result = await reasoner.infer(employeeId);

  if (result.ok) {
    // Find all indirect reports
    const indirectReports = result.value.filter(
      f => f.predicate === 'directReports' && f.ruleId === 'inverse-relation'
    );

    // Find all colleagues
    const colleagues = result.value.filter(
      f => f.predicate === 'sameDepartmentAs'
    );

    return { indirectReports, colleagues };
  }
}

Knowledge Graph Consistency Check

import { createContradictionDetector } from '@contextgraph/reasoning';

async function validateKnowledgeGraph(ckg: CKG) {
  const detector = createContradictionDetector(ckg, storage);

  // Check all person entities
  const result = await detector.detectAllForType('person');

  if (!result.ok) {
    console.error('Failed to check:', result.error);
    return;
  }

  const report = {
    totalContradictions: result.value.length,
    byType: {} as Record<string, number>,
    criticalIssues: [] as Contradiction[],
  };

  for (const contradiction of result.value) {
    // Count by type
    report.byType[contradiction.type] =
      (report.byType[contradiction.type] ?? 0) + 1;

    // Flag critical issues (cardinality on important fields)
    if (
      contradiction.type === 'cardinality' &&
      ['ssn', 'dateOfBirth', 'taxId'].some(f =>
        contradiction.description.includes(f)
      )
    ) {
      report.criticalIssues.push(contradiction);
    }
  }

  return report;
}

Pre-flight Claim Validation

import { createContradictionDetector } from '@contextgraph/reasoning';

async function addClaimSafely(
  ckg: CKG,
  entityId: EntityId,
  predicate: string,
  value: unknown
) {
  const detector = createContradictionDetector(ckg, storage);

  // Check consistency first
  const check = await detector.checkConsistency(entityId, predicate, value);

  if (!check.ok) {
    throw check.error;
  }

  if (!check.value.consistent) {
    // Handle conflicts
    const conflicts = check.value.conflicts;

    // Option 1: Reject
    throw new Error(
      `Cannot add claim: would conflict with ${conflicts.length} existing claims`
    );

    // Option 2: Auto-resolve with latest-wins
    // for (const conflict of conflicts) {
    //   await detector.resolveContradiction(conflict.id, 'latest_wins', ...);
    // }
  }

  // Safe to add
  return ckg.addClaim({ subjectId: entityId, predicate, objectValue: value, ... });
}

Performance Considerations

Limiting Inference Scope

// Only infer specific predicates
const result = await reasoner.infer(entityId, ['manages', 'reportsTo']);

Controlling Iterations

const reasoner = createReasoner(ckg, storage, {
  maxIterations: 50,   // Limit inference depth
  maxFacts: 5000,      // Limit total facts
  minConfidence: 0.6,  // Filter low-confidence inferences
});

Caching Inferences

// Inferences are cached in memory
const result1 = await reasoner.infer(entityId); // Computes
const result2 = await reasoner.infer(entityId); // Uses cache

// Clear cache when data changes
reasoner.clearInferences();

Rule Priority

Higher priority rules fire first. Use this to ensure important rules complete before others:

// Critical rules: 90-100
// Normal rules: 50-89
// Optional rules: 0-49

Batch Processing

For large graphs, process entities in batches:

async function inferAll(entityIds: EntityId[]) {
  const results = [];

  for (const batch of chunk(entityIds, 100)) {
    const batchResults = await Promise.all(
      batch.map(id => reasoner.infer(id))
    );
    results.push(...batchResults);

    // Clear cache between batches if memory is a concern
    reasoner.clearInferences();
  }

  return results;
}

@contextgraph/recommendations - Decision Recommendations Package

Similarity-based decision recommendations for ContextGraph. Analyzes historical decisions to provide intelligent recommendations with risk assessment and confidence scoring.

Table of Contents

Overview

The recommendations package provides:

ComponentPurpose
RecommendationEngineFind similar decisions and generate recommendations
Similarity CalculatorCompare decision contexts with configurable weights
Risk AssessorEvaluate risk based on patterns and precedent outcomes
Feedback SystemTrack accuracy and improve over time

Installation

pnpm add @contextgraph/recommendations

Core Concepts

Decision Context

A decision context captures the key attributes of a decision request:

interface DecisionContext {
  /** Action being requested (e.g., 'create', 'delete', 'approve') */
  action: string;

  /** Type of entity involved (optional) */
  entityType?: string;

  /** Resource path or identifier (optional) */
  resource?: string;

  /** Additional attributes for matching */
  attributes: Record<string, unknown>;

  /** When the decision was made (optional) */
  timestamp?: number;
}

Similarity Matching

Similarity is calculated across four dimensions:

DimensionWeightDescription
Action40%Exact match of action type
Entity Type20%Match of entity type
Resource20%Path similarity (prefix matching)
Attributes20%Jaccard similarity of attributes

Risk Assessment

Risk is evaluated based on:

  1. Action Patterns - Destructive actions (delete, destroy, revoke)
  2. Privilege Patterns - Elevated access (admin, root, sudo)
  3. Precedent Outcomes - Historical failure/rejection rates
  4. Precedent Count - Lack of historical data

API Reference

RecommendationEngine

The main class for generating recommendations.

class RecommendationEngine {
  constructor(
    dtg: DecisionTraceGraph,
    storage: StorageInterface,
    config?: RecommendationEngineConfig
  );

  // Find similar past decisions
  async findSimilarDecisions(context: DecisionContext): Promise<Result<SimilarDecision[]>>;

  // Get a recommendation
  async recommend(context: DecisionContext): Promise<Result<Recommendation>>;

  // Explain a recommendation
  explainRecommendation(recommendationId: string): Result<string>;

  // Submit feedback on a recommendation
  submitFeedback(feedback: RecommendationFeedback): Result<void>;

  // Get statistics
  getStats(): RecommendationStats;

  // Update matching criteria
  setCriteria(criteria: Partial<MatchingCriteria>): void;
}

RecommendationEngineConfig

interface RecommendationEngineConfig {
  /** Matching criteria overrides */
  criteria?: Partial<MatchingCriteria>;

  /** Minimum precedents for confident recommendation (default: 3) */
  minPrecedents?: number;

  /** High-risk action patterns (default: ['delete', 'destroy', 'terminate', 'revoke', 'disable']) */
  highRiskPatterns?: string[];

  /** Escalation-required patterns (default: ['admin', 'root', 'sudo', 'override']) */
  escalationPatterns?: string[];
}

Recommendation

interface Recommendation {
  /** Unique recommendation ID */
  id: string;

  /** Recommended action */
  recommendedAction: 'approve' | 'reject' | 'defer' | 'escalate';

  /** Confidence score (0-1) */
  confidence: number;

  /** Human-readable reasoning */
  reasoning: string;

  /** Similar historical decisions */
  precedents: SimilarDecision[];

  /** Risk assessment */
  risk: RiskAssessment;

  /** When generated */
  generatedAt: number;
}

SimilarDecision

interface SimilarDecision {
  /** Decision ID from DTG */
  decisionId: DecisionId;

  /** Similarity score details */
  similarity: SimilarityScore;

  /** Outcome of the decision */
  outcome: 'approved' | 'rejected' | 'executed' | 'failed';

  /** Age of decision in milliseconds */
  ageMs: number;
}

RiskAssessment

interface RiskAssessment {
  /** Overall risk level */
  level: 'low' | 'medium' | 'high' | 'critical';

  /** Risk score (0-1) */
  score: number;

  /** Individual risk factors */
  factors: RiskFactor[];
}

interface RiskFactor {
  name: string;
  description: string;
  weight: number;
  mitigations?: string[];
}

Example:

import { createRecommendationEngine } from '@contextgraph/recommendations';
import { DecisionTraceGraph } from '@contextgraph/dtg';

const dtg = new DecisionTraceGraph(storage);
const engine = createRecommendationEngine(dtg, storage);

// Get recommendation for a decision
const result = await engine.recommend({
  action: 'delete',
  entityType: 'user',
  resource: '/api/users/123',
  attributes: {
    role: 'admin',
    department: 'engineering',
  },
});

if (result.ok) {
  const rec = result.value;
  console.log(`Recommendation: ${rec.recommendedAction}`);
  console.log(`Confidence: ${(rec.confidence * 100).toFixed(1)}%`);
  console.log(`Reasoning: ${rec.reasoning}`);
  console.log(`Risk Level: ${rec.risk.level}`);
}

calculateSimilarity

Calculate similarity between two decision contexts.

function calculateSimilarity(
  context1: DecisionContext,
  context2: DecisionContext,
  criteria?: MatchingCriteria
): SimilarityScore

SimilarityScore

interface SimilarityScore {
  /** Overall similarity (0-1) */
  score: number;

  /** Breakdown by dimension */
  breakdown: {
    actionMatch: number;
    entityTypeMatch: number;
    resourceMatch: number;
    attributeMatch: number;
  };
}

Example:

import { calculateSimilarity, DEFAULT_CRITERIA } from '@contextgraph/recommendations';

const context1: DecisionContext = {
  action: 'create',
  entityType: 'user',
  resource: '/api/users',
  attributes: { role: 'admin' },
};

const context2: DecisionContext = {
  action: 'create',
  entityType: 'user',
  resource: '/api/users/profile',
  attributes: { role: 'admin', level: 5 },
};

const similarity = calculateSimilarity(context1, context2);

console.log(`Overall: ${similarity.score}`);
console.log(`Action: ${similarity.breakdown.actionMatch}`);       // 1.0 (exact match)
console.log(`Entity: ${similarity.breakdown.entityTypeMatch}`);   // 1.0 (exact match)
console.log(`Resource: ${similarity.breakdown.resourceMatch}`);   // ~0.66 (prefix match)
console.log(`Attributes: ${similarity.breakdown.attributeMatch}`); // ~0.5 (partial overlap)

rankBySimilarity

Rank and filter similar decisions.

function rankBySimilarity<T extends { similarity: SimilarityScore; ageMs: number }>(
  items: T[],
  criteria?: MatchingCriteria
): T[]

Example:

import { rankBySimilarity, DEFAULT_CRITERIA } from '@contextgraph/recommendations';

const decisions = [
  { decisionId: 'a', similarity: { score: 0.7, ... }, ageMs: 1000, outcome: 'approved' },
  { decisionId: 'b', similarity: { score: 0.9, ... }, ageMs: 2000, outcome: 'approved' },
  { decisionId: 'c', similarity: { score: 0.8, ... }, ageMs: 500, outcome: 'rejected' },
];

const ranked = rankBySimilarity(decisions, {
  ...DEFAULT_CRITERIA,
  minSimilarity: 0.5,
  maxPrecedentAge: 7 * 24 * 60 * 60 * 1000, // 7 days
  maxPrecedents: 5,
});

// Returns sorted by score (highest first), filtered by criteria

Configuration

Matching Criteria

interface MatchingCriteria {
  /** Weight for action matching (default: 0.4) */
  actionWeight: number;

  /** Weight for entity type matching (default: 0.2) */
  entityTypeWeight: number;

  /** Weight for resource matching (default: 0.2) */
  resourceWeight: number;

  /** Weight for attribute matching (default: 0.2) */
  attributeWeight: number;

  /** Minimum similarity to consider (default: 0.5) */
  minSimilarity: number;

  /** Maximum age of precedents in ms (default: 90 days) */
  maxPrecedentAge: number;

  /** Maximum number of precedents to return (default: 10) */
  maxPrecedents: number;
}

Default Values:

const DEFAULT_CRITERIA: MatchingCriteria = {
  actionWeight: 0.4,
  entityTypeWeight: 0.2,
  resourceWeight: 0.2,
  attributeWeight: 0.2,
  minSimilarity: 0.5,
  maxPrecedentAge: 90 * 24 * 60 * 60 * 1000, // 90 days
  maxPrecedents: 10,
};

Risk Patterns

Configure which patterns trigger risk flags:

const engine = createRecommendationEngine(dtg, storage, {
  // Actions that increase risk score
  highRiskPatterns: [
    'delete',
    'destroy',
    'terminate',
    'revoke',
    'disable',
    'purge',
    'wipe',
  ],

  // Patterns requiring escalation
  escalationPatterns: [
    'admin',
    'root',
    'sudo',
    'override',
    'bypass',
    'force',
  ],

  // Minimum precedents for confident recommendation
  minPrecedents: 5,
});

Recommendation Types

The engine returns one of four recommendation types:

Approve

Returned when:

  • ≥70% of similar decisions were approved/executed
  • Average similarity ≥60%
  • Risk level is low or medium
{
  recommendedAction: 'approve',
  confidence: 0.85,
  reasoning: '85% of similar decisions were approved. Average similarity: 72%.',
}

Reject

Returned when:

  • ≥60% of similar decisions were rejected/failed
  • Clear pattern of failures
{
  recommendedAction: 'reject',
  confidence: 0.78,
  reasoning: '65% of similar decisions were rejected. Risk factors: High-Risk Action.',
}

Defer

Returned when:

  • Insufficient precedents (below minPrecedents threshold)
  • Mixed results without clear pattern
  • Low confidence in recommendation
{
  recommendedAction: 'defer',
  confidence: 0.6,
  reasoning: 'Insufficient historical precedents (2/5). More context needed.',
}

Escalate

Returned when:

  • Critical risk level detected
  • High risk level with mixed precedent outcomes
  • Escalation patterns detected in action or resource
{
  recommendedAction: 'escalate',
  confidence: 0.9,
  reasoning: 'Critical risk level detected. Manual review by supervisor recommended.',
}

Feedback Loop

Track recommendation accuracy and improve over time:

Submitting Feedback

import type { RecommendationFeedback } from '@contextgraph/recommendations';

const feedback: RecommendationFeedback = {
  recommendationId: 'rec_abc123',
  followed: true,
  actualDecision: 'approved',
  outcome: 'success',
  comments: 'Worked as expected',
  submittedAt: Date.now(),
};

engine.submitFeedback(feedback);

Tracking Statistics

interface RecommendationStats {
  /** Total recommendations generated */
  totalRecommendations: number;

  /** How many recommendations were followed */
  recommendationsFollowed: number;

  /** Average confidence score */
  averageConfidence: number;

  /** Accuracy (correct predictions / total with feedback) */
  accuracy: number;

  /** Distribution of recommendation types */
  decisionDistribution: {
    approve: number;
    reject: number;
    defer: number;
    escalate: number;
  };
}

const stats = engine.getStats();
console.log(`Accuracy: ${(stats.accuracy * 100).toFixed(1)}%`);
console.log(`Average confidence: ${(stats.averageConfidence * 100).toFixed(1)}%`);

Examples

API Request Approval

import { createRecommendationEngine } from '@contextgraph/recommendations';

async function shouldApproveAPIRequest(request: {
  method: string;
  path: string;
  user: { role: string; department: string };
}) {
  const engine = createRecommendationEngine(dtg, storage);

  const recommendation = await engine.recommend({
    action: request.method.toLowerCase(),
    resource: request.path,
    attributes: {
      userRole: request.user.role,
      department: request.user.department,
    },
  });

  if (!recommendation.ok) {
    return { allowed: false, reason: 'Failed to generate recommendation' };
  }

  const rec = recommendation.value;

  switch (rec.recommendedAction) {
    case 'approve':
      return { allowed: true, confidence: rec.confidence };

    case 'reject':
      return { allowed: false, reason: rec.reasoning };

    case 'escalate':
      return { allowed: false, requiresApproval: true, reason: rec.reasoning };

    case 'defer':
    default:
      return { allowed: false, requiresReview: true, reason: rec.reasoning };
  }
}

Workflow Step Recommendation

async function recommendWorkflowAction(
  workflowId: string,
  step: string,
  parameters: Record<string, unknown>
) {
  const engine = createRecommendationEngine(dtg, storage, {
    minPrecedents: 5,
    criteria: {
      actionWeight: 0.3,
      attributeWeight: 0.4, // Higher weight on parameters
    },
  });

  const result = await engine.recommend({
    action: step,
    entityType: 'workflow',
    resource: `/workflows/${workflowId}`,
    attributes: parameters,
  });

  if (!result.ok) {
    return null;
  }

  // Generate explanation
  const explanation = engine.explainRecommendation(result.value.id);

  return {
    recommendation: result.value,
    explanation: explanation.ok ? explanation.value : null,
  };
}

Risk-Aware Batch Processing

async function processBatch(items: Array<{ action: string; target: string }>) {
  const engine = createRecommendationEngine(dtg, storage);
  const results = [];

  for (const item of items) {
    const rec = await engine.recommend({
      action: item.action,
      resource: item.target,
      attributes: {},
    });

    if (!rec.ok) {
      results.push({ item, status: 'error', error: rec.error });
      continue;
    }

    // Auto-approve low-risk items
    if (
      rec.value.recommendedAction === 'approve' &&
      rec.value.risk.level === 'low' &&
      rec.value.confidence >= 0.8
    ) {
      results.push({ item, status: 'auto-approved' });
      continue;
    }

    // Queue high-risk items for manual review
    if (rec.value.risk.level === 'high' || rec.value.risk.level === 'critical') {
      results.push({ item, status: 'manual-review', recommendation: rec.value });
      continue;
    }

    // Default handling
    results.push({ item, status: 'pending', recommendation: rec.value });
  }

  return results;
}

Custom Similarity Weights

// For permission-related decisions, weight action heavily
const permissionEngine = createRecommendationEngine(dtg, storage, {
  criteria: {
    actionWeight: 0.5,    // Most important
    entityTypeWeight: 0.3, // Type of resource matters
    resourceWeight: 0.1,   // Specific path less important
    attributeWeight: 0.1,  // Attributes less important
  },
});

// For data operations, weight resource path heavily
const dataEngine = createRecommendationEngine(dtg, storage, {
  criteria: {
    actionWeight: 0.2,
    entityTypeWeight: 0.1,
    resourceWeight: 0.5,   // Path is critical
    attributeWeight: 0.2,
  },
});

Feedback Integration

class RecommendationService {
  private engine: RecommendationEngine;

  constructor(dtg: DecisionTraceGraph, storage: StorageInterface) {
    this.engine = createRecommendationEngine(dtg, storage);
  }

  async getRecommendation(context: DecisionContext) {
    return this.engine.recommend(context);
  }

  async recordDecisionOutcome(
    recommendationId: string,
    actualDecision: 'approved' | 'rejected',
    wasSuccessful: boolean
  ) {
    const recommendation = this.engine.explainRecommendation(recommendationId);

    if (!recommendation.ok) {
      return;
    }

    // Determine if recommendation was followed
    const wasFollowed =
      (actualDecision === 'approved' && recommendation.value.includes('approve')) ||
      (actualDecision === 'rejected' && recommendation.value.includes('reject'));

    this.engine.submitFeedback({
      recommendationId,
      followed: wasFollowed,
      actualDecision,
      outcome: wasSuccessful ? 'success' : 'failure',
      submittedAt: Date.now(),
    });
  }

  getAccuracyReport() {
    const stats = this.engine.getStats();

    return {
      accuracy: `${(stats.accuracy * 100).toFixed(1)}%`,
      totalRecommendations: stats.totalRecommendations,
      followRate: `${((stats.recommendationsFollowed / stats.totalRecommendations) * 100).toFixed(1)}%`,
      distribution: stats.decisionDistribution,
    };
  }
}

Best Practices

1. Populate Decision History

Recommendations require historical data. Ensure you're recording decisions:

// Record decisions for recommendation training
await dtg.recordDecision({
  type: 'workflow_step',
  title: 'API Request: DELETE /users/123',
  proposedBy: agentId,
  riskLevel: 'medium',
});

// Track outcome
await dtg.executeDecision(decisionId, { success: true });

2. Tune Weights for Your Domain

Different domains need different weight configurations:

// Financial domain - conservative, action-focused
{ actionWeight: 0.5, entityTypeWeight: 0.2, resourceWeight: 0.2, attributeWeight: 0.1 }

// Content management - resource-focused
{ actionWeight: 0.2, entityTypeWeight: 0.2, resourceWeight: 0.4, attributeWeight: 0.2 }

// User management - attribute-focused
{ actionWeight: 0.3, entityTypeWeight: 0.2, resourceWeight: 0.1, attributeWeight: 0.4 }

3. Use Appropriate Confidence Thresholds

// High-stakes decisions
if (rec.confidence < 0.85) {
  // Require manual review
}

// Low-stakes decisions
if (rec.confidence >= 0.7 && rec.risk.level === 'low') {
  // Auto-approve
}

4. Monitor and Improve

// Regular accuracy checks
setInterval(() => {
  const stats = engine.getStats();

  if (stats.accuracy < 0.7) {
    console.warn('Recommendation accuracy dropping');
    // Consider retraining or adjusting criteria
  }
}, 24 * 60 * 60 * 1000); // Daily

5. Handle Edge Cases

async function getRecommendationSafely(context: DecisionContext) {
  const result = await engine.recommend(context);

  if (!result.ok) {
    // Fallback to conservative default
    return {
      recommendedAction: 'defer' as const,
      confidence: 0,
      reasoning: 'Unable to generate recommendation',
      risk: { level: 'medium' as const, score: 0.5, factors: [] },
    };
  }

  // Never auto-approve with no precedents
  if (result.value.precedents.length === 0) {
    return {
      ...result.value,
      recommendedAction: 'defer' as const,
      reasoning: 'No historical precedents available',
    };
  }

  return result.value;
}

6. Attribute Standardization

Ensure consistent attribute naming for better matching:

// Good - consistent naming
{ action: 'create', attributes: { userRole: 'admin', department: 'engineering' } }
{ action: 'create', attributes: { userRole: 'user', department: 'marketing' } }

// Bad - inconsistent naming
{ action: 'create', attributes: { role: 'admin', dept: 'engineering' } }
{ action: 'create', attributes: { userRole: 'user', department: 'marketing' } }

@contextgraph/telemetry

OpenTelemetry-compatible tracing, metrics, and structured logging.

Installation

pnpm add @contextgraph/telemetry

Overview

Observability features:

  • Distributed tracing
  • Metrics collection
  • Structured logging
  • Multiple exporters

Tracing

import { Tracer, Span } from '@contextgraph/telemetry';

const tracer = new Tracer('contextgraph-service');

const span = tracer.startSpan('process-request');
span.setAttribute('request.id', requestId);

try {
  await processRequest();
  span.setStatus({ code: 'OK' });
} catch (error) {
  span.setStatus({ code: 'ERROR', message: error.message });
  span.recordException(error);
} finally {
  span.end();
}

Nested Spans

const parentSpan = tracer.startSpan('workflow');

const childSpan = tracer.startSpan('step-1', { parent: parentSpan });
await doStep1();
childSpan.end();

const childSpan2 = tracer.startSpan('step-2', { parent: parentSpan });
await doStep2();
childSpan2.end();

parentSpan.end();

Metrics

import { Meter, Counter, Histogram, Gauge } from '@contextgraph/telemetry';

const meter = new Meter('contextgraph');

// Counter
const requestCounter = meter.createCounter('requests_total');
requestCounter.add(1, { method: 'GET', path: '/api/entities' });

// Histogram
const latencyHistogram = meter.createHistogram('request_duration_ms');
latencyHistogram.record(45, { method: 'GET' });

// Gauge
const activeConnections = meter.createGauge('active_connections');
activeConnections.set(42);

Pre-defined Metrics

MetricTypeDescription
cg.entities.totalGaugeTotal entities
cg.claims.totalGaugeTotal claims
cg.decisions.pendingGaugePending decisions
cg.executions.totalCounterTotal executions
cg.policy.evaluationsCounterPolicy evaluations

Logging

import { Logger } from '@contextgraph/telemetry';

const logger = new Logger('my-service');

logger.info('Request processed', {
  requestId: '123',
  duration: 45,
});

logger.warn('Rate limit approaching', {
  current: 95,
  limit: 100,
});

logger.error('Failed to process', {
  error: error.message,
  stack: error.stack,
});

Log Levels

LevelUse Case
debugDetailed debugging
infoGeneral information
warnWarning conditions
errorError conditions

Span Correlation

// Logs automatically include span context
logger.info('Processing', { spanId: span.context.spanId });

Exporters

Console Exporter

import { ConsoleExporter } from '@contextgraph/telemetry';

const exporter = new ConsoleExporter();
tracer.addExporter(exporter);

Memory Exporter

import { MemoryExporter } from '@contextgraph/telemetry';

const exporter = new MemoryExporter();
tracer.addExporter(exporter);

// Get collected spans
const spans = exporter.getSpans();

OTLP Exporter

import { OTLPExporter } from '@contextgraph/telemetry';

const exporter = new OTLPExporter({
  endpoint: 'http://collector:4318',
  headers: { 'Authorization': 'Bearer token' },
});

Multi Exporter

import { MultiExporter } from '@contextgraph/telemetry';

const multiExporter = new MultiExporter([
  new ConsoleExporter(),
  new OTLPExporter({ endpoint: '...' }),
]);

@contextgraph/sdk

The unified high-level SDK providing a single entry point for all ContextGraph OS operations.

Installation

pnpm add @contextgraph/sdk

Quick Start

import { ContextGraph } from '@contextgraph/sdk';

// Create a client
const result = await ContextGraph.create();
if (!result.ok) throw result.error;
const client = result.value;

// Create entities, add claims, execute actions...

Client Creation

Default (In-Memory)

const client = await ContextGraph.create();

With SQLite

const client = await ContextGraph.create({
  storage: {
    type: 'sqlite',
    path: './contextgraph.db'
  }
});

With Custom Config

const client = await ContextGraph.create({
  storage: { type: 'memory' },
  provenance: { verifyOnRead: true },
  policy: { enforceOnExecute: true },
});

Entity Operations

Create Entity

const entity = await client.createEntity({
  type: 'person',
  name: 'Alice',
  properties: {
    department: 'Engineering',
    level: 'senior',
  },
});

Get Entity

const entity = await client.getEntity(entityId);

Update Entity

await client.updateEntity(entityId, {
  properties: {
    level: 'principal',
  },
});

List Entities

const entities = await client.listEntities({
  type: 'person',
  limit: 50,
});

Claim Operations

Add Claim

import { createScope, createConfidence } from '@contextgraph/sdk';

await client.addClaim({
  subjectId: entityId,
  predicate: 'has_skill',
  value: 'TypeScript',
  context: {
    scope: createScope('professional'),
    confidence: createConfidence(0.95),
  },
});

Get Claims

const claims = await client.getClaims(entityId);

// With filters
const filteredClaims = await client.getClaims(entityId, {
  predicate: 'has_skill',
  asOf: timestamp,
  minConfidence: 0.8,
});

Revoke Claim

await client.revokeClaim(claimId, 'Superseded by new information');

Agent Operations

Create Agent

const agent = await client.createAgent({
  name: 'data-processor',
  description: 'Processes incoming data files',
  capabilities: ['read', 'transform', 'write'],
});

Get Agent

const agent = await client.getAgent(agentId);
// Or by name
const agent = await client.getAgentByName('data-processor');

List Agents

const agents = await client.listAgents({ status: 'active' });

Action Execution

Register Handler

client.registerHandler('transform', 'csv', async (action, context) => {
  // Process the action
  const data = await readCSV(action.resourceId);
  const transformed = transformData(data, action.parameters);
  return ok({ transformed: true, rowCount: data.length });
});

Execute Action

const result = await client.execute({
  agentId: agent.data.id,
  action: 'transform',
  resourceType: 'csv',
  resourceId: 'data/input.csv',
  parameters: {
    outputFormat: 'json',
  },
});

Decision Management

Record Decision

const decision = await client.recordDecision({
  type: 'deployment',
  title: 'Deploy v2.0.0 to production',
  proposedBy: agentId,
  riskLevel: 'high',
});

Approve/Reject Decision

await client.approveDecision(decisionId, approverId, 'Approved after review');
await client.rejectDecision(decisionId, reviewerId, 'Needs more testing');

Get Pending Decisions

const pending = await client.getPendingDecisions();

Policy Management

Create Policy

await client.createPolicy({
  name: 'Restrict PII Access',
  version: '1.0.0',
  effect: 'deny',
  subjects: ['*'],
  actions: ['read', 'export'],
  resources: ['pii/*'],
  priority: 100,
});

List Policies

const policies = await client.listPolicies({ effect: 'deny' });

Provenance

Verify Chain

const verification = await client.verifyProvenance();

console.log(`Valid: ${verification.value.valid}`);
console.log(`Entries: ${verification.value.entriesVerified}`);

Query Provenance

const entries = await client.queryProvenance({
  subjectId: entityId,
  limit: 100,
});

Context Assembly

const context = await client.assembleContext(entityId, {
  asOf: timestamp,
  jurisdiction: 'EU',
  scope: 'work',
  minConfidence: 0.8,
  depth: 2,
  includeProvenance: true,
});

Audit Trail

const audit = await client.getAuditTrail({
  entityId: entityId,
  limit: 100,
  from: startTime,
  to: endTime,
});

Statistics

const stats = await client.getStats();

console.log(`Entities: ${stats.value.entities}`);
console.log(`Claims: ${stats.value.claims}`);
console.log(`Agents: ${stats.value.agents}`);
console.log(`Decisions: ${stats.value.decisions}`);

Import/Export

Export

// Export to JSON
const jsonData = await client.exportToJSON();
const jsonString = await client.exportToJSONString({ prettyPrint: true });

// Export to CSV
const entitiesCSV = await client.exportEntitiesToCSV();
const claimsCSV = await client.exportClaimsToCSV();

Import

// Import from JSON
await client.importFromJSON(data, {
  dryRun: true,
  merge: true,
  onConflict: 'skip',
});

// Import from CSV
await client.importEntitiesFromCSV(csvString);
await client.importClaimsFromCSV(csvString);

Events

// Subscribe to events
client.on('entity:created', (event) => {
  console.log('Entity created:', event.data);
});

client.on('claim:added', (event) => {
  console.log('Claim added:', event.data);
});

client.on('decision:approved', (event) => {
  console.log('Decision approved:', event.data);
});

// Unsubscribe
client.off('entity:created', handler);

Event Types

EventDescription
entity:createdNew entity created
entity:updatedEntity updated
claim:addedClaim added
claim:revokedClaim revoked
decision:proposedDecision recorded
decision:approvedDecision approved
decision:rejectedDecision rejected
execution:startedAction execution started
execution:completedAction execution completed
policy:createdPolicy created

Helper Functions

import {
  createScope,
  createJurisdiction,
  createConfidence,
  createTimestamp,
  ok,
  err,
} from '@contextgraph/sdk';

Type Exports

export type {
  EntityId,
  ClaimId,
  AgentId,
  DecisionId,
  PolicyId,
  Entity,
  Claim,
  Agent,
  Decision,
  Policy,
  Result,
};

@contextgraph/api

REST API server with authentication and rate limiting.

Installation

pnpm add @contextgraph/api

Starting the Server

# Default configuration
npx contextgraph-api

# Custom port and API key
PORT=8080 API_KEY=your-secret npx contextgraph-api

API Endpoints

Health

GET /api/v1/health

Statistics

GET /api/v1/stats

Entities

GET    /api/v1/entities           # List entities
POST   /api/v1/entities           # Create entity
GET    /api/v1/entities/:id       # Get entity
PUT    /api/v1/entities/:id       # Update entity
DELETE /api/v1/entities/:id       # Delete entity
GET    /api/v1/entities/:id/claims # Get entity claims
POST   /api/v1/entities/:id/claims # Add claim

Agents

GET    /api/v1/agents             # List agents
POST   /api/v1/agents             # Create agent
GET    /api/v1/agents/:id         # Get agent
POST   /api/v1/agents/:id/execute # Execute action

Decisions

GET    /api/v1/decisions           # List decisions
POST   /api/v1/decisions           # Record decision
GET    /api/v1/decisions/:id       # Get decision
POST   /api/v1/decisions/:id/approve # Approve
POST   /api/v1/decisions/:id/reject  # Reject

Policies

GET    /api/v1/policies           # List policies
POST   /api/v1/policies           # Create policy

Provenance

GET    /api/v1/provenance         # Query provenance
POST   /api/v1/provenance/verify  # Verify chain

Audit

GET    /api/v1/audit              # Query audit trail

Authentication

Set API_KEY environment variable:

API_KEY=your-secret-key npx contextgraph-api

Include in requests:

curl -H "X-API-Key: your-secret-key" http://localhost:3000/api/v1/entities

Example Requests

Create Entity

curl -X POST http://localhost:3000/api/v1/entities \
  -H "Content-Type: application/json" \
  -H "X-API-Key: your-key" \
  -d '{
    "type": "person",
    "name": "Alice",
    "properties": {"department": "Engineering"}
  }'

Add Claim

curl -X POST http://localhost:3000/api/v1/entities/ent_xxx/claims \
  -H "Content-Type: application/json" \
  -d '{"predicate": "has_skill", "value": "TypeScript"}'

Verify Provenance

curl -X POST http://localhost:3000/api/v1/provenance/verify

Rate Limiting

Default: 100 requests per minute per IP.

Configure via environment:

RATE_LIMIT=200 RATE_WINDOW=60000 npx contextgraph-api

CORS

Enable CORS for web clients:

CORS_ORIGIN=https://myapp.com npx contextgraph-api

@contextgraph/cli

CLI tools and interactive REPL.

Installation

pnpm add -g @contextgraph/cli

Commands

stats

Show system statistics:

npx contextgraph stats

entities

List entities:

npx contextgraph entities [type] --limit 10

entity

Inspect an entity:

npx contextgraph entity <id> --with-claims

agents

List agents:

npx contextgraph agents

agent

Inspect an agent:

npx contextgraph agent <id|name>

decisions

List pending decisions:

npx contextgraph decisions

policies

List policies:

npx contextgraph policies

audit

Show audit trail:

npx contextgraph audit --json

provenance

Query provenance:

npx contextgraph provenance --subject <id>

verify

Verify provenance chain:

npx contextgraph verify

context

Assemble context:

npx contextgraph context <entity-id>

export

Export data:

npx contextgraph export --format json --output backup.json
npx contextgraph export --format csv --type entities --output entities.csv

import

Import data:

npx contextgraph import backup.json
npx contextgraph import entities.csv --format csv --type entities --dry-run

repl

Start interactive REPL:

npx contextgraph repl

REPL

The interactive REPL provides a shell for exploration:

ContextGraph OS REPL v0.1.0
Type 'help' for available commands, 'exit' to quit.

contextgraph> stats
╔═══════════════════════════════════════╗
║         System Statistics             ║
╠═══════════════════════════════════════╣
║  Entities:    42                      ║
║  Claims:      156                     ║
║  Agents:      3                       ║
╚═══════════════════════════════════════╝

contextgraph> entities person
┌──────────────────┬─────────┬──────────────┐
│ ID               │ Type    │ Name         │
├──────────────────┼─────────┼──────────────┤
│ ent_abc123...    │ person  │ Alice        │
│ ent_def456...    │ person  │ Bob          │
└──────────────────┴─────────┴──────────────┘

contextgraph> json
JSON output mode: ON

contextgraph> exit
Goodbye!

Global Options

OptionDescription
--jsonOutput as JSON
--helpShow help
--versionShow version

@contextgraph/webhooks

Webhook management with delivery queue and retry logic.

Installation

pnpm add @contextgraph/webhooks

Overview

Send HTTP callbacks for system events:

  • Webhook registration
  • Event filtering
  • Delivery queue
  • Retry with backoff

Registering Webhooks

import { WebhookManager } from '@contextgraph/webhooks';

const webhooks = new WebhookManager(storage);
await webhooks.initialize();

await webhooks.register({
  url: 'https://example.com/webhook',
  events: ['entity:created', 'decision:approved'],
  secret: 'webhook-secret',
  headers: {
    'X-Custom-Header': 'value',
  },
});

Supported Events

EventDescription
entity:createdNew entity created
entity:updatedEntity updated
claim:addedClaim added
claim:revokedClaim revoked
decision:proposedDecision recorded
decision:approvedDecision approved
decision:rejectedDecision rejected
execution:completedAction executed
policy:createdPolicy created

Webhook Payload

{
  "event": "entity:created",
  "timestamp": "2024-01-15T10:30:00.000Z",
  "data": {
    "id": "ent_abc123",
    "type": "person",
    "name": "Alice"
  }
}

Signature Verification

Webhooks include HMAC signature:

X-Webhook-Signature: sha256=abc123...

Verify in your handler:

import crypto from 'crypto';

function verifySignature(payload: string, signature: string, secret: string) {
  const expected = crypto
    .createHmac('sha256', secret)
    .update(payload)
    .digest('hex');
  return `sha256=${expected}` === signature;
}

Retry Policy

Failed deliveries are retried:

AttemptDelay
1Immediate
21 minute
35 minutes
430 minutes
52 hours

Managing Webhooks

// List webhooks
const hooks = await webhooks.list();

// Disable webhook
await webhooks.disable(webhookId);

// Enable webhook
await webhooks.enable(webhookId);

// Delete webhook
await webhooks.delete(webhookId);

// Get delivery history
const history = await webhooks.getDeliveries(webhookId, { limit: 50 });

Building an Auditable Agent

This tutorial walks you through building a fully auditable AI agent using ContextGraph OS.

What You'll Build

An agent that:

  • Processes data with full audit trails
  • Records decisions for review
  • Respects policies
  • Has verifiable provenance

Prerequisites

pnpm add @contextgraph/sdk

Step 1: Create the Client

import { ContextGraph, ok } from '@contextgraph/sdk';

async function main() {
  const result = await ContextGraph.create();
  if (!result.ok) throw result.error;
  const client = result.value;

Step 2: Create an Agent

  const agentResult = await client.createAgent({
    name: 'data-processor',
    description: 'Processes incoming data files with full audit trail',
    capabilities: ['read', 'transform', 'write'],
  });

  if (!agentResult.ok) throw agentResult.error;
  const agent = agentResult.value;

  console.log(`Created agent: ${agent.data.name} (${agent.data.id})`);

Step 3: Register Handlers

  // Read handler
  client.registerHandler('read', 'csv', async (action) => {
    console.log(`[Handler] Reading: ${action.resourceId}`);
    // Simulate reading data
    const data = [
      { name: 'Alice', department: 'Engineering' },
      { name: 'Bob', department: 'Product' },
    ];
    return ok({ data, rowCount: data.length });
  });

  // Transform handler
  client.registerHandler('transform', 'data', async (action) => {
    console.log(`[Handler] Transforming data`);
    const input = action.parameters?.data as any[];
    const transformed = input.map(row => ({
      ...row,
      processedAt: new Date().toISOString(),
    }));
    return ok({ data: transformed, rowCount: transformed.length });
  });

  // Write handler
  client.registerHandler('write', 'json', async (action) => {
    console.log(`[Handler] Writing to: ${action.resourceId}`);
    // Simulate writing
    return ok({ written: true, path: action.resourceId });
  });

Step 4: Record a Decision

Before processing, record the decision:

  const decisionResult = await client.recordDecision({
    type: 'data_processing',
    title: 'Process employee data batch',
    description: 'Transform CSV to JSON with timestamp enrichment',
    proposedBy: agent.data.id,
    riskLevel: 'low',
  });

  if (!decisionResult.ok) throw decisionResult.error;
  const decision = decisionResult.value;

  console.log(`Decision recorded: ${decision.data.title}`);

Step 5: Execute Workflow

  // Step 1: Read
  const readResult = await client.execute({
    agentId: agent.data.id,
    action: 'read',
    resourceType: 'csv',
    resourceId: 'data/employees.csv',
  });

  if (!readResult.ok) {
    console.error('Read failed:', readResult.error);
    return;
  }

  console.log(`Read ${readResult.value.output.rowCount} rows`);

  // Step 2: Transform
  const transformResult = await client.execute({
    agentId: agent.data.id,
    action: 'transform',
    resourceType: 'data',
    resourceId: 'memory',
    parameters: { data: readResult.value.output.data },
  });

  if (!transformResult.ok) {
    console.error('Transform failed:', transformResult.error);
    return;
  }

  console.log(`Transformed ${transformResult.value.output.rowCount} rows`);

  // Step 3: Write
  const writeResult = await client.execute({
    agentId: agent.data.id,
    action: 'write',
    resourceType: 'json',
    resourceId: 'output/employees.json',
    parameters: { data: transformResult.value.output.data },
  });

  if (!writeResult.ok) {
    console.error('Write failed:', writeResult.error);
    return;
  }

  console.log(`Wrote output`);

Step 6: Complete the Decision

  await client.approveDecision(
    decision.data.id,
    agent.data.id,
    'Workflow completed successfully'
  );

  console.log('Decision completed');

Step 7: Verify Audit Trail

  // Verify provenance
  const verification = await client.verifyProvenance();
  console.log(`Provenance chain valid: ${verification.value.valid}`);

  // Get audit trail
  const audit = await client.getAuditTrail({ limit: 10 });
  console.log('\nAudit Trail:');
  for (const entry of audit.value) {
    console.log(`  ${entry.timestamp}: ${entry.action} ${entry.resourceType}`);
  }

  // Get statistics
  const stats = await client.getStats();
  console.log('\nStatistics:');
  console.log(`  Agents: ${stats.value.agents}`);
  console.log(`  Decisions: ${stats.value.decisions}`);
  console.log(`  Provenance entries: ${stats.value.provenanceEntries}`);
}

main().catch(console.error);

Full Code

See the complete example at packages/demos/src/agent-workflow.ts.

Key Takeaways

  1. Record decisions before acting - Creates audit trail
  2. Use handlers for actions - Standardized execution
  3. Verify provenance - Ensure data integrity
  4. Check audit trail - Review what happened

Next Steps

Policy-Based Access Control

Learn to implement fine-grained access control using ContextGraph policies.

What You'll Learn

  • Creating allow and deny policies
  • Using conditions
  • Policy evaluation order
  • Handling policy exceptions

Setup

import { ContextGraph } from '@contextgraph/sdk';

const result = await ContextGraph.create();
const client = result.value;

Creating Basic Policies

Allow Policy

await client.createPolicy({
  name: 'Read Access for Analysts',
  version: '1.0.0',
  description: 'Allow analysts to read reports',
  effect: 'allow',
  subjects: ['role:analyst'],
  actions: ['read'],
  resources: ['reports/*'],
  priority: 50,
});

Deny Policy

await client.createPolicy({
  name: 'Block Confidential Reports',
  version: '1.0.0',
  description: 'Deny access to confidential reports',
  effect: 'deny',
  subjects: ['*'],
  actions: ['read', 'export'],
  resources: ['reports/confidential/*'],
  priority: 100, // Higher priority = evaluated first
});

Using Conditions

Time-Based Access

await client.createPolicy({
  name: 'Business Hours Only',
  version: '1.0.0',
  effect: 'allow',
  subjects: ['role:contractor'],
  actions: ['*'],
  resources: ['*'],
  conditions: [
    {
      field: 'time.hour',
      operator: 'between',
      value: [9, 17],
    },
    {
      field: 'time.dayOfWeek',
      operator: 'in',
      value: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri'],
    },
  ],
  priority: 40,
});

Risk-Based Access

await client.createPolicy({
  name: 'High Risk Requires Approval',
  version: '1.0.0',
  effect: 'deny',
  subjects: ['*'],
  actions: ['execute'],
  resources: ['*'],
  conditions: [
    {
      field: 'risk.level',
      operator: 'in',
      value: ['high', 'critical'],
    },
    {
      field: 'approval.status',
      operator: 'not_equals',
      value: 'approved',
    },
  ],
  priority: 90,
});

Testing Policies

Use the policy simulator:

import { PolicySimulator } from '@contextgraph/policy';

const simulator = new PolicySimulator(policyLedger, storage);

const result = await simulator.simulate({
  subject: 'agent:data-processor',
  action: 'read',
  resource: 'reports/confidential/q4-2024',
  context: {
    time: new Date('2024-01-15T14:00:00Z'),
  },
});

console.log(`Effect: ${result.effect}`); // 'deny'
console.log(`Matched ${result.matchedPolicies.length} policies`);

Priority System

Priority 100: Security policies (deny dangerous actions)
Priority 90:  Risk policies (high-risk requires approval)
Priority 50:  Business rules (role-based access)
Priority 10:  Default policies (fallback rules)

When policies conflict at the same priority, deny wins.

Handling Exceptions

import { ExceptionManager } from '@contextgraph/exceptions';

const exceptions = new ExceptionManager(dtg, policyLedger, storage);

// Request exception
const request = await exceptions.request({
  policyId: policyId,
  reason: 'Audit requires temporary access',
  requestedBy: agentId,
  duration: { hours: 4 },
  scope: {
    action: 'read',
    resources: ['reports/confidential/audit-2024'],
  },
});

// Approve exception
await exceptions.approve(request.id, approverId, 'Approved for audit');

Complete Example

async function setupAccessControl(client: ContextGraph) {
  // 1. Base read access
  await client.createPolicy({
    name: 'Base Read Access',
    version: '1.0.0',
    effect: 'allow',
    subjects: ['role:employee'],
    actions: ['read'],
    resources: ['public/*', 'team/*'],
    priority: 30,
  });

  // 2. Analyst access
  await client.createPolicy({
    name: 'Analyst Access',
    version: '1.0.0',
    effect: 'allow',
    subjects: ['role:analyst'],
    actions: ['read', 'export'],
    resources: ['reports/*', 'data/*'],
    priority: 50,
  });

  // 3. Block PII
  await client.createPolicy({
    name: 'Block PII',
    version: '1.0.0',
    effect: 'deny',
    subjects: ['*'],
    actions: ['*'],
    resources: ['pii/*', '*/personal/*'],
    conditions: [
      {
        field: 'agent.clearance',
        operator: 'not_equals',
        value: 'pii-authorized',
      },
    ],
    priority: 100,
  });

  // 4. Admin override
  await client.createPolicy({
    name: 'Admin Full Access',
    version: '1.0.0',
    effect: 'allow',
    subjects: ['role:admin'],
    actions: ['*'],
    resources: ['*'],
    priority: 110, // Higher than deny
  });
}

Best Practices

  1. Start with deny-all - Add explicit allows
  2. Use high priority for security - Security policies first
  3. Document conditions - Clear descriptions
  4. Test with simulator - Before deploying
  5. Monitor evaluations - Track denied requests

Next Steps

Visualizing Decision Trees

Learn to visualize your decision history and provenance chains.

What You'll Learn

  • Generate decision tree diagrams
  • Create provenance visualizations
  • Export to different formats
  • Embed in documentation

Setup

import { ContextGraph } from '@contextgraph/sdk';
import {
  visualizeDecisions,
  visualizeProvenance,
  visualizeEntities,
} from '@contextgraph/viz';

const client = await ContextGraph.create().then(r => r.value);

Visualizing Decisions

Mermaid Diagram

const dtg = client.getDecisionTraceGraph();

const result = await visualizeDecisions(dtg, {
  format: 'mermaid',
  showTimestamps: true,
  colorScheme: 'default',
});

console.log(result.value);

Output:

graph TB
  dec_001["Deploy v2.0.0<br/>2024-01-15"]
  dec_002["Rollback v2.0.0<br/>2024-01-16"]
  dec_001 -->|triggered| dec_002

  classDef approved fill:#90EE90
  classDef rejected fill:#FFB6C1
  class dec_001 approved
  class dec_002 approved

DOT Format

const dotResult = await visualizeDecisions(dtg, {
  format: 'dot',
  showTimestamps: true,
});

// Save to file and render with Graphviz
fs.writeFileSync('decisions.dot', dotResult.value);
// dot -Tpng decisions.dot -o decisions.png

SVG (Standalone)

const svgResult = await visualizeDecisions(dtg, {
  format: 'svg',
  colorScheme: 'default',
});

fs.writeFileSync('decisions.svg', svgResult.value);

Visualizing Provenance

Chain Diagram

const ledger = client.getProvenanceLedger();

const result = await visualizeProvenance(ledger, {
  format: 'mermaid',
  showTimestamps: true,
  maxDepth: 20,
});

console.log(result.value);

Output:

graph TB
  prov_001["claim_created<br/>2024-01-15 10:30"]
  prov_002["entity_updated<br/>2024-01-15 10:31"]
  prov_003["decision_recorded<br/>2024-01-15 10:32"]

  prov_001 -->|chain| prov_002
  prov_002 -->|chain| prov_003

  classDef claim fill:#4a90d9
  classDef entity fill:#50c878
  classDef decision fill:#ffd700

Visualizing Entities

Entity Relationship Graph

const ckg = client.getCKG();

const result = await visualizeEntities(ckg, 'person', {
  format: 'd3',
  showLabels: true,
});

// Use with D3.js force-directed graph
const data = JSON.parse(result.value);

Embedding in Documentation

GitHub README

## Decision History

```mermaid
graph TB
  A[Create User] -->|approved| B[Add Permissions]
  B -->|approved| C[Enable Access]

### HTML Reports

```typescript
async function generateReport() {
  const decisions = await visualizeDecisions(dtg, { format: 'svg' });
  const provenance = await visualizeProvenance(ledger, { format: 'svg' });

  return `
<!DOCTYPE html>
<html>
<head><title>Audit Report</title></head>
<body>
  <h1>Decision History</h1>
  ${decisions.value}

  <h1>Provenance Chain</h1>
  ${provenance.value}
</body>
</html>
  `;
}

Interactive Dashboard

React Integration

import { visualizeGraph } from '@contextgraph/viz';
import ForceGraph2D from 'react-force-graph-2d';

function DecisionGraph({ dtg }) {
  const [data, setData] = useState(null);

  useEffect(() => {
    async function loadGraph() {
      const graphData = await buildDecisionGraph(dtg);
      const result = visualizeGraph(graphData, { format: 'd3' });
      setData(JSON.parse(result.value));
    }
    loadGraph();
  }, [dtg]);

  if (!data) return <div>Loading...</div>;

  return (
    <ForceGraph2D
      graphData={data}
      nodeLabel="label"
      linkLabel="label"
      nodeColor={node => getNodeColor(node.type)}
    />
  );
}

Color Schemes

SchemeUse Case
defaultStandard colors
darkDark backgrounds
lightLight/pastel
colorblindAccessible
await visualizeDecisions(dtg, {
  format: 'mermaid',
  colorScheme: 'colorblind',
});

Next Steps

Temporal Queries

Master point-in-time queries and temporal data management.

What You'll Learn

  • Query data at any point in time
  • Handle temporal validity periods
  • Track changes over time
  • Build temporal reports

Setup

import {
  ContextGraph,
  createTimestamp,
  createScope,
  createConfidence,
} from '@contextgraph/sdk';

const client = await ContextGraph.create().then(r => r.value);

Creating Temporal Data

Claims with Validity Periods

// Current position
await client.addClaim({
  subjectId: aliceId,
  predicate: 'position',
  value: 'Senior Engineer',
  context: {
    validFrom: createTimestamp('2024-01-01'),
    validUntil: null, // Still valid
  },
});

// Previous position
await client.addClaim({
  subjectId: aliceId,
  predicate: 'position',
  value: 'Engineer',
  context: {
    validFrom: createTimestamp('2022-01-01'),
    validUntil: createTimestamp('2023-12-31'),
  },
});

Point-in-Time Queries

Query Current State

const currentClaims = await client.getClaims(aliceId);
// Returns: 'Senior Engineer'

Query Historical State

const march2023Claims = await client.getClaims(aliceId, {
  asOf: createTimestamp('2023-03-15'),
  predicate: 'position',
});
// Returns: 'Engineer'

Query Future State

// Scheduled change
await client.addClaim({
  subjectId: aliceId,
  predicate: 'position',
  value: 'Principal Engineer',
  context: {
    validFrom: createTimestamp('2025-01-01'),
    validUntil: null,
  },
});

const futureState = await client.getClaims(aliceId, {
  asOf: createTimestamp('2025-06-01'),
  predicate: 'position',
});
// Returns: 'Principal Engineer'

Time Range Queries

Claims Valid During Period

const q2_2024_claims = await ckg.query({
  subjectId: aliceId,
  validDuring: {
    from: createTimestamp('2024-04-01'),
    to: createTimestamp('2024-06-30'),
  },
});

All Historical Values

const allPositions = await ckg.query({
  subjectId: aliceId,
  predicate: 'position',
  includeExpired: true,
});

for (const claim of allPositions) {
  console.log(`${claim.data.value}: ${claim.data.validFrom} - ${claim.data.validUntil}`);
}
// Output:
// Engineer: 2022-01-01 - 2023-12-31
// Senior Engineer: 2024-01-01 - null
// Principal Engineer: 2025-01-01 - null

Tracking Changes

Change History

async function getChangeHistory(entityId: EntityId, predicate: string) {
  const claims = await ckg.query({
    subjectId: entityId,
    predicate,
    includeExpired: true,
    sortBy: 'validFrom',
  });

  return claims.map(c => ({
    value: c.data.value,
    from: c.data.validFrom,
    to: c.data.validUntil,
    status: c.data.status,
  }));
}

const history = await getChangeHistory(aliceId, 'position');

When Did Value Change?

async function whenDidValueChange(
  entityId: EntityId,
  predicate: string,
  targetValue: unknown
) {
  const claims = await ckg.query({
    subjectId: entityId,
    predicate,
    includeExpired: true,
  });

  const match = claims.find(c => c.data.value === targetValue);
  return match?.data.validFrom;
}

const promotionDate = await whenDidValueChange(
  aliceId,
  'position',
  'Senior Engineer'
);

Temporal Reports

Snapshot Report

async function generateSnapshotReport(asOf: Timestamp) {
  const entities = await ckg.findEntitiesByType('employee', { limit: 1000 });

  const report = [];
  for (const entity of entities.value) {
    const claims = await client.getClaims(entity.data.id, { asOf });
    report.push({
      id: entity.data.id,
      name: entity.data.name,
      claims: claims.value.map(c => ({
        predicate: c.data.predicate,
        value: c.data.value,
      })),
    });
  }

  return report;
}

// Snapshot from last year
const lastYearReport = await generateSnapshotReport(
  createTimestamp('2023-12-31')
);

Change Report

async function generateChangeReport(from: Timestamp, to: Timestamp) {
  const provenance = await ledger.query({
    type: ['claim_created', 'claim_revoked'],
    from,
    to,
  });

  return provenance.value.map(entry => ({
    type: entry.data.type,
    timestamp: entry.data.timestamp,
    subject: entry.data.subjectId,
    data: entry.data.data,
  }));
}

Best Practices

  1. Always specify validity - Don't rely on defaults
  2. Use appropriate granularity - Day vs. millisecond precision
  3. Handle null validUntil - Means "currently valid"
  4. Index temporal fields - For query performance
  5. Audit temporal changes - Track who changed what when

Next Steps

Type Definitions

Core TypeScript types used throughout ContextGraph OS.

Branded Types

// Unique identifiers with compile-time safety
type EntityId = string & { readonly __brand: 'EntityId' };
type ClaimId = string & { readonly __brand: 'ClaimId' };
type AgentId = string & { readonly __brand: 'AgentId' };
type DecisionId = string & { readonly __brand: 'DecisionId' };
type PolicyId = string & { readonly __brand: 'PolicyId' };
type ProvenanceId = string & { readonly __brand: 'ProvenanceId' };
type Timestamp = string & { readonly __brand: 'Timestamp' };
type Scope = string & { readonly __brand: 'Scope' };
type Jurisdiction = string & { readonly __brand: 'Jurisdiction' };
type Confidence = number & { readonly __brand: 'Confidence' };

Result Type

type Result<T, E = Error> =
  | { ok: true; value: T }
  | { ok: false; error: E };

Entity Types

interface Entity {
  id: EntityId;
  type: string;
  name: string;
  properties: Record<string, unknown>;
  createdAt: Timestamp;
  updatedAt: Timestamp;
}

interface CreateEntityInput {
  type: string;
  name: string;
  properties?: Record<string, unknown>;
}

Claim Types

interface Claim {
  id: ClaimId;
  subjectId: EntityId;
  predicate: string;
  objectValue?: unknown;
  objectId?: EntityId;
  context: ClaimContext;
  status: 'active' | 'revoked';
  createdAt: Timestamp;
  revokedAt?: Timestamp;
  revokedReason?: string;
}

interface ClaimContext {
  scope?: Scope;
  jurisdiction?: Jurisdiction;
  confidence?: Confidence;
  validFrom?: Timestamp;
  validUntil?: Timestamp | null;
}

interface AddClaimInput {
  subjectId: EntityId;
  predicate: string;
  value?: unknown;
  objectId?: EntityId;
  context?: Partial<ClaimContext>;
}

Agent Types

interface Agent {
  id: AgentId;
  name: string;
  description?: string;
  status: AgentStatus;
  capabilities: string[];
  metadata?: Record<string, unknown>;
  parentId?: AgentId;
  createdAt: Timestamp;
  updatedAt: Timestamp;
}

type AgentStatus = 'active' | 'suspended' | 'revoked';

interface CreateAgentInput {
  name: string;
  description?: string;
  capabilities?: string[];
  metadata?: Record<string, unknown>;
}

Decision Types

interface Decision {
  id: DecisionId;
  type: string;
  title: string;
  description?: string;
  status: DecisionStatus;
  riskLevel: RiskLevel;
  proposedBy: AgentId;
  proposedAt: Timestamp;
  approvedBy?: AgentId;
  approvedAt?: Timestamp;
  rejectedBy?: AgentId;
  rejectedAt?: Timestamp;
  executedAt?: Timestamp;
  completedAt?: Timestamp;
  context?: Record<string, unknown>;
  outcome?: Record<string, unknown>;
}

type DecisionStatus =
  | 'proposed'
  | 'approved'
  | 'rejected'
  | 'executed'
  | 'completed'
  | 'failed';

type RiskLevel = 'low' | 'medium' | 'high' | 'critical';

Policy Types

interface Policy {
  id: PolicyId;
  name: string;
  version: string;
  description?: string;
  effect: 'allow' | 'deny';
  subjects: string[];
  actions: string[];
  resources: string[];
  conditions?: Condition[];
  priority: number;
  validFrom?: Timestamp;
  validUntil?: Timestamp;
}

interface Condition {
  field: string;
  operator: ConditionOperator;
  value: unknown;
}

type ConditionOperator =
  | 'equals'
  | 'not_equals'
  | 'greater_than'
  | 'less_than'
  | 'in'
  | 'not_in'
  | 'contains'
  | 'starts_with'
  | 'ends_with'
  | 'matches'
  | 'exists'
  | 'between';

Provenance Types

interface ProvenanceEntry {
  id: ProvenanceId;
  hash: string;
  previousHash: string | null;
  type: ProvenanceType;
  subjectId: string;
  data: unknown;
  source: ProvenanceSource;
  timestamp: Timestamp;
  agentId?: AgentId;
}

type ProvenanceType =
  | 'claim_created'
  | 'claim_revoked'
  | 'entity_created'
  | 'entity_updated'
  | 'decision_recorded'
  | 'decision_approved'
  | 'decision_rejected'
  | 'execution_logged'
  | 'policy_created';

interface ProvenanceSource {
  type: 'agent' | 'user' | 'system' | 'external' | 'inference';
  id: string;
  method?: string;
  metadata?: Record<string, unknown>;
}

Execution Types

interface ActionRequest {
  agentId: AgentId;
  action: string;
  resourceType: string;
  resourceId: string;
  parameters?: Record<string, unknown>;
}

interface ActionResult {
  executionId: string;
  status: 'allowed' | 'denied' | 'error';
  output?: unknown;
  error?: Error;
  duration: number;
  timestamp: Timestamp;
}

Query Types

interface QueryOptions {
  limit?: number;
  offset?: number;
  sortBy?: string;
  sortOrder?: 'asc' | 'desc';
}

interface ClaimQueryOptions extends QueryOptions {
  predicate?: string;
  asOf?: Timestamp;
  jurisdiction?: string;
  scope?: string;
  minConfidence?: number;
  activeOnly?: boolean;
  includeExpired?: boolean;
}

SDK API Reference

Complete API reference for the ContextGraph SDK.

ContextGraph Class

create()

static async create(config?: ContextGraphConfig): Promise<Result<ContextGraph>>

Creates a new ContextGraph client.

Parameters:

  • config - Optional configuration object

Returns: Result<ContextGraph>

Entity Methods

createEntity()

async createEntity(input: CreateEntityInput): Promise<Result<Entity>>

getEntity()

async getEntity(id: EntityId): Promise<Result<Entity | null>>

updateEntity()

async updateEntity(id: EntityId, updates: Partial<Entity>): Promise<Result<Entity>>

listEntities()

async listEntities(options?: ListEntitiesOptions): Promise<Result<Entity[]>>

Claim Methods

addClaim()

async addClaim(input: AddClaimInput): Promise<Result<Claim>>

getClaims()

async getClaims(entityId: EntityId, options?: ClaimQueryOptions): Promise<Result<Claim[]>>

revokeClaim()

async revokeClaim(id: ClaimId, reason?: string): Promise<Result<void>>

Agent Methods

createAgent()

async createAgent(input: CreateAgentInput): Promise<Result<Agent>>

getAgent()

async getAgent(id: AgentId): Promise<Result<Agent | null>>

getAgentByName()

async getAgentByName(name: string): Promise<Result<Agent | null>>

listAgents()

async listAgents(options?: ListAgentsOptions): Promise<Result<Agent[]>>

Execution Methods

registerHandler()

registerHandler(
  action: string,
  resourceType: string,
  handler: ActionHandler
): void

execute()

async execute(request: ActionRequest): Promise<Result<ActionResult>>

Decision Methods

recordDecision()

async recordDecision(input: RecordDecisionInput): Promise<Result<Decision>>

approveDecision()

async approveDecision(
  id: DecisionId,
  approverId: AgentId,
  comment?: string
): Promise<Result<Decision>>

rejectDecision()

async rejectDecision(
  id: DecisionId,
  rejecterId: AgentId,
  reason?: string
): Promise<Result<Decision>>

getPendingDecisions()

async getPendingDecisions(): Promise<Result<Decision[]>>

Policy Methods

createPolicy()

async createPolicy(input: CreatePolicyInput): Promise<Result<Policy>>

listPolicies()

async listPolicies(options?: ListPoliciesOptions): Promise<Result<Policy[]>>

Provenance Methods

verifyProvenance()

async verifyProvenance(): Promise<Result<VerificationResult>>

queryProvenance()

async queryProvenance(options?: ProvenanceQueryOptions): Promise<Result<ProvenanceEntry[]>>

Context Methods

assembleContext()

async assembleContext(
  entityId: EntityId,
  options?: AssemblyOptions
): Promise<Result<AssembledContext>>

Audit Methods

getAuditTrail()

async getAuditTrail(options?: AuditQueryOptions): Promise<Result<AuditEntry[]>>

Statistics

getStats()

async getStats(): Promise<Result<SystemStats>>

Import/Export

exportToJSON()

async exportToJSON(): Promise<Result<ExportData>>

importFromJSON()

async importFromJSON(
  data: ExportData,
  options?: ImportOptions
): Promise<Result<ImportResult>>

Events

on()

on(event: EventType, handler: EventHandler): void

off()

off(event: EventType, handler: EventHandler): void

Helper Functions

createTimestamp()

function createTimestamp(input?: string | Date): Timestamp

createScope()

function createScope(value: string): Scope

createJurisdiction()

function createJurisdiction(value: string): Jurisdiction

createConfidence()

function createConfidence(value: number): Confidence

ok()

function ok<T>(value: T): Result<T>

err()

function err<E = Error>(error: E): Result<never, E>

REST API Reference

Complete REST API reference for ContextGraph.

Base URL

http://localhost:3000/api/v1

Authentication

Include API key in header:

X-API-Key: your-api-key

Endpoints

Health

GET /health

Check service health.

Response:

{
  "status": "healthy",
  "timestamp": "2024-01-15T10:30:00.000Z"
}

Statistics

GET /stats

Get system statistics.

Response:

{
  "entities": 42,
  "claims": 156,
  "agents": 3,
  "decisions": 12,
  "policies": 5,
  "provenanceEntries": 203
}

Entities

GET /entities

List entities.

Query Parameters:

  • type - Filter by entity type
  • limit - Max results (default: 50)
  • offset - Pagination offset

Response:

{
  "data": [
    {
      "id": "ent_abc123",
      "type": "person",
      "name": "Alice",
      "properties": {},
      "createdAt": "2024-01-15T10:30:00.000Z"
    }
  ],
  "total": 42
}

POST /entities

Create entity.

Body:

{
  "type": "person",
  "name": "Alice",
  "properties": {
    "department": "Engineering"
  }
}

GET /entities/:id

Get entity by ID.

PUT /entities/:id

Update entity.

DELETE /entities/:id

Delete entity.

GET /entities/:id/claims

Get claims for entity.

POST /entities/:id/claims

Add claim to entity.

Body:

{
  "predicate": "has_skill",
  "value": "TypeScript",
  "context": {
    "confidence": 0.95
  }
}

Agents

GET /agents

List agents.

POST /agents

Create agent.

Body:

{
  "name": "data-processor",
  "description": "Processes data files",
  "capabilities": ["read", "write"]
}

GET /agents/:id

Get agent by ID or name.

POST /agents/:id/execute

Execute action.

Body:

{
  "action": "read",
  "resourceType": "csv",
  "resourceId": "data/input.csv",
  "parameters": {}
}

Decisions

GET /decisions

List decisions.

Query Parameters:

  • status - Filter by status
  • riskLevel - Filter by risk

POST /decisions

Record decision.

Body:

{
  "type": "deployment",
  "title": "Deploy v2.0.0",
  "proposedBy": "agt_123",
  "riskLevel": "medium"
}

GET /decisions/:id

Get decision.

POST /decisions/:id/approve

Approve decision.

Body:

{
  "approverId": "agt_456",
  "comment": "Approved"
}

POST /decisions/:id/reject

Reject decision.

Policies

GET /policies

List policies.

POST /policies

Create policy.

Body:

{
  "name": "Read Access",
  "version": "1.0.0",
  "effect": "allow",
  "subjects": ["role:analyst"],
  "actions": ["read"],
  "resources": ["reports/*"],
  "priority": 50
}

Audit

GET /audit

Query audit trail.

Query Parameters:

  • entityId - Filter by entity
  • agentId - Filter by agent
  • from - Start timestamp
  • to - End timestamp
  • limit - Max results

Provenance

GET /provenance

Query provenance entries.

POST /provenance/verify

Verify provenance chain.

Response:

{
  "valid": true,
  "entriesVerified": 203,
  "brokenLinks": 0,
  "invalidHashes": 0
}

Error Responses

{
  "error": {
    "code": "NOT_FOUND",
    "message": "Entity not found"
  }
}

Error Codes

CodeHTTP StatusDescription
NOT_FOUND404Resource not found
VALIDATION_ERROR400Invalid input
PERMISSION_DENIED403Access denied
POLICY_DENIED403Policy blocked
CONFLICT409Resource conflict
INTERNAL_ERROR500Server error

Rate Limiting

  • Default: 100 requests/minute
  • Header: X-RateLimit-Remaining
  • Header: X-RateLimit-Reset

Enterprise Deployment Architecture

A production-grade reference architecture for deploying ContextGraph OS with agents, RAG, policies, and human review workflows.

Architectural Philosophy

ContextGraph OS is not another agent framework. It is the governance spine that sits between agent cognition and real-world action.

The architecture enforces a simple but powerful rule:

No agent action is valid unless it is context-justified, policy-evaluated, and decision-traceable.

This is achieved by separating four planes:

PlanePurpose
Cognition PlaneWhere agents think
Knowledge PlaneWhere claims live (with provenance + time)
Governance PlaneWhere policies, risk, and approvals exist
Execution PlaneWhere actions actually occur

High-Level System Diagram

┌───────────────────────────────────────────────────────────┐
│                   Enterprise Interfaces                   │
│  (Dashboards · Audit · Compliance · Admin · CLI · API)    │
└───────────────────────────────────────────────────────────┘
                ▲                        ▲
                │                        │
        Human Review Plane        Audit / Export Plane
                │                        │
┌───────────────────────────────────────────────────────────┐
│               ContextGraph OS (Core)                       │
│                                                           │
│  ┌──────────────┐   ┌──────────────┐   ┌──────────────┐  │
│  │ Knowledge     │   │ Policy Engine│   │ Decision     │  │
│  │ (Claims + KG) │◄──┤ (Deny-first) │──►│ Trace Graph  │  │
│  └──────────────┘   └──────────────┘   └──────────────┘  │
│           ▲                   ▲                   │      │
│           │                   │                   │      │
│  ┌──────────────┐     ┌──────────────┐     ┌──────────┐ │
│  │ Provenance   │     │ Exceptions   │     │ Telemetry│ │
│  │ Ledger       │     │ & Approvals  │     │ & RBAC   │ │
│  └──────────────┘     └──────────────┘     └──────────┘ │
└───────────────────────────────────────────────────────────┘
                ▲
                │
┌───────────────────────────────────────────────────────────┐
│              Agent Runtime Layer                           │
│  (LangChain / AutoGen / Custom Agents / Tools)             │
└───────────────────────────────────────────────────────────┘
                ▲
                │
┌───────────────────────────────────────────────────────────┐
│            Data + RAG Infrastructure                       │
│  (Vector DB · Search · Docs · APIs · OSINT · Internal)     │
└───────────────────────────────────────────────────────────┘

Component Breakdown

Agent Runtime Layer (Pluggable)

Supported Frameworks:

  • LangChain / LangGraph
  • CrewAI / AutoGen
  • Custom internal agents
  • Deterministic tools (APIs, scripts, workflows)

Key Rule: Agents never execute tools directly. They submit Action Intents to ContextGraph OS.

// Agent submits intent, not execution
agent.requestAction({
  type: "publish_report",
  target: "external_audience",
  risk: "HIGH",
  contextQuery: {
    entityId: reportId,
    minConfidence: 0.8,
  }
});

Knowledge Plane (Contextual RAG + Claims)

This is where ContextGraph OS diverges sharply from typical RAG.

Instead of: "Here are top-k chunks"

ContextGraph stores: Claims with full context:

{
  "entity": "Market_Analysis_Q4",
  "claim": "Revenue growth projected at 15%",
  "validFrom": "2024-10-01",
  "validUntil": "2024-12-31",
  "confidence": 0.85,
  "source": "Internal Finance Report",
  "provenanceHash": "0x9af3..."
}

This enables:

  • Time-travel queries ("What did we know in March?")
  • Contradiction coexistence (multiple sources, different claims)
  • Evidence-weighted reasoning
  • Audit-ready provenance

Policy Engine (Deny-First by Design)

Policies are evaluated before any execution.

policy:
  name: external_publish_guard
  effect: deny
  when:
    action: publish
    target: external_audience
    risk: [HIGH, CRITICAL]
  then:
    require_approval: true
    approvers: [role:compliance_officer, role:legal]

Policy evaluation flow:

Action Intent → Policy Engine → DENY (needs approval)
                    │
                    └──→ Exception Queue → Human Review → APPROVE/REJECT
                                                │
                                                └──→ Execution (if approved)

Human Review & Exception Workflow

When a policy denies an action:

  1. Decision enters Exception Queue
  2. Human reviewer sees:
    • Proposed action details
    • Evidence used (claims, entities)
    • Confidence levels
    • Policy that triggered denial
    • Risk assessment
  3. Reviewer can:
    • Approve (with justification)
    • Reject (with reason)
    • Request more evidence

All outcomes are written into the Decision Trace Graph for permanent audit.

Execution Plane (Controlled Dispatch)

Execution only occurs after:

  • Policy ALLOW, or
  • Human approval for denied actions
// Execution is side-effect isolated and traceable
const result = await executor.dispatch({
  decisionId: approvedDecision.id,
  action: action,
  context: assembledContext,
});

// Full execution logged to provenance

Audit & Compliance Plane

Outputs:

  • Quarterly AI decision reports
  • Evidence summaries per decision
  • Policy override logs
  • Provenance verification hashes
  • GDPR data subject reports

Export formats:

  • JSON (machine-readable)
  • PDF (human-readable)
  • CSV (spreadsheet analysis)

Deployment Models

┌─────────────┐  ┌─────────────┐  ┌─────────────┐
│  Agent A    │  │  Agent B    │  │  Agent C    │
└──────┬──────┘  └──────┬──────┘  └──────┬──────┘
       │                │                │
       └────────────────┼────────────────┘
                        │
              ┌─────────▼─────────┐
              │  ContextGraph OS  │
              │  (Central Service)│
              └───────────────────┘

Benefits:

  • Strongest audit posture
  • Unified policy enforcement
  • Cross-agent visibility
  • Single source of truth

Option B: Embedded Mode

┌─────────────────────────┐
│        Agent A          │
│  ┌───────────────────┐  │
│  │  ContextGraph OS  │  │
│  │    (Embedded)     │  │
│  └───────────────────┘  │
└─────────────────────────┘

Benefits:

  • Lower latency
  • No network dependency
  • Simpler deployment

Trade-offs:

  • Reduced cross-org visibility
  • Per-app audit silos

Option C: Hybrid

┌─────────────────────────────────────────┐
│     Central Policy + Provenance         │
└─────────────────────────────────────────┘
         ▲              ▲              ▲
         │              │              │
    ┌────┴────┐    ┌────┴────┐    ┌────┴────┐
    │ Agent A │    │ Agent B │    │ Agent C │
    │ (Local  │    │ (Local  │    │ (Local  │
    │  Cache) │    │  Cache) │    │  Cache) │
    └─────────┘    └─────────┘    └─────────┘

Benefits:

  • Central governance
  • Local performance
  • Scalable architecture

Security Considerations

Data Classification

Data TypeStorageEncryption
ClaimsPersistentAES-256 at rest
DecisionsPersistentAES-256 at rest
ProvenanceImmutableHash-chained
PoliciesVersionedSigned

Access Control

  • RBAC for all operations
  • Policy-based action control
  • Audit logging for all access
  • SSO/SAML integration (Enterprise)

Network Security

  • TLS 1.3 for all connections
  • mTLS for service-to-service
  • API key + JWT authentication
  • Rate limiting

Next Steps

Governance Deep Dive

How ContextGraph OS enforces governance at every layer.

The Governance Problem

Traditional AI systems have a fundamental governance gap:

What ExistsWhat's Missing
LogsAudit trails
PromptsProvenance
OutputsDecision traces
ConfigsPolicy enforcement

ContextGraph OS closes this gap by making governance a first-class architectural concern.

Governance by Construction

Principle 1: No Orphan Data

Every piece of data must have:

  • A source
  • A timestamp
  • A confidence level
  • A provenance record
// This is enforced at the type level
interface Claim {
  id: ClaimId;
  source: ProvenanceSource;      // Required
  confidence: Confidence;         // Required
  validFrom: Timestamp;           // Required
  provenanceId: ProvenanceId;     // Auto-generated
}

Principle 2: No Untraced Decisions

Every decision must be:

  • Recorded before execution
  • Evaluated against policies
  • Traceable to evidence
// Decision lifecycle is enforced
PROPOSED → APPROVED/REJECTED → EXECUTED → COMPLETED/FAILED
    │            │                │              │
    └────────────┴────────────────┴──────────────┘
                All states recorded

Principle 3: Deny-First Policy

The default answer is "no". Actions must be explicitly allowed.

# Default policy (implicit)
default:
  effect: deny
  reason: "No matching allow policy"

# Explicit allows override
explicit_allow:
  effect: allow
  subjects: [role:analyst]
  actions: [read]
  resources: [reports/*]

Principle 4: Time is Truth

Data is always qualified by time:

// Query: "What was the status on March 15?"
const claims = await ckg.query({
  entityId: projectId,
  asOf: createTimestamp('2024-03-15'),
});

// Returns claims valid on that date, not current claims

Governance Layers

Layer 1: Data Governance (CKG)

The Contextual Knowledge Graph enforces:

ConstraintEnforcement
Source requiredSchema validation
Temporal validityQuery filtering
Confidence scoresRetrieval ranking
ContradictionsExplicit handling

Layer 2: Action Governance (Policy Engine)

Every action is policy-evaluated:

Action Request
     │
     ▼
┌─────────────────────────────────────┐
│         Policy Evaluation           │
│                                     │
│  1. Find matching policies          │
│  2. Evaluate conditions             │
│  3. Apply deny-takes-precedence     │
│  4. Return decision + reasons       │
└─────────────────────────────────────┘
     │
     ├── ALLOW → Execute
     │
     └── DENY → Exception Queue (optional)
                      │
                      └── Human Review

Layer 3: Decision Governance (DTG)

All significant decisions tracked:

interface Decision {
  id: DecisionId;
  type: string;
  status: DecisionStatus;

  // Who proposed this?
  proposedBy: AgentId;
  proposedAt: Timestamp;

  // What evidence was used?
  evidenceIds: ClaimId[];

  // What policy evaluated it?
  policyEvaluation: PolicyResult;

  // Who approved/rejected?
  approvedBy?: AgentId;
  rejectedBy?: AgentId;

  // Full audit trail
  history: DecisionEvent[];
}

Layer 4: Audit Governance (Provenance)

Immutable, hash-chained records:

Entry 1 ──hash──► Entry 2 ──hash──► Entry 3
   │                │                 │
   └────────────────┴─────────────────┘
          Tamper-evident chain

Governance Workflows

Workflow 1: High-Risk Action Approval

sequenceDiagram
    participant Agent
    participant Policy
    participant Queue
    participant Human
    participant Executor

    Agent->>Policy: Request Action (risk=HIGH)
    Policy->>Policy: Evaluate policies
    Policy->>Queue: DENY (requires approval)
    Queue->>Human: Show decision + evidence
    Human->>Queue: APPROVE (with reason)
    Queue->>Executor: Execute action
    Executor->>Agent: Result

Workflow 2: Evidence-Weighted Decision

sequenceDiagram
    participant Agent
    participant CKG
    participant Reasoner
    participant Policy

    Agent->>CKG: Query claims for entity
    CKG->>Agent: Claims (with confidence)
    Agent->>Reasoner: Infer additional facts
    Reasoner->>Agent: Inferred facts
    Agent->>Policy: Request action
    Policy->>Policy: Check confidence thresholds
    Policy->>Agent: ALLOW (confidence >= 0.8)

Workflow 3: Compliance Audit

sequenceDiagram
    participant Auditor
    participant Compliance
    participant DTG
    participant Provenance

    Auditor->>Compliance: Generate Q4 report
    Compliance->>DTG: Query decisions (Q4)
    DTG->>Compliance: Decision records
    Compliance->>Provenance: Verify chain
    Provenance->>Compliance: Verification result
    Compliance->>Auditor: PDF report + evidence

Governance Metrics

Track governance health with these metrics:

MetricGoodWarningCritical
Policy deny rate<10%10-30%>30%
Approval latency<1hr1-24hr>24hr
Provenance chain valid100%-<100%
Decision trace coverage100%95-99%<95%

Next Steps

Decision Trace Graph

Complete guide to tracking, auditing, and querying agent decisions.

Why Decision Traces Matter

Traditional AI systems operate as black boxes:

Input → [???] → Output

ContextGraph OS makes every decision transparent:

Input → Context Assembly → Policy Check → Evidence Linking → Execution → Audit Trail
           │                   │                │              │            │
           └───────────────────┴────────────────┴──────────────┴────────────┘
                            All recorded in Decision Trace Graph

Decision Lifecycle

Every decision follows a strict lifecycle:

┌──────────┐     ┌──────────┐     ┌──────────┐     ┌──────────┐
│ PROPOSED │────►│ APPROVED │────►│ EXECUTED │────►│COMPLETED │
└──────────┘     └──────────┘     └──────────┘     └──────────┘
      │                │                │                │
      │          ┌─────┴─────┐    ┌─────┴─────┐    ┌─────┴─────┐
      │          │ REJECTED  │    │ CANCELLED │    │  FAILED   │
      │          └───────────┘    └───────────┘    └───────────┘
      │
      └─────────► NEEDS_REVIEW ────► Human Queue

State Definitions

StateDescriptionCan Transition To
PROPOSEDDecision submitted by agentAPPROVED, REJECTED, NEEDS_REVIEW
APPROVEDPassed policy or human reviewEXECUTED, CANCELLED
REJECTEDDenied by policy or reviewer(terminal)
NEEDS_REVIEWAwaiting human approvalAPPROVED, REJECTED
EXECUTEDAction dispatchedCOMPLETED, FAILED
COMPLETEDSuccessfully finished(terminal)
FAILEDExecution error(terminal)
CANCELLEDWithdrawn before execution(terminal)

Decision Anatomy

interface Decision {
  // Identity
  id: DecisionId;
  type: string;                    // e.g., "publish_report"

  // Current state
  status: DecisionStatus;

  // Who proposed this?
  proposedBy: AgentId;
  proposedAt: Timestamp;

  // What action?
  action: {
    type: string;
    target: string;
    parameters: Record<string, unknown>;
  };

  // What evidence was considered?
  evidenceIds: ClaimId[];

  // Risk assessment
  risk: {
    level: "LOW" | "MEDIUM" | "HIGH" | "CRITICAL";
    score: number;              // 0-1
    factors: RiskFactor[];
  };

  // Policy evaluation
  policyResult: {
    effect: "allow" | "deny";
    matchedPolicies: PolicyId[];
    conditions: EvaluatedCondition[];
  };

  // Approval (if needed)
  approvedBy?: AgentId | UserId;
  approvedAt?: Timestamp;
  approvalReason?: string;

  // Rejection (if denied)
  rejectedBy?: AgentId | UserId;
  rejectedAt?: Timestamp;
  rejectionReason?: string;

  // Execution result
  executedAt?: Timestamp;
  completedAt?: Timestamp;
  result?: unknown;
  error?: string;

  // Full history
  history: DecisionEvent[];
}

Creating Decisions

Basic Decision

import { DecisionTraceGraph } from '@contextgraph/dtg';

const dtg = new DecisionTraceGraph(storage);

// Create a decision
const decision = await dtg.createDecision({
  type: "publish_report",
  proposedBy: agentId,
  action: {
    type: "publish",
    target: "external_audience",
    parameters: {
      reportId: "report_123",
      format: "pdf",
    },
  },
  evidenceIds: [claim1.id, claim2.id, claim3.id],
});

console.log(decision.status); // "proposed"

With Risk Assessment

const decision = await dtg.createDecision({
  type: "delete_customer_data",
  proposedBy: agentId,
  action: {
    type: "delete",
    target: "customer_records",
    parameters: { customerId: "cust_456" },
  },
  risk: {
    level: "HIGH",
    score: 0.85,
    factors: [
      { type: "data_sensitivity", weight: 0.4, description: "PII data" },
      { type: "irreversibility", weight: 0.3, description: "Permanent deletion" },
      { type: "volume", weight: 0.15, description: "Multiple records" },
    ],
  },
  evidenceIds: [deletionRequestClaim.id],
});

State Transitions

Approve Decision

// After policy evaluation or human review
await dtg.transitionDecision(decision.id, "approved", {
  approvedBy: reviewerId,
  reason: "Verified customer deletion request is legitimate",
});

Reject Decision

await dtg.transitionDecision(decision.id, "rejected", {
  rejectedBy: policyEngineId,
  reason: "Policy 'pii-protection' denies deletion without manager approval",
});

Execute Decision

// After approval
await dtg.transitionDecision(decision.id, "executed");

// When execution completes
await dtg.transitionDecision(decision.id, "completed", {
  result: { deletedRecords: 15 },
});

// Or if execution fails
await dtg.transitionDecision(decision.id, "failed", {
  error: "Database connection timeout",
});

Querying Decisions

By Status

// Get all pending decisions
const pending = await dtg.queryDecisions({
  status: "proposed",
});

// Get decisions needing review
const needsReview = await dtg.queryDecisions({
  status: "needs_review",
});

By Agent

// All decisions by a specific agent
const agentDecisions = await dtg.queryDecisions({
  proposedBy: agentId,
});

// High-risk decisions by agent
const highRisk = await dtg.queryDecisions({
  proposedBy: agentId,
  riskLevel: ["HIGH", "CRITICAL"],
});

By Time Range

// Decisions from last 24 hours
const recent = await dtg.queryDecisions({
  proposedAfter: createTimestamp(Date.now() - 24 * 60 * 60 * 1000),
});

// Decisions in Q4
const q4Decisions = await dtg.queryDecisions({
  proposedAfter: createTimestamp('2024-10-01'),
  proposedBefore: createTimestamp('2024-12-31'),
});

By Type

// All publish decisions
const publishes = await dtg.queryDecisions({
  type: "publish_report",
});

// Multiple types
const modifications = await dtg.queryDecisions({
  types: ["update", "delete", "create"],
});

Decision History

Every state change is recorded:

const decision = await dtg.getDecision(decisionId);

for (const event of decision.history) {
  console.log(`${event.timestamp}: ${event.fromStatus} → ${event.toStatus}`);
  console.log(`  By: ${event.actor}`);
  console.log(`  Reason: ${event.reason}`);
}

// Output:
// 2024-03-15T10:00:00Z: null → proposed
//   By: agent:report-generator
//   Reason: null
// 2024-03-15T10:00:05Z: proposed → needs_review
//   By: policy:high-risk-guard
//   Reason: Action risk level HIGH requires human approval
// 2024-03-15T14:30:00Z: needs_review → approved
//   By: user:compliance-officer
//   Reason: Verified report accuracy and authorization
// 2024-03-15T14:30:01Z: approved → executed
//   By: system:executor
//   Reason: null
// 2024-03-15T14:30:05Z: executed → completed
//   By: system:executor
//   Reason: Successfully published to external audience

Evidence Linking

Decisions are linked to their supporting evidence:

// Get evidence for a decision
const evidence = await dtg.getDecisionEvidence(decision.id);

for (const claim of evidence) {
  console.log(`Claim: ${claim.value}`);
  console.log(`  Confidence: ${claim.confidence}`);
  console.log(`  Source: ${claim.source}`);
  console.log(`  Valid: ${claim.validFrom} - ${claim.validUntil}`);
}

Evidence Sufficiency

Check if decision has sufficient evidence:

const sufficiency = await dtg.checkEvidenceSufficiency(decision.id, {
  minClaims: 2,
  minConfidence: 0.8,
  requiredSources: ["internal", "verified"],
});

if (!sufficiency.sufficient) {
  console.log(`Missing: ${sufficiency.gaps}`);
  // e.g., "Missing: verified source claim"
}

Audit Reports

Decision Summary

const summary = await dtg.generateSummary({
  timeRange: {
    from: createTimestamp('2024-01-01'),
    to: createTimestamp('2024-03-31'),
  },
  groupBy: "agent",
});

// Output:
// {
//   "agent:report-generator": {
//     total: 150,
//     approved: 120,
//     rejected: 25,
//     failed: 5,
//     avgRiskScore: 0.45,
//   },
//   "agent:data-processor": {
//     total: 500,
//     approved: 480,
//     rejected: 15,
//     failed: 5,
//     avgRiskScore: 0.25,
//   }
// }

Export for Compliance

const report = await dtg.exportForCompliance({
  format: "json",
  timeRange: { from: q4Start, to: q4End },
  includeEvidence: true,
  includeHistory: true,
  redactPII: true,
});

// Save for compliance records
await writeFile("q4-decisions.json", JSON.stringify(report));

Visualization

import { visualizeDecisions } from '@contextgraph/viz';

// Generate Mermaid diagram of decision flow
const diagram = await visualizeDecisions(dtg, decision.id, {
  format: "mermaid",
  includeEvidence: true,
  includePolicy: true,
});

// Output:
// ```mermaid
// flowchart TD
//     P[Proposed] --> |policy check| NR[Needs Review]
//     NR --> |human approval| A[Approved]
//     A --> |dispatch| E[Executed]
//     E --> |success| C[Completed]
//
//     subgraph Evidence
//         E1[Claim: Revenue +15%]
//         E2[Claim: Q4 Verified]
//     end
// ```

Best Practices

// Good: Decision with evidence
const decision = await dtg.createDecision({
  type: "approve_loan",
  evidenceIds: [creditScore.id, income.id, history.id],
  // ...
});

// Bad: Decision without evidence
const decision = await dtg.createDecision({
  type: "approve_loan",
  evidenceIds: [], // No audit trail!
  // ...
});

2. Use Meaningful Types

// Good: Specific types
type: "publish_quarterly_report"
type: "delete_customer_pii"
type: "escalate_support_ticket"

// Bad: Vague types
type: "action"
type: "do_thing"
type: "process"

3. Include Risk Assessment

// For any action that modifies data or has external effects
risk: {
  level: calculateRiskLevel(action),
  score: calculateRiskScore(action),
  factors: identifyRiskFactors(action),
}

4. Provide Transition Reasons

// Always explain why a transition occurred
await dtg.transitionDecision(id, "rejected", {
  rejectedBy: policyId,
  reason: "Confidence threshold not met (required: 0.8, actual: 0.65)",
});

Integration with Policy Engine

Decisions integrate with policy evaluation:

import { PolicyEngine } from '@contextgraph/policy';
import { DecisionTraceGraph } from '@contextgraph/dtg';

const policy = new PolicyEngine(policyLedger);
const dtg = new DecisionTraceGraph(storage);

// Create decision
const decision = await dtg.createDecision({
  type: "publish",
  action: { type: "publish", target: "external" },
  // ...
});

// Evaluate against policies
const result = await policy.evaluate({
  subject: decision.proposedBy,
  action: decision.action.type,
  resource: decision.action.target,
  context: { risk: decision.risk },
});

// Transition based on result
if (result.effect === "allow") {
  await dtg.transitionDecision(decision.id, "approved", {
    approvedBy: "policy:auto-approve",
    reason: `Allowed by policy ${result.matchedPolicies[0]}`,
  });
} else if (result.requiresApproval) {
  await dtg.transitionDecision(decision.id, "needs_review", {
    reason: `Requires human approval: ${result.reason}`,
  });
} else {
  await dtg.transitionDecision(decision.id, "rejected", {
    rejectedBy: result.matchedPolicies[0],
    reason: result.reason,
  });
}

Next Steps

Policy Configuration

Comprehensive guide to configuring policies in ContextGraph OS.

Policy Anatomy

policy:
  # Identity
  id: pol_abc123
  name: "High Risk Action Guard"
  version: "1.0.0"
  description: "Requires approval for high-risk actions"

  # Effect
  effect: deny  # or 'allow'

  # Who does this apply to?
  subjects:
    - "*"  # All subjects
    # - "role:analyst"
    # - "agent:data-processor"

  # What actions?
  actions:
    - "delete"
    - "publish"
    - "export"

  # Which resources?
  resources:
    - "production/*"
    - "customer-data/*"

  # Under what conditions?
  conditions:
    - field: "risk.level"
      operator: "in"
      value: ["HIGH", "CRITICAL"]

  # Priority (higher = evaluated first)
  priority: 100

  # Temporal validity
  validFrom: "2024-01-01T00:00:00Z"
  validUntil: null  # No expiration

Policy Matching

Subject Matching

# Match all
subjects: ["*"]

# Match specific role
subjects: ["role:admin"]

# Match specific agent
subjects: ["agent:data-processor"]

# Match multiple
subjects: ["role:analyst", "role:data-scientist"]

# Match pattern
subjects: ["agent:*-processor"]

Action Matching

# Match specific action
actions: ["delete"]

# Match multiple
actions: ["read", "write", "delete"]

# Match all
actions: ["*"]

# Match pattern
actions: ["export:*"]

Resource Matching

# Exact match
resources: ["/api/users/123"]

# Wildcard suffix
resources: ["reports/*"]

# Wildcard prefix
resources: ["*/sensitive"]

# Multiple patterns
resources: ["pii/*", "*/personal/*", "customers/*"]

Condition Operators

OperatorDescriptionExample
equalsExact match{ field: "status", operator: "equals", value: "active" }
not_equalsNot equal{ field: "status", operator: "not_equals", value: "deleted" }
inIn array{ field: "role", operator: "in", value: ["admin", "super"] }
not_inNot in array{ field: "role", operator: "not_in", value: ["guest"] }
greater_thanNumeric comparison{ field: "risk.score", operator: "greater_than", value: 0.7 }
less_thanNumeric comparison{ field: "confidence", operator: "less_than", value: 0.5 }
betweenRange{ field: "hour", operator: "between", value: [9, 17] }
containsString contains{ field: "path", operator: "contains", value: "admin" }
starts_withString prefix{ field: "resource", operator: "starts_with", value: "/api/" }
matchesRegex match{ field: "email", operator: "matches", value: ".*@company\\.com$" }
existsField exists{ field: "approval.id", operator: "exists" }

Condition Fields

Built-in Fields

FieldDescription
subject.idSubject identifier
subject.roleSubject's role
subject.typeSubject type (agent, user)
actionAction being performed
resourceTarget resource
time.hourCurrent hour (0-23)
time.dayOfWeekDay name (Mon, Tue, etc.)
time.dateISO date
risk.levelRisk level (LOW, MEDIUM, HIGH, CRITICAL)
risk.scoreRisk score (0-1)
context.jurisdictionJurisdiction code
context.scopeScope

Custom Fields

Access any field from the action context:

conditions:
  - field: "parameters.count"
    operator: "greater_than"
    value: 100
  - field: "metadata.department"
    operator: "equals"
    value: "finance"

Policy Templates

Read-Only Access

policy:
  name: "Read Only Access"
  effect: allow
  subjects: ["role:viewer"]
  actions: ["read", "list", "get"]
  resources: ["*"]
  priority: 30

PII Protection

policy:
  name: "PII Protection"
  effect: deny
  subjects: ["*"]
  actions: ["*"]
  resources: ["pii/*", "*/personal/*"]
  conditions:
    - field: "subject.clearance"
      operator: "not_equals"
      value: "pii-authorized"
  priority: 100

Business Hours Only

policy:
  name: "Business Hours Only"
  effect: deny
  subjects: ["role:contractor"]
  actions: ["*"]
  resources: ["*"]
  conditions:
    - field: "time.hour"
      operator: "not_in"
      value: [9, 10, 11, 12, 13, 14, 15, 16, 17]
  priority: 80

High Risk Approval

policy:
  name: "High Risk Requires Approval"
  effect: deny
  subjects: ["*"]
  actions: ["delete", "publish", "deploy"]
  resources: ["production/*"]
  conditions:
    - field: "risk.level"
      operator: "in"
      value: ["HIGH", "CRITICAL"]
    - field: "approval.status"
      operator: "not_equals"
      value: "approved"
  priority: 90

Jurisdiction Restriction

policy:
  name: "EU Data in EU Only"
  effect: deny
  subjects: ["*"]
  actions: ["export", "transfer"]
  resources: ["eu-data/*"]
  conditions:
    - field: "context.jurisdiction"
      operator: "not_equals"
      value: "EU"
  priority: 95

Policy Evaluation Order

  1. Policies sorted by priority (highest first)
  2. For each policy:
    • Check subject match
    • Check action match
    • Check resource match
    • Evaluate conditions
  3. First DENY wins (deny-takes-precedence)
  4. If no deny, first ALLOW wins
  5. If no match, implicit DENY
Priority 100: Security policies
Priority 90:  Risk policies
Priority 80:  Compliance policies
Priority 50:  Business rules
Priority 30:  Role defaults
Priority 10:  Fallback rules

Testing Policies

Simulation

const simulator = new PolicySimulator(policyLedger, storage);

const result = await simulator.simulate({
  subject: "agent:report-generator",
  action: "publish",
  resource: "production/quarterly-report",
  context: {
    risk: { level: "HIGH" },
    time: { hour: 14 }
  }
});

console.log(result.effect);  // "deny"
console.log(result.matchedPolicies);
console.log(result.reason);

Batch Testing

const scenarios = [
  { subject: "role:admin", action: "delete", resource: "data/*" },
  { subject: "role:analyst", action: "read", resource: "reports/*" },
  { subject: "role:guest", action: "write", resource: "comments/*" },
];

const results = await simulator.simulateMany(scenarios);

Best Practices

  1. Start restrictive - Default deny, explicit allow
  2. Use high priority for security - Security > Compliance > Business
  3. Test before deploy - Use simulator
  4. Version policies - Semantic versioning
  5. Document conditions - Clear descriptions
  6. Monitor evaluations - Track deny rates
  7. Review regularly - Quarterly policy audits

Compliance Reporting

Generate audit-ready reports for regulatory compliance, internal audits, and governance reviews.

Overview

ContextGraph OS provides comprehensive compliance reporting:

  • Automated report generation from decision traces
  • Evidence packages with provenance verification
  • Multi-format export (JSON, PDF, CSV)
  • Configurable redaction for sensitive data
  • Chain-of-custody verification via hash chains

Report Types

1. Decision Audit Report

Complete record of decisions within a time period:

import { ComplianceReporter } from '@contextgraph/compliance';

const reporter = new ComplianceReporter({ dtg, ckg, provenance });

const report = await reporter.generateDecisionAudit({
  timeRange: {
    from: createTimestamp('2024-10-01'),
    to: createTimestamp('2024-12-31'),
  },
  includeEvidence: true,
  includeHistory: true,
  groupBy: 'week',
});

Output Structure:

{
  "reportId": "rpt_abc123",
  "generatedAt": "2025-01-15T10:00:00Z",
  "timeRange": {
    "from": "2024-10-01T00:00:00Z",
    "to": "2024-12-31T23:59:59Z"
  },
  "summary": {
    "totalDecisions": 1250,
    "approved": 1100,
    "rejected": 120,
    "failed": 30,
    "avgProcessingTime": "2.5h",
    "humanReviewRate": "15%"
  },
  "byWeek": [
    {
      "week": "2024-W40",
      "decisions": 95,
      "approved": 85,
      "rejected": 8,
      "failed": 2
    }
  ],
  "decisions": [
    {
      "id": "dec_xyz",
      "type": "publish_report",
      "status": "completed",
      "proposedBy": "agent:report-gen",
      "proposedAt": "2024-10-15T09:00:00Z",
      "evidence": [...],
      "history": [...]
    }
  ]
}

2. Provenance Verification Report

Verify integrity of the audit trail:

const verification = await reporter.generateProvenanceVerification({
  entityIds: [entity1, entity2, entity3],
  verifyChain: true,
  verifyHashes: true,
});

Output:

{
  "reportId": "rpt_def456",
  "generatedAt": "2025-01-15T10:00:00Z",
  "verification": {
    "chainIntegrity": "VALID",
    "entriesVerified": 5000,
    "hashMismatches": 0,
    "brokenLinks": 0
  },
  "entities": [
    {
      "entityId": "ent_123",
      "claims": 50,
      "provenanceEntries": 75,
      "chainValid": true,
      "oldestEntry": "2024-01-15T00:00:00Z",
      "newestEntry": "2025-01-14T23:59:59Z"
    }
  ]
}

3. Policy Evaluation Report

Summary of policy enforcement:

const policyReport = await reporter.generatePolicyReport({
  timeRange: { from: q4Start, to: q4End },
  includeViolations: true,
  includeExceptions: true,
});

Output:

{
  "summary": {
    "totalEvaluations": 5000,
    "allowed": 4200,
    "denied": 800,
    "exceptionsGranted": 50
  },
  "byPolicy": [
    {
      "policyId": "pol_pii_protection",
      "name": "PII Protection",
      "evaluations": 1200,
      "denials": 300,
      "exceptions": 15
    }
  ],
  "violations": [
    {
      "timestamp": "2024-11-15T14:30:00Z",
      "policy": "pol_high_risk",
      "subject": "agent:data-proc",
      "action": "delete",
      "resource": "customer_data/*",
      "resolution": "exception_granted",
      "approvedBy": "user:compliance_mgr"
    }
  ]
}

4. Agent Activity Report

Per-agent decision and activity summary:

const agentReport = await reporter.generateAgentReport({
  agentId: agentId,
  timeRange: { from: monthStart, to: monthEnd },
  includeDecisions: true,
  includeCapabilities: true,
});

Output:

{
  "agent": {
    "id": "agent:report-generator",
    "name": "Report Generator",
    "status": "active",
    "capabilities": ["read", "generate", "publish"]
  },
  "activity": {
    "decisionsProposed": 150,
    "decisionsApproved": 140,
    "decisionsRejected": 8,
    "decisionsFailed": 2,
    "approvalRate": "93.3%",
    "avgRiskScore": 0.35
  },
  "riskProfile": {
    "lowRisk": 100,
    "mediumRisk": 40,
    "highRisk": 10,
    "criticalRisk": 0
  }
}

5. GDPR Data Subject Report

For data subject access requests (DSAR):

const dsarReport = await reporter.generateDSAR({
  subjectId: customerId,
  includeAllClaims: true,
  includeDecisions: true,
  includeProvenance: true,
  redactInternal: true,
});

Output:

{
  "subject": {
    "id": "customer_123",
    "type": "data_subject"
  },
  "data": {
    "claims": [
      {
        "attribute": "email",
        "value": "user@example.com",
        "source": "registration_form",
        "collectedAt": "2023-06-15T10:00:00Z",
        "purpose": "account_management"
      }
    ],
    "decisions": [
      {
        "type": "marketing_email",
        "status": "completed",
        "timestamp": "2024-03-15T09:00:00Z"
      }
    ]
  },
  "provenance": {
    "dataOrigins": ["web_form", "api_import"],
    "processingActivities": 25,
    "thirdPartySharing": []
  }
}

Export Formats

JSON Export

const jsonReport = await reporter.export({
  report: decisionAudit,
  format: 'json',
  pretty: true,
});

PDF Export

const pdfReport = await reporter.export({
  report: decisionAudit,
  format: 'pdf',
  template: 'compliance-formal',
  includeCharts: true,
  includeSignature: true,
});

CSV Export

const csvReport = await reporter.export({
  report: decisionAudit,
  format: 'csv',
  flatten: true,  // Flatten nested objects
  columns: ['id', 'type', 'status', 'proposedAt', 'approvedBy'],
});

Redaction & Privacy

Automatic PII Redaction

const report = await reporter.generateDecisionAudit({
  timeRange: { from, to },
  redaction: {
    enabled: true,
    patterns: ['email', 'phone', 'ssn', 'credit_card'],
    replacement: '[REDACTED]',
  },
});

Custom Redaction Rules

const report = await reporter.generateDecisionAudit({
  timeRange: { from, to },
  redaction: {
    enabled: true,
    rules: [
      {
        field: 'customer.email',
        action: 'hash',  // SHA-256 hash
      },
      {
        field: 'customer.name',
        action: 'mask',  // J*** D**
      },
      {
        field: 'internal_notes',
        action: 'remove',  // Completely remove
      },
    ],
  },
});

Role-Based Redaction

const report = await reporter.generateDecisionAudit({
  timeRange: { from, to },
  redaction: {
    enabled: true,
    byRole: {
      auditor: ['internal_notes'],  // Auditors can't see internal notes
      external: ['*_internal', 'cost_*'],  // External can't see internal fields
    },
  },
  viewerRole: 'external',
});

Scheduling Reports

Automated Generation

import { ReportScheduler } from '@contextgraph/compliance';

const scheduler = new ReportScheduler(reporter);

// Weekly decision audit
scheduler.schedule({
  id: 'weekly-audit',
  type: 'decision_audit',
  cron: '0 0 * * MON',  // Every Monday at midnight
  config: {
    timeRange: 'last_week',
    includeEvidence: true,
  },
  delivery: {
    email: ['compliance@company.com'],
    s3: 's3://compliance-reports/weekly/',
  },
});

// Monthly policy report
scheduler.schedule({
  id: 'monthly-policy',
  type: 'policy_report',
  cron: '0 0 1 * *',  // First of every month
  config: {
    timeRange: 'last_month',
    includeViolations: true,
  },
  delivery: {
    email: ['security@company.com'],
  },
});

On-Demand Generation

// Generate report immediately
const report = await scheduler.runNow('weekly-audit');

// Generate with custom time range
const customReport = await scheduler.runNow('weekly-audit', {
  timeRange: {
    from: createTimestamp('2024-10-01'),
    to: createTimestamp('2024-10-31'),
  },
});

Compliance Dashboards

Metrics Query

const metrics = await reporter.getMetrics({
  timeRange: { from: monthStart, to: now },
});

// Returns:
// {
//   decisions: { total: 1000, approved: 900, rejected: 80, failed: 20 },
//   policies: { evaluations: 5000, denials: 400, exceptions: 25 },
//   provenance: { entries: 10000, verified: 10000, invalid: 0 },
//   agents: { active: 15, suspended: 2, total: 17 },
// }

Trend Analysis

const trends = await reporter.getTrends({
  timeRange: { from: yearStart, to: now },
  granularity: 'month',
  metrics: ['decision_volume', 'rejection_rate', 'avg_risk_score'],
});

// Returns monthly data points for trend visualization

Regulatory Templates

SOC 2 Report

const soc2 = await reporter.generateSOC2({
  timeRange: { from: auditStart, to: auditEnd },
  trustPrinciples: ['security', 'availability', 'confidentiality'],
});

ISO 27001 Evidence

const iso27001 = await reporter.generateISO27001Evidence({
  controls: ['A.9.4.1', 'A.12.4.1', 'A.16.1.2'],
  timeRange: { from: auditStart, to: auditEnd },
});

HIPAA Audit Trail

const hipaa = await reporter.generateHIPAAReport({
  timeRange: { from: auditStart, to: auditEnd },
  includePHIAccess: true,
  includeDisclosures: true,
});

Verification & Signing

Hash Verification

// Verify report hasn't been tampered with
const verification = reporter.verifyReport(reportJson);

// {
//   valid: true,
//   hash: "sha256:abc123...",
//   generatedAt: "2025-01-15T10:00:00Z",
//   verifiedAt: "2025-01-15T12:00:00Z"
// }

Digital Signatures

const signedReport = await reporter.signReport(report, {
  signer: 'compliance-officer',
  certificate: '/path/to/cert.pem',
  timestamp: true,
});

Best Practices

1. Regular Verification

// Run weekly provenance verification
scheduler.schedule({
  id: 'weekly-verification',
  type: 'provenance_verification',
  cron: '0 2 * * SUN',  // Sunday 2 AM
  config: {
    verifyChain: true,
    verifyHashes: true,
    alertOnFailure: true,
  },
  alerts: {
    onFailure: ['security@company.com'],
  },
});

2. Retain Reports

// Archive reports with retention policy
const report = await reporter.generateDecisionAudit({
  timeRange: { from, to },
  archive: {
    enabled: true,
    location: 's3://compliance-archive/',
    retention: '7 years',
    encryption: 'AES-256',
  },
});

3. Test Report Generation

// Validate report generation in CI/CD
describe('Compliance Reports', () => {
  it('generates valid decision audit', async () => {
    const report = await reporter.generateDecisionAudit({
      timeRange: { from: testStart, to: testEnd },
    });

    expect(report.summary).toBeDefined();
    expect(report.decisions.length).toBeGreaterThan(0);
    expect(reporter.verifyReport(report).valid).toBe(true);
  });
});

4. Document Report Recipients

// Maintain audit trail of report distribution
const distribution = await reporter.distribute(report, {
  recipients: ['compliance@company.com', 'auditor@external.com'],
  logDistribution: true,
  requireAcknowledgment: true,
});

Next Steps

Commercial Hosted Architecture

Technical architecture for the ContextGraph OS commercial hosted product.

System Overview

┌─────────────────────────────────────────────────────────────────────────┐
│                         Customer Applications                            │
│   (LangChain · AutoGen · Custom Agents · Internal Tools)                │
└───────────────────────────────────┬─────────────────────────────────────┘
                                    │ SDK / REST API
                                    ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                      ContextGraph Cloud Platform                         │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                         API Gateway                              │   │
│  │           (Rate Limiting · Auth · Tenant Routing)                │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                    │                                     │
│       ┌────────────────────────────┼────────────────────────────┐       │
│       ▼                            ▼                            ▼       │
│  ┌──────────┐              ┌──────────────┐              ┌──────────┐  │
│  │  Auth    │              │  Core API    │              │ Dashboard │  │
│  │ Service  │              │   Service    │              │  (React)  │  │
│  └──────────┘              └──────────────┘              └──────────┘  │
│       │                            │                            │       │
│       │     ┌──────────────────────┼──────────────────────┐     │       │
│       │     ▼                      ▼                      ▼     │       │
│       │ ┌────────┐          ┌──────────────┐        ┌────────┐  │       │
│       │ │ Policy │          │   Decision   │        │ Report │  │       │
│       │ │ Engine │          │   Processor  │        │ Worker │  │       │
│       │ └────────┘          └──────────────┘        └────────┘  │       │
│       │     │                      │                      │     │       │
│       └─────┼──────────────────────┼──────────────────────┼─────┘       │
│             │                      │                      │             │
│             ▼                      ▼                      ▼             │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                      PostgreSQL (Multi-Tenant)                   │   │
│  │     Claims · Decisions · Policies · Provenance · Agents          │   │
│  └─────────────────────────────────────────────────────────────────┘   │
│                                    │                                     │
│  ┌─────────────────────────────────────────────────────────────────┐   │
│  │                         Redis Cluster                            │   │
│  │           (Sessions · Cache · Real-time Events)                  │   │
│  └─────────────────────────────────────────────────────────────────┘   │
└─────────────────────────────────────────────────────────────────────────┘

Core Components

1. API Gateway

Technology: NGINX / Kong / AWS API Gateway

Responsibilities:

  • Request routing to services
  • Rate limiting per tenant
  • API key validation
  • Request/response logging
  • SSL termination
# Rate limits per tier
rate_limits:
  team:
    requests_per_minute: 1000
    burst: 100
  enterprise:
    requests_per_minute: 10000
    burst: 500

2. Authentication Service

Technology: Node.js + Passport.js / Auth0

Features:

  • API key management
  • SSO/SAML integration (Team+)
  • JWT token issuance
  • Role-based access control
  • Multi-factor authentication (Enterprise)
interface TenantAuth {
  tenantId: string;
  plan: 'team' | 'enterprise';
  apiKeys: ApiKey[];
  ssoConfig?: SSOConfig;
  users: User[];
  roles: Role[];
}

3. Core API Service

Technology: Node.js + TypeScript + Express/Fastify

Endpoints:

  • /api/v1/claims - CRUD for claims
  • /api/v1/decisions - Decision lifecycle
  • /api/v1/policies - Policy management
  • /api/v1/agents - Agent registry
  • /api/v1/provenance - Audit trail queries

Multi-tenancy:

// Every request is tenant-scoped
app.use((req, res, next) => {
  const tenantId = extractTenantId(req);
  req.context = { tenantId };
  next();
});

// Queries are automatically filtered
const claims = await ckg.query({
  tenantId: req.context.tenantId,  // Enforced
  entityId: 'report_123',
});

4. Policy Engine Service

Technology: Node.js + @contextgraph/policy

Features:

  • Real-time policy evaluation
  • Policy simulation/dry-run
  • Template management
  • Policy versioning

5. Decision Processor

Technology: Node.js + Bull Queue

Responsibilities:

  • Async decision processing
  • Human review queue management
  • Webhook delivery
  • Status transitions
// Decision processing flow
queue.process('decision', async (job) => {
  const { decisionId, tenantId } = job.data;

  // Evaluate policies
  const result = await policyEngine.evaluate(decision);

  if (result.effect === 'deny' && result.requiresApproval) {
    await notifyReviewers(tenantId, decision);
    return { status: 'needs_review' };
  }

  // Auto-approve
  await dtg.transition(decisionId, 'approved');
  return { status: 'approved' };
});

6. Report Worker

Technology: Node.js + PDFKit + Bull Queue

Features:

  • Scheduled report generation
  • On-demand compliance exports
  • PDF/CSV/JSON formats
  • Email delivery

7. Dashboard (Web UI)

Technology: React + TypeScript + TailwindCSS

Features:

  • Real-time decision monitoring
  • Policy editor (visual + YAML)
  • Agent status & capabilities
  • Compliance report generation
  • Team management
  • Audit log viewer

Database Schema (PostgreSQL)

-- Multi-tenant partitioning
CREATE TABLE tenants (
  id UUID PRIMARY KEY,
  name TEXT NOT NULL,
  plan TEXT NOT NULL,  -- 'team' | 'enterprise'
  settings JSONB,
  created_at TIMESTAMPTZ DEFAULT NOW()
);

-- All tables include tenant_id
CREATE TABLE claims (
  id UUID PRIMARY KEY,
  tenant_id UUID REFERENCES tenants(id),
  entity_id TEXT NOT NULL,
  attribute TEXT NOT NULL,
  value JSONB NOT NULL,
  confidence DECIMAL(3,2),
  valid_from TIMESTAMPTZ NOT NULL,
  valid_until TIMESTAMPTZ,
  provenance_id UUID,
  created_at TIMESTAMPTZ DEFAULT NOW(),

  -- Partition by tenant for isolation
  CONSTRAINT claims_tenant_fk FOREIGN KEY (tenant_id) REFERENCES tenants(id)
);

CREATE INDEX idx_claims_tenant_entity ON claims(tenant_id, entity_id);
CREATE INDEX idx_claims_valid_range ON claims(tenant_id, valid_from, valid_until);

-- Row-level security for tenant isolation
ALTER TABLE claims ENABLE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON claims
  USING (tenant_id = current_setting('app.tenant_id')::uuid);

Infrastructure Requirements

Team Tier (Managed)

ComponentSpecification
API Servers2x (load balanced)
DatabasePostgreSQL RDS (db.t3.medium)
RedisElastiCache (cache.t3.micro)
StorageS3 for reports
CDNCloudFront for dashboard

Estimated Cost: ~$200-400/month base infrastructure

Enterprise Tier (Self-Hosted)

ComponentSpecification
Kubernetes3-node cluster minimum
DatabasePostgreSQL (dedicated, encrypted)
Redis3-node cluster
StorageLocal or cloud storage
MonitoringPrometheus + Grafana

Deployment Options:

  • AWS EKS / GCP GKE / Azure AKS
  • On-premise Kubernetes
  • Docker Compose (small scale)

Deployment Architecture

Kubernetes Deployment

# Helm chart structure
contextgraph-cloud/
├── Chart.yaml
├── values.yaml
├── templates/
│   ├── api-deployment.yaml
│   ├── api-service.yaml
│   ├── auth-deployment.yaml
│   ├── dashboard-deployment.yaml
│   ├── worker-deployment.yaml
│   ├── ingress.yaml
│   ├── configmap.yaml
│   └── secrets.yaml
# values.yaml
replicaCount:
  api: 3
  auth: 2
  dashboard: 2
  worker: 2

postgresql:
  enabled: true
  auth:
    database: contextgraph
  primary:
    persistence:
      size: 100Gi

redis:
  enabled: true
  architecture: replication

ingress:
  enabled: true
  className: nginx
  hosts:
    - host: api.contextgraph.io
      paths:
        - path: /
          pathType: Prefix

Docker Compose (Development/Small Scale)

version: '3.8'

services:
  api:
    build: ./packages/cloud-api
    ports:
      - "3000:3000"
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/contextgraph
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis

  dashboard:
    build: ./packages/cloud-dashboard
    ports:
      - "3001:80"

  worker:
    build: ./packages/cloud-worker
    environment:
      - DATABASE_URL=postgresql://postgres:password@db:5432/contextgraph
      - REDIS_URL=redis://redis:6379
    depends_on:
      - db
      - redis

  db:
    image: postgres:15
    volumes:
      - pgdata:/var/lib/postgresql/data
    environment:
      - POSTGRES_DB=contextgraph
      - POSTGRES_PASSWORD=password

  redis:
    image: redis:7-alpine
    volumes:
      - redisdata:/data

volumes:
  pgdata:
  redisdata:

Security Considerations

Data Isolation

  • Row-Level Security: PostgreSQL RLS enforces tenant isolation
  • Encryption at Rest: AES-256 for all stored data
  • Encryption in Transit: TLS 1.3 for all connections
  • API Key Hashing: bcrypt for stored API keys

Compliance

StandardFeatures
SOC 2Audit logging, access controls, encryption
HIPAABAA available, PHI handling procedures
GDPRData export, deletion, consent tracking
ISO 27001Security controls, incident response

Audit Trail

// All operations are logged
interface AuditLog {
  id: string;
  tenantId: string;
  userId: string;
  action: string;       // 'claim.create', 'decision.approve', etc.
  resource: string;
  resourceId: string;
  metadata: Record<string, unknown>;
  ip: string;
  userAgent: string;
  timestamp: Date;
}

Monitoring & Observability

Metrics (Prometheus)

# Key metrics
- contextgraph_decisions_total{tenant, status}
- contextgraph_policy_evaluations_total{tenant, effect}
- contextgraph_api_latency_seconds{endpoint, method}
- contextgraph_claims_count{tenant}
- contextgraph_agents_active{tenant}

Logging (Structured JSON)

{
  "level": "info",
  "timestamp": "2024-03-15T10:30:00Z",
  "service": "api",
  "tenantId": "tenant_123",
  "requestId": "req_abc",
  "message": "Decision approved",
  "decisionId": "dec_xyz",
  "duration_ms": 45
}

Alerting

AlertThreshold
API Error Rate> 1% for 5 min
API Latency P99> 500ms for 5 min
Database Connections> 80% pool
Queue Backlog> 1000 jobs
Decision Failure Rate> 5% for 10 min

Next Steps

  1. Phase 1: Core API + PostgreSQL multi-tenancy
  2. Phase 2: Dashboard MVP + Basic auth
  3. Phase 3: SSO integration + Advanced policies
  4. Phase 4: Self-hosted Helm chart
  5. Phase 5: Compliance certifications

Contributing

Thank you for your interest in contributing to ContextGraph OS!

Getting Started

  1. Fork the repository
  2. Clone your fork
  3. Install dependencies: pnpm install
  4. Build: pnpm -r build
  5. Run tests: pnpm -r test

Development Workflow

Branch Naming

  • feature/description - New features
  • fix/description - Bug fixes
  • docs/description - Documentation
  • refactor/description - Code refactoring

Commit Messages

Follow conventional commits:

feat: add new visualization format
fix: resolve policy evaluation order
docs: update SDK documentation
refactor: simplify storage interface
test: add CKG integration tests

Pull Requests

  1. Create a feature branch
  2. Make your changes
  3. Write/update tests
  4. Update documentation
  5. Submit PR with description

Code Style

  • TypeScript strict mode
  • ESLint + Prettier
  • 100% test coverage for new code

Testing

# Run all tests
pnpm -r test

# Run specific package tests
pnpm --filter @contextgraph/sdk test

# Run with coverage
pnpm -r test:coverage

Documentation

  • Update relevant docs for any API changes
  • Add JSDoc comments to public APIs
  • Include examples in documentation

Questions?

Open an issue for questions or discussions.

Changelog

All notable changes to ContextGraph OS.

[0.3.0] - 2024

Added

  • Visualization Package (@contextgraph/viz)

    • DOT/Graphviz renderer
    • Mermaid diagram renderer
    • D3.js JSON format
    • SVG standalone renderer
    • High-level APIs: visualizeProvenance, visualizeEntities, visualizeDecisions
  • Semantic Reasoning (@contextgraph/reasoning)

    • RelationRegistry with transitive, symmetric, inverse relations
    • RuleEngine with forward chaining inference
    • Pattern matching with variable bindings
    • ContradictionDetector for finding and resolving conflicts
    • Built-in rules for common inference patterns
  • Decision Recommendations (@contextgraph/recommendations)

    • Similarity-based precedent matching
    • Configurable matching weights
    • Risk assessment with customizable patterns
    • Feedback loop for accuracy improvement

[0.2.0] - 2024

Added

  • Policy Templates & Simulation

    • 6 built-in templates
    • PolicyTemplateManager
    • PolicySimulator for dry-run testing
  • Agent Hierarchies & Delegation

    • AgentHierarchyManager
    • Capability delegation
    • Cascade operations
  • OpenTelemetry Integration (@contextgraph/telemetry)

    • Tracing with spans
    • Metrics (Counter, Gauge, Histogram)
    • Structured logging
    • Multiple exporters

[0.1.0] - 2024

Added

  • Initial release
  • Core foundation packages
  • Contextual Knowledge Graph
  • Provenance ledger
  • Decision Trace Graph
  • Policy enforcement
  • Agent execution framework
  • SDK and CLI
  • REST API