/** * Control Plane Core Interfaces * * This module defines the core abstractions for the claude-monorepo-guard * control plane, which separates scanning, processing, evaluation, and rules * into composable, testable components. * * ## Architecture Overview * * The control plane implements a progressive pipeline: * * ``` * Scanner → ScanArtifact → Processor → State → Evaluation → Rule → Violation * ``` * * ## Key Concepts * * - **Scanner**: Reads from external reality (filesystem, git, AST, network) * - **ScanArtifact**: Immutable snapshot of external reality at a point in time * - **Processor**: Transforms artifacts into structured, domain-rich State * - **State**: Versioned, queryable snapshot of processed data * - **EvaluationInterface**: Exposes typed query operations against State * - **EvaluationResult**: Rich result of a query (not just boolean) * - **Rule**: Declarative specification of "what must hold" about a State * - **Violation**: When a rule's condition is not satisfied * * ## Design Principles * * 1. **Separation of Concerns**: Each component has a single responsibility * 2. **Immutability**: Artifacts and States are immutable snapshots * 3. **Composability**: Processors can combine multiple artifact sources * 4. **Testability**: Each component is independently testable * 5. **Extensibility**: New scanners/processors/evaluations added without changing core * 6. **Type Safety**: Strong typing throughout with generics for payloads * * @module control-plane/core * @since 3.0.0 */ /** * A Scanner reads from external reality and produces immutable ScanArtifacts. * * Scanners are responsible for interacting with external systems like: * - Filesystem (reading directory trees) * - Git (querying branches, commits, status) * - AST parsers (parsing source code) * - Network (fetching remote data) * - Package managers (reading dependencies) * * ## Characteristics * * Scanners should be: * - **Pure**: Same inputs → same outputs (given stable external state) * - **Isolated**: No shared mutable state between scans * - **Testable**: External dependencies should be mockable * - **Configurable**: Accept configuration for frequency, scope, etc. * * ## Lifecycle * * 1. Instantiated with dependencies (adapters, config) * 2. `scan()` called by orchestrator/engine * 3. Produces immutable `ScanArtifact` * 4. Artifact is cached/persisted for reuse * * @template TPayload The type of data this scanner produces * @template TConfig The type of configuration this scanner accepts * * @example * ```typescript * class GitScanner implements Scanner { * readonly id = 'git'; * * async scan(config: GitScanConfig): Promise> { * const branch = await this.gitAdapter.getCurrentBranch(); * const hasChanges = await this.gitAdapter.hasUncommittedChanges(); * * return { * scannerId: this.id, * timestamp: new Date(), * payload: { branch, hasChanges } * }; * } * } * ``` */ export interface Scanner { /** * Unique identifier for this scanner type. * * Must be unique across all scanners in the system. * Used for artifact correlation and caching. * * @example 'git', 'filesystem', 'ast', 'npm' */ readonly id: string; /** * Default configuration for this scanner. * * Provides sensible defaults when scanner is used without explicit config. * Can be overridden per-scan via `scan(config)`. */ readonly defaultConfig?: TConfig; /** * Perform a scan and return an immutable artifact. * * This method should: * 1. Read current state from external system * 2. Transform into scanner-specific payload format * 3. Package as timestamped artifact * * Should not throw on recoverable errors; include errors in metadata instead. * * @param config Scanner-specific configuration * @returns Promise resolving to immutable scan artifact * @throws Only on unrecoverable errors (e.g., scanner misconfiguration) */ scan(config: TConfig): Promise>; } /** * An immutable snapshot of external reality at a point in time. * * Artifacts are the bridge between external systems and the domain model. * They are intentionally "raw" - minimal processing, close to source data. * * ## Characteristics * * - **Immutable**: Once created, never modified * - **Timestamped**: Captures when the scan occurred * - **Cacheable**: Can be reused across multiple evaluations * - **Serializable**: Can be persisted and reconstructed * * ## Lifecycle * * 1. Created by Scanner * 2. Cached by orchestrator (to avoid re-scanning) * 3. Passed to one or more Processors * 4. Optionally persisted for historical analysis * * @template TPayload The type of data contained in this artifact * * @example * ```typescript * const artifact: ScanArtifact = { * scannerId: 'git', * timestamp: new Date('2025-11-24T12:00:00Z'), * payload: { * currentBranch: 'feature/new-arch', * uncommittedFiles: ['src/foo.ts'], * lastCommit: 'abc123' * }, * metadata: { * scanDurationMs: 45, * gitVersion: '2.39.0' * } * }; * ``` */ export interface ScanArtifact { /** * ID of the scanner that produced this artifact. * * Must match the `id` property of the Scanner that created it. * Used for routing artifacts to appropriate processors. */ scannerId: string; /** * When this scan was performed. * * Represents the point in time when external state was read. * Used for: * - Cache invalidation * - Historical analysis * - Incremental processing decisions */ timestamp: Date; /** * Scanner-specific payload (raw or lightly processed data). * * Format is defined by the scanner. Processors know how to interpret * payloads from scanners they consume. * * Should be JSON-serializable for persistence. */ payload: TPayload; /** * Optional metadata about the scan itself. * * Use for: * - Performance metrics (scan duration, items scanned) * - Warnings (partial scan, missing permissions) * - Version info (tool versions, API versions) * - Debug info (command executed, config used) */ metadata?: Record; } /** * A Processor transforms ScanArtifacts into structured, domain-rich State. * * Processors are the "intelligence layer" that converts raw scan data into * queryable, semantically meaningful structures. * * ## Responsibilities * * - Transform raw artifacts into domain objects * - Build indices, trees, graphs for efficient querying * - Compute derived data (summaries, statistics) * - Handle incremental updates (diff against previous state) * * ## Characteristics * * - **Multi-input**: Can consume multiple artifact types * - **Incremental**: Can accept previous state and produce diff * - **Versioned**: Output states are versioned * - **Pure**: Same artifacts + previous state → same new state * * @template TStateData The type of state data this processor produces * * @example * ```typescript * class PropertyGraphProcessor implements Processor { * readonly id = 'property-graph-processor'; * readonly consumes = ['filesystem', 'ast']; * readonly produces = 'property-graph'; * * async process(input: ProcessorInput): Promise> { * const fsArtifact = input.artifacts.find(a => a.scannerId === 'filesystem'); * const astArtifact = input.artifacts.find(a => a.scannerId === 'ast'); * * // Build dependency graph from filesystem + AST * const graph = this.buildGraph(fsArtifact.payload, astArtifact.payload); * * return { * id: { type: 'property-graph', key: 'default' }, * version: (input.state?.version ?? 0) + 1, * timestamp: new Date(), * data: graph * }; * } * } * ``` */ export interface Processor { /** * Unique identifier for this processor. * * Used for: * - Logging/debugging * - Processor selection by orchestrator * - Dependency tracking */ readonly id: string; /** * List of scanner IDs this processor consumes. * * The orchestrator uses this to: * - Determine which scanners to run before this processor * - Route artifacts to appropriate processors * - Detect missing dependencies * * @example ['filesystem', 'ast'] */ readonly consumes: string[]; /** * State type this processor produces. * * Must match the `StateId.type` in produced states. * Used for: * - State routing to evaluation interfaces * - Rule targeting (rules specify target state type) * * @example 'filesystem-state', 'git-state', 'property-graph' */ readonly produces: string; /** * Process artifacts (and optionally previous state) into new state. * * This method should: * 1. Extract required artifacts from input * 2. Transform artifacts into domain objects * 3. Build indices/structures for efficient querying * 4. Optionally compute diff from previous state * 5. Return versioned, immutable state * * @param input Artifacts and optional previous state * @returns Promise resolving to new state * @throws On unrecoverable errors (missing artifacts, invalid data) */ process(input: ProcessorInput): Promise>; } /** * Input to a processor: artifacts + optional previous state. * * Supports both full rebuild and incremental processing: * - **Full rebuild**: `state` is undefined, build from artifacts * - **Incremental**: `state` is provided, compute diff and merge */ export interface ProcessorInput { /** * Previous state (if doing incremental update). * * When provided, processor can: * - Compare against new artifacts to find changes * - Produce smaller diff-based update * - Preserve unchanged portions of state * * When undefined: * - Full rebuild from artifacts * - First time processing */ state?: State; /** * One or more scan artifacts to process. * * Processors should: * - Validate all required artifacts are present * - Extract artifacts matching their `consumes` list * - Handle missing optional artifacts gracefully */ artifacts: ScanArtifact[]; } /** * Unique identifier for a state instance. * * States are identified by a combination of: * - **type**: What kind of state (filesystem, git, ast, graph) * - **key**: Which instance (repo path, workspace id, etc.) * * This allows multiple states of the same type to coexist * (e.g., filesystem state for multiple directories). * * @example * ```typescript * { type: 'filesystem-state', key: '/Users/sam/project' } * { type: 'git-state', key: 'current' } * { type: 'ast-state', key: 'src/domain' } * { type: 'property-graph', key: 'default' } * ``` */ export interface StateId { /** * Type of state. * * Must match the processor's `produces` value. * Used for routing states to evaluation interfaces. * * Convention: Use kebab-case with '-state' suffix. * * @example 'filesystem-state', 'git-state', 'ast-state' */ type: string; /** * Instance key. * * Distinguishes multiple instances of same state type. * Format is state-type-specific: * - Filesystem state: absolute path * - Git state: 'current', 'upstream', etc. * - Workspace state: workspace ID * * @example '/Users/sam/project', 'current', 'workspace:foo' */ key: string; } /** * A versioned, immutable snapshot of processed, structured data. * * States are the "source of truth" for evaluation and rule checking. * They are domain-rich, queryable structures built from raw artifacts. * * ## Characteristics * * - **Versioned**: Incremented on each update * - **Immutable**: Once created, never modified * - **Queryable**: Via EvaluationInterfaces * - **Viewable**: Via StateViews for rendering * - **Cacheable**: Can be reused across rules * * ## Lifecycle * * 1. Created by Processor from artifacts * 2. Cached by orchestrator (keyed by StateId) * 3. Passed to EvaluationInterfaces for querying * 4. Optionally rendered via StateViews * 5. May be persisted for incremental updates * * @template TData The type of data contained in this state * * @example * ```typescript * const state: State = { * id: { type: 'filesystem-state', key: '/Users/sam/project' }, * version: 5, * timestamp: new Date(), * data: { * root: FilePath.create('/Users/sam/project'), * fileTree: {...}, * fileIndex: new Map(...), * totalFiles: 1247 * }, * metadata: { * processingDurationMs: 123, * artifactsUsed: ['filesystem'] * } * }; * ``` */ export interface State { /** * Unique identifier for this state. * * Combination of type + key uniquely identifies this state instance. */ id: StateId; /** * Version number (incremented on each update). * * Used for: * - Incremental processing (processor checks version) * - Cache invalidation * - Historical tracking * - Optimistic locking (if needed) * * Starts at 1, increments by 1 on each update. */ version: number; /** * When this state version was created. * * Represents when processing completed, not when scan occurred * (see artifact.timestamp for scan time). */ timestamp: Date; /** * State-specific data (domain objects, indices, graphs, etc.). * * Format is defined by the processor that created this state. * Evaluation interfaces know how to query states of specific types. * * Should be serializable for persistence (if needed). */ data: TData; /** * Optional metadata about state creation. * * Use for: * - Performance metrics (processing time, memory used) * - Provenance (which artifacts, which version of processor) * - Statistics (counts, sizes, summaries) * - Debug info (warnings, skipped items) */ metadata?: Record; } /** * An EvaluationInterface exposes typed query operations against a State. * * Evaluation interfaces are the "API" for querying states. They provide * strongly-typed, semantically meaningful operations tailored to each * state type. * * ## Examples * * - **FilesystemInterface**: `exists(path)`, `glob(pattern)`, `list(dir)` * - **GitInterface**: `branchIsProtected(name)`, `hasChanges()` * - **GraphInterface**: `dependencyPath(from, to)`, `violatesLayering(rules)` * - **ASTInterface**: `hasExportedSymbol(name)`, `importsFrom(module)` * * ## Characteristics * * - **Pure**: Same state + params → same result * - **Rich results**: Return detailed payload, not just boolean * - **Composable**: Multiple interfaces can operate on same state type * - **Typed**: Strong typing for params and payloads * * @template TParams The type of parameters this interface accepts * @template TPayload The type of result payload this interface returns * * @example * ```typescript * class FilesystemEvaluations implements EvaluationInterface { * readonly id = 'filesystem'; * readonly stateType = 'filesystem-state'; * * async evaluate(state: State, params: FsEvalParams): Promise> { * const fsState = state.data as FilesystemStateData; * * switch (params.operation) { * case 'exists': * const exists = fsState.fileIndex.has(params.path); * return { * interfaceId: this.id, * stateId: state.id, * ok: exists, * payload: { path: params.path, exists } * }; * // ... other operations * } * } * } * ``` */ export interface EvaluationInterface { /** * Unique identifier for this interface. * * Used for: * - Interface selection by rules * - Logging/debugging * - Result correlation * * @example 'filesystem', 'git', 'graph', 'ast' */ readonly id: string; /** * Type of state this interface operates on. * * Must match the `StateId.type` of states passed to `evaluate()`. * Used for: * - Interface routing * - Validation (ensure state type matches) * * @example 'filesystem-state', 'git-state', 'property-graph' */ readonly stateType: string; /** * Evaluate a query against state. * * This method should: * 1. Extract state data (cast to expected type) * 2. Execute query based on params * 3. Return rich result with detailed payload * * Should not throw on query failures; encode failures in result instead: * - `ok: false` for query that didn't match/satisfy * - Include diagnostic info in payload * * @param state State to query * @param params Query parameters (operation-specific) * @returns Promise resolving to evaluation result * @throws Only on invalid params or state type mismatch */ evaluate(state: State, params: TParams): Promise>; } /** * The result of an evaluation: ok/not-ok + rich payload. * * Results are intentionally rich - not just boolean. They include: * - Whether the query succeeded (`ok`) * - Detailed payload (matches, counts, paths, etc.) * - Human-readable message * - Metadata for debugging * * This allows rules to make sophisticated decisions and violations * to include detailed context. * * @template TPayload The type of result payload * * @example * ```typescript * // Filesystem glob result * { * interfaceId: 'filesystem', * stateId: { type: 'filesystem-state', key: '/project' }, * ok: true, * payload: { * pattern: '**\/*.ts', * matches: ['src/foo.ts', 'src/bar.ts'], * count: 2 * }, * message: 'Found 2 files matching **\/*.ts' * } * * // Graph violation result * { * interfaceId: 'graph', * stateId: { type: 'property-graph', key: 'default' }, * ok: false, * payload: { * violations: [ * { from: 'domain/Check.ts', to: 'infrastructure/GitAdapter.ts' } * ] * }, * message: 'Found 1 architecture violation' * } * ``` */ export interface EvaluationResult { /** * ID of the evaluation interface used. * * Must match the `id` of the EvaluationInterface that created this result. */ interfaceId: string; /** * ID of the state evaluated. * * References the state this result was computed from. * Useful for: * - Tracing result provenance * - Cache invalidation * - Multi-state operations */ stateId: StateId; /** * Whether the evaluation succeeded. * * Interpretation depends on the operation: * - For existence checks: true if exists * - For validation checks: true if valid * - For search operations: true if found matches * * Rules use this + `mustHold` to determine violations. */ ok: boolean; /** * Evaluation-specific payload. * * Contains detailed results: * - For glob: array of matching paths * - For dependency check: list of violations * - For count: the count value * * Format is evaluation-interface-specific. */ payload: TPayload; /** * Optional human-readable message. * * Summarizes the result in natural language. * Used for: * - Logging * - Violation messages * - UI display */ message?: string; /** * Optional metadata about evaluation. * * Use for: * - Performance metrics (query duration) * - Debug info (query plan, cache hits) * - Warnings (partial results, approximations) */ metadata?: Record; } /** * A Rule is a declarative specification of "what must hold" about a State. * * Rules are the highest-level abstraction in the control plane. They * declaratively specify conditions that must be satisfied, without * specifying *how* to check them (that's evaluation interfaces' job). * * ## Structure * * A rule declares: * - **What to check**: Target state * - **When to check**: Trigger conditions * - **What must hold**: Evaluation + condition * - **How severe**: Error, warning, or info * * ## Lifecycle * * 1. Loaded from configuration * 2. Registered with RuleEngine * 3. Triggered by scanner completion or state update * 4. Evaluated by RuleEngine using appropriate evaluation interface * 5. If violated, produces Violation * * @example * ```typescript * const rule: Rule = { * id: 'branch-protection', * name: 'Protected Branch Check', * description: 'Prevents work on protected branches', * targetState: { type: 'git-state', key: 'current' }, * trigger: { onScanOf: ['git'] }, * condition: { * interfaceId: 'git', * params: { * operation: 'branchIsProtected', * protectedBranches: ['main', 'master'] * }, * mustHold: 'always' * }, * severity: 'error', * enabled: true * }; * ``` */ export interface Rule { /** * Unique identifier for this rule. * * Used for: * - Configuration (enabling/disabling) * - Violation correlation * - Logging/debugging * * @example 'branch-protection', 'monorepo-root', 'architecture-layering' */ id: string; /** * Human-readable name. * * Displayed in: * - CLI output * - Violation messages * - UI dashboards * * @example 'Protected Branch Check', 'Architecture Layering' */ name: string; /** * Optional description. * * Explains the rule's purpose and what it enforces. * Used for: * - Documentation * - Help text * - Violation suggestions */ description?: string; /** * Which state this rule evaluates. * * Identifies both: * - State type (routes to correct evaluation interface) * - State instance (which specific state to check) * * The target state must exist before rule can be evaluated. */ targetState: StateId; /** * When to evaluate this rule. * * Defines trigger conditions: * - After specific scanners complete * - When specific states update * - On a schedule * * Multiple triggers can be specified (OR semantics). */ trigger: RuleTrigger; /** * What must hold for rule to be satisfied. * * Specifies: * - Which evaluation interface to use * - Parameters for the evaluation * - Condition that must hold (always/never/threshold) */ condition: RuleCondition; /** * Severity of violations. * * Determines: * - Exit code (errors fail build) * - Display formatting * - Filtering/reporting * * @example 'error', 'warning', 'info' */ severity: 'error' | 'warning' | 'info'; /** * Whether this rule is enabled. * * Disabled rules: * - Are not evaluated * - Do not trigger scanners * - Do not produce violations * * Defaults to true if not specified. */ enabled?: boolean; } /** * When a rule should be evaluated. * * Rules can be triggered by: * - Scanner completion (e.g., after git scan) * - State updates (e.g., when filesystem state changes) * - Schedule (e.g., daily, hourly) * * Multiple trigger types can be specified; rule evaluates on any match. * * @example * ```typescript * // Trigger after git scan * { onScanOf: ['git'] } * * // Trigger when any state updates * { onStateUpdateOf: ['filesystem-state', 'git-state'] } * * // Trigger daily at 9am * { schedule: '0 9 * * *' } * * // Multiple triggers * { * onScanOf: ['filesystem'], * onStateUpdateOf: ['filesystem-state'], * schedule: '0 * * * *' // Also run hourly * } * ``` */ export interface RuleTrigger { /** * Evaluate when these scanners complete. * * Rule triggers after any of the specified scanners finish. * Scanner IDs must match registered scanners. * * @example ['git', 'filesystem', 'ast'] */ onScanOf?: string[]; /** * Evaluate when these state types update. * * Rule triggers after any of the specified states are updated. * State types must match registered processors. * * @example ['filesystem-state', 'git-state', 'property-graph'] */ onStateUpdateOf?: string[]; /** * Evaluate on a schedule (cron-like). * * Uses cron syntax: * - Minute (0-59) * - Hour (0-23) * - Day of month (1-31) * - Month (1-12) * - Day of week (0-6, 0=Sunday) * * @example * ```typescript * '0 9 * * *' // Daily at 9am * '0 * * * *' // Every hour * '0/15 * * * *' // Every 15 minutes * ``` */ schedule?: string; } /** * What must hold for a rule to be satisfied. * * Specifies both: * - Which evaluation to run (interface + params) * - What condition must hold (always/never/threshold/custom) * * @example * ```typescript * // File must exist * { * interfaceId: 'filesystem', * params: { operation: 'exists', path: '.claudemonorepo' }, * mustHold: 'always' // evaluation.ok must be true * } * * // Branch must NOT be protected * { * interfaceId: 'git', * params: { operation: 'branchIsProtected', branches: ['main'] }, * mustHold: 'never' // evaluation.ok must be false * } * * // File count under threshold * { * interfaceId: 'filesystem', * params: { operation: 'count', pattern: '**\/*.ts' }, * mustHold: 'threshold', * threshold: { max: 1000 } // payload.count <= 1000 * } * ``` */ export interface RuleCondition { /** * Which evaluation interface to use. * * Must match the `id` of a registered EvaluationInterface. * The interface's `stateType` must match the rule's `targetState.type`. * * @example 'filesystem', 'git', 'graph', 'ast' */ interfaceId: string; /** * Parameters to pass to the evaluation. * * Format is interface-specific. Typically includes: * - Operation name (if interface supports multiple operations) * - Query parameters (paths, patterns, etc.) * * Should be JSON-serializable for configuration. */ params: unknown; /** * What must hold about the evaluation result. * * - **always**: `result.ok` must be `true` * - **never**: `result.ok` must be `false` * - **threshold**: Numeric value in payload must satisfy threshold * - **custom**: Custom predicate function */ mustHold: 'always' | 'never' | 'threshold' | 'custom'; /** * For threshold conditions: min/max values. * * Applies to numeric values in evaluation payload. * Example: file count, dependency depth, complexity score. * * @example * { min: 1, max: 100 } // Between 1 and 100 * { max: 10 } // At most 10 * { min: 0 } // At least 0 */ threshold?: { min?: number; max?: number; }; /** * For custom conditions: predicate function. * * Receives evaluation result and returns boolean. * Allows complex conditions beyond always/never/threshold. * * Note: Custom predicates cannot be serialized to config. * Use for programmatic rules only. * * @example * (result) => { * const count = result.payload.count; * const ratio = result.payload.ratio; * return count < 100 && ratio > 0.8; * } */ customPredicate?: (result: EvaluationResult) => boolean; } /** * A Violation represents a rule not being satisfied. * * Violations are produced when a rule's condition is not met. * They include: * - Which rule was violated * - The evaluation that failed * - Human-readable message * - Severity (from rule) * - Context (file, line, suggestion, etc.) * * @example * ```typescript * const violation: Violation = { * ruleId: 'branch-protection', * evaluation: { * interfaceId: 'git', * stateId: { type: 'git-state', key: 'current' }, * ok: false, * payload: { currentBranch: 'main', isProtected: true } * }, * message: 'Cannot work on protected branch "main"', * severity: 'error', * context: { * suggestion: 'Switch to a feature branch: git checkout -b feature/my-feature' * } * }; * ``` */ export interface Violation { /** * ID of the rule that was violated. * * References the Rule that produced this violation. */ ruleId: string; /** * The evaluation that caused the violation. * * Contains full evaluation result including: * - What was checked * - What was found * - Why it failed */ evaluation: EvaluationResult; /** * Human-readable message. * * Explains what went wrong and why it's a violation. * Should be actionable (tell user what to fix). * * @example * 'Cannot work on protected branch "main"' * 'Architecture violation: domain imports from infrastructure' * 'Missing required file: .claudemonorepo' */ message: string; /** * Severity (inherited from rule). * * Determines: * - Exit code (errors = non-zero) * - Display formatting * - Filtering */ severity: 'error' | 'warning' | 'info'; /** * Optional context for violation. * * Use for: * - File/line location * - Suggestion for fixing * - Related violations * - Debug info * * @example * { * file: 'src/domain/Check.ts', * line: 42, * suggestion: 'Remove import from infrastructure layer', * relatedViolations: ['other-rule-id'] * } */ context?: Record; } /** * A StateView renders a State for human/client consumption. * * Views are separate from States to keep domain layer pure (no UI concerns). * They transform state data into presentation-friendly formats. * * ## Use Cases * * - **CLI**: Render state as tree, table, or list * - **Web UI**: Transform state into React components * - **API**: Convert state to JSON response * - **Reports**: Generate markdown, HTML, PDF * * ## Characteristics * * - **Pure**: Same state → same view model * - **Stateless**: No side effects * - **Multiple per state**: Tree view, list view, JSON view, etc. * * @template TStateData The type of state data this view renders * @template TViewModel The type of view model this view produces * * @example * ```typescript * class FilesystemTreeView implements StateView { * readonly id = 'filesystem-tree'; * readonly stateType = 'filesystem-state'; * * buildView(state: State): TreeViewModel { * return { * root: state.data.root.toString(), * tree: this.renderTree(state.data.tree), * stats: { * totalFiles: state.data.totalFiles, * totalSize: formatBytes(state.data.totalSize) * } * }; * } * } * ``` */ export interface StateView { /** * Unique identifier for this view. * * Used for: * - View selection (CLI --format=tree) * - Logging/debugging * * @example 'filesystem-tree', 'git-status', 'graph-diagram' */ readonly id: string; /** * Type of state this view renders. * * Must match the `StateId.type` of states passed to `buildView()`. * * @example 'filesystem-state', 'git-state', 'property-graph' */ readonly stateType: string; /** * Build a view model from state. * * Transforms domain state into presentation-friendly format. * Should be pure (no side effects, same input → same output). * * @param state State to render * @returns View model (UI-friendly data structure) */ buildView(state: State): TViewModel; } //# sourceMappingURL=core.d.ts.map