crs

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: Mar 11, 2026 License: AGPL-3.0 Imports: 38 Imported by: 0

README

CRS - Constraint Reasoning System

The Constraint Reasoning System (CRS) provides persistent, queryable state for agent sessions. It maintains six synchronized indexes and supports cross-session persistence via BadgerDB backup/restore.

Architecture

┌─────────────────────────────────────────────────────────────────────────────┐
│                              CRS ARCHITECTURE                                │
├─────────────────────────────────────────────────────────────────────────────┤
│                                                                              │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                          CRS Instance                                  │  │
│  │                                                                        │  │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐  │  │
│  │  │ Proof Index  │ │ Constraint   │ │ Similarity   │ │ Dependency   │  │  │
│  │  │              │ │ Index        │ │ Index        │ │ Index        │  │  │
│  │  └──────────────┘ └──────────────┘ └──────────────┘ └──────────────┘  │  │
│  │                                                                        │  │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────────────────────┐   │  │
│  │  │ History      │ │ Streaming    │ │ Clause Index (CDCL)          │   │  │
│  │  │ Index        │ │ Index        │ │                              │   │  │
│  │  └──────────────┘ └──────────────┘ └──────────────────────────────┘   │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                     │                                        │
│                                     ▼                                        │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                        BadgerJournal (WAL)                            │  │
│  │                                                                        │  │
│  │  • CRC32 checksums for integrity                                      │  │
│  │  • Streaming replay for recovery                                      │  │
│  │  • Degraded mode support                                              │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                     │                                        │
│                                     ▼                                        │
│  ┌───────────────────────────────────────────────────────────────────────┐  │
│  │                      PersistenceManager (GR-33)                       │  │
│  │                                                                        │  │
│  │  • SaveBackup() - Compressed, integrity-verified backups              │  │
│  │  • LoadBackup() - Restore with version compatibility check            │  │
│  │  • flock-based file locking                                           │  │
│  │  • Atomic file operations                                             │  │
│  └───────────────────────────────────────────────────────────────────────┘  │
│                                                                              │
└─────────────────────────────────────────────────────────────────────────────┘

Disk Persistence

CRS state can be persisted to disk and restored across sessions using the PersistenceManager.

File Layout
~/.aleutian/crs/
├── {project_hash}/                     # Per-project isolation
│   ├── badger/                         # Live BadgerDB directory
│   │   ├── MANIFEST
│   │   └── *.sst
│   ├── backups/                        # Backup files
│   │   ├── latest.backup.gz            # Gzipped BadgerDB backup
│   │   └── latest.backup.gz.lock       # flock advisory lock
│   ├── metadata.json                   # Backup metadata with hash
│   └── export.json                     # JSON export (portable)
│
└── index.json                          # All projects index
Session Initialization (Load Backup)
package main

import (
    "context"
    "fmt"
    "log/slog"
    "path/filepath"
    "time"

    "github.com/AleutianAI/AleutianFOSS/services/trace/agent/mcts/crs"
)

func InitializeSession(ctx context.Context, projectPath string) (*crs.CRS, *crs.BadgerJournal, *crs.PersistenceManager, error) {
    // 1. Compute project hash from path (consistent across sessions)
    projectHash := crs.ComputeProjectHash(projectPath)

    // 2. Create persistence manager
    pmConfig := crs.DefaultPersistenceConfig()
    pm, err := crs.NewPersistenceManager(&pmConfig)
    if err != nil {
        return nil, nil, nil, fmt.Errorf("create persistence manager: %w", err)
    }

    // 3. Create journal for this session
    journalConfig := crs.JournalConfig{
        SessionID:  "session-" + time.Now().Format("20060102-150405"),
        Path:       filepath.Join(pm.ProjectDir(projectHash), "badger"),
        SyncWrites: true,
    }
    journal, err := crs.NewBadgerJournal(journalConfig)
    if err != nil {
        pm.Close()
        return nil, nil, nil, fmt.Errorf("create journal: %w", err)
    }

    // 4. Create CRS instance
    crsInstance := crs.New(nil)

    // 5. LOAD EXISTING BACKUP (if one exists)
    metadata, err := crsInstance.LoadCheckpointFromDisk(ctx, pm, projectHash, journal)
    if err != nil {
        slog.Error("failed to restore from backup, starting fresh",
            slog.String("project_hash", projectHash),
            slog.String("error", err.Error()),
        )
    } else if metadata != nil {
        slog.Info("restored CRS state from backup",
            slog.String("project_hash", projectHash),
            slog.Int64("generation", metadata.Generation),
            slog.Duration("backup_age", metadata.Age()),
        )
    } else {
        slog.Info("no previous backup found, starting fresh",
            slog.String("project_hash", projectHash),
        )
    }

    return crsInstance, journal, pm, nil
}
Session Shutdown (Save Backup)
func ShutdownSession(ctx context.Context, crsInstance *crs.CRS, journal *crs.BadgerJournal, pm *crs.PersistenceManager, projectHash string) error {
    defer pm.Close()
    defer journal.Close()

    // Save checkpoint to disk
    metadata, err := crsInstance.SaveCheckpointToDisk(ctx, pm, projectHash, journal)
    if err != nil {
        return fmt.Errorf("save checkpoint: %w", err)
    }

    slog.Info("session state saved",
        slog.String("project_hash", projectHash),
        slog.Int64("generation", metadata.Generation),
        slog.Int64("compressed_bytes", metadata.CompressedSize),
        slog.Float64("compression_ratio", metadata.CompressionRatio()),
    )

    return nil
}
Complete Session Lifecycle
func main() {
    ctx := context.Background()
    projectPath := "/path/to/my/project"

    // Initialize session and restore previous state
    crsInstance, journal, pm, err := InitializeSession(ctx, projectPath)
    if err != nil {
        log.Fatalf("Failed to initialize: %v", err)
    }

    projectHash := crs.ComputeProjectHash(projectPath)

    // ... do work with crsInstance ...

    // Option A: Direct Apply + manual journal append
    delta := &crs.ProofDelta{
        Updates: map[string]crs.ProofNumber{
            "node1": {Proof: 10, Status: crs.ProofStatusExpanded},
        },
    }
    if _, err := crsInstance.Apply(ctx, delta); err != nil {
        log.Printf("Apply failed: %v", err)
    }
    if err := journal.Append(ctx, delta); err != nil {
        log.Printf("Journal append failed: %v", err)
    }

    // Option B: Via Bridge (recommended — handles Apply + journal automatically)
    // bridge := integration.NewBridge(crsInstance, nil, integration.WithJournal(journal))
    // bridge.RunActivity(ctx, activity, input)
    //   → activity.Execute() → delta
    //   → crs.Apply(delta)        (automatic)
    //   → journal.Append(delta)   (automatic)

    // Shutdown and save state
    if err := ShutdownSession(ctx, crsInstance, journal, pm, projectHash); err != nil {
        log.Fatalf("Failed to save state: %v", err)
    }
}
Checking Backup Status
func CheckBackupStatus(projectPath string) error {
    projectHash := crs.ComputeProjectHash(projectPath)

    pm, err := crs.NewPersistenceManager(nil)
    if err != nil {
        return err
    }
    defer pm.Close()

    if !pm.HasBackup(projectHash) {
        fmt.Printf("No backup exists for project %s\n", projectHash)
        return nil
    }

    metadata, err := pm.GetBackupMetadata(projectHash)
    if err != nil {
        return fmt.Errorf("read metadata: %w", err)
    }

    fmt.Printf("Backup found:\n")
    fmt.Printf("  Project Hash: %s\n", metadata.ProjectHash)
    fmt.Printf("  Created At:   %s\n", time.UnixMilli(metadata.CreatedAt).Format(time.RFC3339))
    fmt.Printf("  Age:          %s\n", metadata.Age().Round(time.Second))
    fmt.Printf("  Generation:   %d\n", metadata.Generation)
    fmt.Printf("  Delta Count:  %d\n", metadata.DeltaCount)
    fmt.Printf("  Compressed:   %d bytes\n", metadata.CompressedSize)
    fmt.Printf("  Uncompressed: %d bytes\n", metadata.UncompressedSize)
    fmt.Printf("  Ratio:        %.1f%%\n", metadata.CompressionRatio()*100)

    return nil
}
Error Handling
import "errors"

func HandleRestoreError(err error) {
    switch {
    case errors.Is(err, crs.ErrBackupNotFound):
        // No backup exists - normal for first run
        log.Println("No previous backup found, starting fresh")

    case errors.Is(err, crs.ErrBackupCorrupted):
        // Backup failed integrity check
        log.Println("Backup corrupted, starting fresh")

    case errors.Is(err, crs.ErrBackupVersionMismatch):
        // BadgerDB version changed
        log.Println("Backup incompatible with current BadgerDB version")

    case errors.Is(err, crs.ErrBackupLockFailed):
        // Another process holds the lock
        log.Println("Could not acquire lock - another process may be using this project")

    default:
        log.Printf("Restore failed: %v", err)
    }
}

Execute Loop Integration (CRS-WIRE-01)

CRS receives data from the execute phase through three paths:

┌──────────────────────────────────────────────────────────────────────────────┐
│                                                                              │
│  Execute Loop                                                               │
│       │                                                                     │
│       ├──→ PATH 1: Execution Counting                                       │
│       │    recordTraceStep() → TraceStepToStepRecord() → CRS.RecordStep()  │
│       │    Feeds: CountToolExecutions() → circuit breaker count fallback    │
│       │                                                                     │
│       ├──→ PATH 2: Proof Numbers                                            │
│       │    updateProofNumber() → CRS.UpdateProofNumber()                   │
│       │    Feeds: CheckCircuitBreaker() → proof-based decisions            │
│       │                                                                     │
│       └──→ PATH 3: Learning Loop                                            │
│            emitCoordinatorEvent() → Coordinator.HandleEvent()              │
│              → Activities analyze outcome → produce Delta                  │
│              → Bridge.Apply(delta) → CRS indexes updated                   │
│              → Journal.Append(delta) → persisted for recovery              │
│            Feeds: Learned constraints, CDCL clauses, session restore       │
│                                                                              │
│  Events emitted: ToolSelected, ToolExecuted, ToolFailed,                   │
│                  CircuitBreaker, SemanticRepetition, CycleDetected,        │
│                  GraphRefreshed                                             │
│                                                                              │
│  Journal modes:                                                             │
│    enableSessionRestore=true  → persistent (disk, survives restart)        │
│    enableSessionRestore=false → in-memory (within-session learning only)   │
│                                                                              │
└──────────────────────────────────────────────────────────────────────────────┘

For the full data flow documentation, see docs/opensource/trace/mcts/02_crs_state_management.md, section "Execute Loop Integration: How CRS Gets Its Data".

Key Components

File Description
crs.go Core CRS implementation with Apply/Snapshot
journal.go BadgerJournal WAL with Backup/Restore
trace_recorder.go TraceStep recording and TraceStepToStepRecord bridge
persistence.go PersistenceManager for disk backup/restore
hash.go Project hash utilities
types.go Delta types, indexes, constraints
serializer.go JSON export/import for portability
history.go Delta history tracking (GR-35)

Observability

Prometheus Metrics
Metric Type Description
crs_backup_duration_seconds Histogram Time to create backup
crs_restore_duration_seconds Histogram Time to restore from backup
crs_backup_size_bytes Gauge Compressed backup size
crs_backup_operations_total Counter Total backup operations
crs_backup_age_seconds Gauge Age of most recent backup
OpenTelemetry Spans
  • crs.Persistence.SaveBackup
  • crs.Persistence.LoadBackup
  • journal.Backup
  • journal.Restore
  • crs.SaveCheckpointToDisk
  • crs.LoadCheckpointFromDisk

Testing

# Run persistence tests
go test ./services/trace/agent/mcts/crs/... -v -run "TestPersistence"

# Run hash tests
go test ./services/trace/agent/mcts/crs/... -v -run "TestProjectHash"

# Run integration test
go test ./services/trace/agent/mcts/crs/... -v -run "TestPersistenceIntegration"

# Run all CRS tests
go test ./services/trace/agent/mcts/crs/...

Documentation

Overview

Package crs provides the Code Reasoning State (CRS) - the central mutable state container for the Aleutian Hybrid MCTS system.

Architecture Overview

CRS sits between the application layer (activities) and the algorithm layer, providing immutable snapshots for reading and delta-based mutations for writing.

┌─────────────────────────────────────────────────────────────────────────┐
│                         APPLICATION LAYER                                │
│                    (Activities: Search, Constraint, etc.)               │
└────────────────────────────────┬────────────────────────────────────────┘
                                 │
                                 │ 1. Snapshot() - get immutable view
                                 │ 2. Apply(delta) - atomic mutation
                                 ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         CRS (Code Reasoning State)                       │
│                                                                          │
│  ┌────────────────────────────────────────────────────────────────────┐ │
│  │                       Snapshot (Immutable)                         │ │
│  │  ┌─────────┐ ┌────────────┐ ┌────────────┐ ┌────────────┐        │ │
│  │  │  Proof  │ │ Constraint │ │ Similarity │ │ Dependency │        │ │
│  │  │  Index  │ │   Index    │ │   Index    │ │   Index    │        │ │
│  │  └─────────┘ └────────────┘ └────────────┘ └────────────┘        │ │
│  │  ┌─────────┐ ┌────────────┐                                       │ │
│  │  │ History │ │ Streaming  │                                       │ │
│  │  │  Index  │ │   Index    │                                       │ │
│  │  └─────────┘ └────────────┘                                       │ │
│  └────────────────────────────────────────────────────────────────────┘ │
│                                                                          │
│  Key Operations:                                                         │
│  • Snapshot() → CRSSnapshot (copy-on-write, immutable)                  │
│  • Apply(delta) → validates, updates indexes, increments generation     │
│  • Query() → cross-index query API                                      │
│  • Generation() → current state version                                 │
└────────────────────────────────┬────────────────────────────────────────┘
                                 │
                                 │ Algorithms produce deltas
                                 ▼
┌─────────────────────────────────────────────────────────────────────────┐
│                         ALGORITHM LAYER                                  │
│  ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐          │
│  │ PN-MCTS │ │  CDCL   │ │   TMS   │ │   HTN   │ │ MinHash │  ...     │
│  └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘          │
│                                                                          │
│  Pure Functions: Process(ctx, snapshot, input) → (output, delta, error) │
└─────────────────────────────────────────────────────────────────────────┘

Core Concepts

## Snapshot

A snapshot is an immutable view of CRS at a point in time. Algorithms read from snapshots, never from CRS directly. This ensures:

  • Thread safety: multiple algorithms can read the same snapshot concurrently
  • Consistency: all reads within an algorithm see the same state
  • Performance: copy-on-write semantics make snapshots cheap to create

## Delta

A delta represents a change to CRS state. Algorithms produce deltas as output, which activities then merge and apply to CRS. Deltas support:

  • Validation: deltas are validated before application
  • Merging: multiple deltas can be combined
  • Conflict detection: overlapping changes are detected
  • Atomicity: either all changes apply or none do

## Generation

Every Apply() increments the generation counter. This enables:

  • Cache invalidation: detect when cached data is stale
  • Ordering: determine which state is newer
  • Debugging: trace state evolution

Thread Safety

CRS uses a single RWMutex for thread safety:

  • Snapshot() acquires read lock briefly, returns immutable snapshot
  • Apply() acquires write lock, validates and applies delta
  • Multiple readers can hold snapshots concurrently
  • Writer blocks until all readers release

All CRS methods accept context.Context and respect cancellation.

Observability

CRS implements the eval.Evaluable interface, exposing:

  • Properties: snapshot_immutability, delta_idempotence, generation_monotonic
  • Metrics: crs_snapshot_duration, crs_apply_duration, crs_generation
  • Health checks: index connectivity, memory bounds

Hard/Soft Signal Boundary

CRS enforces the hard/soft signal boundary from CB-28C:

  • Hard signals (compiler errors, test results): can mark nodes DISPROVEN
  • Soft signals (LLM feedback): cannot mark nodes DISPROVEN

Delta validation rejects any delta that violates this boundary.

Usage Example

// Create CRS
crs := crs.New(crs.DefaultConfig())

// Get snapshot for algorithm
snapshot := crs.Snapshot()

// Algorithm processes snapshot
output, delta, err := algorithm.Process(ctx, snapshot, input)
if err != nil {
    return err
}

// Apply delta to CRS
metrics, err := crs.Apply(ctx, delta)
if err != nil {
    return fmt.Errorf("apply delta: %w", err)
}

// Check new generation
fmt.Printf("New generation: %d\n", crs.Generation())

Index

Constants

View Source
const (
	// ProofKeyAnalyticsHotspotsDone indicates hotspots analysis was run.
	ProofKeyAnalyticsHotspotsDone = "analytics:hotspots:done"

	// ProofKeyAnalyticsHotspotsFound indicates hotspots were found.
	ProofKeyAnalyticsHotspotsFound = "analytics:hotspots:found"

	// ProofKeyAnalyticsDeadCodeDone indicates dead code analysis was run.
	ProofKeyAnalyticsDeadCodeDone = "analytics:dead_code:done"

	// ProofKeyAnalyticsDeadCodeFound indicates dead code was found.
	ProofKeyAnalyticsDeadCodeFound = "analytics:dead_code:found"

	// ProofKeyAnalyticsCyclesDone indicates cycle detection was run.
	ProofKeyAnalyticsCyclesDone = "analytics:cycles:done"

	// ProofKeyAnalyticsCyclesFound indicates cycles were found.
	ProofKeyAnalyticsCyclesFound = "analytics:cycles:found"

	// ProofKeyAnalyticsPathDone indicates path analysis was run.
	ProofKeyAnalyticsPathDone = "analytics:path:done"

	// ProofKeyAnalyticsPathFound indicates a path was found.
	ProofKeyAnalyticsPathFound = "analytics:path:found"

	// ProofKeyAnalyticsCouplingDone indicates coupling analysis was run.
	ProofKeyAnalyticsCouplingDone = "analytics:coupling:done"

	// ProofKeyAnalyticsCouplingFound indicates coupling issues were found.
	ProofKeyAnalyticsCouplingFound = "analytics:coupling:found"

	// ProofKeyAnalyticsReferencesDone indicates references analysis was run.
	ProofKeyAnalyticsReferencesDone = "analytics:references:done"

	// ProofKeyAnalyticsReferencesFound indicates references were found.
	ProofKeyAnalyticsReferencesFound = "analytics:references:found"
)

ProofKeyAnalytics* are typed constants for analytics proof keys. Using constants prevents typos and enables IDE autocomplete.

View Source
const (
	// DefaultCheckpointMaxAge is the maximum age of a checkpoint before invalidation.
	// GR-36 Code Review Fix: S2 - No magic numbers.
	DefaultCheckpointMaxAge = 7 * 24 * time.Hour

	// DefaultMaxFilesToRefresh is the maximum files to mark dirty after restore.
	// Beyond this threshold, a full rebuild is more efficient.
	DefaultMaxFilesToRefresh = 1000

	// CheckpointKeyHashBytes is the number of bytes used for checkpoint key hash.
	// GR-36 Code Review Fix: R1 - Use 16 bytes (128 bits) to match ProjectHashLength.
	CheckpointKeyHashBytes = 16

	// DefaultSessionRestoreRetries is the number of retry attempts on transient failures.
	// GR-36 Code Review Fix: R4 - Add retry logic.
	DefaultSessionRestoreRetries = 3
)
View Source
const BadgerDBVersion = "v4.9.1"

BadgerDBVersion is the version of BadgerDB used for backup compatibility.

IMPORTANT: This MUST match the version in go.mod. When upgrading BadgerDB:

  1. Update go.mod: go get github.com/dgraph-io/badger/v4@vX.Y.Z
  2. Update this constant to match
  3. Test backup/restore with existing backups

Reference: go.mod -> github.com/dgraph-io/badger/v4 Build verification: go generate ./... should fail if mismatch (TODO: add check)

View Source
const CurrentSchemaVersion = "1.0"

CurrentSchemaVersion is the backup schema version.

View Source
const DefaultCircuitBreakerThreshold = 2

DefaultCircuitBreakerThreshold is the fallback threshold for tool execution count when no proof data exists. If a tool has been executed this many times, the circuit breaker fires. This is used for backwards compatibility with code that hasn't yet integrated proof numbers.

NOTE: This value should match maxRepeatedToolCalls in execute.go. Feb 13, 2026: Lowered from 5 to 2 based on integration test evidence. Threshold=5 allowed too much wasteful exploration (Test 95: 5 identical calls). Threshold=2 prevents tool loops while still allowing legitimate multi-call patterns. CRS-16: Per-tool overrides are configured via Config.CircuitBreakerThresholds.

View Source
const DefaultDeltaQueryChannelSize = 10

DefaultDeltaQueryChannelSize is the buffer size for the query channel.

View Source
const DefaultDeltaRecordChannelSize = 100

DefaultDeltaRecordChannelSize is the buffer size for the record channel.

View Source
const DefaultInitialProofNumber uint64 = 10

DefaultInitialProofNumber is the starting proof number for new nodes. Represents moderate cost to prove - not too easy, not too hard.

View Source
const DefaultMaxDeltaRecords = 1000

DefaultMaxDeltaRecords is the default maximum number of delta records to keep.

View Source
const DefaultMaxDependencyEdges = 100_000

DefaultMaxDependencyEdges is the default limit for dependency edges export.

View Source
const DefaultMaxSimilarityPairs = 100_000

DefaultMaxSimilarityPairs is the default limit for similarity pairs export.

View Source
const MaxAnalyticsHistoryRecords = 100

MaxAnalyticsHistoryRecords is the maximum number of analytics records to keep.

View Source
const MaxPropagationDepth = 100

MaxPropagationDepth prevents runaway propagation in cyclic graphs. 100 levels is sufficient for any reasonable decision tree.

View Source
const MaxResultsPerRecord = 50

MaxResultsPerRecord limits stored result IDs to prevent memory bloat.

View Source
const ProjectHashLength = 16 // 64 bits = 16 hex chars

ProjectHashLength is the expected length of a project hash (SHA256 truncated).

View Source
const ProofNumberInfinite uint64 = ^uint64(0)

ProofNumberInfinite represents a disproven path (infinite cost to prove). Using max uint64 value.

Variables

View Source
var (
	// ErrGraphQueryClosed is returned when operations are attempted on a closed adapter.
	ErrGraphQueryClosed = errors.New("graph query adapter is closed")

	// ErrGraphNotAvailable is returned when graph is not available.
	ErrGraphNotAvailable = errors.New("graph is not available")
)
View Source
var (
	// ErrJournalClosed is returned when operations are called on a closed journal.
	ErrJournalClosed = errors.New("journal is closed")

	// ErrJournalCorrupted is returned when journal data fails integrity check.
	ErrJournalCorrupted = errors.New("journal entry corrupted (CRC mismatch)")

	// ErrJournalFull is returned when journal exceeds MaxJournalBytes.
	ErrJournalFull = errors.New("journal size limit exceeded")

	// ErrJournalDegraded is returned when journal is operating in degraded mode.
	ErrJournalDegraded = errors.New("journal operating in degraded mode")

	// ErrJournalSequenceGap is returned when replay detects sequence number gaps.
	ErrJournalSequenceGap = errors.New("journal sequence number gap detected")

	// ErrNilDeltaJournal is returned when attempting to append nil delta.
	ErrNilDeltaJournal = errors.New("delta must not be nil")
)
View Source
var (
	// ErrBackupCorrupted indicates backup data failed integrity check.
	ErrBackupCorrupted = errors.New("backup corrupted: content hash mismatch")

	// ErrBackupVersionMismatch indicates BadgerDB version incompatibility.
	ErrBackupVersionMismatch = errors.New("backup BadgerDB version mismatch")

	// ErrBackupNotFound indicates no backup exists for the project.
	ErrBackupNotFound = errors.New("backup not found")

	// ErrBackupLockFailed indicates file lock acquisition failed.
	ErrBackupLockFailed = errors.New("failed to acquire backup lock")

	// ErrPersistenceManagerClosed indicates the manager has been closed.
	ErrPersistenceManagerClosed = errors.New("persistence manager is closed")

	// ErrRestoreInProgress indicates a restore is already in progress.
	ErrRestoreInProgress = errors.New("restore already in progress")
)
View Source
var (
	// ErrSessionIdentifierNil is returned when session identifier is nil.
	ErrSessionIdentifierNil = errors.New("session identifier must not be nil")

	// ErrProjectPathEmpty is returned when project path is empty.
	ErrProjectPathEmpty = errors.New("project path must not be empty")

	// ErrCheckpointTooOld is returned when checkpoint exceeds max age.
	ErrCheckpointTooOld = errors.New("checkpoint too old")

	// ErrProjectHashMismatch is returned when project hash doesn't match checkpoint.
	ErrProjectHashMismatch = errors.New("project hash mismatch")

	// ErrSchemaVersionMismatch is returned when schema versions don't match.
	ErrSchemaVersionMismatch = errors.New("schema version mismatch")

	// ErrTooManyModifiedFiles is returned when modified file count exceeds threshold.
	ErrTooManyModifiedFiles = errors.New("too many modified files, full rebuild recommended")
)
View Source
var (
	// ErrNilContext is returned when context is nil.
	ErrNilContext = errors.New("context must not be nil")

	// ErrNilDelta is returned when delta is nil.
	ErrNilDelta = errors.New("delta must not be nil")

	// ErrDeltaValidation is returned when delta validation fails.
	ErrDeltaValidation = errors.New("delta validation failed")

	// ErrDeltaConflict is returned when deltas conflict.
	ErrDeltaConflict = errors.New("delta conflict detected")

	// ErrSnapshotStale is returned when snapshot is too old.
	ErrSnapshotStale = errors.New("snapshot is stale")

	// ErrIndexNotFound is returned when an index doesn't exist.
	ErrIndexNotFound = errors.New("index not found")

	// ErrHardSoftBoundaryViolation is returned when soft signal attempts hard action.
	ErrHardSoftBoundaryViolation = errors.New("soft signal cannot perform hard action")

	// ErrApplyRollback is returned when apply partially failed and rolled back.
	ErrApplyRollback = errors.New("apply failed, changes rolled back")
)
View Source
var DefaultClauseConfig = ClausePersistence{
	Scope:      ClauseScopeProject,
	TTL:        7 * 24 * time.Hour,
	MaxClauses: 1000,
}

DefaultClauseConfig is the default clause persistence configuration.

View Source
var ErrDeltaHistoryClosed = errors.New("delta history worker is closed")

ErrDeltaHistoryClosed is returned when querying a closed delta history worker.

View Source
var ErrMetadataCorrupted = errors.New("metadata corrupted: hash mismatch")

ErrMetadataCorrupted indicates the metadata file failed integrity check.

View Source
var ErrUnknownQueryType = errors.New("unknown analytics query type")

ErrUnknownQueryType is returned when an unknown query type is used.

Functions

func ComputeProjectHash

func ComputeProjectHash(projectPath string) string

ComputeProjectHash generates a project hash from a path.

Description:

Computes a SHA256 hash of the project path and returns the first
16 hex characters. This provides consistent project identification
across sessions.

Inputs:

  • projectPath: Absolute path to the project root.

Outputs:

  • string: 16-character hex hash.

Thread Safety: Safe for concurrent use (stateless).

func FindFilesModifiedSince

func FindFilesModifiedSince(
	ctx context.Context,
	projectPath string,
	since time.Time,
	config *SessionRestorerConfig,
) ([]string, error)

FindFilesModifiedSince finds files changed since a timestamp.

Description:

Uses git status for git repositories (much faster) and falls back
to mtime scan for non-git directories. Returns an error if more
files than MaxFilesToRefresh are found.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • projectPath: Path to the project root.
  • since: Find files modified after this time.
  • config: Configuration with UseGitStatus and MaxFilesToRefresh.

Outputs:

  • []string: Paths of modified files relative to project root.
  • error: Non-nil on failure or if too many files modified.

Thread Safety: Safe for concurrent use.

func GetStateKey

func GetStateKey(step StepRecord) string

GetStateKey generates a normalized state key for cycle detection.

Description:

Creates a canonical state representation that ignores step numbers
but captures the semantically relevant state:
- Tool name (what tool was used/selected)
- Outcome (success/failure/forced)
- Actor (who made the decision)

This enables detecting semantic cycles like:
"router selects list_packages -> executes -> router selects list_packages"
regardless of step numbers or timestamps.

Inputs:

step - The step record to generate a key for.

Outputs:

string - Normalized state key (e.g., "tool:list_packages:success:router").

func GetToolStateKey

func GetToolStateKey(step StepRecord) string

GetToolStateKey generates a tool-focused state key for cycle detection.

Description:

Creates a simpler state key focused only on tool usage.
Use this for detecting tool repetition cycles specifically.

Inputs:

step - The step record to generate a key for.

Outputs:

string - Tool-focused state key (e.g., "list_packages:success").

func RegisterDeltaTypesForSSE

func RegisterDeltaTypesForSSE()

RegisterDeltaTypesForSSE exposes gob type registration for SSE decoding. CRS-27: Called by the SSE handler to ensure delta types are registered before decoding NATS messages.

Thread Safety: Safe for concurrent use (uses sync.Once internally).

func ValidateProjectHash

func ValidateProjectHash(hash string) error

ValidateProjectHash checks if a hash is valid.

Description:

Validates that the project hash is a hex string of 8-64 characters.
Used by GR-33, GR-34, and GR-36 for consistent validation.

Inputs:

  • hash: The project hash to validate.

Outputs:

  • error: Non-nil if validation fails.

Thread Safety: Safe for concurrent use (stateless).

Types

type Actor

type Actor string

Actor identifies who made a decision in the agent reasoning process.

const (
	// ActorRouter is the tool router (e.g., granite4:micro-h).
	ActorRouter Actor = "router"

	// ActorMainAgent is the main LLM (e.g., glm-4.7-flash).
	ActorMainAgent Actor = "main_agent"

	// ActorSystem is the system (circuit breaker, retries, timeouts).
	ActorSystem Actor = "system"
)

func (Actor) IsValid

func (a Actor) IsValid() bool

IsValid returns true if the actor is a known value.

func (Actor) String

func (a Actor) String() string

String returns the string representation of Actor.

type AnalyticsDelta

type AnalyticsDelta struct {

	// Record is the analytics record to add.
	Record *AnalyticsRecord
	// contains filtered or unexported fields
}

AnalyticsDelta represents an analytics query to record in CRS.

Description:

When applied, records the analytics query in CRS history,
sets relevant proof numbers, and emits events for coordination.

Thread Safety: NOT safe for concurrent modification.

func CreateAnalyticsDelta

func CreateAnalyticsDelta(
	queryType AnalyticsQueryType,
	resultCount int,
	executionMs int64,
) *AnalyticsDelta

createAnalyticsDeltaFromParams is a helper to create an analytics delta.

Description:

Convenience function for tools to create analytics deltas.

Inputs:

  • queryType: The type of analytics query.
  • resultCount: Number of results.
  • executionMs: Execution time in milliseconds.

Outputs:

*AnalyticsDelta: The new delta.

func NewAnalyticsDelta

func NewAnalyticsDelta(source SignalSource, record *AnalyticsRecord) *AnalyticsDelta

NewAnalyticsDelta creates a new analytics delta.

Inputs:

  • source: The signal source (should be SignalSourceHard for analytics).
  • record: The analytics record to add. Must not be nil.

Outputs:

*AnalyticsDelta: The new delta.

func (*AnalyticsDelta) ConflictsWith

func (d *AnalyticsDelta) ConflictsWith(_ Delta) bool

ConflictsWith returns true if this delta conflicts with another.

func (*AnalyticsDelta) IndexesAffected

func (d *AnalyticsDelta) IndexesAffected() []string

IndexesAffected returns which indexes this delta will modify.

Description:

Analytics deltas affect both the analytics history and proof index
(for completion markers).

Thread Safety: Returns a shared slice. Callers must not modify.

func (*AnalyticsDelta) Merge

func (d *AnalyticsDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*AnalyticsDelta) Source

func (d *AnalyticsDelta) Source() SignalSource

func (*AnalyticsDelta) Timestamp

func (d *AnalyticsDelta) Timestamp() int64

func (*AnalyticsDelta) Type

func (d *AnalyticsDelta) Type() DeltaType

Type returns the delta type.

func (*AnalyticsDelta) Validate

func (d *AnalyticsDelta) Validate(_ Snapshot) error

Validate checks if this delta can be applied.

Description:

Validates the analytics record structure. Ensures required fields
are present and results match the query type.

Inputs:

snapshot: Current CRS state (unused for analytics).

Outputs:

error: Non-nil if validation fails.

Thread Safety: Safe for concurrent use (read-only operation).

type AnalyticsHistory

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

AnalyticsHistory stores recent analytics records.

Description:

Thread-safe ring buffer for analytics history with O(1) insertions.
Uses channel-based concurrency pattern per project standards.

Thread Safety: Safe for concurrent use.

func NewAnalyticsHistory

func NewAnalyticsHistory(maxSize int) *AnalyticsHistory

NewAnalyticsHistory creates a new analytics history.

Inputs:

maxSize: Maximum number of records to keep.

Outputs:

*AnalyticsHistory: The new history instance.

func (*AnalyticsHistory) Add

func (h *AnalyticsHistory) Add(record *AnalyticsRecord)

Add records an analytics query.

Inputs:

record: The record to add. Must not be nil.

func (*AnalyticsHistory) All

func (h *AnalyticsHistory) All() []*AnalyticsRecord

All returns a copy of all records in chronological order.

Outputs:

[]*AnalyticsRecord: Copy of all records.

func (*AnalyticsHistory) GetLast

func (h *AnalyticsHistory) GetLast(queryType AnalyticsQueryType) *AnalyticsRecord

GetLast returns the most recent record of a given type.

Inputs:

queryType: The type of query to find.

Outputs:

*AnalyticsRecord: The most recent matching record, or nil if not found.

func (*AnalyticsHistory) HasRun

func (h *AnalyticsHistory) HasRun(queryType AnalyticsQueryType) bool

HasRun returns true if a query type has been run.

Inputs:

queryType: The type of query to check.

Outputs:

bool: True if the query type has been run.

func (*AnalyticsHistory) Size

func (h *AnalyticsHistory) Size() int

Size returns the number of records stored.

type AnalyticsQueryParams

type AnalyticsQueryParams struct {
	// Limit is the maximum results for hotspots queries.
	Limit int `json:"limit,omitempty"`

	// FromSymbol is the starting symbol for path queries.
	FromSymbol string `json:"from_symbol,omitempty"`

	// ToSymbol is the target symbol for path queries.
	ToSymbol string `json:"to_symbol,omitempty"`

	// TargetSymbol is the symbol for references queries.
	TargetSymbol string `json:"target_symbol,omitempty"`

	// PackageName is the package for coupling queries.
	PackageName string `json:"package_name,omitempty"`
}

AnalyticsQueryParams contains typed parameters for analytics queries.

Description:

Replaces map[string]any to comply with CLAUDE.md §4.5.
Each field is optional - only populate what's relevant for the query type.

Thread Safety: NOT safe for concurrent modification.

type AnalyticsQueryType

type AnalyticsQueryType string

AnalyticsQueryType identifies the type of analytics query.

const (
	// AnalyticsQueryHotspots finds the most connected symbols.
	AnalyticsQueryHotspots AnalyticsQueryType = "hotspots"

	// AnalyticsQueryDeadCode finds unreachable code.
	AnalyticsQueryDeadCode AnalyticsQueryType = "dead_code"

	// AnalyticsQueryCycles detects cyclic dependencies.
	AnalyticsQueryCycles AnalyticsQueryType = "cycles"

	// AnalyticsQueryPath finds the shortest path between symbols.
	AnalyticsQueryPath AnalyticsQueryType = "path"

	// AnalyticsQueryReferences finds all references to a symbol.
	AnalyticsQueryReferences AnalyticsQueryType = "references"

	// AnalyticsQueryCoupling computes package coupling metrics.
	AnalyticsQueryCoupling AnalyticsQueryType = "coupling"
)

func (AnalyticsQueryType) IsValid

func (t AnalyticsQueryType) IsValid() bool

IsValid returns true if this is a known analytics query type.

type AnalyticsRecord

type AnalyticsRecord struct {
	// ID is a unique identifier for this record.
	ID string `json:"id"`

	// QueryType identifies the type of analytics query.
	QueryType AnalyticsQueryType `json:"query_type"`

	// QueryTime is when the query was executed (Unix milliseconds UTC).
	QueryTime int64 `json:"query_time"`

	// GraphGeneration is the graph version when this analytics was run.
	// Used to correlate analytics with graph state.
	GraphGeneration int64 `json:"graph_generation,omitempty"`

	// Params contains typed query parameters.
	// Replaces QueryParams map[string]any per CLAUDE.md §4.5.
	Params AnalyticsQueryParams `json:"params,omitempty"`

	// QueryParams is DEPRECATED. Use Params instead.
	// Retained for backwards compatibility with existing records.
	QueryParams map[string]any `json:"query_params,omitempty"`

	// ResultCount is the number of results returned.
	ResultCount int `json:"result_count"`

	// Results contains symbol IDs found (for hotspots, dead_code).
	// Limited to MaxResultsPerRecord entries.
	Results []string `json:"results,omitempty"`

	// Cycles contains detected cycles (for cycle detection).
	// Each inner slice is a cycle path.
	Cycles [][]string `json:"cycles,omitempty"`

	// Path contains the path between symbols (for path queries).
	Path []string `json:"path,omitempty"`

	// ExecutionMs is how long the query took in milliseconds.
	// Must be non-negative.
	ExecutionMs int64 `json:"execution_ms"`
}

AnalyticsRecord represents a single analytics query and its results.

Description:

Stores metadata and results from an analytics query for CRS tracking.
Used for learning and activity coordination.

Thread Safety: NOT safe for concurrent modification.

func NewAnalyticsRecord

func NewAnalyticsRecord(
	queryType AnalyticsQueryType,
	queryTime int64,
	resultCount int,
	executionMs int64,
) *AnalyticsRecord

NewAnalyticsRecord creates a new analytics record with the given parameters.

Description:

Creates a new AnalyticsRecord with a unique ID. The ID includes a
monotonic counter to ensure uniqueness even at millisecond precision.

Inputs:

  • queryType: The type of analytics query.
  • queryTime: When the query was executed (Unix milliseconds UTC).
  • resultCount: Number of results returned.
  • executionMs: How long the query took in milliseconds.

Outputs:

*AnalyticsRecord: The new record.

Thread Safety: Safe for concurrent use.

func (*AnalyticsRecord) GetProofDoneKey

func (r *AnalyticsRecord) GetProofDoneKey() string

GetProofDoneKey returns the proof key for "done" status.

Description:

Returns the typed constant for known query types. For unknown types,
generates a key dynamically and logs a warning.

Thread Safety: Safe for concurrent use.

func (*AnalyticsRecord) GetProofFoundKey

func (r *AnalyticsRecord) GetProofFoundKey() string

GetProofFoundKey returns the proof key for "found" status.

Description:

Returns the typed constant for known query types. For unknown types,
generates a key dynamically and logs a warning.

Thread Safety: Safe for concurrent use.

func (*AnalyticsRecord) HasResults

func (r *AnalyticsRecord) HasResults() bool

HasResults returns true if the record has results.

func (*AnalyticsRecord) TruncateResults

func (r *AnalyticsRecord) TruncateResults() *AnalyticsRecord

TruncateResults limits Results to MaxResultsPerRecord.

Description:

Called before storing to prevent memory bloat from large result sets.
Logs if truncation occurs.

Thread Safety: NOT safe for concurrent use.

func (*AnalyticsRecord) WithCycles

func (r *AnalyticsRecord) WithCycles(cycles [][]string) *AnalyticsRecord

WithCycles adds cycle data to the record.

func (*AnalyticsRecord) WithGraphGeneration

func (r *AnalyticsRecord) WithGraphGeneration(gen int64) *AnalyticsRecord

WithGraphGeneration sets the graph generation for correlation.

Description:

Records the graph version when this analytics was run.
Enables correlation of analytics results with graph state.

Thread Safety: NOT safe for concurrent use.

func (*AnalyticsRecord) WithParams

func (r *AnalyticsRecord) WithParams(params map[string]any) *AnalyticsRecord

WithParams adds query parameters to the record.

DEPRECATED: Use WithTypedParams instead per CLAUDE.md §4.5.

func (*AnalyticsRecord) WithPath

func (r *AnalyticsRecord) WithPath(path []string) *AnalyticsRecord

WithPath adds path data to the record.

func (*AnalyticsRecord) WithResults

func (r *AnalyticsRecord) WithResults(results []string) *AnalyticsRecord

WithResults adds symbol IDs to the record.

func (*AnalyticsRecord) WithTypedParams

func (r *AnalyticsRecord) WithTypedParams(params AnalyticsQueryParams) *AnalyticsRecord

WithTypedParams sets typed query parameters.

Description:

Sets the Params field with typed parameters. Preferred over
WithParams which uses map[string]any.

Thread Safety: NOT safe for concurrent use.

type ApplyMetrics

type ApplyMetrics struct {
	// DeltaType is the type of delta applied.
	DeltaType DeltaType

	// ApplyDuration is how long the apply took.
	ApplyDuration time.Duration

	// ValidationDuration is how long validation took.
	ValidationDuration time.Duration

	// IndexesUpdated identifies which indexes were updated (bitmask).
	// Use IndexesUpdated.Has(IndexProof) to check, .Names() for string slice.
	IndexesUpdated IndexMask

	// EntriesModified is the number of entries modified.
	EntriesModified int

	// OldGeneration is the generation before apply.
	OldGeneration int64

	// NewGeneration is the generation after apply.
	NewGeneration int64
}

ApplyMetrics contains metrics about an Apply operation.

type BackupMetadata

type BackupMetadata struct {
	// ProjectHash identifies the project this backup belongs to.
	ProjectHash string `json:"project_hash"`

	// CreatedAt is when this backup was created (Unix milliseconds UTC).
	CreatedAt int64 `json:"created_at"`

	// BadgerVersion is the BadgerDB version used to create the backup.
	BadgerVersion string `json:"badger_version"`

	// ContentHash is the SHA256 hash of the compressed backup file.
	ContentHash string `json:"content_hash"`

	// UncompressedSize is the original size before compression.
	UncompressedSize int64 `json:"uncompressed_size"`

	// CompressedSize is the size of the compressed backup file.
	CompressedSize int64 `json:"compressed_size"`

	// Generation is the CRS generation at backup time.
	Generation int64 `json:"generation"`

	// SessionID is the session that created this backup.
	SessionID string `json:"session_id,omitempty"`

	// DeltaCount is the number of deltas in the journal.
	DeltaCount int64 `json:"delta_count"`

	// SchemaVersion is the backup format version for future compatibility.
	SchemaVersion string `json:"schema_version"`

	// ExportPath is the path to the companion JSON export (if created).
	ExportPath string `json:"export_path,omitempty"`

	// MetadataHash is the SHA256 hash of this metadata (excluding this field).
	// Used to detect metadata file corruption (P2 fix: I2).
	MetadataHash string `json:"metadata_hash,omitempty"`
}

BackupMetadata contains information about a backup for verification.

Description:

Stored alongside the backup file to enable integrity verification
and version compatibility checking. Uses int64 timestamps per
CLAUDE.md standards.

Thread Safety: Immutable after creation.

func (*BackupMetadata) Age

func (m *BackupMetadata) Age() time.Duration

Age returns the age of the backup.

func (*BackupMetadata) CompressionRatio

func (m *BackupMetadata) CompressionRatio() float64

CompressionRatio returns the compression ratio.

type BackupOptions

type BackupOptions struct {
	// CreateJSONExport also creates a portable JSON export.
	CreateJSONExport bool

	// SessionID to record in metadata.
	SessionID string
}

BackupOptions configures backup behavior.

type BadgerJournal

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

BadgerJournal implements Journal using BadgerDB.

Description:

Provides persistent WAL storage using BadgerDB. Each delta is stored
with a CRC32 checksum for integrity verification.

Key format: "delta:{session_id}:{seq_num:016d}" Value format: [4-byte CRC32][gob-encoded delta]

Thread Safety: Safe for concurrent use.

func NewBadgerJournal

func NewBadgerJournal(config JournalConfig) (*BadgerJournal, error)

NewBadgerJournal creates a journal at the specified path.

Inputs:

config - Journal configuration. Must pass Validate().

Outputs:

*BadgerJournal - Ready-to-use journal.
error - Non-nil if BadgerDB initialization fails and AllowDegraded is false.

Thread Safety: Safe for concurrent use.

func (*BadgerJournal) Append

func (j *BadgerJournal) Append(ctx context.Context, delta Delta) error

Append writes a delta with CRC checksum.

func (*BadgerJournal) AppendBatch

func (j *BadgerJournal) AppendBatch(ctx context.Context, deltas []Delta) error

AppendBatch writes multiple deltas atomically.

func (*BadgerJournal) Backup

func (j *BadgerJournal) Backup(ctx context.Context, w io.Writer) error

Backup creates a portable backup of the journal.

Description:

Uses BadgerDB's built-in streaming backup to export all KV pairs.
The writer receives a binary stream suitable for later restore.
Caller is responsible for compression and storage.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • w: Writer to receive backup stream. Must not be nil.

Outputs:

  • error: Non-nil if backup fails.

Thread Safety: Safe for concurrent use.

func (*BadgerJournal) Checkpoint

func (j *BadgerJournal) Checkpoint(ctx context.Context) error

Checkpoint marks current position and truncates old entries.

func (*BadgerJournal) Close

func (j *BadgerJournal) Close() error

Close syncs and releases resources.

func (*BadgerJournal) DB

func (j *BadgerJournal) DB() *badger.DB

DB returns the underlying BadgerDB wrapper.

Description:

Provides access to the underlying database for backup operations.
Used by PersistenceManager.SaveBackup and LoadBackup.

INTERNAL USE ONLY: This method is intended for use by the persistence package only. External callers should use the Journal interface methods (Append, Replay, Checkpoint) rather than accessing the database directly. Direct database access bypasses journal safety mechanisms including:

  • CRC checksums on entries
  • Sequence number tracking
  • Degraded mode handling

Outputs:

  • *badger.DB: The database wrapper. May be nil in degraded mode.

Thread Safety: Safe for concurrent use.

func (*BadgerJournal) IsAvailable

func (j *BadgerJournal) IsAvailable() bool

IsAvailable returns false if journal is in degraded mode or closed.

func (*BadgerJournal) IsDegraded

func (j *BadgerJournal) IsDegraded() bool

IsDegraded returns true if operating with reduced durability.

func (*BadgerJournal) Replay

func (j *BadgerJournal) Replay(ctx context.Context) ([]Delta, error)

Replay returns all deltas since last checkpoint with validation.

func (*BadgerJournal) ReplayStream

func (j *BadgerJournal) ReplayStream(ctx context.Context) (<-chan DeltaOrError, error)

ReplayStream returns a channel for streaming replay.

func (*BadgerJournal) Restore

func (j *BadgerJournal) Restore(ctx context.Context, r io.Reader) error

Restore loads state from a backup.

Description:

Uses BadgerDB's built-in Load function to restore from a backup
stream. After restore, re-initializes internal state (sequence
numbers, counters).

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • r: Reader with backup stream. Must not be nil.

Outputs:

  • error: Non-nil if restore fails.

Limitations:

  • Restore is all-or-nothing: failure leaves journal in undefined state.
  • Caller should discard journal and create new one on restore failure.

Thread Safety: NOT safe for concurrent use with Append/Replay. Caller must ensure exclusive access during restore.

func (*BadgerJournal) Stats

func (j *BadgerJournal) Stats() JournalStats

Stats returns journal statistics.

func (*BadgerJournal) Sync

func (j *BadgerJournal) Sync() error

Sync flushes pending writes.

type CRS

type CRS interface {
	eval.Evaluable

	// Snapshot returns an immutable view of the current state.
	//
	// Description:
	//
	//   Creates a copy-on-write snapshot that algorithms can read from safely.
	//   The snapshot is immutable and will not change even if Apply() is called.
	//
	// Outputs:
	//   - Snapshot: The immutable snapshot. Never nil.
	//
	// Thread Safety: Safe for concurrent use.
	Snapshot() Snapshot

	// Apply atomically applies a delta to the state.
	//
	// Description:
	//
	//   Validates the delta, then applies all changes atomically. If any
	//   index update fails, all changes are rolled back.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - delta: The delta to apply. Must not be nil.
	//
	// Outputs:
	//   - ApplyMetrics: Metrics about the apply operation.
	//   - error: Non-nil if apply failed.
	//
	// Thread Safety: Safe for concurrent use. Acquires write lock.
	Apply(ctx context.Context, delta Delta) (ApplyMetrics, error)

	// Generation returns the current state version.
	//
	// Description:
	//
	//   Generation increments with each successful Apply(). Use for cache
	//   invalidation and ordering.
	//
	// Outputs:
	//   - int64: The current generation. Always >= 0.
	//
	// Thread Safety: Safe for concurrent use.
	Generation() int64

	// Checkpoint creates a restorable checkpoint for chaos testing.
	//
	// Description:
	//
	//   Creates a checkpoint that can be restored later. Used for chaos
	//   testing and debugging.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - Checkpoint: The checkpoint. Never nil on success.
	//   - error: Non-nil if checkpoint creation failed.
	//
	// Thread Safety: Safe for concurrent use.
	Checkpoint(ctx context.Context) (Checkpoint, error)

	// Restore returns to a previous checkpoint.
	//
	// Description:
	//
	//   Restores CRS to the state at the checkpoint. All changes since
	//   the checkpoint are discarded.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - cp: The checkpoint to restore. Must not be nil.
	//
	// Outputs:
	//   - error: Non-nil if restore failed.
	//
	// Thread Safety: Safe for concurrent use. Acquires write lock.
	Restore(ctx context.Context, cp Checkpoint) error

	// RecordStep adds a step to the CRS step history.
	//
	// Description:
	//
	//   Validates the step, then atomically appends to the session's history.
	//   Recording is non-blocking - steps are added directly to the index.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - step: The step to record. Must pass Validate().
	//
	// Outputs:
	//   - error: Non-nil if validation fails or context cancelled.
	//
	// Thread Safety: Safe for concurrent use.
	RecordStep(ctx context.Context, step StepRecord) error

	// GetStepHistory returns all steps for a session, ordered by step number.
	//
	// Inputs:
	//   - sessionID: The session to query. Must not be empty.
	//
	// Outputs:
	//   - []StepRecord: Steps in order. Empty slice if session not found.
	//
	// Thread Safety: Safe for concurrent use.
	GetStepHistory(sessionID string) []StepRecord

	// GetLastStep returns the most recent step for a session.
	//
	// Inputs:
	//   - sessionID: The session to query. Must not be empty.
	//
	// Outputs:
	//   - *StepRecord: The last step, or nil if session not found/empty.
	//
	// Thread Safety: Safe for concurrent use.
	GetLastStep(sessionID string) *StepRecord

	// CountToolExecutions returns how many times a tool was EXECUTED in a session.
	//
	// Description:
	//
	//   Counts steps where Decision == DecisionExecuteTool, not DecisionSelectTool.
	//   For circuit breaker, we care about actual executions, not selection attempts.
	//
	// Inputs:
	//   - sessionID: The session to query. Must not be empty.
	//   - tool: The tool name to count. Must not be empty.
	//
	// Outputs:
	//   - int: Number of executions. 0 if session not found or tool not used.
	//
	// Thread Safety: Safe for concurrent use.
	CountToolExecutions(sessionID string, tool string) int

	// GetStepsByActor returns steps filtered by actor.
	//
	// Inputs:
	//   - sessionID: The session to query. Must not be empty.
	//   - actor: The actor to filter by.
	//
	// Outputs:
	//   - []StepRecord: Matching steps in order. Empty slice if none found.
	//
	// Thread Safety: Safe for concurrent use.
	GetStepsByActor(sessionID string, actor Actor) []StepRecord

	// GetStepsByOutcome returns steps filtered by outcome.
	//
	// Inputs:
	//   - sessionID: The session to query. Must not be empty.
	//   - outcome: The outcome to filter by.
	//
	// Outputs:
	//   - []StepRecord: Matching steps in order. Empty slice if none found.
	//
	// Thread Safety: Safe for concurrent use.
	GetStepsByOutcome(sessionID string, outcome Outcome) []StepRecord

	// ClearStepHistory removes all steps for a session.
	//
	// Description:
	//
	//   Use when starting a new query or when session is complete.
	//
	// Inputs:
	//   - sessionID: The session to clear. Must not be empty.
	//
	// Thread Safety: Safe for concurrent use.
	ClearStepHistory(sessionID string)

	// UpdateProofNumber applies a proof update to a node.
	//
	// Description:
	//
	//   Updates the proof number for a node based on the update type:
	//   - Increment: Increases proof number (failure = path harder to prove)
	//   - Decrement: Decreases proof number (success = path easier to prove)
	//   - Disproven: Marks path as disproven (infinite cost)
	//   - Proven: Marks path as proven (solution found)
	//
	//   IMPORTANT: Proof number represents COST TO PROVE. Lower = better.
	//   This is counterintuitive but matches PN-MCTS semantics.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - update: The proof update to apply. Must pass Validate().
	//
	// Outputs:
	//   - error: Non-nil if validation fails or context cancelled.
	//
	// Thread Safety: Safe for concurrent use.
	UpdateProofNumber(ctx context.Context, update ProofUpdate) error

	// GetProofStatus returns the current proof status for a node.
	//
	// Description:
	//
	//   Returns the full ProofNumber struct for a node, including proof number,
	//   disproof number, status, and last update time.
	//
	// Inputs:
	//   - nodeID: The node to query. Must not be empty.
	//
	// Outputs:
	//   - ProofNumber: The current proof status. Zero value if node not found.
	//   - bool: True if node was found, false otherwise.
	//
	// Thread Safety: Safe for concurrent use.
	GetProofStatus(nodeID string) (ProofNumber, bool)

	// CheckCircuitBreaker checks if the circuit breaker should fire for a tool.
	//
	// Description:
	//
	//   Checks if a tool path is disproven or has exhausted proof number.
	//   This replaces ad-hoc counting in execute.go with proof-based logic.
	//
	//   Circuit breaker fires when:
	//   - Node status is ProofStatusDisproven
	//   - Proof number >= ProofNumberInfinite (exhausted)
	//
	// Inputs:
	//   - sessionID: The session to check. Must not be empty.
	//   - tool: The tool name to check. Must not be empty.
	//
	// Outputs:
	//   - CircuitBreakerResult: Contains ShouldFire bool and Reason.
	//
	// Thread Safety: Safe for concurrent use.
	CheckCircuitBreaker(sessionID string, tool string) CircuitBreakerResult

	// PropagateDisproof propagates disproof to parent decisions.
	//
	// Description:
	//
	//   When a node is disproven, parent decisions that depended on it have
	//   their proof numbers increased (harder to prove). Uses BFS with depth
	//   limit to prevent stack overflow.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - nodeID: The disproven node. Must not be empty.
	//
	// Outputs:
	//   - int: Number of nodes affected by propagation.
	//
	// Thread Safety: Safe for concurrent use.
	PropagateDisproof(ctx context.Context, nodeID string) int

	// AddClause adds a learned clause to the constraint index.
	//
	// Description:
	//
	//   Adds a clause learned from CDCL analysis. Checks for semantic
	//   duplicates and handles LRU eviction when MaxClauses is reached.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - clause: The clause to add. Must pass Validate().
	//
	// Outputs:
	//   - error: Non-nil if validation fails or context cancelled.
	//
	// Thread Safety: Safe for concurrent use.
	AddClause(ctx context.Context, clause *Clause) error

	// CheckDecisionAllowed checks if a proposed decision violates learned clauses.
	//
	// Description:
	//
	//   Builds an assignment from the step history and the proposed decision,
	//   then checks against all learned clauses using watched literals.
	//
	// Inputs:
	//   - sessionID: The session to check. Must not be empty.
	//   - tool: The proposed tool selection.
	//
	// Outputs:
	//   - bool: True if the decision is allowed.
	//   - string: Reason if the decision is blocked.
	//
	// Thread Safety: Safe for concurrent use.
	CheckDecisionAllowed(sessionID string, tool string) (bool, string)

	// GarbageCollectClauses removes expired clauses based on TTL.
	//
	// Description:
	//
	//   Removes clauses older than their TTL. Call periodically or after
	//   session ends to clean up stale clauses.
	//
	// Outputs:
	//   - int: Number of clauses removed.
	//
	// Thread Safety: Safe for concurrent use.
	GarbageCollectClauses() int

	// RecordSimilarity records a similarity pair between two queries.
	//
	// Description:
	//
	//   Records that two queries (identified by tool+query keys) are
	//   semantically similar with the given distance score. This data
	//   persists across sessions via the journal and is used for
	//   cross-session semantic dedup.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - key1: First query key (e.g., "tool:query_hash"). Must not be empty.
	//   - key2: Second query key. Must not be empty or equal to key1.
	//   - distance: Similarity distance (0 = identical, higher = less similar).
	//     Must be non-negative.
	//
	// Outputs:
	//   - error: Non-nil if validation fails or context cancelled.
	//
	// Thread Safety: Safe for concurrent use.
	RecordSimilarity(ctx context.Context, key1, key2 string, distance float64) error

	// GetSimilarity returns the recorded similarity distance between two keys.
	//
	// Inputs:
	//   - key1: First query key. Must not be empty.
	//   - key2: Second query key. Must not be empty.
	//
	// Outputs:
	//   - float64: The distance, or -1 if no similarity recorded.
	//   - bool: True if a similarity record exists.
	//
	// Thread Safety: Safe for concurrent use.
	GetSimilarity(key1, key2 string) (float64, bool)

	// SetSessionID sets the current session ID for delta history tracking.
	//
	// Description:
	//
	//   Deltas recorded via Apply() will be associated with this session ID.
	//   Call this at the start of each agent session.
	//
	// Inputs:
	//   - sessionID: The session identifier. Can be empty to clear.
	//
	// Thread Safety: Safe for concurrent use.
	SetSessionID(sessionID string)

	// ApplyWithSource applies a delta with explicit source and metadata tracking.
	//
	// Description:
	//
	//   Like Apply(), but allows specifying a custom source string and metadata
	//   for delta history recording. Use this when you want to track which
	//   activity or component caused the delta.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - delta: The delta to apply. Must not be nil.
	//   - source: Human-readable source identifier (e.g., "AwarenessActivity").
	//   - metadata: Optional additional context for the delta.
	//
	// Outputs:
	//   - ApplyMetrics: Metrics about the apply operation.
	//   - error: Non-nil on validation failure or apply error.
	//
	// Thread Safety: Safe for concurrent use.
	ApplyWithSource(ctx context.Context, delta Delta, source string, metadata map[string]string) (ApplyMetrics, error)

	// DeltaHistory returns the delta history for querying.
	//
	// Description:
	//
	//   Returns the delta history worker which provides methods to query
	//   what deltas were applied, when, and by whom. Use this to understand
	//   causality and build reasoning traces.
	//
	// Outputs:
	//   - DeltaHistoryView: Read-only view of delta history. May be nil if not initialized.
	//
	// Thread Safety: Safe for concurrent use.
	DeltaHistory() DeltaHistoryView

	// Close releases resources held by the CRS.
	//
	// Description:
	//
	//   Stops background workers (like delta history). Should be called when
	//   the CRS is no longer needed to prevent goroutine leaks.
	//
	// Thread Safety: Safe for concurrent use. Idempotent.
	Close()

	// SetGraphProvider sets the graph query provider.
	//
	// Description:
	//
	//   Registers a GraphQuery implementation that will be included in all
	//   future snapshots. Activities can then call snapshot.GraphQuery() to
	//   access the actual code graph.
	//
	//   Call this after the graph is initialized and after graph refreshes
	//   to update the adapter with the new graph state.
	//
	// Inputs:
	//   - provider: The graph query implementation. May be nil to clear.
	//
	// Thread Safety: Safe for concurrent use.
	SetGraphProvider(provider GraphQuery)

	// InvalidateGraphCache invalidates graph-backed dependency index caches.
	//
	// Description:
	//
	//   Called after the graph is refreshed (GR-29) to ensure subsequent queries
	//   see fresh data. Invalidates the Size() cache in GraphBackedDependencyIndex,
	//   which in turn invalidates the CRSGraphAdapter analytics cache (PageRank,
	//   communities, edge count).
	//
	//   This is a no-op if graph-backed index is not in use (legacy mode).
	//
	// Thread Safety: Safe for concurrent use.
	InvalidateGraphCache()

	// GetAnalyticsHistory returns all analytics records.
	//
	// Description:
	//
	//   Returns a copy of all analytics records in chronological order.
	//
	// Outputs:
	//   - []*AnalyticsRecord: Copy of all records.
	//
	// Thread Safety: Safe for concurrent use.
	GetAnalyticsHistory() []*AnalyticsRecord

	// GetLastAnalytics returns the most recent analytics of a given type.
	//
	// Description:
	//
	//   Searches analytics history for the most recent record of the
	//   specified query type.
	//
	// Inputs:
	//   - queryType: The type of analytics query to find.
	//
	// Outputs:
	//   - *AnalyticsRecord: The most recent matching record, or nil if not found.
	//
	// Thread Safety: Safe for concurrent use.
	GetLastAnalytics(queryType AnalyticsQueryType) *AnalyticsRecord

	// HasRunAnalytics checks if a specific analytics type has been run.
	//
	// Description:
	//
	//   Returns true if an analytics query of the given type has been
	//   recorded in history.
	//
	// Inputs:
	//   - queryType: The type of analytics query to check.
	//
	// Outputs:
	//   - bool: True if the query type has been run.
	//
	// Thread Safety: Safe for concurrent use.
	HasRunAnalytics(queryType AnalyticsQueryType) bool
}

CRS is the central mutable state container for the Aleutian Hybrid MCTS system.

Description:

CRS manages 6 indexes that algorithms read from and write to. It provides
immutable snapshots for reading and delta-based mutations for writing.

Thread Safety: Safe for concurrent use. Uses RWMutex internally.

func New

func New(config *Config) CRS

New creates a new CRS instance.

Inputs:

  • config: Configuration. If nil, uses DefaultConfig().

Outputs:

  • CRS: The new CRS instance. Never nil.

Thread Safety: Safe for concurrent use.

type CRSExport

type CRSExport struct {
	// SessionID identifies the session this export belongs to.
	SessionID string `json:"session_id"`

	// Generation is the CRS generation at export time.
	Generation int64 `json:"generation"`

	// Timestamp is when this export was created (Unix milliseconds UTC).
	Timestamp int64 `json:"timestamp"`

	// Indexes contains all six CRS indexes in exportable form.
	Indexes IndexesExport `json:"indexes"`

	// Summary provides high-level metrics about the reasoning state.
	Summary ReasoningSummary `json:"summary"`
}

CRSExport is the JSON-serializable representation of CRS state.

Description:

Contains a complete snapshot of CRS state suitable for JSON export.
Includes all six indexes and computed summary metrics.

type Checkpoint

type Checkpoint struct {
	// ID is the unique checkpoint identifier.
	ID string

	// Generation is the generation at checkpoint time.
	Generation int64

	// CreatedAt is when the checkpoint was created (Unix milliseconds UTC).
	CreatedAt int64
	// contains filtered or unexported fields
}

Checkpoint represents a restorable state checkpoint.

type CircuitBreakerResult

type CircuitBreakerResult struct {
	// ShouldFire is true if the circuit breaker should intervene.
	ShouldFire bool `json:"should_fire"`

	// Reason explains why the circuit breaker fired (or didn't).
	Reason string `json:"reason,omitempty"`

	// ProofNumber is the current proof number for the checked path.
	ProofNumber uint64 `json:"proof_number,omitempty"`

	// Status is the current proof status.
	Status ProofStatus `json:"status,omitempty"`
}

CircuitBreakerResult contains the result of a circuit breaker check.

Description:

Returned by CheckCircuitBreaker to indicate whether the circuit breaker
should fire and why.

type Clause

type Clause struct {
	// ID is the unique clause identifier.
	ID string `json:"id"`

	// Literals are the disjuncts (OR'd together).
	Literals []Literal `json:"literals"`

	// Source indicates where this clause was learned.
	// Must be HARD for CDCL-learned clauses.
	Source SignalSource `json:"source"`

	// LearnedAt is when this clause was created (Unix milliseconds UTC).
	LearnedAt int64 `json:"learned_at"`

	// FailureType categorizes what kind of failure created this clause.
	FailureType FailureType `json:"failure_type"`

	// SessionID is the session where this was learned (for debugging).
	SessionID string `json:"session_id,omitempty"`

	// UseCount tracks how often this clause blocks decisions (for GC).
	UseCount int64 `json:"use_count"`

	// LastUsed is when this clause last blocked a decision (Unix milliseconds UTC).
	LastUsed int64 `json:"last_used,omitempty"`
}

Clause represents a learned constraint in CNF (Conjunctive Normal Form).

Description:

A clause is a disjunction (OR) of literals that must be satisfied.
Learned clauses prevent the agent from repeating the same mistakes.

Example: (¬tool:list_packages ∨ ¬outcome:success ∨ ¬tool:list_packages)
Meaning: "Don't select list_packages after it already succeeded"

Thread Safety: Clause is safe for concurrent read access.

func (*Clause) IsSatisfied

func (c *Clause) IsSatisfied(assignment map[string]bool) bool

IsSatisfied checks if the clause is satisfied by the given assignment.

Description:

A clause is satisfied if ANY literal is true (disjunction).
For a literal to be true:
  - If positive: the variable must be true in the assignment
  - If negated: the variable must be false in the assignment

Inputs:

assignment - Map of variable names to their boolean values.

Outputs:

bool - True if the clause is satisfied.

func (*Clause) IsViolated

func (c *Clause) IsViolated(assignment map[string]bool) bool

IsViolated checks if the clause is violated by the given assignment.

Description:

A clause is violated when ALL literals are false under the assignment.
This is the opposite of IsSatisfied, but with explicit handling:
if a variable is unassigned, the clause is not yet violated.

Inputs:

assignment - Map of variable names to their boolean values.

Outputs:

bool - True if the clause is violated (all literals are false).

func (*Clause) String

func (c *Clause) String() string

String returns the string representation of a Clause.

func (*Clause) Validate

func (c *Clause) Validate() error

Validate checks that the Clause has required fields.

type ClauseCheckResult

type ClauseCheckResult struct {
	// Conflict is true if a clause is violated by the assignment.
	Conflict bool `json:"conflict"`

	// ViolatedClause is the clause that was violated, if any.
	ViolatedClause *Clause `json:"violated_clause,omitempty"`

	// Reason explains why the conflict occurred.
	Reason string `json:"reason,omitempty"`
}

ClauseCheckResult contains the result of checking an assignment against clauses.

type ClausePersistence

type ClausePersistence struct {
	// Scope determines clause visibility.
	Scope ClauseScope

	// TTL is how long clauses remain valid.
	TTL time.Duration

	// MaxClauses is the maximum clauses per scope (LRU eviction).
	MaxClauses int
}

ClausePersistence configures clause storage and garbage collection.

type ClauseScope

type ClauseScope string

ClauseScope determines clause visibility and persistence.

const (
	// ClauseScopeSession: Clauses only valid for current session.
	// TTL: session duration. GC: session end.
	ClauseScopeSession ClauseScope = "session"

	// ClauseScopeProject: Clauses valid across sessions for same project.
	// TTL: 7 days. GC: LRU eviction when MaxClauses reached.
	ClauseScopeProject ClauseScope = "project"

	// ClauseScopeGlobal: Clauses valid across all projects (rare).
	// TTL: 30 days. GC: Manual review required.
	// Use sparingly - only for universal patterns like "don't infinite loop".
	ClauseScopeGlobal ClauseScope = "global"
)

type CompositeDelta

type CompositeDelta struct {

	// Deltas are the contained deltas.
	Deltas []Delta
	// contains filtered or unexported fields
}

CompositeDelta contains multiple deltas for atomic application.

func NewCompositeDelta

func NewCompositeDelta(deltas ...Delta) *CompositeDelta

NewCompositeDelta creates a composite delta from multiple deltas.

func (*CompositeDelta) ConflictsWith

func (d *CompositeDelta) ConflictsWith(other Delta) bool

ConflictsWith returns true if any contained delta conflicts.

func (*CompositeDelta) IndexesAffected

func (d *CompositeDelta) IndexesAffected() []string

IndexesAffected returns all indexes affected by contained deltas.

func (*CompositeDelta) Merge

func (d *CompositeDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*CompositeDelta) Source

func (d *CompositeDelta) Source() SignalSource

func (*CompositeDelta) Timestamp

func (d *CompositeDelta) Timestamp() int64

func (*CompositeDelta) Type

func (d *CompositeDelta) Type() DeltaType

Type returns the delta type.

func (*CompositeDelta) Validate

func (d *CompositeDelta) Validate(snapshot Snapshot) error

Validate checks if all contained deltas can be applied.

type Config

type Config struct {
	// MaxGeneration is the maximum generation before wrapping.
	// Default: 0 (no maximum, wraps at int64 max).
	MaxGeneration int64

	// SnapshotEpochLimit is how many generations a snapshot is valid.
	// Default: 1000 (snapshots older than 1000 generations are stale).
	SnapshotEpochLimit int64

	// EnableMetrics enables metrics collection.
	// Default: true.
	EnableMetrics bool

	// EnableTracing enables OpenTelemetry tracing.
	// Default: true.
	EnableTracing bool

	// CircuitBreakerThresholds maps tool names to their per-tool circuit
	// breaker thresholds. When a tool has been executed this many times in
	// a session (and no proof data exists), the circuit breaker fires.
	// Tools not in this map fall back to DefaultCircuitBreakerThreshold.
	//
	// CRS-16: Per-tool circuit breaker thresholds.
	CircuitBreakerThresholds map[string]int
}

Config configures a CRS instance.

func DefaultConfig

func DefaultConfig() *Config

DefaultConfig returns the default configuration.

func (*Config) Validate

func (c *Config) Validate() error

Validate checks if the configuration is valid.

type Constraint

type Constraint struct {
	// ID is the unique constraint identifier.
	ID string

	// Type is the constraint type.
	Type ConstraintType

	// Nodes are the nodes this constraint affects.
	Nodes []string

	// Expression is the constraint expression.
	Expression string

	// Active indicates if the constraint is currently active.
	Active bool

	// Source indicates where this constraint came from.
	Source SignalSource

	// CreatedAt is when this constraint was created (Unix milliseconds UTC).
	CreatedAt int64
}

Constraint represents a constraint on the search space.

type ConstraintDelta

type ConstraintDelta struct {

	// Add contains constraints to add.
	Add []Constraint

	// Remove contains constraint IDs to remove.
	Remove []string

	// Update contains constraints to update (by ID).
	Update map[string]Constraint
	// contains filtered or unexported fields
}

ConstraintDelta represents changes to constraints.

func NewConstraintDelta

func NewConstraintDelta(source SignalSource) *ConstraintDelta

NewConstraintDelta creates a new constraint delta.

func (*ConstraintDelta) ConflictsWith

func (d *ConstraintDelta) ConflictsWith(other Delta) bool

ConflictsWith returns true if this delta conflicts with another.

func (*ConstraintDelta) IndexesAffected

func (d *ConstraintDelta) IndexesAffected() []string

IndexesAffected returns which indexes this delta will modify.

Thread Safety: Returns a shared slice. Callers must not modify.

func (*ConstraintDelta) Merge

func (d *ConstraintDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*ConstraintDelta) Source

func (d *ConstraintDelta) Source() SignalSource

func (*ConstraintDelta) Timestamp

func (d *ConstraintDelta) Timestamp() int64

func (*ConstraintDelta) Type

func (d *ConstraintDelta) Type() DeltaType

Type returns the delta type.

func (*ConstraintDelta) Validate

func (d *ConstraintDelta) Validate(snapshot Snapshot) error

Validate checks if this delta can be applied.

type ConstraintEntry

type ConstraintEntry struct {
	// ID is the unique constraint identifier.
	ID string `json:"id"`

	// Type is the constraint type: "mutual_exclusion", "implication", "ordering", "resource".
	Type string `json:"type"`

	// Nodes are the node IDs affected by this constraint.
	Nodes []string `json:"nodes"`

	// Expression is the constraint expression if any.
	Expression string `json:"expression,omitempty"`

	// Active indicates if the constraint is currently enforced.
	Active bool `json:"active"`

	// Source indicates signal source: "unknown", "hard", "soft".
	Source string `json:"source"`

	// CreatedAt is when this constraint was created.
	CreatedAt time.Time `json:"created_at"`
}

ConstraintEntry represents a single constraint.

type ConstraintIndexExport

type ConstraintIndexExport struct {
	// Constraints contains all active constraints.
	Constraints []ConstraintEntry `json:"constraints"`
}

ConstraintIndexExport is the serializable form of the Constraint Index.

type ConstraintIndexView

type ConstraintIndexView interface {
	// Get returns a constraint by ID.
	Get(constraintID string) (Constraint, bool)

	// FindByType returns all constraints of a type.
	FindByType(constraintType ConstraintType) []Constraint

	// FindByNode returns all constraints affecting a node.
	FindByNode(nodeID string) []Constraint

	// All returns all constraints.
	All() map[string]Constraint

	// Size returns the number of constraints.
	Size() int

	// GetClause returns a learned clause by ID.
	GetClause(clauseID string) (*Clause, bool)

	// AllClauses returns all learned clauses.
	AllClauses() map[string]*Clause

	// ClauseCount returns the number of learned clauses.
	ClauseCount() int

	// CheckAssignment checks if an assignment violates any learned clauses.
	//
	// Description:
	//
	//   Uses watched literals for efficient checking. Returns the first
	//   violated clause if any.
	//
	// Inputs:
	//
	//   assignment - Map of variable names to their boolean values.
	//
	// Outputs:
	//
	//   ClauseCheckResult - Result of the check.
	CheckAssignment(assignment map[string]bool) ClauseCheckResult
}

ConstraintIndexView provides read-only access to constraints.

Thread Safety: Safe for concurrent use (immutable).

type ConstraintType

type ConstraintType int

ConstraintType represents the type of constraint.

const (
	// ConstraintTypeUnknown is an unknown constraint type.
	ConstraintTypeUnknown ConstraintType = iota

	// ConstraintTypeMutualExclusion means nodes cannot be selected together.
	ConstraintTypeMutualExclusion

	// ConstraintTypeImplication means selecting one node implies another.
	ConstraintTypeImplication

	// ConstraintTypeOrdering means nodes must be selected in order.
	ConstraintTypeOrdering

	// ConstraintTypeResource means nodes share a resource limit.
	ConstraintTypeResource
)

func (ConstraintType) String

func (t ConstraintType) String() string

String returns the string representation of ConstraintType.

type ConstraintUpdate

type ConstraintUpdate struct {
	// ID is the constraint ID.
	ID string `json:"id"`

	// Type is the constraint type (typed enum).
	Type ConstraintType `json:"type"`

	// Nodes are the affected nodes.
	Nodes []string `json:"nodes"`

	// Source indicates where this constraint came from.
	Source SignalSource `json:"source"`
}

ConstraintUpdate represents a constraint being added.

Thread Safety: ConstraintUpdate is immutable after creation.

type CycleAnalysis

type CycleAnalysis struct {
	// SessionID is the session that was analyzed.
	SessionID string `json:"session_id"`

	// TotalSCCs is the total number of strongly connected components.
	TotalSCCs int `json:"total_sccs"`

	// CyclicSCCs contains SCCs with more than one node (actual cycles).
	CyclicSCCs [][]string `json:"cyclic_sccs"`

	// LargestSCCSize is the size of the largest cyclic SCC.
	LargestSCCSize int `json:"largest_scc_size"`

	// AnalysisTime is when the analysis was performed.
	AnalysisTime time.Time `json:"analysis_time"`

	// AnalysisDuration is how long the analysis took.
	AnalysisDuration time.Duration `json:"analysis_duration_ns"`
}

CycleAnalysis contains post-session cycle analysis results.

func AnalyzeSessionCycles

func AnalyzeSessionCycles(ctx context.Context, crsInstance CRS, sessionID string) (*CycleAnalysis, error)

AnalyzeSessionCycles performs comprehensive cycle analysis using Tarjan SCC.

Description:

Called at session end or on-demand for debugging. Uses Tarjan's algorithm
to find ALL strongly connected components in the decision graph.

Unlike Brent's algorithm (which detects single cycles as they form),
Tarjan finds ALL cycles including:
- Multi-step cycles that Brent's might miss
- Complex SCCs with multiple entry/exit points
- Cycles that span multiple decision branches

This is more expensive than Brent's (O(V+E) vs O(1) per step) but provides
complete analysis for debugging and learning.

When to call:

  • Session end (for learning)
  • On-demand debugging
  • NOT in the hot path (too expensive)

Inputs:

ctx - Context for cancellation.
crsInstance - The CRS instance to get step history from.
sessionID - The session to analyze.

Outputs:

*CycleAnalysis - Analysis results. Never nil.
error - Non-nil on failure.

type CycleDetectionResult

type CycleDetectionResult struct {
	// Detected is true if a cycle was found.
	Detected bool

	// Cycle contains the states in the detected cycle.
	// Empty if no cycle detected.
	Cycle []string

	// CycleLength is the length of the detected cycle.
	CycleLength int

	// TailLength is the number of states before the cycle starts.
	TailLength int

	// StateKey is the state key that triggered detection.
	StateKey string

	// Errors contains any non-fatal errors during detection.
	// A-02: Added to report partial failures.
	Errors []error
}

CycleDetectionResult contains the result of a cycle detection check.

func CheckCycleOnStep

func CheckCycleOnStep(
	ctx context.Context,
	crsInstance CRS,
	step StepRecord,
	detector *CycleDetector,
) CycleDetectionResult

CheckCycleOnStep checks for cycles after a step is recorded.

Description:

Called after each step to detect cycles in real-time using Brent's algorithm.
If a cycle is detected, marks all cycle states as disproven in the proof index
and records a circuit breaker step.

This is the main integration point between cycle detection and CRS.

Inputs:

ctx - Context for cancellation and tracing.
crsInstance - The CRS instance for recording and proof updates.
step - The step that was just recorded.
detector - The cycle detector for this session.

Outputs:

CycleDetectionResult - The detection result.

Thread Safety: Safe for concurrent use.

type CycleDetector

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

CycleDetector uses Brent's algorithm for online cycle detection.

Description:

Maintains state between calls to detect cycles incrementally.
Each AddStep() call is O(1) amortized, enabling real-time detection.

Brent's algorithm uses the "teleporting tortoise" approach:
- Hare moves one step at a time
- Tortoise teleports to hare's position when power of 2 is reached
- Cycle detected when hare == tortoise

This is preferred over Floyd's tortoise-hare because:
- Fewer comparisons on average (24-36% fewer iterations)
- Same O(λ + μ) time complexity where λ=cycle length, μ=tail length
- O(1) space (vs O(V) for Tarjan SCC)

Thread Safety: Safe for concurrent use.

func NewCycleDetector

func NewCycleDetector(config *CycleDetectorConfig) *CycleDetector

NewCycleDetector creates a new cycle detector.

Description:

Creates a CycleDetector configured for real-time cycle detection.
The detector maintains state between AddStep() calls to enable
online cycle detection with O(1) amortized time per step.

Inputs:

config - Configuration options. Uses defaults if nil.

Outputs:

*CycleDetector - The configured detector. Never nil.

func (*CycleDetector) AddStep

func (d *CycleDetector) AddStep(state string) CycleDetectionResult

AddStep processes a new state and returns cycle detection result.

Description:

Uses Brent's cycle detection algorithm incrementally.
State is a normalized representation of the current decision.

Time Complexity: O(1) amortized per call.
Space Complexity: O(maxHistory) total.

Inputs:

state - Normalized state string (e.g., "tool:list_packages:success:router").

Outputs:

CycleDetectionResult - Contains detection status and cycle if found.

Thread Safety: Safe for concurrent use.

func (*CycleDetector) Reset

func (d *CycleDetector) Reset()

Reset clears all detector state.

Description:

Use when starting a new session or when cycle has been handled.

Thread Safety: Safe for concurrent use.

func (*CycleDetector) Stats

func (d *CycleDetector) Stats() CycleDetectorStats

Stats returns the detector statistics.

Thread Safety: Safe for concurrent use.

type CycleDetectorConfig

type CycleDetectorConfig struct {
	// MaxHistory is the maximum number of states to track for cycle extraction.
	// Larger values enable detecting longer cycles but use more memory.
	// Default: 1000
	MaxHistory int
}

CycleDetectorConfig configures the cycle detector.

func DefaultCycleDetectorConfig

func DefaultCycleDetectorConfig() *CycleDetectorConfig

DefaultCycleDetectorConfig returns the default cycle detector configuration.

type CycleDetectorStats

type CycleDetectorStats struct {
	StepsProcessed int
	CyclesDetected int
	LastCycleTime  time.Time
	HistorySize    int
	MaxHistory     int
}

CycleDetectorStats contains detector statistics.

type Decision

type Decision string

Decision identifies what type of decision was made.

const (
	// DecisionSelectTool means the router selected a tool.
	DecisionSelectTool Decision = "select_tool"

	// DecisionExecuteTool means a tool was executed.
	DecisionExecuteTool Decision = "execute_tool"

	// DecisionSynthesize means generating a final answer.
	DecisionSynthesize Decision = "synthesize"

	// DecisionCircuitBreaker means the circuit breaker intervened.
	DecisionCircuitBreaker Decision = "circuit_breaker"

	// DecisionRetry means a retry was triggered.
	DecisionRetry Decision = "retry"

	// DecisionComplete means the session completed.
	DecisionComplete Decision = "complete"

	// DecisionError means an error occurred.
	DecisionError Decision = "error"
)

func (Decision) IsValid

func (d Decision) IsValid() bool

IsValid returns true if the decision is a known value.

func (Decision) String

func (d Decision) String() string

String returns the string representation of Decision.

type DecisionGraph

type DecisionGraph struct {
	// Nodes are the node IDs (state keys).
	Nodes []string

	// Edges maps each node to its successors.
	Edges map[string][]string
}

DecisionGraph represents a graph of decisions for cycle analysis.

func BuildDecisionGraph

func BuildDecisionGraph(steps []StepRecord) *DecisionGraph

BuildDecisionGraph constructs a decision graph from step records.

Description:

Builds a directed graph where:
- Nodes are state keys (from GetStateKey)
- Edges connect consecutive states

This graph can then be analyzed with Tarjan SCC for comprehensive
cycle detection.

Inputs:

steps - The step records to build the graph from.

Outputs:

*DecisionGraph - The decision graph. Never nil.

type Delta

type Delta interface {
	// Type returns the delta type.
	Type() DeltaType

	// Validate checks if this delta can be applied to the snapshot.
	//
	// Inputs:
	//   - snapshot: The snapshot to validate against.
	//
	// Outputs:
	//   - error: Non-nil if validation fails.
	Validate(snapshot Snapshot) error

	// Merge combines this delta with another delta.
	//
	// Inputs:
	//   - other: The delta to merge with.
	//
	// Outputs:
	//   - Delta: The merged delta.
	//   - error: Non-nil if deltas cannot be merged.
	Merge(other Delta) (Delta, error)

	// ConflictsWith returns true if this delta conflicts with another.
	//
	// Inputs:
	//   - other: The delta to check for conflicts.
	//
	// Outputs:
	//   - bool: True if deltas conflict.
	ConflictsWith(other Delta) bool

	// Source returns the signal source for this delta.
	Source() SignalSource

	// Timestamp returns when this delta was created (Unix milliseconds UTC).
	Timestamp() int64

	// IndexesAffected returns which indexes this delta will modify.
	IndexesAffected() []string
}

Delta represents an atomic change to CRS state.

Description:

Algorithms produce deltas that describe state changes. Deltas are
validated before application and can be merged with other deltas.

Thread Safety: Implementations must be safe for concurrent use.

type DeltaHistoryView

type DeltaHistoryView interface {
	// GetRange returns deltas between two generations (exclusive start, inclusive end).
	GetRange(ctx context.Context, fromGen, toGen int64) ([]DeltaRecord, error)

	// GetByNode returns all deltas that affected a specific node.
	GetByNode(ctx context.Context, nodeID string) ([]DeltaRecord, error)

	// GetByGeneration returns the delta applied at a specific generation.
	GetByGeneration(ctx context.Context, gen int64) (DeltaRecord, bool, error)

	// Explain returns the causality chain for a node's current state.
	Explain(ctx context.Context, nodeID string) ([]DeltaRecord, error)

	// Size returns the current number of records in history.
	Size(ctx context.Context) (int, error)
}

DeltaHistoryView provides read-only access to delta history from snapshots.

Thread Safety: All methods are safe for concurrent use.

type DeltaHistoryWorker

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

DeltaHistoryWorker manages delta history using a channel-based architecture.

Description:

Uses buffered channels for parallel delta recording and queries.
A single goroutine owns all history state, eliminating mutex contention.
This design follows the "share memory by communicating" principle.

Thread Safety: Safe for concurrent use. All operations go through channels.

func NewDeltaHistoryWorker

func NewDeltaHistoryWorker(maxRecords int, logger *slog.Logger) *DeltaHistoryWorker

NewDeltaHistoryWorker creates a new delta history worker.

Description:

Starts a background goroutine that owns all history state.
The worker accepts record and query requests via buffered channels.

Inputs:

  • maxRecords: Maximum records to keep (uses DefaultMaxDeltaRecords if <= 0).
  • logger: Logger instance. If nil, uses slog.Default().

Outputs:

  • *DeltaHistoryWorker: The new worker. Never nil.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) All

All returns all records in chronological order.

Inputs:

  • ctx: Context for cancellation. Must not be nil.

Outputs:

  • []DeltaRecord: All records in chronological order.
  • error: Non-nil on context cancellation.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) Close

func (w *DeltaHistoryWorker) Close()

Close stops the worker goroutine.

Description:

Signals the worker to stop and waits for it to finish.
Safe to call multiple times.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) Explain

func (w *DeltaHistoryWorker) Explain(ctx context.Context, nodeID string) ([]DeltaRecord, error)

Explain returns the causality chain for a node's current state.

Description:

Returns all deltas that affected the node, in chronological order.
This provides a "reasoning trace" showing how the node reached its current state.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • nodeID: The node to explain.

Outputs:

  • []DeltaRecord: All deltas affecting this node, ordered chronologically.
  • error: Non-nil on context cancellation.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) GetByGeneration

func (w *DeltaHistoryWorker) GetByGeneration(ctx context.Context, gen int64) (DeltaRecord, bool, error)

GetByGeneration returns the delta applied at a specific generation.

Description:

Returns the single delta that was applied to reach the specified generation.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • gen: The generation to look up.

Outputs:

  • DeltaRecord: The matching record (zero value if not found).
  • bool: True if a record was found.
  • error: Non-nil on context cancellation.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) GetByNode

func (w *DeltaHistoryWorker) GetByNode(ctx context.Context, nodeID string) ([]DeltaRecord, error)

GetByNode returns all deltas that affected a specific node.

Description:

Returns all deltas where the node appears in AffectedNodes, ordered chronologically.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • nodeID: The node to look up.

Outputs:

  • []DeltaRecord: Matching records, ordered by generation. Empty if none found.
  • error: Non-nil on context cancellation.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) GetRange

func (w *DeltaHistoryWorker) GetRange(ctx context.Context, fromGen, toGen int64) ([]DeltaRecord, error)

GetRange returns deltas between two generations (exclusive start, inclusive end).

Description:

Returns all deltas where fromGen < generation <= toGen, ordered by generation.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • fromGen: Exclusive lower bound (deltas after this generation).
  • toGen: Inclusive upper bound (deltas up to and including this generation).

Outputs:

  • []DeltaRecord: Matching records, ordered by generation. Empty if none found.
  • error: Non-nil on context cancellation.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) Record

func (w *DeltaHistoryWorker) Record(delta Delta, gen int64, source, sessionID string, metadata map[string]string)

Record adds a delta to history.

Description:

Non-blocking operation using a buffered channel. If the channel is full,
the record is dropped and a warning is logged.

Inputs:

  • delta: The delta that was applied. Must not be nil.
  • gen: The CRS generation after applying the delta.
  • source: What caused this delta (activity name, tool name, etc.).
  • sessionID: The session that applied this delta.
  • metadata: Optional additional context.

Thread Safety: Safe for concurrent use.

func (*DeltaHistoryWorker) Size

func (w *DeltaHistoryWorker) Size(ctx context.Context) (int, error)

Size returns the current number of records in history.

Inputs:

  • ctx: Context for cancellation. Must not be nil.

Outputs:

  • int: Number of records.
  • error: Non-nil on context cancellation.

Thread Safety: Safe for concurrent use.

type DeltaOrError

type DeltaOrError struct {
	// Delta is the decoded delta (nil if error).
	Delta Delta

	// SeqNum is the sequence number of this entry.
	SeqNum uint64

	// Err is set if decoding failed.
	Err error

	// Skipped is true if the delta was corrupted and skipped.
	Skipped bool
}

DeltaOrError is used for streaming replay.

Description:

Yields either a successfully decoded delta or an error.
Skipped indicates the delta was corrupted but skipped per config.

type DeltaRecord

type DeltaRecord struct {
	// ID is a stable identifier for this record (not slice index).
	ID string `json:"id"`

	// Generation is the CRS generation after this delta was applied.
	Generation int64 `json:"generation"`

	// Timestamp is when this delta was applied (Unix milliseconds UTC).
	Timestamp int64 `json:"timestamp"`

	// DeltaType identifies the type of delta (proof, constraint, etc.).
	DeltaType DeltaType `json:"delta_type"`

	// DeltaBytes contains the JSON-serialized delta.
	// Storing as bytes avoids interface serialization issues.
	DeltaBytes []byte `json:"delta_bytes"`

	// Source identifies what caused this delta (activity name, tool, etc.).
	Source string `json:"source"`

	// SessionID identifies the session that applied this delta.
	SessionID string `json:"session_id"`

	// Metadata contains additional context about the delta.
	Metadata map[string]string `json:"metadata,omitempty"`

	// AffectedNodes lists node IDs affected by this delta.
	AffectedNodes []string `json:"affected_nodes"`
}

DeltaRecord captures a single delta application with full context.

Description:

Stores metadata about when and why a delta was applied, along with
the serialized delta itself. Uses stable string IDs for indexing
to avoid invalidation issues with slice-based indexing.

Thread Safety: Immutable after creation.

type DeltaType

type DeltaType int

DeltaType identifies the type of delta.

const (
	// DeltaTypeUnknown is an unknown delta type.
	DeltaTypeUnknown DeltaType = iota

	// DeltaTypeProof updates proof numbers.
	DeltaTypeProof

	// DeltaTypeConstraint updates constraints.
	DeltaTypeConstraint

	// DeltaTypeSimilarity updates similarity scores.
	DeltaTypeSimilarity

	// DeltaTypeDependency updates dependencies.
	DeltaTypeDependency

	// DeltaTypeHistory adds history entries.
	DeltaTypeHistory

	// DeltaTypeStreaming updates streaming statistics.
	DeltaTypeStreaming

	// DeltaTypeComposite contains multiple deltas.
	DeltaTypeComposite

	// DeltaTypeAnalytics records analytics queries.
	// GR-31: Added for analytics CRS routing.
	DeltaTypeAnalytics
)

func (DeltaType) String

func (t DeltaType) String() string

String returns the string representation of DeltaType.

type DependencyDelta

type DependencyDelta struct {

	// AddEdges contains edges to add (from -> to).
	AddEdges [][2]string

	// RemoveEdges contains edges to remove (from -> to).
	RemoveEdges [][2]string
	// contains filtered or unexported fields
}

DependencyDelta represents changes to dependencies.

func NewDependencyDelta

func NewDependencyDelta(source SignalSource) *DependencyDelta

NewDependencyDelta creates a new dependency delta.

func (*DependencyDelta) ConflictsWith

func (d *DependencyDelta) ConflictsWith(other Delta) bool

ConflictsWith returns true if this delta conflicts with another.

func (*DependencyDelta) IndexesAffected

func (d *DependencyDelta) IndexesAffected() []string

IndexesAffected returns which indexes this delta will modify.

Thread Safety: Returns a shared slice. Callers must not modify.

func (*DependencyDelta) Merge

func (d *DependencyDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*DependencyDelta) Source

func (d *DependencyDelta) Source() SignalSource

func (*DependencyDelta) Timestamp

func (d *DependencyDelta) Timestamp() int64

func (*DependencyDelta) Type

func (d *DependencyDelta) Type() DeltaType

Type returns the delta type.

func (*DependencyDelta) Validate

func (d *DependencyDelta) Validate(snapshot Snapshot) error

Validate checks if this delta can be applied.

type DependencyEdge

type DependencyEdge struct {
	// From is the dependent node.
	From string `json:"from"`

	// To is the dependency target.
	To string `json:"to"`

	// Source indicates where this dependency was discovered.
	Source SignalSource `json:"source"`
}

DependencyEdge represents a dependency relationship.

Thread Safety: DependencyEdge is immutable after creation.

type DependencyEdgeExport

type DependencyEdgeExport struct {
	// FromID is the source node (the caller/dependent).
	FromID string `json:"from_id"`

	// ToID is the target node (the callee/dependency).
	ToID string `json:"to_id"`
}

DependencyEdgeExport represents a single dependency edge.

type DependencyIndexExport

type DependencyIndexExport struct {
	// EdgeCount is the number of dependency edges.
	EdgeCount int `json:"edge_count"`

	// Edges contains all dependency edges for full export.
	// Empty for graph-backed indexes (use graph persistence instead).
	Edges []DependencyEdgeExport `json:"edges,omitempty"`

	// Source indicates the index implementation type.
	// Values: "legacy" (edges exported) or "graph_backed" (edges not exported).
	Source string `json:"source,omitempty"`

	// Truncated indicates if Edges was truncated due to limits.
	Truncated bool `json:"truncated,omitempty"`
}

DependencyIndexExport is the serializable form of the Dependency Index.

Export behavior depends on the index implementation:

  • Legacy dependencyGraph: Full edges are exported.
  • GraphBackedDependencyIndex (GR-32): Only EdgeCount is exported; edges live in the graph which has its own persistence.

type DependencyIndexView

type DependencyIndexView interface {
	// DependsOn returns all nodes that nodeID depends on.
	DependsOn(nodeID string) []string

	// DependedBy returns all nodes that depend on nodeID.
	DependedBy(nodeID string) []string

	// HasCycle returns true if there's a cycle involving nodeID.
	HasCycle(nodeID string) bool

	// Size returns the number of dependency edges.
	Size() int

	// AllEdges returns all dependency edges for export.
	//
	// Description:
	//
	//   Returns a deep copy of all dependency edges for serialization.
	//   Format: map[fromID][]toIDs.
	//
	//   For GraphBackedDependencyIndex (GR-32), returns nil since edges
	//   live in the graph which has its own persistence mechanism.
	//
	// Outputs:
	//   - map[string][]string: Copy of forward edges, or nil for graph-backed.
	//
	// Thread Safety: Returns deep copy; caller can modify without affecting source.
	AllEdges() map[string][]string

	// IsGraphBacked returns true if this index delegates to the graph.
	//
	// Description:
	//
	//   When true, AllEdges() returns nil and edge export should be skipped.
	//   The graph has its own persistence; use graph backup instead.
	//
	// Outputs:
	//   - bool: True if graph-backed (GR-32), false for legacy implementation.
	IsGraphBacked() bool
}

DependencyIndexView provides read-only access to dependencies.

Thread Safety: Safe for concurrent use (immutable).

type ErrorCategory

type ErrorCategory string

ErrorCategory categorizes errors for retry and learning logic. Using typed enums instead of raw strings enables compile-time checking and better analytics.

const (
	// ErrorCategoryNone means no error occurred.
	ErrorCategoryNone ErrorCategory = ""

	// ErrorCategoryToolNotFound means the tool doesn't exist.
	ErrorCategoryToolNotFound ErrorCategory = "tool_not_found"

	// ErrorCategoryInvalidParams means the tool parameters were invalid.
	ErrorCategoryInvalidParams ErrorCategory = "invalid_params"

	// ErrorCategoryTimeout means the operation timed out.
	ErrorCategoryTimeout ErrorCategory = "timeout"

	// ErrorCategoryRateLimited means the operation was rate limited.
	ErrorCategoryRateLimited ErrorCategory = "rate_limited"

	// ErrorCategoryPermission means permission was denied.
	ErrorCategoryPermission ErrorCategory = "permission"

	// ErrorCategoryNetwork means a network error occurred.
	ErrorCategoryNetwork ErrorCategory = "network"

	// ErrorCategoryInternal means an internal error occurred.
	ErrorCategoryInternal ErrorCategory = "internal"

	// ErrorCategorySafety means the safety gate blocked the operation.
	ErrorCategorySafety ErrorCategory = "safety"
)

func (ErrorCategory) IsRetryable

func (e ErrorCategory) IsRetryable() bool

IsRetryable returns true if this error category is potentially retryable.

func (ErrorCategory) String

func (e ErrorCategory) String() string

String returns the string representation of ErrorCategory.

type ExportOptions

type ExportOptions struct {
	// MaxSimilarityPairs limits how many similarity pairs to export.
	// 0 = use default (100K), -1 = unlimited.
	MaxSimilarityPairs int

	// MaxDependencyEdges limits how many dependency edges to export.
	// 0 = use default (100K), -1 = unlimited.
	MaxDependencyEdges int
}

ExportOptions configures export behavior.

type ExportResult

type ExportResult struct {
	// Export is the CRS export data.
	Export *CRSExport

	// Truncated indicates if any index was truncated.
	Truncated bool

	// Warnings contains messages about truncation or other issues.
	Warnings []string
}

ExportResult contains the export data and metadata.

type FailureEvent

type FailureEvent struct {
	// SessionID is the session where the failure occurred.
	SessionID string `json:"session_id"`

	// FailureType classifies the type of failure.
	FailureType FailureType `json:"failure_type"`

	// DecisionPath is the sequence of decisions leading to failure.
	// This is analyzed by CDCL to find the conflict cut.
	DecisionPath []StepRecord `json:"decision_path"`

	// FailedStep is the step that failed.
	FailedStep StepRecord `json:"failed_step"`

	// ErrorMessage is the error message if applicable.
	ErrorMessage string `json:"error_message,omitempty"`

	// ErrorCategory categorizes the error for learning.
	ErrorCategory ErrorCategory `json:"error_category,omitempty"`

	// Tool is the tool that was involved in the failure.
	Tool string `json:"tool,omitempty"`

	// Source indicates signal source (should be hard for learning).
	Source SignalSource `json:"source"`
}

FailureEvent represents something the agent should learn from.

Description:

When a failure occurs (tool error, cycle, circuit breaker), a FailureEvent
is created and passed to the Learning Activity for CDCL analysis. The
Learning Activity extracts a conflict clause that prevents the same failure.

Thread Safety: FailureEvent is immutable after creation.

func (*FailureEvent) Validate

func (e *FailureEvent) Validate() error

Validate checks that the FailureEvent has required fields.

type FailureType

type FailureType string

FailureType identifies the type of failure that triggered learning.

const (
	// FailureTypeToolError means a tool execution failed.
	FailureTypeToolError FailureType = "tool_error"

	// FailureTypeCycleDetected means a reasoning cycle was detected.
	FailureTypeCycleDetected FailureType = "cycle_detected"

	// FailureTypeCircuitBreaker means the circuit breaker intervened.
	FailureTypeCircuitBreaker FailureType = "circuit_breaker"

	// FailureTypeTimeout means the operation timed out.
	FailureTypeTimeout FailureType = "timeout"

	// FailureTypeInvalidOutput means the output was invalid.
	FailureTypeInvalidOutput FailureType = "invalid_output"

	// FailureTypeSafety means the safety gate blocked the operation.
	FailureTypeSafety FailureType = "safety"

	// FailureTypeSemanticRepetition means a semantically similar tool call was detected.
	// CB-30c: Added to prevent repeated similar queries (e.g., Grep("parseConfig") then Grep("parse_config")).
	FailureTypeSemanticRepetition FailureType = "semantic_repetition"

	// FailureTypeBatchFiltered means the router filtered out this tool call as redundant.
	// GR-39a: Added for batch filter learning to inform CDCL clause generation.
	FailureTypeBatchFiltered FailureType = "batch_filtered"

	// FailureTypeResolutionDemotion means the post-LLM validator overrode the LLM's
	// conceptual symbol pick because a better tier0 candidate existed.
	// D3: Added for CRS-governed conceptual resolution prune/annotate/validate pipeline.
	FailureTypeResolutionDemotion FailureType = "resolution_demotion"
)

func (FailureType) IsValid

func (f FailureType) IsValid() bool

IsValid returns true if the failure type is a known value.

func (FailureType) String

func (f FailureType) String() string

String returns the string representation of FailureType.

type GraphAnalyticsQuery

type GraphAnalyticsQuery interface {
	// HotSpots returns the top N most-connected symbols.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - k: Number of hotspots to return.
	//
	// Outputs:
	//   - []GraphHotSpot: Top k hotspots sorted by score descending.
	//   - error: Non-nil on failure.
	//
	// Thread Safety: Safe for concurrent use.
	HotSpots(ctx context.Context, k int) ([]GraphHotSpot, error)

	// DeadCode returns symbols that are never called/referenced.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - []string: Symbol IDs of dead code.
	//   - error: Non-nil on failure.
	//
	// Thread Safety: Safe for concurrent use.
	DeadCode(ctx context.Context) ([]string, error)

	// CyclicDependencies returns groups of symbols with cyclic dependencies.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - [][]string: Groups of symbol IDs forming cycles.
	//   - error: Non-nil on failure.
	//
	// Thread Safety: Safe for concurrent use.
	CyclicDependencies(ctx context.Context) ([][]string, error)

	// PageRank returns PageRank scores for all symbols.
	//
	// Description:
	//
	//   Results are cached and recomputed only when cache is invalidated.
	//   May take significant time on first call for large graphs.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - map[string]float64: Symbol ID to PageRank score.
	//   - error: Non-nil on failure or timeout.
	//
	// Thread Safety: Safe for concurrent use.
	PageRank(ctx context.Context) (map[string]float64, error)

	// Communities returns groups of related symbols.
	//
	// Description:
	//
	//   Results are cached and recomputed only when cache is invalidated.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - []GraphCommunity: Community groups.
	//   - error: Non-nil on failure.
	//
	// Thread Safety: Safe for concurrent use.
	Communities(ctx context.Context) ([]GraphCommunity, error)
}

GraphAnalyticsQuery provides read-only access to graph analytics results.

Description:

Analytics results may be cached for performance. The cache is invalidated
when InvalidateCache() is called on the adapter.

Thread Safety: All methods are safe for concurrent use.

type GraphBackedDependencyIndex

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

GraphBackedDependencyIndex provides dependency queries backed by the actual graph.

Description:

This replaces the standalone dependencyGraph that stored duplicate edge data.
Instead of maintaining its own forward/reverse maps, it delegates all queries
to the GraphQuery interface (typically CRSGraphAdapter from GR-28), which
wraps the actual HierarchicalGraph.

The index implements DependencyIndexView interface for use in CRS snapshots.

Architecture:

┌──────────────────────────────┐
│ GraphBackedDependencyIndex   │
│  └── adapter GraphQuery ─────┼──► CRSGraphAdapter ──► HierarchicalGraph
└──────────────────────────────┘

Limitations:

  • Does NOT provide point-in-time snapshot semantics. Queries see live graph state.
  • Interface methods cannot accept context.Context (existing interface constraint).
  • Uses internal timeout (5 seconds) to prevent unbounded queries.

Thread Safety: Safe for concurrent read access.

func NewGraphBackedDependencyIndex

func NewGraphBackedDependencyIndex(adapter GraphQuery) (*GraphBackedDependencyIndex, error)

NewGraphBackedDependencyIndex creates a dependency index backed by the graph.

Description:

Creates an index that delegates dependency queries to the GraphQuery
interface (typically CRSGraphAdapter from GR-28). This eliminates data
duplication between CRS and the code graph.

Inputs:

  • adapter: The GraphQuery implementation. Must not be nil.

Outputs:

  • *GraphBackedDependencyIndex: The new index.
  • error: Non-nil if adapter is nil.

Example:

adapter, _ := graph.NewCRSGraphAdapter(g, idx, gen, refresh, nil)
depIndex, err := crs.NewGraphBackedDependencyIndex(adapter)
if err != nil {
    return fmt.Errorf("create dependency index: %w", err)
}

Thread Safety: The returned index is safe for concurrent read access. The caller must ensure the adapter outlives this index.

func (*GraphBackedDependencyIndex) AllEdges

func (d *GraphBackedDependencyIndex) AllEdges() map[string][]string

AllEdges returns nil for graph-backed indexes.

Description:

For GraphBackedDependencyIndex, edges live in the actual code graph
which has its own persistence mechanism. Exporting edges from here
would be expensive (requires querying all nodes) and redundant.

Use the graph's own backup/restore mechanism for edge persistence.

Outputs:

  • nil: Always returns nil. Use graph persistence instead.

Thread Safety: Safe for concurrent use.

func (*GraphBackedDependencyIndex) DependedBy

func (d *GraphBackedDependencyIndex) DependedBy(nodeID string) []string

DependedBy returns all nodes that depend on nodeID (callers).

Description:

Returns the IDs of all symbols that call the given node. Delegates to
GraphQuery.FindCallers() and extracts symbol IDs.

Inputs:

  • nodeID: The symbol ID to find dependents for.

Outputs:

  • []string: Symbol IDs of callers. Empty slice if none or on error.

Limitations:

  • Cannot accept context.Context due to interface constraint.
  • Uses internal 5-second timeout.

Thread Safety: Safe for concurrent use.

func (*GraphBackedDependencyIndex) DependsOn

func (d *GraphBackedDependencyIndex) DependsOn(nodeID string) []string

DependsOn returns all nodes that nodeID depends on (callees).

Description:

Returns the IDs of all symbols that the given node calls. Delegates to
GraphQuery.FindCallees() and extracts symbol IDs.

Inputs:

  • nodeID: The symbol ID to find dependencies for.

Outputs:

  • []string: Symbol IDs of callees. Empty slice if none or on error.

Limitations:

  • Cannot accept context.Context due to interface constraint.
  • Uses internal 5-second timeout.

Thread Safety: Safe for concurrent use.

func (*GraphBackedDependencyIndex) Generation

func (d *GraphBackedDependencyIndex) Generation() int64

Generation returns the adapter's generation for staleness detection.

Thread Safety: Safe for concurrent use.

func (*GraphBackedDependencyIndex) HasCycle

func (d *GraphBackedDependencyIndex) HasCycle(nodeID string) bool

HasCycle returns true if there's a cycle involving nodeID.

Description:

Checks if following call edges from nodeID eventually leads back to itself.
Delegates to CRSGraphAdapter.HasCycleFrom() if supported.

Inputs:

  • nodeID: The symbol ID to check for cycles from.

Outputs:

  • bool: True if cycle exists, false otherwise or on error.

Limitations:

  • Cannot accept context.Context due to interface constraint.
  • Uses internal 5-second timeout.
  • Returns false on error (safe default).

Thread Safety: Safe for concurrent use.

func (*GraphBackedDependencyIndex) InvalidateCache

func (d *GraphBackedDependencyIndex) InvalidateCache()

InvalidateCache marks the cache as stale.

Description:

Called when the underlying graph is refreshed. This forces Size() to
recompute on next call. Also invalidates the adapter's cache if supported.

Thread Safety: Safe for concurrent use.

func (*GraphBackedDependencyIndex) IsGraphBacked

func (d *GraphBackedDependencyIndex) IsGraphBacked() bool

IsGraphBacked returns true for GraphBackedDependencyIndex.

Description:

Indicates that this index delegates to the graph for dependency data.
When true, AllEdges() returns nil and edge export should be skipped.

Outputs:

  • true: Always returns true.

Thread Safety: Safe for concurrent use.

func (*GraphBackedDependencyIndex) Size

func (d *GraphBackedDependencyIndex) Size() int

Size returns the number of dependency edges (call edges).

Description:

Returns the total number of CALLS edges in the graph. Uses invalidation-based
caching with singleflight to prevent thundering herd.

Outputs:

  • int: Number of call edges. Returns 0 on error.

Thread Safety: Safe for concurrent use.

type GraphCommunity

type GraphCommunity struct {
	// ID is the community identifier.
	ID string `json:"id"`

	// SymbolIDs are the symbols in this community.
	SymbolIDs []string `json:"symbol_ids"`

	// Modularity is the community's modularity score.
	Modularity float64 `json:"modularity"`
}

GraphCommunity represents a group of related symbols.

type GraphHotSpot

type GraphHotSpot struct {
	// SymbolID is the unique symbol identifier.
	SymbolID string `json:"symbol_id"`

	// Name is the symbol name.
	Name string `json:"name"`

	// Score is the connectivity score (higher = more connected).
	Score int `json:"score"`

	// InDegree is the number of incoming edges.
	InDegree int `json:"in_degree"`

	// OutDegree is the number of outgoing edges.
	OutDegree int `json:"out_degree"`
}

GraphHotSpot represents a highly-connected symbol in the graph.

type GraphQuery

type GraphQuery interface {

	// FindSymbolByID returns a symbol by its unique ID.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - id: The unique symbol ID.
	//
	// Outputs:
	//   - *ast.Symbol: The symbol, or nil if not found.
	//   - bool: True if symbol was found.
	//   - error: Non-nil on context cancellation or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindSymbolByID(ctx context.Context, id string) (*ast.Symbol, bool, error)

	// FindSymbolsByName returns all symbols with the given name.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - name: The symbol name to search for.
	//
	// Outputs:
	//   - []*ast.Symbol: Matching symbols. Empty slice if none found.
	//   - error: Non-nil on context cancellation or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindSymbolsByName(ctx context.Context, name string) ([]*ast.Symbol, error)

	// FindSymbolsByKind returns all symbols of the given kind.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - kind: The symbol kind to filter by.
	//
	// Outputs:
	//   - []*ast.Symbol: Matching symbols. Empty slice if none found.
	//   - error: Non-nil on context cancellation or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindSymbolsByKind(ctx context.Context, kind ast.SymbolKind) ([]*ast.Symbol, error)

	// FindSymbolsInFile returns all symbols in the given file.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - filePath: The file path to search in.
	//
	// Outputs:
	//   - []*ast.Symbol: Symbols in the file. Empty slice if none found.
	//   - error: Non-nil on context cancellation or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindSymbolsInFile(ctx context.Context, filePath string) ([]*ast.Symbol, error)

	// FindCallers returns symbols that call the given symbol.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - symbolID: The symbol to find callers for.
	//
	// Outputs:
	//   - []*ast.Symbol: Caller symbols. Empty slice if none found.
	//   - error: Non-nil on graph query failure or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindCallers(ctx context.Context, symbolID string) ([]*ast.Symbol, error)

	// FindCallees returns symbols that the given symbol calls.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - symbolID: The symbol to find callees for.
	//
	// Outputs:
	//   - []*ast.Symbol: Callee symbols. Empty slice if none found.
	//   - error: Non-nil on graph query failure or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindCallees(ctx context.Context, symbolID string) ([]*ast.Symbol, error)

	// FindImplementations returns types that implement the given interface.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - interfaceName: The interface name to find implementations for.
	//
	// Outputs:
	//   - []*ast.Symbol: Implementing types. Empty slice if none found.
	//   - error: Non-nil on graph query failure or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindImplementations(ctx context.Context, interfaceName string) ([]*ast.Symbol, error)

	// FindReferences returns symbols that reference the given symbol.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - symbolID: The symbol to find references for.
	//
	// Outputs:
	//   - []*ast.Symbol: Referencing symbols. Empty slice if none found.
	//   - error: Non-nil on graph query failure or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	FindReferences(ctx context.Context, symbolID string) ([]*ast.Symbol, error)

	// GetCallChain returns the call chain from source to target.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - fromID: The source symbol ID.
	//   - toID: The target symbol ID.
	//   - maxDepth: Maximum traversal depth.
	//
	// Outputs:
	//   - []string: Symbol IDs in the call chain. Empty if no path found.
	//   - error: Non-nil on graph query failure or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	GetCallChain(ctx context.Context, fromID, toID string, maxDepth int) ([]string, error)

	// ShortestPath returns the shortest path between two symbols.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - fromID: The source symbol ID.
	//   - toID: The target symbol ID.
	//
	// Outputs:
	//   - []string: Symbol IDs in the path. Empty if no path found.
	//   - error: Non-nil on graph query failure or adapter closed.
	//
	// Thread Safety: Safe for concurrent use.
	ShortestPath(ctx context.Context, fromID, toID string) ([]string, error)

	// Analytics returns the analytics query interface.
	//
	// Outputs:
	//   - GraphAnalyticsQuery: The analytics interface. Never nil.
	//
	// Thread Safety: Safe for concurrent use.
	Analytics() GraphAnalyticsQuery

	// NodeCount returns the number of nodes in the graph.
	//
	// Thread Safety: Safe for concurrent use.
	NodeCount() int

	// EdgeCount returns the number of edges in the graph.
	//
	// Thread Safety: Safe for concurrent use.
	EdgeCount() int

	// Generation returns the graph generation this adapter was created with.
	//
	// Description:
	//
	//   Use for staleness detection. If the current graph generation is higher
	//   than this value, the adapter may return stale data.
	//
	// Thread Safety: Safe for concurrent use.
	Generation() int64

	// LastRefreshTime returns when the graph was last refreshed (Unix milliseconds UTC).
	//
	// Thread Safety: Safe for concurrent use.
	LastRefreshTime() int64

	// Close releases resources held by the adapter.
	//
	// Description:
	//
	//   Must be called when the adapter is no longer needed to prevent
	//   resource leaks. After Close, all methods return ErrGraphQueryClosed.
	//
	// Thread Safety: Safe for concurrent use. Idempotent.
	Close() error
}

GraphQuery provides read-only access to the code graph from CRS activities.

Description:

GraphQuery is the interface that allows CRS activities to query the actual
code graph for structural information. This enables activities to use graph
algorithms (PageRank, community detection, etc.) rather than relying solely
on CRS's internal DependencyIndex.

The interface is read-only from the CRS perspective - the graph is owned
by the graph package and mutations happen via graph refresh, not CRS deltas.

Thread Safety: All methods are safe for concurrent use.

type GraphQueryConfig

type GraphQueryConfig struct {
	// CacheTTLMs is how long cached analytics results are valid (milliseconds).
	// Default: 300000 (5 minutes)
	CacheTTLMs int64

	// PageRankTimeoutMs is the maximum time for PageRank computation (milliseconds).
	// Default: 30000 (30 seconds)
	PageRankTimeoutMs int64
}

GraphQueryConfig configures the GraphQuery adapter.

func DefaultGraphQueryConfig

func DefaultGraphQueryConfig() *GraphQueryConfig

DefaultGraphQueryConfig returns the default configuration.

type GraphRefreshCoordinator

type GraphRefreshCoordinator interface {
	// Pause stops graph refresh operations.
	Pause(ctx context.Context) error

	// Resume allows graph refresh operations to continue.
	Resume(ctx context.Context) error

	// IsPaused returns true if currently paused.
	IsPaused() bool
}

GraphRefreshCoordinator is implemented by components that need to pause during CRS restore operations.

Description:

During restore, the graph must not be refreshed as this could
introduce inconsistent state. Components implementing this
interface will be paused during restore and resumed after.

type HistoryDelta

type HistoryDelta struct {

	// Entries contains history entries to add.
	Entries []HistoryEntry
	// contains filtered or unexported fields
}

HistoryDelta represents additions to history.

func NewHistoryDelta

func NewHistoryDelta(source SignalSource, entries []HistoryEntry) *HistoryDelta

NewHistoryDelta creates a new history delta.

func (*HistoryDelta) ConflictsWith

func (d *HistoryDelta) ConflictsWith(_ Delta) bool

ConflictsWith returns true if this delta conflicts with another.

func (*HistoryDelta) IndexesAffected

func (d *HistoryDelta) IndexesAffected() []string

IndexesAffected returns which indexes this delta will modify.

Thread Safety: Returns a shared slice. Callers must not modify.

func (*HistoryDelta) Merge

func (d *HistoryDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*HistoryDelta) Source

func (d *HistoryDelta) Source() SignalSource

func (*HistoryDelta) Timestamp

func (d *HistoryDelta) Timestamp() int64

func (*HistoryDelta) Type

func (d *HistoryDelta) Type() DeltaType

Type returns the delta type.

func (*HistoryDelta) Validate

func (d *HistoryDelta) Validate(_ Snapshot) error

Validate checks if this delta can be applied.

type HistoryEntry

type HistoryEntry struct {
	// ID is the unique entry identifier.
	ID string

	// NodeID is the node this decision was about.
	NodeID string

	// Action is the action taken.
	Action string

	// Result is the outcome of the action.
	Result string

	// Source indicates where this decision came from.
	Source SignalSource

	// Timestamp is when this decision was made (Unix milliseconds UTC).
	Timestamp int64

	// Metadata contains additional context.
	Metadata map[string]string
}

HistoryEntry represents a single decision in the history.

type HistoryEntryExport

type HistoryEntryExport struct {
	// ID is the unique entry identifier.
	ID string `json:"id"`

	// NodeID is the node this decision was about.
	NodeID string `json:"node_id"`

	// Action is the action taken.
	Action string `json:"action"`

	// Result is the outcome of the action.
	Result string `json:"result"`

	// Source indicates signal source.
	Source string `json:"source"`

	// Timestamp is when this decision was made (Unix milliseconds UTC).
	Timestamp int64 `json:"timestamp"`

	// Metadata contains additional context.
	Metadata map[string]string `json:"metadata,omitempty"`
}

HistoryEntryExport represents a single history entry.

type HistoryIndexExport

type HistoryIndexExport struct {
	// EntryCount is the total number of history entries.
	EntryCount int `json:"entry_count"`

	// RecentEntries contains the most recent history entries.
	RecentEntries []HistoryEntryExport `json:"recent_entries,omitempty"`
}

HistoryIndexExport is the serializable form of the History Index.

type HistoryIndexView

type HistoryIndexView interface {
	// Trace returns the decision trace for a node.
	Trace(nodeID string) []HistoryEntry

	// Recent returns the N most recent history entries.
	Recent(n int) []HistoryEntry

	// Size returns the number of history entries.
	Size() int
}

HistoryIndexView provides read-only access to decision history.

Thread Safety: Safe for concurrent use (immutable).

type ImportOptions

type ImportOptions struct {
	// StrictValidation returns error on count mismatches.
	// Default: true.
	StrictValidation bool
}

ImportOptions configures import behavior.

func DefaultImportOptions

func DefaultImportOptions() *ImportOptions

DefaultImportOptions returns the default import options.

type ImportedState

type ImportedState struct {
	// Generation is the CRS generation from the export.
	Generation int64

	// SessionID is the session identifier from the export.
	SessionID string

	// ProofData contains proof numbers keyed by node ID.
	ProofData map[string]ProofNumber

	// ConstraintData contains constraints keyed by constraint ID.
	ConstraintData map[string]Constraint

	// SimilarityData contains similarity scores (bidirectional).
	// Format: map[fromID]map[toID]similarity
	SimilarityData map[string]map[string]float64

	// DependencyForward contains forward edges: node -> nodes it depends on.
	DependencyForward map[string]map[string]struct{}

	// DependencyReverse contains reverse edges: node -> nodes that depend on it.
	DependencyReverse map[string]map[string]struct{}

	// HistoryData contains history entries in order.
	HistoryData []HistoryEntry
}

ImportedState contains the data reconstructed from a CRS export.

Description:

This struct holds all the data needed to restore CRS state. It uses
the internal data structures that CRS expects, allowing direct
restoration without additional transformation.

type IndexMask

type IndexMask uint8

IndexMask identifies which CRS indexes were modified using a bitmask.

Description:

Uses bitmask for O(1) operations and zero allocation. There are exactly
6 CRS indexes, so a uint8 is sufficient.

Thread Safety: IndexMask is immutable; safe for concurrent use.

const (
	// IndexProof indicates the proof index was modified.
	IndexProof IndexMask = 1 << iota
	// IndexConstraint indicates the constraint index was modified.
	IndexConstraint
	// IndexSimilarity indicates the similarity index was modified.
	IndexSimilarity
	// IndexDependency indicates the dependency index was modified.
	IndexDependency
	// IndexHistory indicates the history index was modified.
	IndexHistory
	// IndexStreaming indicates the streaming index was modified.
	IndexStreaming
)

func IndexMaskFromDelta

func IndexMaskFromDelta(delta Delta) IndexMask

IndexMaskFromDelta converts a delta's IndexesAffected to IndexMask.

func IndexMaskFromStrings

func IndexMaskFromStrings(names []string) IndexMask

IndexMaskFromStrings converts string names to IndexMask.

func (IndexMask) Add

func (m IndexMask) Add(idx IndexMask) IndexMask

Add returns a new mask with the given index added.

func (IndexMask) Has

func (m IndexMask) Has(idx IndexMask) bool

Has returns true if the mask contains the given index.

func (IndexMask) MarshalJSON

func (m IndexMask) MarshalJSON() ([]byte, error)

MarshalJSON implements json.Marshaler for backwards-compatible JSON output.

Outputs a JSON array of index names: ["proof", "constraint"]

func (IndexMask) Names

func (m IndexMask) Names() []string

Names returns the names of all indexes in the mask.

Thread Safety: Allocates a new slice on each call.

func (IndexMask) String

func (m IndexMask) String() string

String returns a comma-separated list of index names.

func (*IndexMask) UnmarshalJSON

func (m *IndexMask) UnmarshalJSON(data []byte) error

UnmarshalJSON implements json.Unmarshaler.

Accepts either a JSON array of strings or a number (bitmask).

type IndexesExport

type IndexesExport struct {
	// Proof contains proof/disproof numbers for nodes.
	Proof ProofIndexExport `json:"proof"`

	// Constraint contains active constraints.
	Constraint ConstraintIndexExport `json:"constraint"`

	// Similarity contains node similarity data.
	Similarity SimilarityIndexExport `json:"similarity"`

	// Dependency contains dependency graph data.
	Dependency DependencyIndexExport `json:"dependency"`

	// History contains decision history.
	History HistoryIndexExport `json:"history"`

	// Streaming contains streaming statistics.
	Streaming StreamingIndexExport `json:"streaming"`
}

IndexesExport contains all six indexes in JSON-serializable form.

type Journal

type Journal interface {
	// Append writes a delta with CRC checksum.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - delta: The CRS delta to persist. Must not be nil.
	//
	// Outputs:
	//   - error: Non-nil if write fails or context cancelled.
	//
	// Performance: ~100-200µs per append (BadgerDB sync write + CRC).
	Append(ctx context.Context, delta Delta) error

	// AppendBatch writes multiple deltas atomically in a single transaction.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - deltas: Deltas to persist. Must not be nil or empty.
	//
	// Outputs:
	//   - error: Non-nil if write fails or context cancelled.
	//
	// Performance: More efficient than individual Append calls.
	AppendBatch(ctx context.Context, deltas []Delta) error

	// Replay returns all deltas since last checkpoint with validation.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - []Delta: Deltas in order. Empty if no journal exists.
	//   - error: Non-nil if read fails or validation errors (unless SkipCorrupted).
	//
	// Usage: Called once at session start to recover state.
	Replay(ctx context.Context) ([]Delta, error)

	// ReplayStream returns a channel for streaming replay (low memory).
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - <-chan DeltaOrError: Channel yielding deltas or errors.
	//   - error: Non-nil if replay cannot start.
	//
	// Usage: For large journals where loading all into memory is prohibitive.
	ReplayStream(ctx context.Context) (<-chan DeltaOrError, error)

	// Checkpoint marks current position, enabling journal truncation.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//
	// Outputs:
	//   - error: Non-nil if checkpoint fails.
	//
	// Usage: Called after successful state persistence to Weaviate.
	Checkpoint(ctx context.Context) error

	// IsAvailable returns false if journal is in degraded mode.
	IsAvailable() bool

	// IsDegraded returns true if journal is operating with reduced durability.
	IsDegraded() bool

	// Sync flushes pending writes to disk.
	//
	// Outputs:
	//   - error: Non-nil if sync fails.
	Sync() error

	// Close syncs and releases resources.
	//
	// Outputs:
	//   - error: Non-nil if close fails.
	Close() error

	// Stats returns journal statistics.
	Stats() JournalStats

	// Backup creates a portable backup of the journal.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - w: Writer to receive backup data. Must not be nil.
	//
	// Outputs:
	//   - error: Non-nil if backup fails.
	//
	// Thread Safety: Safe for concurrent use.
	Backup(ctx context.Context, w io.Writer) error

	// Restore loads state from a backup.
	//
	// Inputs:
	//   - ctx: Context for cancellation. Must not be nil.
	//   - r: Reader containing backup data. Must not be nil.
	//
	// Outputs:
	//   - error: Non-nil if restore fails.
	//
	// Limitations:
	//   - Restore is all-or-nothing: failure leaves journal in undefined state.
	//
	// Thread Safety: NOT safe for concurrent use. Caller must ensure exclusivity.
	Restore(ctx context.Context, r io.Reader) error
}

Journal provides crash recovery for CRS via Write-Ahead Logging.

Description:

Appends deltas synchronously to BadgerDB with CRC checksums.
On restart, replays all deltas since last checkpoint to reconstruct state.

Thread Safety: Safe for concurrent use from multiple goroutines.

type JournalConfig

type JournalConfig struct {
	// Path is the directory for BadgerDB files.
	// Required for persistent mode.
	Path string

	// SessionID scopes this journal to a specific session.
	// Required. Used as key prefix for isolation.
	SessionID string

	// SyncWrites enables synchronous writes for durability.
	// MUST be true for WAL correctness. Default: true.
	SyncWrites bool

	// MaxJournalBytes triggers checkpoint when exceeded.
	// Default: 1GB. Set to 0 to disable limit.
	MaxJournalBytes int64

	// AllowDegraded allows startup even if BadgerDB unavailable.
	// When true, journal operates in memory-only mode with reduced durability.
	// Default: false (strict mode).
	AllowDegraded bool

	// SkipCorruptedDeltas continues replay past corrupted entries.
	// Corrupted entries are logged and skipped.
	// Default: false (fail fast).
	SkipCorruptedDeltas bool

	// InMemory uses in-memory BadgerDB (for testing).
	// Default: false.
	InMemory bool

	// Logger for journal operations.
	// Default: slog.Default().
	Logger *slog.Logger
}

JournalConfig configures journal behavior.

Description:

Contains all settings for journal operation including durability,
size limits, and degradation behavior.

func DefaultJournalConfig

func DefaultJournalConfig() JournalConfig

DefaultJournalConfig returns sensible defaults for production use.

Outputs:

JournalConfig - Ready-to-use production configuration.

func (*JournalConfig) Validate

func (c *JournalConfig) Validate() error

Validate checks if the configuration is valid.

type JournalStats

type JournalStats struct {
	// TotalDeltas is the count of deltas in the journal.
	TotalDeltas int64

	// TotalBytes is approximate size of journal data.
	TotalBytes int64

	// LastSeqNum is the most recent sequence number.
	LastSeqNum uint64

	// LastCheckpoint is when the last checkpoint occurred.
	LastCheckpoint time.Time

	// CorruptedCount is the number of corrupted entries encountered.
	CorruptedCount int64

	// Degraded indicates if running in degraded mode.
	Degraded bool
}

JournalStats contains journal metrics.

type KeyValue

type KeyValue struct {
	Key   string `json:"key"`
	Value string `json:"value"`
}

KeyValue is a typed key-value pair for extra parameters. This provides a bounded, auditable alternative to map[string]any.

type Literal

type Literal struct {
	// Variable is the decision variable name.
	// Format: "<type>:<value>" e.g., "tool:list_packages", "outcome:success"
	Variable string `json:"variable"`

	// Negated indicates if this literal is negated (NOT).
	Negated bool `json:"negated"`
}

Literal represents a single variable assignment in a clause.

Description:

A literal is either a variable (positive) or its negation (negative).
Variables use the format "<type>:<value>" e.g., "tool:list_packages",
"outcome:success", "error:file_not_found".

Thread Safety: Literal is immutable after creation.

func (Literal) String

func (l Literal) String() string

String returns the string representation of a Literal.

type NATSJournal

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

NATSJournal implements the Journal interface using NATS JetStream.

Description:

Replaces BadgerJournal with NATS JetStream for CRS delta persistence.
Each delta is published to a subject scoped by session ID, encoded as
[4-byte CRC32][gob-encoded Delta]. NATS message sequence numbers
replace manual seqNum counters. Checkpoints are stored as separate
messages containing the sequence number up to which deltas can be purged.

Thread Safety: Safe for concurrent use from multiple goroutines.

func NewNATSJournal

func NewNATSJournal(config NATSJournalConfig) (*NATSJournal, error)

NewNATSJournal creates a new NATS JetStream journal.

Description:

Initializes a journal backed by NATS JetStream. The stream must already
exist (created by storage/nats.Client.EnsureStream). The journal
initializes its sequence number from the latest message in the stream.

Inputs:

  • config: Journal configuration. SessionID and JS are required.

Outputs:

  • *NATSJournal: Configured journal ready for use.
  • error: Non-nil if configuration is invalid or stream is inaccessible.

Thread Safety: Safe for concurrent use after creation.

func (*NATSJournal) Append

func (j *NATSJournal) Append(ctx context.Context, delta Delta) error

Append writes a delta to NATS JetStream with CRC checksum.

Description:

Publishes a CRC32+gob encoded delta to the session's delta subject.
NATS assigns a monotonically increasing sequence number automatically.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • delta: The CRS delta to persist. Must not be nil.

Outputs:

  • error: Non-nil if publish fails or context is cancelled.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) AppendBatch

func (j *NATSJournal) AppendBatch(ctx context.Context, deltas []Delta) error

AppendBatch writes multiple deltas sequentially to NATS JetStream.

Description:

Publishes deltas one at a time under a mutex for ordering guarantees.
NATS JetStream does not support atomic multi-message transactions,
but deltas are idempotent on replay so partial batches are acceptable.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • deltas: Deltas to persist. Must not be nil or empty.

Outputs:

  • error: Non-nil if any publish fails.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) Backup

func (j *NATSJournal) Backup(ctx context.Context, w io.Writer) error

Backup creates a portable backup of the journal.

Description:

Iterates all delta messages on this session's subject via an ordered
consumer and writes them as length-prefixed frames to the writer.
Frame format: [4-byte big-endian length][message data].

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • w: Writer to receive backup data. Must not be nil.

Outputs:

  • error: Non-nil if backup fails.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) Checkpoint

func (j *NATSJournal) Checkpoint(ctx context.Context) error

Checkpoint marks the current position for journal truncation.

Description:

Publishes the current sequence number to the checkpoint subject and
purges delta messages up to that sequence number.

Inputs:

  • ctx: Context for cancellation. Must not be nil.

Outputs:

  • error: Non-nil if checkpoint fails.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) Close

func (j *NATSJournal) Close() error

Close marks the journal as closed.

Description:

Marks the journal as closed so future operations return ErrJournalClosed.
Does not close the underlying NATS connection (that's managed by the Client).

Outputs:

  • error: Always nil.

Thread Safety: Safe for concurrent use. Idempotent.

func (*NATSJournal) IsAvailable

func (j *NATSJournal) IsAvailable() bool

IsAvailable returns true if the journal is operational.

Description:

Returns false if the journal is in degraded mode (NATS unavailable).

Outputs:

  • bool: True if journal can accept writes.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) IsDegraded

func (j *NATSJournal) IsDegraded() bool

IsDegraded returns true if the journal is in degraded mode.

Description:

Degraded mode occurs when NATS becomes unavailable after initial
connection. In degraded mode, writes return ErrJournalDegraded
and replays return empty results.

Outputs:

  • bool: True if operating with reduced durability.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) Replay

func (j *NATSJournal) Replay(ctx context.Context) ([]Delta, error)

Replay returns all deltas since last checkpoint with validation.

Description:

Creates an ordered consumer to fetch all deltas from the checkpoint
sequence + 1 to the latest message. Validates CRC on each delta.

Inputs:

  • ctx: Context for cancellation. Must not be nil.

Outputs:

  • []Delta: Ordered deltas since last checkpoint. Empty if no journal.
  • error: Non-nil if read fails or CRC validation errors.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) ReplayStream

func (j *NATSJournal) ReplayStream(ctx context.Context) (<-chan DeltaOrError, error)

ReplayStream returns a channel for streaming replay.

Description:

Same as Replay but yields deltas through a channel for low-memory
consumption on large journals.

Inputs:

  • ctx: Context for cancellation. Must not be nil.

Outputs:

  • <-chan DeltaOrError: Channel yielding deltas or errors.
  • error: Non-nil if replay cannot start.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) Restore

func (j *NATSJournal) Restore(ctx context.Context, r io.Reader) error

Restore loads state from a backup.

Description:

Reads length-prefixed frames from the reader and publishes each
as a delta message to NATS JetStream. This rebuilds the journal
state from a portable backup.

Inputs:

  • ctx: Context for cancellation. Must not be nil.
  • r: Reader containing backup data. Must not be nil.

Outputs:

  • error: Non-nil if restore fails.

Limitations:

  • Restore is all-or-nothing: partial restores leave partial data.

Thread Safety: NOT safe for concurrent use. Caller must ensure exclusivity.

func (*NATSJournal) Stats

func (j *NATSJournal) Stats() JournalStats

Stats returns journal statistics.

Description:

Returns current journal metrics including delta count, byte size,
sequence number, and degraded status. For NATS journals, attempts
to query JetStream stream info for accurate counts.

Outputs:

  • JournalStats: Current journal statistics.

Thread Safety: Safe for concurrent use.

func (*NATSJournal) Sync

func (j *NATSJournal) Sync() error

Sync is a no-op for NATS JetStream (file storage is durable).

Description:

JetStream with file storage is already durable on publish acknowledgment.
This method exists only to satisfy the Journal interface.

Outputs:

  • error: Always nil.

Thread Safety: Safe for concurrent use.

type NATSJournalConfig

type NATSJournalConfig struct {
	// SessionID scopes this journal to a specific session.
	// Required. Used in NATS subject names for isolation.
	SessionID string

	// JS is the JetStream context for publishing and subscribing.
	// Required. Must not be nil.
	JS nats.JetStreamContext

	// StreamName is the JetStream stream name.
	// Required. Must match the stream created by EnsureStream.
	StreamName string

	// MaxJournalBytes limits total journal size. 0 means no limit.
	MaxJournalBytes int64

	// AllowDegraded allows journal to continue in degraded mode
	// if NATS becomes unavailable after initial connection.
	AllowDegraded bool

	// SkipCorruptedDeltas skips corrupted deltas during replay
	// instead of returning an error.
	SkipCorruptedDeltas bool

	// Logger is the structured logger.
	// If nil, slog.Default() is used.
	Logger *slog.Logger
}

NATSJournalConfig configures the NATS JetStream journal.

Description:

Contains settings for NATS-backed CRS delta journaling. Uses JetStream
for durable, observable storage with built-in sequence numbering.

Inputs:

SessionID - Unique session identifier for subject scoping.
JS - JetStream context obtained from a NATS client.
StreamName - Name of the JetStream stream (e.g., "CRS_DELTAS").

Limitations:

  • Requires an active NATS connection with JetStream enabled.

Assumptions:

  • The stream has already been created (via storage/nats.Client.EnsureStream).

type NodeFrequency

type NodeFrequency struct {
	NodeID    string
	Frequency uint64
}

NodeFrequency pairs a node ID with its access frequency.

type NodeStats

type NodeStats struct {
	// NodeID is the node identifier.
	NodeID string

	// Proof information (if exists).
	HasProof    bool
	ProofNumber ProofNumber

	// Constraint information.
	ConstraintCount int
	Constraints     []string // Constraint IDs

	// Similarity information.
	SimilarNodeCount int
	NearestNeighbor  string
	NearestDistance  float64

	// Dependency information.
	DependsOnCount  int
	DependedByCount int
	HasCycle        bool

	// History information.
	HistoryEntryCount int
	LastAction        string
	LastActionTime    string

	// Streaming information.
	AccessFrequency uint64
}

NodeStats contains comprehensive statistics about a node.

type Outcome

type Outcome string

Outcome identifies the result of a step.

const (
	// OutcomeSuccess means the step completed successfully.
	OutcomeSuccess Outcome = "success"

	// OutcomeFailure means the step failed.
	OutcomeFailure Outcome = "failure"

	// OutcomeSkipped means the step was skipped (e.g., low confidence).
	OutcomeSkipped Outcome = "skipped"

	// OutcomeForced means the outcome was forced (e.g., circuit breaker).
	OutcomeForced Outcome = "forced"
)

func (Outcome) IsValid

func (o Outcome) IsValid() bool

IsValid returns true if the outcome is a known value.

func (Outcome) String

func (o Outcome) String() string

String returns the string representation of Outcome.

type PersistenceConfig

type PersistenceConfig struct {
	// BaseDir is the root directory for all CRS persistence data.
	// Default: ~/.aleutian/crs/
	BaseDir string

	// CompressionLevel is the gzip compression level (1-9).
	// Higher = smaller files, slower. Default: 6.
	CompressionLevel int

	// LockTimeoutSec is how long to wait for file lock.
	// Default: 30 seconds.
	LockTimeoutSec int

	// MaxBackupRetries is the number of retry attempts on transient failures.
	// Default: 3.
	MaxBackupRetries int

	// ValidateOnRestore enables integrity checks during restore.
	// Default: true.
	ValidateOnRestore bool

	// Logger for persistence operations.
	Logger *slog.Logger
}

PersistenceConfig configures the PersistenceManager.

func DefaultPersistenceConfig

func DefaultPersistenceConfig() PersistenceConfig

DefaultPersistenceConfig returns production defaults.

func (*PersistenceConfig) Validate

func (c *PersistenceConfig) Validate() error

Validate checks if the configuration is valid.

type PersistenceManager

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

PersistenceManager handles CRS state persistence across sessions.

Description:

Provides backup and restore functionality for CRS state using
BadgerDB's native backup mechanism. Features include:
  - Gzip compression for space efficiency
  - SHA256 content hashing for integrity verification
  - flock-based file locking for concurrent access safety
  - Atomic file operations to prevent corruption
  - BadgerDB version compatibility checking
  - Optional JSON export for portability

Thread Safety: Safe for concurrent use.

func NewPersistenceManager

func NewPersistenceManager(config *PersistenceConfig) (*PersistenceManager, error)

NewPersistenceManager creates a new persistence manager.

Description:

Creates a manager rooted at the configured base directory.
Creates the directory structure if it doesn't exist.

Inputs:

  • config: Configuration. If nil, uses DefaultPersistenceConfig().

Outputs:

  • *PersistenceManager: The new manager.
  • error: Non-nil if configuration is invalid or directory creation fails.

Example:

cfg := DefaultPersistenceConfig()
pm, err := NewPersistenceManager(&cfg)
if err != nil {
    return fmt.Errorf("create persistence manager: %w", err)
}
defer pm.Close()

Thread Safety: Safe for concurrent use.

func (*PersistenceManager) BackupPath

func (pm *PersistenceManager) BackupPath(projectHash string) string

BackupPath returns the path to the backup file.

func (*PersistenceManager) Close

func (pm *PersistenceManager) Close() error

Close releases resources.

func (*PersistenceManager) ExportPath

func (pm *PersistenceManager) ExportPath(projectHash string) string

ExportPath returns the path to the JSON export file.

func (*PersistenceManager) GetBackupMetadata

func (pm *PersistenceManager) GetBackupMetadata(projectHash string) (*BackupMetadata, error)

GetBackupMetadata returns metadata for an existing backup.

Outputs:

  • *BackupMetadata: Metadata, or nil if no backup exists.
  • error: Non-nil on read error (not on missing backup).

Thread Safety: Safe for concurrent use.

func (*PersistenceManager) HasBackup

func (pm *PersistenceManager) HasBackup(projectHash string) bool

HasBackup checks if a backup exists for a project.

Thread Safety: Safe for concurrent use.

func (*PersistenceManager) LoadBackup

func (pm *PersistenceManager) LoadBackup(ctx context.Context, projectHash string, journal Journal) (*BackupMetadata, error)

LoadBackup restores journal state from a backup.

Description:

Restores the journal from a compressed backup file. Performs
integrity verification (SHA256 hash check) and version
compatibility check before restoring. Coordinates with the
graph refresh coordinator if set.

Inputs:

  • ctx: Context for cancellation and tracing. Must not be nil.
  • projectHash: Project identifier. Must not be empty.
  • journal: The BadgerJournal to restore into. Must not be nil.

Outputs:

  • *BackupMetadata: Metadata about the restored backup.
  • error: Non-nil if restore fails. ErrBackupNotFound if no backup exists.

Example:

meta, err := pm.LoadBackup(ctx, "abc123def456", journal)
if errors.Is(err, ErrBackupNotFound) {
    // First run, no backup exists
    return nil
}
if err != nil {
    return fmt.Errorf("restore: %w", err)
}

Thread Safety: Safe for concurrent use. Uses file locking.

func (*PersistenceManager) LockPath

func (pm *PersistenceManager) LockPath(projectHash string) string

LockPath returns the path to the lock file.

func (*PersistenceManager) MetadataPath

func (pm *PersistenceManager) MetadataPath(projectHash string) string

MetadataPath returns the path to the metadata file.

func (*PersistenceManager) PrevBackupPath

func (pm *PersistenceManager) PrevBackupPath(projectHash string) string

PrevBackupPath returns the path to the previous generation backup. CRS-21: Two-generation checkpoint rotation for crash safety.

func (*PersistenceManager) PrevMetadataPath

func (pm *PersistenceManager) PrevMetadataPath(projectHash string) string

PrevMetadataPath returns the path to the previous generation metadata. CRS-21: Two-generation checkpoint rotation for crash safety.

func (*PersistenceManager) ProjectDir

func (pm *PersistenceManager) ProjectDir(projectHash string) string

ProjectDir returns the directory for a specific project.

func (*PersistenceManager) SaveBackup

func (pm *PersistenceManager) SaveBackup(ctx context.Context, projectHash string, journal Journal, opts *BackupOptions) (*BackupMetadata, error)

SaveBackup creates a backup of the journal state.

Description:

Creates a compressed, integrity-verified backup of the BadgerDB
journal. Uses atomic file operations to ensure backup is either
completely written or not at all. Optionally creates a JSON
export for portability. Implements retry logic for transient failures.

Inputs:

  • ctx: Context for cancellation and tracing. Must not be nil.
  • projectHash: Project identifier (8-64 hex chars). Must not be empty.
  • journal: The BadgerJournal to backup. Must not be nil.
  • opts: Optional backup options (nil for defaults).

Outputs:

  • *BackupMetadata: Metadata about the created backup.
  • error: Non-nil if backup fails after all retries.

Example:

meta, err := pm.SaveBackup(ctx, "abc123def456", journal, nil)
if err != nil {
    return fmt.Errorf("backup: %w", err)
}
log.Info("backup created", "size", meta.CompressedSize)

Thread Safety: Safe for concurrent use. Uses file locking for safety.

func (*PersistenceManager) SetRefreshCoordinator

func (pm *PersistenceManager) SetRefreshCoordinator(coordinator GraphRefreshCoordinator)

SetRefreshCoordinator registers the graph refresh coordinator.

Description:

The coordinator will be paused during restore operations to
prevent graph refresh from interfering with state restoration.

Inputs:

  • coordinator: The coordinator to pause/resume. May be nil.

Thread Safety: Safe for concurrent use.

type ProofDelta

type ProofDelta struct {

	// Updates maps node ID to new proof number.
	Updates map[string]ProofNumber
	// contains filtered or unexported fields
}

ProofDelta represents changes to proof numbers.

func NewProofDelta

func NewProofDelta(source SignalSource, updates map[string]ProofNumber) *ProofDelta

NewProofDelta creates a new proof delta.

Inputs:

  • source: The signal source (hard or soft).
  • updates: Map of node ID to proof number updates.

Outputs:

  • *ProofDelta: The new delta.

func (*ProofDelta) ConflictsWith

func (d *ProofDelta) ConflictsWith(other Delta) bool

ConflictsWith returns true if this delta conflicts with another.

func (*ProofDelta) IndexesAffected

func (d *ProofDelta) IndexesAffected() []string

IndexesAffected returns which indexes this delta will modify.

Thread Safety: Returns a shared slice. Callers must not modify.

func (*ProofDelta) Merge

func (d *ProofDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*ProofDelta) Source

func (d *ProofDelta) Source() SignalSource

func (*ProofDelta) Timestamp

func (d *ProofDelta) Timestamp() int64

func (*ProofDelta) Type

func (d *ProofDelta) Type() DeltaType

Type returns the delta type.

func (*ProofDelta) Validate

func (d *ProofDelta) Validate(snapshot Snapshot) error

Validate checks if this delta can be applied.

Description:

Validates the hard/soft signal boundary. Soft signals cannot mark
nodes as DISPROVEN.

type ProofEntry

type ProofEntry struct {
	// NodeID is the unique node identifier.
	NodeID string `json:"node_id"`

	// Proof is the proof number (cost to prove).
	Proof uint64 `json:"proof"`

	// Disproof is the disproof number (cost to disprove).
	Disproof uint64 `json:"disproof"`

	// Status is the current proof status: "unknown", "proven", "disproven", "expanded".
	Status string `json:"status"`

	// Source indicates signal source: "unknown", "hard", "soft".
	Source string `json:"source"`

	// UpdatedAt is when this entry was last updated (RFC3339 format).
	UpdatedAt time.Time `json:"updated_at"`
}

ProofEntry represents a single proof number entry.

type ProofIndexExport

type ProofIndexExport struct {
	// Entries contains all proof number entries.
	Entries []ProofEntry `json:"entries"`
}

ProofIndexExport is the serializable form of the Proof Index.

type ProofIndexView

type ProofIndexView interface {
	// Get returns the proof number for a node.
	Get(nodeID string) (ProofNumber, bool)

	// All returns all proof numbers.
	All() map[string]ProofNumber

	// Size returns the number of entries.
	Size() int
}

ProofIndexView provides read-only access to proof numbers.

Thread Safety: Safe for concurrent use (immutable).

type ProofNumber

type ProofNumber struct {
	// Proof is the proof number (cost to prove this node).
	Proof uint64

	// Disproof is the disproof number (cost to disprove this node).
	Disproof uint64

	// Status is the current proof status.
	Status ProofStatus

	// Source indicates where this proof came from.
	Source SignalSource

	// UpdatedAt is when this proof was last updated (Unix milliseconds UTC).
	UpdatedAt int64
}

ProofNumber represents proof and disproof numbers for a node.

type ProofStatus

type ProofStatus int

ProofStatus represents the proof status of a node.

const (
	// ProofStatusUnknown means the node's proof status is not determined.
	ProofStatusUnknown ProofStatus = iota

	// ProofStatusProven means the node is proven (leads to solution).
	ProofStatusProven

	// ProofStatusDisproven means the node is disproven (does not lead to solution).
	ProofStatusDisproven

	// ProofStatusExpanded means the node has been expanded but not proven/disproven.
	ProofStatusExpanded
)

func (ProofStatus) String

func (s ProofStatus) String() string

String returns the string representation of ProofStatus.

type ProofUpdate

type ProofUpdate struct {
	// NodeID is the node whose proof status changed.
	NodeID string `json:"node_id"`

	// Type is the type of update to apply.
	Type ProofUpdateType `json:"type"`

	// Delta is the amount to increment/decrement (ignored for Disproven/Proven).
	Delta uint64 `json:"delta,omitempty"`

	// Reason explains why this update is being made.
	Reason string `json:"reason,omitempty"`

	// Source indicates where this signal came from (hard, soft, safety).
	Source SignalSource `json:"source"`

	// Status is the new status (used for JSON serialization compatibility).
	// Deprecated: Use Type instead. This field exists for backwards compatibility
	// with trace_recorder.go format.
	Status string `json:"status,omitempty"`
}

ProofUpdate represents a proof number update operation.

Description:

Used by RecordStep and UpdateProofNumber to track changes to proof numbers.
The update type determines how the proof number is modified:
  - Increment: Add Delta to proof number (failure)
  - Decrement: Subtract Delta from proof number (success)
  - Disproven: Set status to disproven regardless of current number
  - Proven: Set status to proven regardless of current number

Thread Safety: ProofUpdate is immutable after creation.

func (*ProofUpdate) Validate

func (u *ProofUpdate) Validate() error

Validate checks that the ProofUpdate has required fields.

type ProofUpdateType

type ProofUpdateType int

ProofUpdateType identifies the type of proof number update.

Description:

In PN-MCTS, proof number represents the COST TO PROVE a node (lower = easier).
This is the OPPOSITE of a "success count":
  - Success DECREASES proof number (path is viable, easier to prove)
  - Failure INCREASES proof number (path is problematic, harder to prove)
  - Disproven means infinite cost (path cannot lead to solution)
const (
	// ProofUpdateTypeUnknown is an unknown update type.
	ProofUpdateTypeUnknown ProofUpdateType = iota

	// ProofUpdateTypeIncrement increases proof number (failure = harder to prove).
	ProofUpdateTypeIncrement

	// ProofUpdateTypeDecrement decreases proof number (success = easier to prove).
	ProofUpdateTypeDecrement

	// ProofUpdateTypeDisproven marks node as disproven (infinite cost).
	ProofUpdateTypeDisproven

	// ProofUpdateTypeProven marks node as proven (solution found via this path).
	ProofUpdateTypeProven

	// ProofUpdateTypeReset resets proof number to initial value.
	ProofUpdateTypeReset
)

func (ProofUpdateType) IsValid

func (t ProofUpdateType) IsValid() bool

IsValid returns true if the update type is a known value.

func (ProofUpdateType) String

func (t ProofUpdateType) String() string

String returns the string representation of ProofUpdateType.

type QueryAPI

type QueryAPI interface {
	// FindProvenNodes returns all nodes with PROVEN status.
	//
	// Outputs:
	//   - []string: Node IDs that are proven. Empty if none.
	FindProvenNodes() []string

	// FindDisprovenNodes returns all nodes with DISPROVEN status.
	//
	// Outputs:
	//   - []string: Node IDs that are disproven. Empty if none.
	FindDisprovenNodes() []string

	// FindUnexploredNodes returns nodes that haven't been fully explored.
	//
	// Description:
	//   A node is unexplored if it has proof status UNKNOWN or EXPANDED
	//   but not PROVEN or DISPROVEN.
	//
	// Outputs:
	//   - []string: Node IDs that are unexplored. Empty if none.
	FindUnexploredNodes() []string

	// FindByProofRange returns nodes with proof numbers in the given range.
	//
	// Inputs:
	//   - minProof: Minimum proof number (inclusive).
	//   - maxProof: Maximum proof number (inclusive).
	//
	// Outputs:
	//   - []string: Node IDs in range, sorted by proof number ascending.
	FindByProofRange(minProof, maxProof uint64) []string

	// FindConstrainedNodes returns nodes that have constraints on them.
	//
	// Inputs:
	//   - constraintType: Type of constraint to filter by. Use ConstraintTypeUnknown for all.
	//
	// Outputs:
	//   - []string: Node IDs with constraints. Empty if none.
	FindConstrainedNodes(constraintType ConstraintType) []string

	// FindViolatedConstraints returns constraints where at least one node is DISPROVEN.
	//
	// Outputs:
	//   - []Constraint: Constraints with disproven nodes. Empty if none.
	FindViolatedConstraints() []Constraint

	// FindSimilarWithProofStatus returns nodes similar to nodeID with the given status.
	//
	// Inputs:
	//   - nodeID: The node to find similar nodes for.
	//   - status: The proof status to filter by.
	//   - k: Maximum number of similar nodes to return.
	//
	// Outputs:
	//   - []SimilarityMatch: Similar nodes with matching status, sorted by distance.
	FindSimilarWithProofStatus(nodeID string, status ProofStatus, k int) []SimilarityMatch

	// FindDependencyChain returns the dependency chain from start to end.
	//
	// Description:
	//   Uses BFS to find the shortest path from start to end through dependencies.
	//
	// Inputs:
	//   - startNodeID: The starting node.
	//   - endNodeID: The target node.
	//
	// Outputs:
	//   - []string: Path from start to end (inclusive). Empty if no path exists.
	FindDependencyChain(startNodeID, endNodeID string) []string

	// FindAffectedByNode returns all nodes that would be affected if nodeID changed.
	//
	// Description:
	//   Computes the transitive closure of nodes that depend on nodeID.
	//
	// Inputs:
	//   - nodeID: The node to check impact for.
	//
	// Outputs:
	//   - []string: All nodes affected (transitively). Empty if none.
	FindAffectedByNode(nodeID string) []string

	// FindHotNodes returns the most frequently accessed nodes.
	//
	// Inputs:
	//   - n: Number of hot nodes to return.
	//
	// Outputs:
	//   - []NodeFrequency: Top N nodes by frequency.
	FindHotNodes(n int) []NodeFrequency

	// FindRecentDecisions returns recent history entries with optional filtering.
	//
	// Inputs:
	//   - n: Maximum number of entries.
	//   - source: Filter by source. Use SignalSourceUnknown for all.
	//
	// Outputs:
	//   - []HistoryEntry: Recent entries matching filter.
	FindRecentDecisions(n int, source SignalSource) []HistoryEntry

	// NodeStats returns comprehensive statistics about a node across all indexes.
	//
	// Inputs:
	//   - nodeID: The node to get stats for.
	//
	// Outputs:
	//   - *NodeStats: Statistics about the node. Nil if node not found anywhere.
	NodeStats(nodeID string) *NodeStats
}

QueryAPI provides cross-index query capabilities.

Description:

QueryAPI enables queries that span multiple indexes, such as finding
proven nodes that satisfy certain constraints or nodes with high
similarity that are dependencies of each other.

Thread Safety: Safe for concurrent use (operates on immutable snapshot).

type ReasoningSummary

type ReasoningSummary struct {
	// NodesExplored is the total number of nodes in the proof index.
	NodesExplored int `json:"nodes_explored"`

	// NodesProven is the count of nodes with PROVEN status.
	NodesProven int `json:"nodes_proven"`

	// NodesDisproven is the count of nodes with DISPROVEN status.
	NodesDisproven int `json:"nodes_disproven"`

	// NodesUnknown is the count of nodes with UNKNOWN or EXPANDED status.
	NodesUnknown int `json:"nodes_unknown"`

	// ConstraintsApplied is the number of active constraints.
	ConstraintsApplied int `json:"constraints_applied"`

	// ExplorationDepth is the number of history entries (proxy for depth).
	ExplorationDepth int `json:"exploration_depth"`

	// ConfidenceScore is the ratio of proven nodes to explored nodes.
	// Value is between 0.0 and 1.0. Use with caution - this is a coverage
	// metric, not a statistical confidence interval.
	ConfidenceScore float64 `json:"confidence_score"`
}

ReasoningSummary provides high-level metrics about reasoning progress.

Description:

Computed from CRS state to give a quick overview of reasoning
progress without requiring full index inspection.

type ReasoningTrace

type ReasoningTrace struct {
	// SessionID identifies the session this trace belongs to.
	SessionID string `json:"session_id"`

	// TotalSteps is the number of steps recorded.
	TotalSteps int `json:"total_steps"`

	// Duration is the total time from first to last step.
	Duration string `json:"total_duration"`

	// StartTime is when the first step occurred (Unix milliseconds UTC).
	StartTime int64 `json:"start_time,omitempty"`

	// EndTime is when the last step occurred (Unix milliseconds UTC).
	EndTime int64 `json:"end_time,omitempty"`

	// Trace contains all recorded steps.
	Trace []TraceStep `json:"trace"`
}

ReasoningTrace is the exportable trace format.

type RestoreResult

type RestoreResult struct {
	// Restored is true if a checkpoint was successfully restored.
	Restored bool `json:"restored"`

	// CheckpointID is the ID of the restored checkpoint (empty if not restored).
	CheckpointID string `json:"checkpoint_id,omitempty"`

	// Generation is the CRS generation of the restored state.
	Generation int64 `json:"generation,omitempty"`

	// CheckpointTime is when the checkpoint was created (Unix milliseconds UTC).
	// GR-36 Code Review Fix: S3 - Use int64 instead of time.Time.
	CheckpointTime int64 `json:"checkpoint_time,omitempty"`

	// CheckpointAge is how old the checkpoint was at restore time.
	CheckpointAge time.Duration `json:"checkpoint_age,omitempty"`

	// Reason explains why restore succeeded or failed.
	Reason string `json:"reason"`

	// ModifiedFiles is the list of files modified since checkpoint.
	ModifiedFiles []string `json:"modified_files,omitempty"`

	// ModifiedFileCount is the total count (may exceed len(ModifiedFiles) if truncated).
	ModifiedFileCount int `json:"modified_file_count"`

	// DurationMs is how long the restore took in milliseconds.
	DurationMs int64 `json:"duration_ms"`
}

RestoreResult describes what happened during session restore.

Description:

Contains the outcome of a restore attempt including whether it
succeeded, checkpoint details, and any modified files detected.

Thread Safety: Immutable after creation; safe for concurrent use.

type Sanitizer

type Sanitizer interface {
	// Sanitize redacts sensitive data from a string.
	//
	// Inputs:
	//   s - The string to sanitize.
	//
	// Outputs:
	//   string - The sanitized string with secrets replaced by [REDACTED].
	Sanitize(s string) string
}

Sanitizer sanitizes sensitive data before recording.

Description:

Implementations should redact secrets, PII, and other sensitive data
to prevent leakage into audit trails.

Thread Safety:

Implementations must be safe for concurrent use.

type SecretSanitizer

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

SecretSanitizer detects and redacts secrets from strings.

Description:

SecretSanitizer uses regex patterns to detect common secret formats
including API keys, tokens, passwords, and private keys. Detected
secrets are replaced with [REDACTED].

Thread Safety:

SecretSanitizer is safe for concurrent use after initialization.

func NewSecretSanitizer

func NewSecretSanitizer() *SecretSanitizer

NewSecretSanitizer creates a new secret sanitizer with default patterns.

Description:

Creates a sanitizer that detects common secret patterns including:
- AWS keys (AKIA*, ASIA*, etc.)
- Google Cloud API keys (AIza*)
- GitHub tokens (ghp_*, gho_*, etc.)
- Slack tokens (xox*)
- Private keys (-----BEGIN * PRIVATE KEY-----)
- Generic API keys and passwords
- Database connection strings with credentials
- JWT secrets

Outputs:

*SecretSanitizer - The configured sanitizer.

func (*SecretSanitizer) Sanitize

func (ss *SecretSanitizer) Sanitize(s string) string

Sanitize replaces detected secrets with [REDACTED].

Description:

Scans the input string for known secret patterns and replaces
any matches with [REDACTED]. Multiple secrets in the same string
are all replaced.

Inputs:

s - The string to sanitize.

Outputs:

string - The sanitized string with secrets replaced.

Thread Safety:

This method is safe for concurrent use.

func (*SecretSanitizer) SanitizeMap

func (ss *SecretSanitizer) SanitizeMap(m map[string]string) map[string]string

SanitizeMap sanitizes all values in a string map.

Description:

Creates a new map with all values sanitized. Keys are preserved.

Inputs:

m - The map to sanitize.

Outputs:

map[string]string - New map with sanitized values.

Thread Safety:

This method is safe for concurrent use.

type Serializer

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

Serializer converts CRS snapshots to exportable JSON formats.

Thread Safety: Safe for concurrent use (stateless).

func NewSerializer

func NewSerializer(logger *slog.Logger) *Serializer

NewSerializer creates a new CRS serializer.

Inputs:

logger - Logger for serialization events. Uses default if nil.

Outputs:

*Serializer - The configured serializer.

func (*Serializer) ComputeSummary

func (s *Serializer) ComputeSummary(snapshot Snapshot) ReasoningSummary

ComputeSummary computes summary metrics from a snapshot.

Thread Safety: Safe for concurrent use.

func (*Serializer) Export

func (s *Serializer) Export(snapshot Snapshot, sessionID string) *CRSExport

Export converts a CRS snapshot to exportable JSON format.

Description:

Takes an immutable CRS snapshot and converts all indexes
to JSON-serializable structures. Computes summary metrics.

Inputs:

snapshot - Immutable CRS snapshot. If nil, returns empty export.
sessionID - Session identifier for the export.

Outputs:

*CRSExport - Serializable CRS state. Never nil.

Thread Safety: Safe for concurrent use.

func (*Serializer) ExportFull

func (s *Serializer) ExportFull(ctx context.Context, snapshot Snapshot, sessionID string, opts *ExportOptions) (*ExportResult, error)

ExportFull creates a full export of CRS state with options.

Description:

Creates a JSON-serializable export of all CRS indexes, including
the previously incomplete SimilarityIndex and DependencyIndex.
Operates on an immutable snapshot for thread safety.

Inputs:

  • ctx: Context for cancellation and tracing. Must not be nil.
  • snapshot: Immutable CRS snapshot to export. Must not be nil.
  • sessionID: Session identifier for the export.
  • opts: Export options. If nil, uses defaults (100K pair limit).

Outputs:

  • *ExportResult: Export data and metadata. Never nil on success.
  • error: Non-nil if context is nil, snapshot is nil, or export fails.

Example:

result, err := serializer.ExportFull(ctx, snap, "session-123", nil)
if err != nil {
    return fmt.Errorf("export: %w", err)
}
if result.Truncated {
    log.Warn("export truncated", "warnings", result.Warnings)
}

Thread Safety: Safe for concurrent use (reads immutable snapshot).

func (*Serializer) ExportSummaryOnly

func (s *Serializer) ExportSummaryOnly(snapshot Snapshot) ReasoningSummary

ExportSummaryOnly returns just the summary without full indexes.

Description:

Lightweight export for including in API responses.
Computes metrics without serializing full indexes.

Inputs:

snapshot - Immutable CRS snapshot. If nil, returns empty summary.

Outputs:

ReasoningSummary - High-level metrics.

Thread Safety: Safe for concurrent use.

func (*Serializer) Import

func (s *Serializer) Import(ctx context.Context, export *CRSExport, opts *ImportOptions) (*ImportedState, error)

Import reconstructs CRS state from an export.

Description:

Parses a CRSExport and constructs internal data structures that can be
used to restore CRS state. Uses transactional semantics: all data is
validated and built before constructing the result.

For similarity pairs, both directions are reconstructed since only one
direction is exported. For dependency edges, duplicates are deduplicated.

Inputs:

  • ctx: Context for cancellation and tracing. Must not be nil.
  • export: The CRS export to import. Must not be nil.
  • opts: Import options. If nil, uses defaults (strict validation).

Outputs:

  • *ImportedState: The imported state data. Never nil on success.
  • error: Non-nil if validation fails or import errors occur.

Example:

state, err := serializer.Import(ctx, export, nil)
if err != nil {
    return fmt.Errorf("import: %w", err)
}
// Use state to restore CRS...

Thread Safety: Safe for concurrent use.

type SessionIdentifier

type SessionIdentifier struct {
	// ProjectPath is the canonical absolute path to the project root.
	ProjectPath string `json:"project_path"`

	// ProjectHash is the SHA256 hash of lock files (go.mod, go.sum, etc.).
	// Used to detect dependency changes between sessions.
	ProjectHash string `json:"project_hash"`

	// GitCommitHash is the git commit hash if available.
	// Provides additional change detection beyond lock files.
	GitCommitHash string `json:"git_commit_hash,omitempty"`

	// ComputedAt is when this identifier was computed (Unix milliseconds UTC).
	ComputedAt int64 `json:"computed_at"`
}

SessionIdentifier uniquely identifies a project/workspace for checkpoint lookup.

Description:

Combines the canonical project path with a content hash of lock files
to detect when dependencies change. The checkpoint key is a hash of
the project path for filesystem-safe storage.

Thread Safety: Immutable after creation; safe for concurrent use.

func NewSessionIdentifier

func NewSessionIdentifier(ctx context.Context, projectPath string) (*SessionIdentifier, error)

NewSessionIdentifier creates a session identifier for a project.

Description:

Computes a stable project identifier from the project path and its
dependency lock files. The identifier includes a hash of go.mod,
go.sum, package.json, etc. to detect when the project changes.

Inputs:

  • ctx: Context for cancellation and tracing. Must not be nil.
  • projectPath: Path to the project root. Must not be empty.

Outputs:

  • *SessionIdentifier: The computed identifier. Never nil on success.
  • error: Non-nil if path resolution fails or context is nil.

Example:

sid, err := crs.NewSessionIdentifier(ctx, "/path/to/project")
if err != nil {
    return fmt.Errorf("compute session ID: %w", err)
}
key := sid.CheckpointKey()

Thread Safety: Safe for concurrent use.

func (*SessionIdentifier) Age

func (s *SessionIdentifier) Age() time.Duration

Age returns the age of this session identifier.

Thread Safety: Safe for concurrent use.

func (*SessionIdentifier) CheckpointKey

func (s *SessionIdentifier) CheckpointKey() string

CheckpointKey returns a filesystem-safe key for checkpoint storage.

Description:

Computes a SHA256 hash of the project path and returns the first
16 bytes (128 bits) as a hex string. This provides a stable key
for checkpoint storage that avoids filesystem issues with long paths.

Outputs:

  • string: 32-character hex string (16 bytes = 128 bits).

Thread Safety: Safe for concurrent use.

type SessionRestoreFailedEvent

type SessionRestoreFailedEvent struct {
	// ProjectPath is the project that failed to restore.
	ProjectPath string

	// Reason explains why restore failed.
	Reason string

	// Error is the underlying error if any.
	Error error
}

SessionRestoreFailedEvent is emitted when session restore fails.

type SessionRestoredEvent

type SessionRestoredEvent struct {
	// ProjectPath is the project that was restored.
	ProjectPath string

	// ProjectHash is the hash used to identify the project.
	ProjectHash string

	// Generation is the CRS generation after restore.
	Generation int64

	// CheckpointAge is how old the restored checkpoint was.
	CheckpointAge time.Duration

	// ModifiedFileCount is the number of files modified since checkpoint.
	ModifiedFileCount int

	// RestoreDuration is how long the restore took.
	RestoreDuration time.Duration
}

SessionRestoredEvent is emitted when a session is successfully restored.

type SessionRestorer

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

SessionRestorer handles checkpoint loading at session start.

Description:

Integrates with GR-33's PersistenceManager to load checkpoints
and restore CRS state. Validates checkpoint compatibility using
project hash and age checks.

Thread Safety: Safe for concurrent use.

func NewSessionRestorer

func NewSessionRestorer(pm *PersistenceManager, config *SessionRestorerConfig) (*SessionRestorer, error)

NewSessionRestorer creates a new session restorer.

Description:

Creates a restorer that uses the provided PersistenceManager
for checkpoint storage. Reuses GR-33 infrastructure instead
of creating a separate CheckpointStorage interface.

Inputs:

  • pm: Persistence manager from GR-33. Must not be nil.
  • config: Configuration. If nil, uses DefaultSessionRestorerConfig().

Outputs:

  • *SessionRestorer: The new restorer. Never nil on success.
  • error: Non-nil if pm is nil or config is invalid.

Thread Safety: Safe for concurrent use.

func (*SessionRestorer) TryRestore

func (r *SessionRestorer) TryRestore(
	ctx context.Context,
	crsi CRS,
	journal Journal,
	sessionID *SessionIdentifier,
) (*RestoreResult, error)

TryRestore attempts to restore CRS state from a previous session.

Description:

Loads a checkpoint from disk and restores it into the CRS instance
if it passes validation. Validation includes project hash matching
and age checks. Returns a RestoreResult describing the outcome.

This method uses GR-33's LoadBackup infrastructure rather than
creating a parallel path.

Inputs:

  • ctx: Context for cancellation and tracing. Must not be nil.
  • crsi: The CRS instance to restore into. Must not be nil.
  • journal: The Journal for replay. Must not be nil.
  • sessionID: Session identifier for checkpoint lookup. Must not be nil.

Outputs:

  • *RestoreResult: Describes what happened during restore. Never nil on success.
  • error: Non-nil only for fatal errors (restore failures return in result).

Example:

result, err := restorer.TryRestore(ctx, crs, journal, sessionID)
if err != nil {
    return fmt.Errorf("restore: %w", err)
}
if result.Restored {
    log.Info("restored checkpoint", "generation", result.Generation)
}

Thread Safety: Safe for concurrent use.

type SessionRestorerConfig

type SessionRestorerConfig struct {
	// CheckpointMaxAge is how old a checkpoint can be before it's invalid.
	// Default: 7 days.
	CheckpointMaxAge time.Duration

	// MaxFilesToRefresh is the maximum files to mark dirty after restore.
	// If more files changed, skip restore and trigger full rebuild.
	// Default: 1000.
	MaxFilesToRefresh int

	// UseGitStatus uses `git status` to find modified files instead of mtime scan.
	// Much faster for git repositories.
	// Default: true.
	UseGitStatus bool

	// MaxRetries is the number of retry attempts on transient failures.
	// Default: 3.
	MaxRetries int

	// Logger for session restore operations.
	// If nil, uses slog.Default().
	Logger *slog.Logger
}

SessionRestorerConfig configures session restore behavior.

Description:

Provides configuration options for session restore including
checkpoint age limits, file refresh thresholds, and git integration.

Thread Safety: Immutable after creation; safe for concurrent use.

func DefaultSessionRestorerConfig

func DefaultSessionRestorerConfig() SessionRestorerConfig

DefaultSessionRestorerConfig returns production defaults.

func (*SessionRestorerConfig) Validate

func (c *SessionRestorerConfig) Validate() error

Validate checks if the configuration is valid.

GR-36 Code Review Fix: S6 - Add config validation.

type SignalSource

type SignalSource int

SignalSource indicates where a signal came from (hard vs soft).

const (
	// SignalSourceUnknown means the source is not known.
	SignalSourceUnknown SignalSource = iota

	// SignalSourceHard means the signal came from a hard source (compiler, tests).
	SignalSourceHard

	// SignalSourceSoft means the signal came from a soft source (LLM).
	SignalSourceSoft

	// SignalSourceSafety means the signal came from the safety gate.
	//
	// Safety violations are treated as HARD signals for learning purposes.
	// When the safety gate blocks an action, CDCL should learn to avoid
	// the pattern that caused the violation.
	//
	// This addresses the "Safety Blocking Learning Signal" problem:
	// without this, safety-blocked actions would be soft errors that
	// MCTS/CDCL ignores, potentially retrying the same blocked pattern.
	SignalSourceSafety
)

func (SignalSource) IsHard

func (s SignalSource) IsHard() bool

IsHard returns true if this is a hard signal source.

Safety violations are treated as hard signals because: 1. They represent definitive failures (the action WILL be blocked) 2. CDCL should learn to avoid patterns that trigger safety blocks 3. Unlike soft signals, safety rules are deterministic

func (SignalSource) IsSafety

func (s SignalSource) IsSafety() bool

IsSafety returns true if this signal came from the safety gate.

func (SignalSource) IsValid

func (s SignalSource) IsValid() bool

IsValid returns true if the signal source is a known value (not Unknown).

Description:

Used for validation to ensure proof updates have explicit signal sources.
SignalSourceUnknown is not considered valid as it indicates missing attribution.

func (SignalSource) String

func (s SignalSource) String() string

String returns the string representation of SignalSource.

type SimilarityDelta

type SimilarityDelta struct {

	// Updates maps (node1, node2) to distance.
	Updates map[[2]string]float64
	// contains filtered or unexported fields
}

SimilarityDelta represents changes to similarity scores.

func NewSimilarityDelta

func NewSimilarityDelta(source SignalSource) *SimilarityDelta

NewSimilarityDelta creates a new similarity delta.

func (*SimilarityDelta) ConflictsWith

func (d *SimilarityDelta) ConflictsWith(other Delta) bool

ConflictsWith returns true if this delta conflicts with another.

func (*SimilarityDelta) IndexesAffected

func (d *SimilarityDelta) IndexesAffected() []string

IndexesAffected returns which indexes this delta will modify.

Thread Safety: Returns a shared slice. Callers must not modify.

func (*SimilarityDelta) Merge

func (d *SimilarityDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*SimilarityDelta) Source

func (d *SimilarityDelta) Source() SignalSource

func (*SimilarityDelta) Timestamp

func (d *SimilarityDelta) Timestamp() int64

func (*SimilarityDelta) Type

func (d *SimilarityDelta) Type() DeltaType

Type returns the delta type.

func (*SimilarityDelta) Validate

func (d *SimilarityDelta) Validate(_ Snapshot) error

Validate checks if this delta can be applied.

type SimilarityIndexExport

type SimilarityIndexExport struct {
	// PairCount is the number of similarity pairs stored.
	PairCount int `json:"pair_count"`

	// Pairs contains all similarity pairs for full export.
	// Only one direction is exported (FromID < ToID) to avoid duplicates.
	Pairs []SimilarityPairExport `json:"pairs,omitempty"`

	// Truncated indicates if Pairs was truncated due to limits.
	Truncated bool `json:"truncated,omitempty"`
}

SimilarityIndexExport is the serializable form of the Similarity Index.

type SimilarityIndexView

type SimilarityIndexView interface {
	// Distance returns the similarity distance between two nodes.
	Distance(node1, node2 string) (float64, bool)

	// NearestNeighbors returns the k nearest neighbors of a node.
	NearestNeighbors(nodeID string, k int) []SimilarityMatch

	// Size returns the number of entries.
	Size() int

	// AllPairs returns all similarity pairs for export.
	//
	// Description:
	//
	//   Returns a deep copy of the similarity matrix for serialization.
	//   Format: map[fromID]map[toID]similarity.
	//
	// Outputs:
	//   - map[string]map[string]float64: Copy of all pairs. Never nil.
	//
	// Thread Safety: Returns deep copy; caller can modify without affecting source.
	AllPairs() map[string]map[string]float64

	// AllPairsFiltered returns similarity pairs in one direction only for efficient export.
	//
	// Description:
	//
	//   Returns pairs where fromID < toID to avoid duplicates. This is more
	//   memory-efficient than AllPairs() for export since similarity is symmetric.
	//   Respects maxPairs limit and returns truncated flag.
	//
	// Inputs:
	//   - maxPairs: Maximum pairs to return. -1 for unlimited.
	//
	// Outputs:
	//   - []SimilarityPairData: Pairs in one direction. Never nil.
	//   - bool: True if truncated due to limit.
	//
	// Thread Safety: Safe for concurrent use.
	AllPairsFiltered(maxPairs int) ([]SimilarityPairData, bool)
}

SimilarityIndexView provides read-only access to similarity scores.

Thread Safety: Safe for concurrent use (immutable).

type SimilarityMatch

type SimilarityMatch struct {
	// NodeID is the matching node.
	NodeID string

	// Distance is the similarity distance (lower = more similar).
	Distance float64
}

SimilarityMatch represents a similarity search result.

type SimilarityPairData

type SimilarityPairData struct {
	FromID     string
	ToID       string
	Similarity float64
}

SimilarityPairData holds a similarity pair for export.

type SimilarityPairExport

type SimilarityPairExport struct {
	// FromID is the first node ID (lexicographically smaller).
	FromID string `json:"from_id"`

	// ToID is the second node ID (lexicographically larger).
	ToID string `json:"to_id"`

	// Similarity is the similarity score between the nodes (0.0 to 1.0).
	Similarity float64 `json:"similarity"`
}

SimilarityPairExport represents a single similarity pair.

NOTE: Only one direction is exported (FromID < ToID) since similarity is symmetric. Import reconstructs both directions.

type Snapshot

type Snapshot interface {
	// Generation returns the generation when this snapshot was created.
	Generation() int64

	// CreatedAt returns when this snapshot was created (Unix milliseconds UTC).
	CreatedAt() int64

	// ProofIndex returns the proof numbers index view.
	ProofIndex() ProofIndexView

	// ConstraintIndex returns the constraint index view.
	ConstraintIndex() ConstraintIndexView

	// SimilarityIndex returns the similarity index view.
	SimilarityIndex() SimilarityIndexView

	// DependencyIndex returns the dependency index view.
	DependencyIndex() DependencyIndexView

	// HistoryIndex returns the history index view.
	HistoryIndex() HistoryIndexView

	// StreamingIndex returns the streaming statistics index view.
	StreamingIndex() StreamingIndexView

	// Query returns the cross-index query API.
	//
	// Description:
	//
	//   Query provides methods that span multiple indexes, such as finding
	//   proven nodes that satisfy constraints or computing dependency chains.
	//
	// Outputs:
	//   - QueryAPI: The query API. Never nil.
	Query() QueryAPI

	// GraphQuery returns read-only access to the code graph (GR-28).
	//
	// Description:
	//
	//   Returns the graph query interface for activities to query the actual
	//   code graph structure. This enables activities to use graph algorithms
	//   (PageRank, community detection, etc.) rather than relying solely on
	//   CRS's internal DependencyIndex.
	//
	// Outputs:
	//   - GraphQuery: The graph query interface, or nil if unavailable.
	//
	// Thread Safety: Safe for concurrent use (snapshot is immutable).
	GraphQuery() GraphQuery

	// AnalyticsHistory returns recent analytics records (GR-31).
	//
	// Description:
	//
	//   Returns a copy of analytics records in chronological order.
	//   Activities can use this to check what analytics have been run.
	//
	// Outputs:
	//   - []*AnalyticsRecord: Copy of analytics records.
	//
	// Thread Safety: Safe for concurrent use (snapshot is immutable).
	AnalyticsHistory() []*AnalyticsRecord

	// LastAnalytics returns the most recent analytics of a given type (GR-31).
	//
	// Description:
	//
	//   Searches analytics history for the most recent record of the
	//   specified query type.
	//
	// Inputs:
	//   - queryType: The type of analytics query to find.
	//
	// Outputs:
	//   - *AnalyticsRecord: The most recent matching record, or nil if not found.
	//
	// Thread Safety: Safe for concurrent use (snapshot is immutable).
	LastAnalytics(queryType AnalyticsQueryType) *AnalyticsRecord

	// HasRunAnalytics checks if a specific analytics type has been run (GR-31).
	//
	// Description:
	//
	//   Returns true if an analytics query of the given type has been
	//   recorded in history.
	//
	// Inputs:
	//   - queryType: The type of analytics query to check.
	//
	// Outputs:
	//   - bool: True if the query type has been run.
	//
	// Thread Safety: Safe for concurrent use (snapshot is immutable).
	HasRunAnalytics(queryType AnalyticsQueryType) bool
}

Snapshot is an immutable view of CRS at a point in time.

Description:

Snapshots are created by CRS.Snapshot() and provide read-only access
to all indexes. Snapshots are thread-safe and can be shared across
goroutines.

Thread Safety: Safe for concurrent use (immutable).

type StepRecord

type StepRecord struct {

	// StepNumber is the 1-indexed step number.
	StepNumber int `json:"step_number"`

	// Timestamp is when this step started (Unix milliseconds UTC).
	Timestamp int64 `json:"timestamp"`

	// SessionID links this step to its session. Must not be empty.
	SessionID string `json:"session_id"`

	// Actor identifies who made this decision.
	Actor Actor `json:"actor"`

	// Model is the specific model used (e.g., "granite4:micro-h", "glm-4.7-flash").
	Model string `json:"model,omitempty"`

	// Decision is the type of decision made.
	Decision Decision `json:"decision"`

	// Tool is the tool name if applicable.
	Tool string `json:"tool,omitempty"`

	// ToolParams are the parameters passed to the tool (typed struct, not map[string]any).
	ToolParams *ToolParams `json:"tool_params,omitempty"`

	// Confidence is the router confidence if applicable (0.0-1.0).
	Confidence float64 `json:"confidence,omitempty"`

	// Reasoning explains why this decision was made.
	Reasoning string `json:"reasoning,omitempty"`

	// Outcome is the result of this step.
	Outcome Outcome `json:"outcome"`

	// ErrorMessage contains the error message if outcome is failure.
	ErrorMessage string `json:"error_message,omitempty"`

	// ErrorCategory categorizes the error for retry logic (typed enum, not string).
	ErrorCategory ErrorCategory `json:"error_category,omitempty"`

	// DurationMs is how long this step took in milliseconds.
	// NOTE: Using int64 with explicit _ms suffix for JSON clarity.
	// time.Duration encodes as nanoseconds in JSON which is confusing.
	DurationMs int64 `json:"duration_ms"`

	// ResultSummary is a brief summary of what was found/produced.
	ResultSummary string `json:"result_summary,omitempty"`

	// TokensUsed is the number of tokens consumed.
	TokensUsed int `json:"tokens_used,omitempty"`

	// Propagate indicates if this step's outcome should influence the next step.
	Propagate bool `json:"propagate"`

	// Terminal indicates if this is a final answer (no more steps needed).
	Terminal bool `json:"terminal"`

	// NextHint provides guidance for the next step.
	NextHint string `json:"next_hint,omitempty"`

	// ProofUpdates for MCTS proof number changes.
	ProofUpdates []ProofUpdate `json:"proof_updates,omitempty"`

	// ConstraintsAdded for MCTS constraint additions.
	ConstraintsAdded []ConstraintUpdate `json:"constraints_added,omitempty"`

	// DependenciesFound for MCTS dependency edges.
	DependenciesFound []DependencyEdge `json:"dependencies_found,omitempty"`
}

StepRecord captures one step in the agent reasoning process.

Description:

This is the primary unit of CRS recording. Each step represents a discrete
decision point with explicit attribution, outcome, and context for the next step.
StepRecord replaces the old TraceStep with typed fields instead of untyped strings.

Thread Safety: StepRecord is immutable after creation.

func TraceStepToStepRecord

func TraceStepToStepRecord(step TraceStep, sessionID string) StepRecord

TraceStepToStepRecord converts a lightweight TraceStep into a full StepRecord.

Description:

Bridge function connecting the execute phase's TraceStep recording with the
CRS StepRecord system. Maps available TraceStep fields to StepRecord fields
and provides sensible defaults for fields not present in TraceStep.
This enables CRS.CountToolExecutions() to return accurate counts,
which the circuit breaker uses as a fallback when no proof data exists.

Inputs:

  • step: The TraceStep to convert. Should have non-empty Tool field for the conversion to be meaningful for execution counting.
  • sessionID: The session ID to attach. Must not be empty.

Outputs:

  • StepRecord: The converted record ready for CRS.RecordStep().

Limitations:

  • TraceStep does not carry Actor or Decision context; defaults to ActorSystem / DecisionExecuteTool.
  • ToolParams are not available in TraceStep; left nil in StepRecord.
  • Confidence is not available; defaults to 0.

func (*StepRecord) IsCircuitBreakerIntervention

func (s *StepRecord) IsCircuitBreakerIntervention() bool

IsCircuitBreakerIntervention returns true if this step was a circuit breaker action.

func (*StepRecord) IsTerminal

func (s *StepRecord) IsTerminal() bool

IsTerminal returns true if this step marks the end of reasoning.

func (*StepRecord) IsToolExecution

func (s *StepRecord) IsToolExecution() bool

IsToolExecution returns true if this step represents an actual tool execution. Used by CountToolExecutions to count real executions, not selections.

func (*StepRecord) Validate

func (s *StepRecord) Validate() error

Validate checks that the StepRecord has required fields and valid values.

Description:

Call this before recording to catch errors early. Validation is strict:
all required fields must be present and values must be in valid ranges.

Outputs:

error - Non-nil if validation fails with a description of the problem.

Thread Safety: Safe for concurrent use (read-only).

type StreamDelta

type StreamDelta struct {
	// Type is the delta type name (e.g., "proof", "constraint", "similarity").
	Type string `json:"type"`

	// SeqNum is the NATS JetStream message sequence number.
	SeqNum uint64 `json:"seq"`

	// Timestamp is when the delta was created (Unix milliseconds UTC).
	Timestamp int64 `json:"timestamp"`

	// Summary is a human-readable description of the delta.
	Summary string `json:"summary"`
}

StreamDelta represents a delta event for SSE streaming.

Description:

Provides a JSON-serializable summary of a CRS delta for Server-Sent Events.
Contains the delta type, sequence number, timestamp, and a human-readable
summary suitable for real-time observability dashboards.

Thread Safety: Immutable after creation.

func DeltaToStreamDelta

func DeltaToStreamDelta(delta Delta, seqNum uint64, timestampMs int64) StreamDelta

DeltaToStreamDelta converts a Delta to a StreamDelta for SSE.

Description:

Extracts the type and generates a human-readable summary from a Delta.
Used by the SSE endpoint to stream live CRS decisions.

Inputs:

  • delta: The CRS delta to summarize. Must not be nil.
  • seqNum: The NATS message sequence number.
  • timestampMs: The delta timestamp (Unix milliseconds UTC).

Outputs:

  • StreamDelta: SSE-ready summary of the delta.

Thread Safety: Safe for concurrent use (read-only on delta).

type StreamingDelta

type StreamingDelta struct {

	// Increments maps item to frequency increment.
	Increments map[string]uint64

	// CardinalityItems are items to count for cardinality.
	CardinalityItems []string
	// contains filtered or unexported fields
}

StreamingDelta represents updates to streaming statistics.

func NewStreamingDelta

func NewStreamingDelta(source SignalSource) *StreamingDelta

NewStreamingDelta creates a new streaming delta.

func (*StreamingDelta) ConflictsWith

func (d *StreamingDelta) ConflictsWith(_ Delta) bool

ConflictsWith returns true if this delta conflicts with another.

func (*StreamingDelta) IndexesAffected

func (d *StreamingDelta) IndexesAffected() []string

IndexesAffected returns which indexes this delta will modify.

Thread Safety: Returns a shared slice. Callers must not modify.

func (*StreamingDelta) Merge

func (d *StreamingDelta) Merge(other Delta) (Delta, error)

Merge combines this delta with another delta.

func (*StreamingDelta) Source

func (d *StreamingDelta) Source() SignalSource

func (*StreamingDelta) Timestamp

func (d *StreamingDelta) Timestamp() int64

func (*StreamingDelta) Type

func (d *StreamingDelta) Type() DeltaType

Type returns the delta type.

func (*StreamingDelta) Validate

func (d *StreamingDelta) Validate(_ Snapshot) error

Validate checks if this delta can be applied.

type StreamingIndexExport

type StreamingIndexExport struct {
	// Cardinality is the estimated unique item count.
	Cardinality uint64 `json:"cardinality"`

	// ApproximateBytes is the approximate memory usage.
	ApproximateBytes int `json:"approximate_bytes"`
}

StreamingIndexExport is the serializable form of the Streaming Index.

type StreamingIndexView

type StreamingIndexView interface {
	// Estimate returns the frequency estimate for an item.
	Estimate(item string) uint64

	// Cardinality returns the estimated unique item count.
	Cardinality() uint64

	// Size returns the approximate memory usage in bytes.
	Size() int
}

StreamingIndexView provides read-only access to streaming statistics.

Thread Safety: Safe for concurrent use (immutable).

type ToolParams

type ToolParams struct {
	// Target is the primary target (file path, symbol name, etc.).
	Target string `json:"target,omitempty"`

	// Query is the search query or pattern.
	Query string `json:"query,omitempty"`

	// Depth limits recursion or traversal depth.
	Depth int `json:"depth,omitempty"`

	// Limit caps the number of results.
	Limit int `json:"limit,omitempty"`

	// Flags are boolean options.
	Flags []string `json:"flags,omitempty"`

	// Extra holds additional string key-value pairs for tool-specific params.
	// This is bounded and auditable unlike map[string]any.
	Extra []KeyValue `json:"extra,omitempty"`
}

ToolParams captures tool invocation parameters with typed fields.

Description:

Per CLAUDE.md Section 4.5, we use typed structs instead of map[string]any.
This ensures type safety at compile time and proper serialization.

Thread Safety: ToolParams is immutable after creation.

type TraceConfig

type TraceConfig struct {
	// MaxSteps limits trace size to prevent unbounded growth.
	// When exceeded, oldest steps are evicted.
	// Default: 1000.
	MaxSteps int

	// RecordSymbols enables recording of discovered symbols.
	// Default: true.
	RecordSymbols bool

	// RecordMetadata enables recording of step metadata.
	// Default: true.
	RecordMetadata bool

	// Sanitizer sanitizes trace data before recording.
	// If nil, no sanitization is performed.
	// SECURITY: Should be set to prevent secrets from leaking into traces.
	Sanitizer Sanitizer
}

TraceConfig configures trace recording behavior.

func DefaultTraceConfig

func DefaultTraceConfig() TraceConfig

DefaultTraceConfig returns sensible defaults.

func SecureTraceConfig

func SecureTraceConfig() TraceConfig

SecureTraceConfig returns config with secret sanitization enabled.

Description:

Returns a TraceConfig with the default SecretSanitizer configured.
Use this configuration to prevent secrets from leaking into audit trails.

Outputs:

TraceConfig - Configuration with sanitization enabled.

type TraceRecorder

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

TraceRecorder captures reasoning steps for audit and debugging.

Description:

Records each reasoning action and its effects on CRS state.
Steps are stored in order and can be exported as a complete trace.

Thread Safety: Safe for concurrent use.

func NewTraceRecorder

func NewTraceRecorder(config TraceConfig) *TraceRecorder

NewTraceRecorder creates a new trace recorder.

Inputs:

config - Configuration for the recorder. Uses defaults if zero-valued.

Outputs:

*TraceRecorder - The configured recorder.

func (*TraceRecorder) Clear

func (r *TraceRecorder) Clear()

Clear removes all recorded steps.

Thread Safety: Safe for concurrent use.

func (*TraceRecorder) Export

func (r *TraceRecorder) Export(sessionID string) ReasoningTrace

Export returns the trace in exportable format.

Description:

Creates a ReasoningTrace containing all recorded steps,
suitable for JSON serialization.

Inputs:

sessionID - Session identifier for the export.

Outputs:

ReasoningTrace - The exportable trace.

Thread Safety: Safe for concurrent use.

func (*TraceRecorder) GetSteps

func (r *TraceRecorder) GetSteps() []TraceStep

GetSteps returns a copy of all recorded steps.

Outputs:

[]TraceStep - Copy of recorded steps in order.

Thread Safety: Safe for concurrent use.

func (*TraceRecorder) LastStep

func (r *TraceRecorder) LastStep() *TraceStep

LastStep returns the most recently recorded step, or nil if empty.

Thread Safety: Safe for concurrent use.

func (*TraceRecorder) RecordStep

func (r *TraceRecorder) RecordStep(step TraceStep)

RecordStep adds a step to the trace.

Description:

Called after each reasoning action to capture what was done,
what was found, and how CRS was updated. Automatically assigns
step numbers and timestamps.

SECURITY: If a Sanitizer is configured, all string fields are
sanitized before storage to prevent secrets from leaking into
audit trails. This is critical because safety scanners may block
actions that contain secrets, but the attempted action (including
the secret) would otherwise be recorded in the trace.

Inputs:

step - The trace step to record. Step number and timestamp
       will be overwritten by the recorder.

Thread Safety: Safe for concurrent use.

func (*TraceRecorder) StepCount

func (r *TraceRecorder) StepCount() int

StepCount returns the number of recorded steps.

Thread Safety: Safe for concurrent use.

type TraceStep

type TraceStep struct {
	// Step is the 1-indexed step number (assigned by recorder).
	Step int `json:"step"`

	// Timestamp is when this step occurred (Unix milliseconds UTC).
	Timestamp int64 `json:"timestamp"`

	// Action describes what was done (e.g., "explore", "analyze", "trace_flow").
	Action string `json:"action"`

	// Target is the file or symbol being operated on.
	Target string `json:"target"`

	// Tool is the tool that triggered this action (optional).
	Tool string `json:"tool,omitempty"`

	// Duration is how long this step took.
	Duration time.Duration `json:"duration_ms"`

	// SymbolsFound lists symbols discovered in this step.
	SymbolsFound []string `json:"symbols_found,omitempty"`

	// ProofUpdates lists proof status changes.
	ProofUpdates []ProofUpdate `json:"proof_updates,omitempty"`

	// ConstraintsAdded lists new constraints added.
	ConstraintsAdded []ConstraintUpdate `json:"constraints_added,omitempty"`

	// DependenciesFound lists new dependency edges found.
	DependenciesFound []DependencyEdge `json:"dependencies_found,omitempty"`

	// Error contains any error that occurred.
	Error string `json:"error,omitempty"`

	// Metadata contains additional step context.
	Metadata map[string]string `json:"metadata,omitempty"`
}

TraceStep represents one step in the reasoning process.

Description:

Captures what action was taken, what was found, and how CRS was updated.
Steps are recorded in order and can be exported for audit/debugging.

type TraceStepBuilder

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

TraceStepBuilder helps construct TraceStep instances.

func NewTraceStepBuilder

func NewTraceStepBuilder() *TraceStepBuilder

NewTraceStepBuilder creates a new builder.

func (*TraceStepBuilder) Build

func (b *TraceStepBuilder) Build() TraceStep

Build returns the constructed TraceStep.

func (*TraceStepBuilder) WithAction

func (b *TraceStepBuilder) WithAction(action string) *TraceStepBuilder

WithAction sets the action.

func (*TraceStepBuilder) WithConstraint

func (b *TraceStepBuilder) WithConstraint(id, constraintType string, nodes []string) *TraceStepBuilder

WithConstraint adds a constraint update.

func (*TraceStepBuilder) WithDependency

func (b *TraceStepBuilder) WithDependency(from, to string) *TraceStepBuilder

WithDependency adds a dependency edge.

func (*TraceStepBuilder) WithDuration

func (b *TraceStepBuilder) WithDuration(d time.Duration) *TraceStepBuilder

WithDuration sets the duration.

func (*TraceStepBuilder) WithError

func (b *TraceStepBuilder) WithError(err string) *TraceStepBuilder

WithError sets the error.

func (*TraceStepBuilder) WithMetadata

func (b *TraceStepBuilder) WithMetadata(key, value string) *TraceStepBuilder

WithMetadata adds a metadata key-value pair.

func (*TraceStepBuilder) WithProofUpdate

func (b *TraceStepBuilder) WithProofUpdate(nodeID, status, reason, source string) *TraceStepBuilder

WithProofUpdate adds a proof update.

Parameters accept string values for backwards compatibility:

  • status: "proven", "disproven", "expanded", "unknown", "increment", "decrement"
  • source: "hard", "soft", "safety"

func (*TraceStepBuilder) WithProofUpdateTyped

func (b *TraceStepBuilder) WithProofUpdateTyped(nodeID string, updateType ProofUpdateType, delta uint64, reason string, source SignalSource) *TraceStepBuilder

WithProofUpdateTyped adds a proof update with typed parameters. Prefer this over WithProofUpdate for new code.

func (*TraceStepBuilder) WithSymbolsFound

func (b *TraceStepBuilder) WithSymbolsFound(symbols []string) *TraceStepBuilder

WithSymbolsFound sets the symbols found.

func (*TraceStepBuilder) WithTarget

func (b *TraceStepBuilder) WithTarget(target string) *TraceStepBuilder

WithTarget sets the target.

func (*TraceStepBuilder) WithTool

func (b *TraceStepBuilder) WithTool(tool string) *TraceStepBuilder

WithTool sets the tool.

Directories

Path Synopsis
Package indexes provides the 6 index implementations for CRS.
Package indexes provides the 6 index implementations for CRS.

Jump to

Keyboard shortcuts

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