"""Immutable generic project, node, edge, and context contracts.""" from __future__ import annotations from dataclasses import asdict, dataclass from pathlib import Path @dataclass(frozen=True) class Limits: max_source_bytes: int = 1_000_000 max_nodes: int = 10_000 max_query_chars: int = 500 max_results: int = 100 max_traversal_depth: int = 8 max_context_tokens: int = 32_000 max_tool_output_chars: int = 200_000 @dataclass(frozen=True) class ContextProfile: profile_id: str families: tuple[str, ...] statuses: tuple[str, ...] required_nodes: tuple[str, ...] token_budget: int dependency_depth: int @dataclass(frozen=True) class ProjectDescriptor: schema_version: int project_id: str title: str adapter: str root: Path descriptor_path: Path descriptor_hash: str content_roots: tuple[Path, ...] authority_files: tuple[Path, ...] cache_root: Path index_path: Path allowed_relations: tuple[str, ...] profiles: tuple[ContextProfile, ...] limits: Limits @dataclass(frozen=True) class Node: node_id: str title: str family: str authority: str status: str tags: tuple[str, ...] summary: str content: str source_path: str source_anchor: str | None content_hash: str def as_dict(self, *, include_content: bool = True) -> dict[str, object]: result = asdict(self) if not include_content: result.pop("content") return result @dataclass(frozen=True) class Edge: source_id: str relation: str target_id: str def as_dict(self) -> dict[str, str]: return asdict(self) @dataclass(frozen=True) class ProjectSnapshot: descriptor: ProjectDescriptor nodes: tuple[Node, ...] edges: tuple[Edge, ...] source_hash: str revision: str @dataclass(frozen=True) class ContextEntry: node_id: str reason: str estimated_tokens: int source_path: str content_hash: str text: str def as_dict(self) -> dict[str, object]: return asdict(self)