use serde::{Deserialize, Serialize};
use std::collections::{BTreeMap, BTreeSet};
pub const SCHEMA_VERSION: &str = "0.1.0";
pub mod handles;
pub mod identity;
pub mod languages;
pub mod lex;
pub mod resolution;
pub mod retrieval;
pub use handles::{fnv1a64_hex, ContentHandle, HandleError, HandleKind};
pub use languages::{
extracted_language_ids, language_by_id, language_registry, support_matrix_markdown,
LanguageCapability, LanguageTier, LANGUAGE_REGISTRY,
};
pub use lex::{
bm25_rank, bm25_scores, bm25_scores_with_stats, classify_query, extract_query_mentions,
is_exact_anchor, mention_matches_doc, path_matches_locus, ranking_arm_ids, relevance_hits,
relevance_hits_with_stats, route_query, subtokens, Bm25CorpusStats, LexDoc, LexField, QueryLocus,
QueryMention, QueryShape,
RankingArm, RelevanceHit, RetrievalPlan, BM25_B, BM25_K1, QUERY_MENTION_MAX_RAW, WEIGHT_BODY,
WEIGHT_DOC, WEIGHT_NAME, WEIGHT_PATH,
};
pub use resolution::{
choose_representation, AnalysisQuality, CallQuality, FileQuality, RecvKind,
RepresentationChoice, RepresentationKind, ResolutionClass,
};
pub use retrieval::{mean_reciprocal_rank, recall_at_k};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
pub enum Provenance {
Extracted,
Resolved,
Observed,
Declared,
Inferred,
Stale,
}
impl Provenance {
pub fn as_str(&self) -> &'static str {
match self {
Provenance::Extracted => "EXTRACTED",
Provenance::Resolved => "RESOLVED",
Provenance::Observed => "OBSERVED",
Provenance::Declared => "DECLARED",
Provenance::Inferred => "INFERRED",
Provenance::Stale => "STALE",
}
}
pub fn default_confidence(&self) -> f64 {
match self {
Provenance::Extracted => 1.0,
Provenance::Resolved => 0.98,
Provenance::Observed => 1.0,
Provenance::Declared => 1.0,
Provenance::Inferred => 0.7,
Provenance::Stale => 0.0,
}
}
pub fn is_trusted(&self) -> bool {
!matches!(self, Provenance::Stale)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum Severity {
Info,
Low,
Medium,
High,
Critical,
}
impl Severity {
pub fn rank(&self) -> u8 {
match self {
Severity::Info => 0,
Severity::Low => 1,
Severity::Medium => 2,
Severity::High => 3,
Severity::Critical => 4,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum FlowKind {
Architecture,
Workflow,
Sequence,
Dataflow,
Lifecycle,
}
impl FlowKind {
pub fn as_str(&self) -> &'static str {
match self {
FlowKind::Architecture => "architecture",
FlowKind::Workflow => "workflow",
FlowKind::Sequence => "sequence",
FlowKind::Dataflow => "dataflow",
FlowKind::Lifecycle => "lifecycle",
}
}
}
pub fn flow_kind_str(k: &FlowKind) -> &'static str {
k.as_str()
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum EvidenceType {
Source,
Config,
Runtime,
Test,
Intent,
History,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Archetype {
ServiceApplication,
Cli,
LibrarySdk,
WebFramework,
CompilerLanguageTool,
PluginFramework,
InfrastructureProject,
MonorepoPlatform,
Unknown,
}
impl Archetype {
pub fn as_str(&self) -> &'static str {
match self {
Archetype::ServiceApplication => "service_application",
Archetype::Cli => "cli",
Archetype::LibrarySdk => "library_sdk",
Archetype::WebFramework => "web_framework",
Archetype::CompilerLanguageTool => "compiler_language_tool",
Archetype::PluginFramework => "plugin_framework",
Archetype::InfrastructureProject => "infrastructure_project",
Archetype::MonorepoPlatform => "monorepo_platform",
Archetype::Unknown => "unknown",
}
}
pub fn label(&self) -> &'static str {
match self {
Archetype::ServiceApplication => "service application",
Archetype::Cli => "cli",
Archetype::LibrarySdk => "library/sdk",
Archetype::WebFramework => "web framework",
Archetype::CompilerLanguageTool => "compiler/language tool",
Archetype::PluginFramework => "plugin framework",
Archetype::InfrastructureProject => "infrastructure project",
Archetype::MonorepoPlatform => "monorepo platform",
Archetype::Unknown => "unknown",
}
}
pub const PRECEDENCE: [Archetype; 9] = [
Archetype::MonorepoPlatform,
Archetype::InfrastructureProject,
Archetype::WebFramework,
Archetype::ServiceApplication,
Archetype::Cli,
Archetype::LibrarySdk,
Archetype::CompilerLanguageTool,
Archetype::PluginFramework,
Archetype::Unknown,
];
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Repository {
pub id: String,
pub name: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Snapshot {
pub revision: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub branch: Option<String>,
pub indexed_at: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Entity {
pub id: String,
pub kind: String,
pub name: String,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub attributes: BTreeMap<String, serde_json::Value>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub evidence: Vec<String>,
}
impl Entity {
pub fn new(id: impl Into<String>, kind: impl Into<String>, name: impl Into<String>) -> Self {
Entity {
id: id.into(),
kind: kind.into(),
name: name.into(),
attributes: BTreeMap::new(),
evidence: Vec::new(),
}
}
pub fn attr(&mut self, key: &str, value: impl Into<serde_json::Value>) -> &mut Self {
self.attributes.insert(key.to_string(), value.into());
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Occurrence {
pub id: String,
pub concept: String,
pub path: String,
pub owner: String,
pub line: u32,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Relationship {
pub id: String,
pub subject: String,
pub predicate: String,
pub object: String,
pub provenance: Provenance,
pub confidence: f64,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub evidence: Vec<String>,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub verified_at: String,
}
impl Relationship {
pub fn new(
id: impl Into<String>,
subject: impl Into<String>,
predicate: impl Into<String>,
object: impl Into<String>,
provenance: Provenance,
) -> Self {
Relationship {
id: id.into(),
subject: subject.into(),
predicate: predicate.into(),
object: object.into(),
provenance,
confidence: provenance.default_confidence(),
evidence: Vec::new(),
verified_at: String::new(),
}
}
pub fn with_confidence(mut self, c: f64) -> Self {
self.confidence = c;
self
}
pub fn with_evidence(mut self, evidence: Vec<String>) -> Self {
self.evidence = evidence;
self
}
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct FlowStep {
pub id: String,
pub order: u32,
pub actor: String,
pub operation: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub r#async: Option<bool>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub timeout_ms: Option<u64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub retry_policy: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub failure_outcome: Option<String>,
#[serde(default)]
pub provenance: Option<Provenance>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Flow {
pub id: String,
pub kind: FlowKind,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trigger: Option<String>,
pub steps: Vec<FlowStep>,
#[serde(default, skip_serializing_if = "BTreeMap::is_empty")]
pub attributes: BTreeMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Invariant {
pub id: String,
pub statement: String,
pub severity: Severity,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub scope: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub enforced_by: Vec<String>,
#[serde(default)]
pub provenance: Option<Provenance>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Evidence {
pub id: String,
#[serde(rename = "type")]
pub r#type: EvidenceType,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub symbol: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub start_line: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub end_line: Option<u32>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub revision: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub content_hash: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extractor: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub extractor_version: Option<String>,
}
impl Evidence {
pub fn source(id: impl Into<String>, path: impl Into<String>) -> Self {
Evidence {
id: id.into(),
r#type: EvidenceType::Source,
path: Some(path.into()),
symbol: None,
start_line: None,
end_line: None,
revision: None,
content_hash: None,
extractor: None,
extractor_version: None,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum FlowEdgeKind {
Next,
Branch,
Error,
Retry,
Fallback,
Async,
Publish,
Consume,
Join,
Return,
Timeout,
Compensation,
Read,
Write,
Transform,
Validate,
Authorize,
Cache,
Invalidate,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowNode {
pub id: u32,
pub actor: String,
pub operation: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowEdge {
pub from: u32,
pub to: u32,
pub kind: FlowEdgeKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub condition: Option<String>,
#[serde(default)]
pub provenance: Option<Provenance>,
#[serde(default)]
pub confidence: f64,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub evidence: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FlowGraph {
pub id: String,
pub kind: FlowKind,
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trigger: Option<String>,
#[serde(default)]
pub nodes: Vec<FlowNode>,
#[serde(default)]
pub edges: Vec<FlowEdge>,
#[serde(default)]
pub entrypoints: Vec<u32>,
#[serde(default)]
pub exits: Vec<u32>,
#[serde(default)]
pub provenance_summary: BTreeMap<String, usize>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct AtlasComponent {
pub name: String,
pub purpose: String,
#[serde(default)]
pub implementation: Vec<String>,
#[serde(default)]
pub implementation_paths: Vec<String>,
#[serde(default)]
pub symbols: Vec<String>,
#[serde(default)]
pub consumes: Vec<String>,
#[serde(default)]
pub produces: Vec<String>,
#[serde(default)]
pub upstream: Vec<String>,
#[serde(default)]
pub downstream: Vec<String>,
#[serde(default)]
pub failure_behavior: Vec<String>,
#[serde(default)]
pub owns: Vec<AtlasOwnershipClaim>,
#[serde(default)]
pub layer: String,
#[serde(default)]
pub parent: Option<String>,
#[serde(default)]
pub role: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct AtlasHierarchyNode {
pub id: String,
pub name: String,
pub kind: String,
#[serde(default)]
pub members: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct AtlasOwnershipClaim {
pub target: String,
pub provenance: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct AtlasFlow {
pub name: String,
pub kind: FlowKind,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub trigger: Option<String>,
#[serde(default)]
pub steps: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct AtlasEntrypoint {
pub name: String,
pub kind: String,
pub trigger: String,
#[serde(default)]
pub symbol: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct AtlasInvariant {
pub statement: String,
pub severity: Severity,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, Default, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum ContractSubclass {
CallContract,
PublicApi,
#[default]
Http,
Rpc,
Cli,
Event,
Message,
Schema,
Configuration,
Plugin,
Extension,
Serialization,
}
impl ContractSubclass {
pub fn as_str(&self) -> &'static str {
match self {
ContractSubclass::CallContract => "call",
ContractSubclass::PublicApi => "public-api",
ContractSubclass::Http => "http",
ContractSubclass::Rpc => "rpc",
ContractSubclass::Cli => "cli",
ContractSubclass::Event => "event",
ContractSubclass::Message => "message",
ContractSubclass::Schema => "schema",
ContractSubclass::Configuration => "config",
ContractSubclass::Plugin => "plugin",
ContractSubclass::Extension => "extension",
ContractSubclass::Serialization => "serialization",
}
}
pub fn from_kind_str(kind: &str) -> Option<ContractSubclass> {
Some(match kind {
"http" | "route" => ContractSubclass::Http,
"cli" => ContractSubclass::Cli,
"event" | "topic" => ContractSubclass::Event,
"config" | "configuration" | "next-config" => ContractSubclass::Configuration,
"factory" | "export" | "public-api" | "public_api" => ContractSubclass::PublicApi,
"builder" => ContractSubclass::Configuration,
"serialization" | "serialize" | "deserialize" => ContractSubclass::Serialization,
"extension" => ContractSubclass::Extension,
"plugin" => ContractSubclass::Plugin,
"rpc" => ContractSubclass::Rpc,
"message" | "queue" => ContractSubclass::Message,
"schema" => ContractSubclass::Schema,
"call" | "task" | "bean" | "rule" | "middleware" | "callback" => {
ContractSubclass::CallContract
}
_ => return None,
})
}
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct Contract {
pub id: String,
pub kind: String,
#[serde(default)]
pub subclass: ContractSubclass,
#[serde(default)]
pub producer: String,
#[serde(default)]
pub consumers: Vec<String>,
#[serde(default)]
pub operations: Vec<String>,
#[serde(default)]
pub evidence: Vec<String>,
}
impl Contract {
pub fn new(
id: impl Into<String>,
kind: impl Into<String>,
producer: impl Into<String>,
) -> Self {
Contract {
id: id.into(),
kind: kind.into(),
subclass: ContractSubclass::default(),
producer: producer.into(),
consumers: Vec::new(),
operations: Vec::new(),
evidence: Vec::new(),
}
}
pub fn with_subclass(mut self, subclass: ContractSubclass) -> Self {
self.subclass = subclass;
self
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum InvocationSurfaceKind {
Process,
Http,
Cli,
PublicApi,
Event,
Queue,
Schedule,
Plugin,
FrameworkCallback,
Lifecycle,
}
impl InvocationSurfaceKind {
pub fn as_str(&self) -> &'static str {
match self {
InvocationSurfaceKind::Process => "process",
InvocationSurfaceKind::Http => "http",
InvocationSurfaceKind::Cli => "cli",
InvocationSurfaceKind::PublicApi => "public_api",
InvocationSurfaceKind::Event => "event",
InvocationSurfaceKind::Queue => "queue",
InvocationSurfaceKind::Schedule => "schedule",
InvocationSurfaceKind::Plugin => "plugin",
InvocationSurfaceKind::FrameworkCallback => "framework_callback",
InvocationSurfaceKind::Lifecycle => "lifecycle",
}
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct InvocationSurface {
pub symbol: String,
pub kind: InvocationSurfaceKind,
pub trigger: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SystemAtlas {
pub repository: String,
pub revision: String,
pub indexed_at: String,
pub freshness: String,
pub purpose: String,
#[serde(default)]
pub components: Vec<AtlasComponent>,
#[serde(default)]
pub entrypoints: Vec<AtlasEntrypoint>,
#[serde(default)]
pub contracts: Vec<Contract>,
#[serde(default)]
pub coverage: BTreeMap<String, String>,
#[serde(default)]
pub flows: Vec<AtlasFlow>,
#[serde(default)]
pub invariants: Vec<AtlasInvariant>,
#[serde(default)]
pub deployment_units: Vec<String>,
#[serde(default)]
pub external_systems: Vec<String>,
#[serde(default)]
pub trust_boundaries: Vec<String>,
#[serde(default)]
pub async_boundaries: Vec<String>,
#[serde(default)]
pub implementation_map: BTreeMap<String, Vec<String>>,
#[serde(default)]
pub data_stores: Vec<String>,
#[serde(default)]
pub archetype: Option<Archetype>,
#[serde(default)]
pub state_authority: BTreeMap<String, Vec<String>>,
#[serde(default)]
pub hierarchy: Vec<AtlasHierarchyNode>,
#[serde(default)]
pub evidence_summary: BTreeMap<String, usize>,
#[serde(default)]
pub warnings: Vec<String>,
#[serde(default)]
pub public_api: BTreeMap<String, Vec<String>>,
#[serde(default)]
pub framework_semantics: BTreeMap<String, Vec<String>>,
#[serde(default)]
pub pipeline: Vec<String>,
#[serde(default)]
pub landmarks: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SystemIr {
pub schema_version: String,
pub repository: Repository,
pub snapshot: Snapshot,
pub entities: Vec<Entity>,
pub relationships: Vec<Relationship>,
pub flows: Vec<Flow>,
pub invariants: Vec<Invariant>,
#[serde(default)]
pub evidence: Vec<Evidence>,
}
impl SystemIr {
pub fn empty(repository: Repository, snapshot: Snapshot) -> Self {
SystemIr {
schema_version: SCHEMA_VERSION.to_string(),
repository,
snapshot,
entities: Vec::new(),
relationships: Vec::new(),
flows: Vec::new(),
invariants: Vec::new(),
evidence: Vec::new(),
}
}
}
pub fn sanitize_key(input: &str) -> String {
let mut out = String::with_capacity(input.len());
let mut prev_dash = false;
for c in input.chars() {
let ok = c.is_ascii_alphanumeric() || c == '_' || c == '-' || c == '.' || c == '/';
if ok {
if c == '/' {
out.push('/');
prev_dash = false;
} else if c == '-' || c == '_' {
if !prev_dash {
out.push('-');
}
prev_dash = true;
} else {
out.push(c.to_ascii_lowercase());
prev_dash = false;
}
} else {
if !prev_dash {
out.push('-');
}
prev_dash = true;
}
}
while out.ends_with('-') {
out.pop();
}
if out.is_empty() {
out.push_str("unnamed");
}
out
}
pub fn entity_id(repo: &str, kind: &str, key: &str) -> String {
format!("repo://{}/{}/{}", sanitize_key(repo), kind, sanitize_key(key))
}
pub fn occurrence_id(repo: &str, concept: &str, path: &str, owner: &str, line: u32) -> String {
format!(
"repo://{}/occurrence/{}@{}@{}@{}",
sanitize_key(repo),
encode_component(concept),
encode_component(path),
encode_component(owner),
line
)
}
pub fn encode_component(input: &str) -> String {
let mut out = String::with_capacity(input.len());
for b in input.bytes() {
let keep = b.is_ascii_alphanumeric() || matches!(b, b'/' | b'.' | b'_' | b'-');
if keep {
out.push(b as char);
} else {
out.push('%');
out.push_str(&format!("{b:02X}"));
}
}
out
}
pub fn decode_component(input: &str) -> String {
let bytes = input.as_bytes();
let mut out = Vec::with_capacity(bytes.len());
let mut i = 0;
while i < bytes.len() {
if bytes[i] == b'%' && i + 2 < bytes.len() {
let hi = (bytes[i + 1] as char).to_digit(16);
let lo = (bytes[i + 2] as char).to_digit(16);
if let (Some(hi), Some(lo)) = (hi, lo) {
out.push((hi * 16 + lo) as u8);
i += 3;
continue;
}
}
out.push(bytes[i]);
i += 1;
}
String::from_utf8_lossy(&out).to_string()
}
pub fn symbol_id(repo: &str, file: &str, name: &str) -> String {
format!(
"repo://{}/symbol/{}/{}",
sanitize_key(repo),
encode_component(file),
encode_component(name)
)
}
pub fn evidence_id(n: u64) -> String {
format!("evidence:{n}")
}
pub fn relationship_id(n: u64) -> String {
format!("rel:{n}")
}
pub mod kinds {
pub const FILE: &str = "file";
pub const SYMBOL: &str = "symbol";
pub const PACKAGE: &str = "package";
pub const MODULE: &str = "module";
pub const SYSTEM: &str = "system";
pub const SUBSYSTEM: &str = "subsystem";
pub const SERVICE: &str = "service";
pub const COMPONENT: &str = "component";
pub const DEPLOYMENT_UNIT: &str = "deployment_unit";
pub const ROUTE: &str = "route";
pub const ENDPOINT: &str = "endpoint";
pub const EVENT: &str = "event";
pub const TOPIC: &str = "topic";
pub const QUEUE: &str = "queue";
pub const DATA_ENTITY: &str = "data";
pub const DATA_STORE: &str = "store";
pub const TABLE: &str = "table";
pub const COLLECTION: &str = "collection";
pub const CACHE: &str = "cache";
pub const EXTERNAL_SYSTEM: &str = "external_system";
pub const EXTERNAL_API: &str = "external_api";
pub const CONFIGURATION: &str = "configuration";
pub const FEATURE_FLAG: &str = "feature_flag";
pub const SECRET_REFERENCE: &str = "secret_reference";
pub const CONTRACT: &str = "contract";
pub const INVARIANT: &str = "invariant";
pub const TEST: &str = "test";
pub const TEST_SUITE: &str = "test_suite";
pub const FLOW: &str = "flow";
pub const WORKFLOW: &str = "workflow";
pub const STATE: &str = "state";
pub const EXPORT: &str = "export";
pub const ANNOTATION: &str = "annotation";
pub const FIELD: &str = "field";
pub const REGISTRY: &str = "registry";
pub const MIDDLEWARE: &str = "middleware";
pub const DI_BINDING: &str = "di_binding";
pub const TRANSITION: &str = "transition";
pub const RESOURCE: &str = "resource";
pub const TRUST_BOUNDARY: &str = "trust_boundary";
pub const SECURITY_CONTROL: &str = "security_control";
pub const RUNTIME_OBSERVATION: &str = "runtime_observation";
pub const SCHEMA: &str = "schema";
pub const REACTIVE: &str = "reactive";
pub const OCCURRENCE: &str = "occurrence";
pub const ALL: &[&str] = &[
FILE, SYMBOL, PACKAGE, MODULE, SYSTEM, SUBSYSTEM, SERVICE, COMPONENT, DEPLOYMENT_UNIT,
ROUTE, ENDPOINT, EVENT, TOPIC, QUEUE, DATA_ENTITY, DATA_STORE, TABLE, COLLECTION, CACHE,
EXTERNAL_SYSTEM, EXTERNAL_API, CONFIGURATION, FEATURE_FLAG, SECRET_REFERENCE, CONTRACT,
INVARIANT, TEST, TEST_SUITE, FLOW, WORKFLOW, STATE, EXPORT, ANNOTATION, FIELD, REGISTRY,
MIDDLEWARE, DI_BINDING, TRANSITION, RESOURCE, TRUST_BOUNDARY, SECURITY_CONTROL,
RUNTIME_OBSERVATION, SCHEMA, REACTIVE, OCCURRENCE,
];
}
pub mod predicates {
pub const CONTAINS: &str = "contains";
pub const IMPLEMENTS: &str = "implements";
pub const INHERITS: &str = "inherits";
pub const IMPORTS: &str = "imports";
pub const CALLS: &str = "calls";
pub const READS: &str = "reads";
pub const WRITES: &str = "writes";
pub const QUERIES: &str = "queries";
pub const OWNS: &str = "owns";
pub const PUBLISHES: &str = "publishes";
pub const CONSUMES: &str = "consumes";
pub const SUBSCRIBES: &str = "subscribes";
pub const PRODUCES: &str = "produces";
pub const TRANSFORMS: &str = "transforms";
pub const VALIDATES: &str = "validates";
pub const DEFINES: &str = "defines";
pub const COMPOSES: &str = "composes";
pub const ROUTES_TO: &str = "routes_to";
pub const HANDLES: &str = "handles";
pub const INVOKES: &str = "invokes";
pub const DEPENDS_ON: &str = "depends_on";
pub const DEPLOYED_WITH: &str = "deployed_with";
pub const DEPLOYED_IN: &str = "deployed_in";
pub const CONFIGURED_BY: &str = "configured_by";
pub const PROTECTED_BY: &str = "protected_by";
pub const CROSSES_BOUNDARY: &str = "crosses_boundary";
pub const ENFORCES: &str = "enforces";
pub const TESTED_BY: &str = "tested_by";
pub const PARTICIPATES_IN: &str = "participates_in";
pub const PRECEDES: &str = "precedes";
pub const FOLLOWS: &str = "follows";
pub const BRANCHES_TO: &str = "branches_to";
pub const RETRIES: &str = "retries";
pub const FALLS_BACK_TO: &str = "falls_back_to";
pub const OBSERVED_AS: &str = "observed_as";
pub const DECLARED_AS: &str = "declared_as";
pub const IMPLEMENTED_BY: &str = "implemented_by";
pub const EXPORTS: &str = "exports";
pub const ANNOTATES: &str = "annotates";
pub const REGISTERS: &str = "registers";
pub const INJECTS: &str = "injects";
pub const HANDLES_CALLBACK: &str = "handles_callback";
pub const DECORATES: &str = "decorates";
pub const OCCURS: &str = "occurs";
pub const ALL: &[&str] = &[
CONTAINS, IMPLEMENTS, INHERITS, IMPORTS, CALLS, READS, WRITES, QUERIES, OWNS, PUBLISHES,
CONSUMES, SUBSCRIBES, PRODUCES, TRANSFORMS, VALIDATES, DEFINES, COMPOSES, ROUTES_TO,
HANDLES, INVOKES, DEPENDS_ON, DEPLOYED_WITH, DEPLOYED_IN, CONFIGURED_BY, PROTECTED_BY,
CROSSES_BOUNDARY, ENFORCES, TESTED_BY, PARTICIPATES_IN, PRECEDES, FOLLOWS, BRANCHES_TO,
RETRIES, FALLS_BACK_TO, OBSERVED_AS, DECLARED_AS, IMPLEMENTED_BY, EXPORTS, ANNOTATES,
REGISTERS, INJECTS, HANDLES_CALLBACK, DECORATES, OCCURS,
];
}
pub fn ontology_registries() -> (&'static [&'static str], &'static [&'static str]) {
(kinds::ALL, predicates::ALL)
}
pub fn estimate_tokens(text: &str) -> usize {
let chars = text.chars().count();
chars.div_ceil(4)
}
pub fn truncate_to_budget(text: &str, budget: usize) -> String {
if estimate_tokens(text) <= budget {
return text.to_string();
}
let max_chars = budget.saturating_mul(4);
let mut end = 0;
let mut chars = 0;
for (i, c) in text.char_indices() {
chars += 1;
if chars > max_chars {
break;
}
end = i + c.len_utf8();
}
let min_chars = max_chars / 2;
let mut cut = end;
if let Some(nl) = text[..end].rfind('\n') {
let prefix_chars = text[..nl].chars().count();
if prefix_chars >= min_chars {
cut = nl;
}
}
let mut out = text[..cut].to_string();
out.push_str("\n… (truncated by token budget)");
out
}
pub fn now_rfc3339() -> String {
chrono::Utc::now().to_rfc3339_opts(chrono::SecondsFormat::Secs, true)
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SourceRange {
pub path: String,
pub start_line: u32,
pub end_line: u32,
}
impl SourceRange {
pub fn new(path: impl Into<String>, start_line: u32, end_line: u32) -> Self {
SourceRange {
path: path.into(),
start_line,
end_line,
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum Visibility {
Public,
Protected,
Private,
Package,
}
impl Visibility {
pub fn as_str(&self) -> &'static str {
match self {
Visibility::Public => "public",
Visibility::Protected => "protected",
Visibility::Private => "private",
Visibility::Package => "package",
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, schemars::JsonSchema)]
#[serde(rename_all = "snake_case")]
pub enum SurfaceKind {
Function,
Method,
Constructor,
Class,
Interface,
Trait,
Type,
Enum,
Const,
Module,
Record,
}
impl SurfaceKind {
pub fn as_str(&self) -> &'static str {
match self {
SurfaceKind::Function => "function",
SurfaceKind::Method => "method",
SurfaceKind::Constructor => "constructor",
SurfaceKind::Class => "class",
SurfaceKind::Interface => "interface",
SurfaceKind::Trait => "trait",
SurfaceKind::Type => "type",
SurfaceKind::Enum => "enum",
SurfaceKind::Const => "const",
SurfaceKind::Module => "module",
SurfaceKind::Record => "record",
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SemanticParameter {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub ty: Option<String>,
#[serde(default)]
pub receiver: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub default: Option<String>,
#[serde(default)]
pub variadic: bool,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SemanticSignature {
pub name: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub owner: Option<String>,
#[serde(default)]
pub visibility: Option<Visibility>,
#[serde(default)]
pub async_: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub generic_parameters: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub parameters: Vec<SemanticParameter>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub returns: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub constraints: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SurfaceRank {
pub task_ppr: f64,
pub global_ppr: f64,
pub lexical: f64,
pub semantic: f64,
pub confidence: f64,
pub criticality: f64,
pub change_risk: f64,
pub novelty: f64,
pub total: f64,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub reasons: Vec<String>,
}
impl Default for SurfaceRank {
fn default() -> Self {
SurfaceRank {
task_ppr: 0.0,
global_ppr: 0.0,
lexical: 0.0,
semantic: 0.0,
confidence: 0.0,
criticality: 0.0,
change_risk: 0.0,
novelty: 0.0,
total: 0.0,
reasons: Vec::new(),
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SurfaceEntry {
pub id: String,
pub symbol_id: String,
pub qualified_name: String,
pub kind: SurfaceKind,
pub path: String,
pub range: SourceRange,
pub source_signature: String,
pub canonical_signature: String,
pub semantic_signature: SemanticSignature,
pub visibility: Visibility,
#[serde(default)]
pub exported: bool,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub modifiers: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub annotations: Vec<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub component: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub subsystem: Option<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub flows: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub contracts: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub state_authorities: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub invocation_surfaces: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub callers: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub callees: Vec<String>,
#[serde(default)]
pub caller_count: usize,
#[serde(default)]
pub callee_count: usize,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub importance: Option<ImportanceProfile>,
pub provenance: Provenance,
#[serde(default)]
pub confidence: f32,
#[serde(default)]
pub rank: SurfaceRank,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct ImportanceProfile {
pub overall: f64,
pub caller_count: usize,
pub callee_count: usize,
pub global_ppr: f64,
pub task_ppr: f64,
pub entrypoint: bool,
pub exported: bool,
pub flow_count: usize,
pub contract_count: usize,
pub state_read_count: usize,
pub state_write_count: usize,
pub dependent_count: usize,
pub change_risk: f64,
pub badges: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SurfaceOmission {
pub count: usize,
pub kind: String,
pub reason: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SystemSurfaceMap {
pub repository: String,
pub revision: String,
pub epoch: String,
pub entries: Vec<SurfaceEntry>,
#[serde(default)]
pub token_count: usize,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub omitted: Vec<SurfaceOmission>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, schemars::JsonSchema)]
pub struct SurfaceRenderResult {
pub text: String,
pub rendered_ids: Vec<String>,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub rendered_entries: Vec<SurfaceEntry>,
pub omitted_ids: Vec<String>,
pub omissions: Vec<SurfaceOmission>,
pub token_count: usize,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub critical_drops: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct RankNode {
pub id: String,
pub kind: String,
pub name: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ReferenceEdge {
pub source_symbol: String,
pub target_symbol: String,
pub kind: ReferenceKind,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub locations: Vec<SourceRange>,
pub provenance: Provenance,
#[serde(default)]
pub confidence: f32,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum ReferenceKind {
Read,
Write,
#[default]
Call,
TypeUse,
Instantiate,
Implement,
Extend,
Decorate,
Register,
Import,
Export,
Macro,
FieldAccess,
Construct,
}
impl ReferenceKind {
pub fn as_str(&self) -> &'static str {
match self {
ReferenceKind::Read => "read",
ReferenceKind::Write => "write",
ReferenceKind::Call => "call",
ReferenceKind::TypeUse => "type_use",
ReferenceKind::Instantiate => "instantiate",
ReferenceKind::Implement => "implement",
ReferenceKind::Extend => "extend",
ReferenceKind::Decorate => "decorate",
ReferenceKind::Register => "register",
ReferenceKind::Import => "import",
ReferenceKind::Export => "export",
ReferenceKind::Macro => "macro",
ReferenceKind::FieldAccess => "field_access",
ReferenceKind::Construct => "construct",
}
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContextItem {
pub id: String,
pub value: f64,
pub token_cost: usize,
#[serde(default)]
pub required: bool,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub group: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContextBudget {
pub total: usize,
pub atlas: usize,
pub surface: usize,
pub task_delta: usize,
pub structural_source: usize,
}
impl Default for ContextBudget {
fn default() -> Self {
ContextBudget {
total: 20_000,
atlas: 13_000,
surface: 7_000,
task_delta: 3_000,
structural_source: 6_000,
}
}
}
const MASSIVE_SURFACE_CAP: usize = 10_000;
impl ContextBudget {
pub fn adaptive(
total: usize,
entity_count: usize,
component_count: usize,
flow_count: usize,
surface_candidates: usize,
) -> ContextBudget {
let (surface_pct, boost, cap): (f64, f64, Option<usize>) = if entity_count > 20_000 {
(30.0, 0.0, Some(MASSIVE_SURFACE_CAP))
} else if entity_count > 5_000 || component_count > 30 {
(35.0, 5.0, None)
} else if flow_count > 40 {
(35.0, 5.0, None)
} else if entity_count < 200 {
(45.0, 5.0, None)
} else {
(40.0, 5.0, None)
};
let boost_pp = (surface_candidates / 2_000).min(boost as usize) as f64;
let surface_pct = (surface_pct + boost_pp).min(50.0);
let atlas_pct = 100.0 - surface_pct;
let mut surface = ((total as f64) * surface_pct / 100.0).round() as usize;
if let Some(c) = cap {
surface = surface.min(c);
}
let atlas = ((total as f64) * atlas_pct / 100.0).round() as usize;
let def = ContextBudget::default();
ContextBudget {
total,
atlas,
surface,
task_delta: def.task_delta,
structural_source: def.structural_source,
}
}
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct ContextLedger {
pub model_epoch: String,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub visible_entities: BTreeSet<String>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub visible_symbols: BTreeSet<String>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub visible_files: BTreeSet<String>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub visible_components: BTreeSet<String>,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub visible_flows: BTreeSet<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub last_task: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct StructuralSourceUnit {
pub path: String,
pub source: String,
pub representation: String,
pub revision: String,
pub content: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub handle: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub representation_reason: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ContextArtifact {
pub kind: String,
pub epoch: String,
pub renderer_version: String,
pub trust_policy: String,
pub budget: ContextBudget,
pub sha256: String,
#[serde(default, skip_serializing_if = "String::is_empty")]
pub content_hash: String,
pub text: String,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct TaskSeed {
pub kind: String,
pub id: String,
pub weight: f64,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn estimate_tokens_is_chars_ceiling() {
assert_eq!(estimate_tokens(""), 0);
assert_eq!(estimate_tokens("a"), 1);
assert_eq!(estimate_tokens("abcd"), 1);
assert_eq!(estimate_tokens("abcde"), 2);
assert_eq!(estimate_tokens("fn main() { println!(\"hi\"); }"), 8);
assert_eq!(estimate_tokens("日本語"), 1);
assert_eq!(estimate_tokens("日本語テスト"), 2);
let path = "crates/scc-context/src/surface/structural_source_selection_policy.rs";
assert_eq!(estimate_tokens(path), path.chars().count().div_ceil(4));
}
#[test]
fn component_encode_decode_roundtrip() {
for name in [
"Normalizer",
"GET /api/transcripts/:id handler",
"OrderStateMachine.advance",
"test_normalization_preserves_raw",
"src/app.ts",
"a b%c",
] {
assert_eq!(decode_component(&encode_component(name)), name, "{name}");
}
}
#[test]
fn component_ids_are_collision_free() {
assert_ne!(
encode_component("foo_bar"),
encode_component("foo-bar"),
"underscore and dash must not collide"
);
}
#[test]
fn occurrence_ids_are_collision_free_per_site() {
assert_ne!(
occurrence_id("r", "expr", "a.ts", "A", 1),
occurrence_id("r", "expr", "b.ts", "A", 1)
);
assert_ne!(
occurrence_id("r", "expr", "a.ts", "A", 1),
occurrence_id("r", "expr", "a.ts", "B", 1)
);
assert_ne!(
occurrence_id("r", "expr", "a.ts", "A", 1),
occurrence_id("r", "expr", "a.ts", "A", 2)
);
assert_ne!(
occurrence_id("r", "expr1", "a.ts", "A", 1),
occurrence_id("r", "expr2", "a.ts", "A", 1)
);
assert_ne!(
occurrence_id("r", "expr", "a_b.ts", "A", 1),
occurrence_id("r", "expr", "a-b.ts", "A", 1)
);
assert_ne!(
occurrence_id("r", "expr", "a.ts", "Foo", 1),
occurrence_id("r", "expr", "a.ts", "foo", 1)
);
assert_eq!(
occurrence_id("r", "expr", "a.ts", "A", 1),
occurrence_id("r", "expr", "a.ts", "A", 1)
);
}
#[test]
fn occurrence_roundtrips_through_serde() {
let o = Occurrence {
id: occurrence_id("r", "z.object({ x: z.string() })", "src/a.ts", "make", 7),
concept: entity_id("r", kinds::SCHEMA, "z.object({ x: z.string() })"),
path: "src/a.ts".into(),
owner: "make".into(),
line: 7,
};
let json = serde_json::to_string(&o).unwrap();
let back: Occurrence = serde_json::from_str(&json).unwrap();
assert_eq!(back.id, o.id);
assert_eq!(back.concept, o.concept);
assert_eq!(back.line, 7);
}
#[test]
fn adaptive_budget_scales_split_by_repo_complexity() {
let tiny = ContextBudget::adaptive(20_000, 50, 2, 1, 10);
let tiny_ratio = tiny.surface as f64 / tiny.total as f64;
assert!(
(tiny_ratio - 0.45).abs() <= 0.01,
"tiny: surface share {tiny_ratio}"
);
assert_eq!(tiny.total, 20_000);
assert!(tiny.atlas + tiny.surface <= 20_000);
let normal = ContextBudget::adaptive(20_000, 1_000, 5, 4, 10);
let normal_ratio = normal.surface as f64 / normal.total as f64;
assert!(
(normal_ratio - 0.40).abs() <= 0.01,
"normal: surface share {normal_ratio}"
);
let large = ContextBudget::adaptive(20_000, 1_000, 40, 4, 10);
let large_ratio = large.surface as f64 / large.total as f64;
assert!(
(large_ratio - 0.35).abs() <= 0.01,
"large: surface share {large_ratio}"
);
let massive = ContextBudget::adaptive(20_000, 25_000, 60, 30, 10);
assert!(massive.atlas > massive.surface);
let massive_ratio = massive.surface as f64 / massive.total as f64;
assert!(
(massive_ratio - 0.30).abs() <= 0.01,
"massive: surface share {massive_ratio}"
);
let massive_huge = ContextBudget::adaptive(100_000, 25_000, 60, 30, 10);
assert_eq!(
massive_huge.surface, MASSIVE_SURFACE_CAP,
"massive surface absolutely capped"
);
assert_ne!(
tiny.atlas as f64 / tiny.surface as f64,
large.atlas as f64 / large.surface as f64,
"tiny vs large splits must differ"
);
}
#[test]
fn adaptive_budget_candidate_pool_boosts_surface_share() {
let boosted = ContextBudget::adaptive(20_000, 1_000, 5, 4, 10_000);
let boosted_ratio = boosted.surface as f64 / boosted.total as f64;
assert!(
(boosted_ratio - 0.45).abs() <= 0.01,
"boosted: surface share {boosted_ratio}"
);
let huge = ContextBudget::adaptive(20_000, 100, 2, 1, 100_000);
assert!(huge.surface <= huge.total / 2, "surface capped at 50%");
}
#[test]
fn adaptive_budget_is_deterministic_and_total_preserving() {
let a = ContextBudget::adaptive(15_000, 3_000, 8, 6, 500);
let b = ContextBudget::adaptive(15_000, 3_000, 8, 6, 500);
assert_eq!(a, b);
assert_eq!(a.total, 15_000);
assert!(a.atlas + a.surface <= 15_000);
}
#[test]
fn contract_kind_renders_as_operation_lines() {
let mut c = Contract::new(
"repo://repo/contract/http/get--api-x",
"http",
"repo://repo/symbol/main.py/handler",
);
c.operations.push("GET /api/x".into());
c.consumers.push("repo://repo/symbol/main.py/handler".into());
let lines: Vec<String> = c
.operations
.iter()
.map(|op| format!("{}: {}", c.kind, op))
.collect();
assert_eq!(lines, vec!["http: GET /api/x"]);
}
#[test]
fn contract_subclass_ontology_maps_and_renders() {
assert_eq!(ContractSubclass::Http.as_str(), "http");
assert_eq!(ContractSubclass::Cli.as_str(), "cli");
assert_eq!(ContractSubclass::Event.as_str(), "event");
assert_eq!(ContractSubclass::Configuration.as_str(), "config");
assert_eq!(ContractSubclass::PublicApi.as_str(), "public-api");
assert_eq!(ContractSubclass::Extension.as_str(), "extension");
assert_eq!(ContractSubclass::Serialization.as_str(), "serialization");
assert_eq!(ContractSubclass::CallContract.as_str(), "call");
assert_eq!(ContractSubclass::Rpc.as_str(), "rpc");
assert_eq!(ContractSubclass::Message.as_str(), "message");
assert_eq!(ContractSubclass::Schema.as_str(), "schema");
assert_eq!(ContractSubclass::Plugin.as_str(), "plugin");
assert_eq!(ContractSubclass::from_kind_str("http"), Some(ContractSubclass::Http));
assert_eq!(ContractSubclass::from_kind_str("route"), Some(ContractSubclass::Http));
assert_eq!(ContractSubclass::from_kind_str("cli"), Some(ContractSubclass::Cli));
assert_eq!(ContractSubclass::from_kind_str("event"), Some(ContractSubclass::Event));
assert_eq!(
ContractSubclass::from_kind_str("config"),
Some(ContractSubclass::Configuration)
);
assert_eq!(
ContractSubclass::from_kind_str("next-config"),
Some(ContractSubclass::Configuration)
);
assert_eq!(
ContractSubclass::from_kind_str("builder"),
Some(ContractSubclass::Configuration)
);
assert_eq!(
ContractSubclass::from_kind_str("factory"),
Some(ContractSubclass::PublicApi)
);
assert_eq!(
ContractSubclass::from_kind_str("serialization"),
Some(ContractSubclass::Serialization)
);
assert_eq!(
ContractSubclass::from_kind_str("extension"),
Some(ContractSubclass::Extension)
);
assert_eq!(ContractSubclass::from_kind_str("plugin"), Some(ContractSubclass::Plugin));
assert_eq!(ContractSubclass::from_kind_str("rpc"), Some(ContractSubclass::Rpc));
assert_eq!(ContractSubclass::from_kind_str("message"), Some(ContractSubclass::Message));
assert_eq!(ContractSubclass::from_kind_str("schema"), Some(ContractSubclass::Schema));
assert_eq!(ContractSubclass::from_kind_str("task"), Some(ContractSubclass::CallContract));
assert_eq!(ContractSubclass::from_kind_str("include_router"), None);
assert_eq!(ContractSubclass::from_kind_str("add_middleware"), None);
let mut c = Contract::new(
"repo://repo/contract/http/get--api-x",
"http",
"repo://repo/symbol/main.py/handler",
);
assert_eq!(c.subclass, ContractSubclass::Http);
c.subclass = ContractSubclass::Serialization;
let json = serde_json::to_string(&c).unwrap();
let back: Contract = serde_json::from_str(&json).unwrap();
assert_eq!(back.subclass, ContractSubclass::Serialization);
let legacy = serde_json::json!({
"id": "repo://repo/contract/x",
"kind": "http",
"producer": "p",
"operations": ["GET /x"],
"evidence": [],
"consumers": [],
});
let c2: Contract = serde_json::from_value(legacy).unwrap();
assert_eq!(c2.subclass, ContractSubclass::Http);
}
#[test]
fn invocation_surface_kinds_stringify() {
assert_eq!(InvocationSurfaceKind::PublicApi.as_str(), "public_api");
assert_eq!(InvocationSurfaceKind::Queue.as_str(), "queue");
assert_eq!(InvocationSurfaceKind::Lifecycle.as_str(), "lifecycle");
assert_eq!(InvocationSurfaceKind::FrameworkCallback.as_str(), "framework_callback");
let json = serde_json::to_string(&InvocationSurfaceKind::PublicApi).unwrap();
assert_eq!(json, "\"public_api\"");
let back: InvocationSurfaceKind = serde_json::from_str(&json).unwrap();
assert_eq!(back, InvocationSurfaceKind::PublicApi);
}
#[test]
fn surface_render_result_roundtrips_through_serde() {
let r = SurfaceRenderResult {
text: "SCC SYSTEM SURFACE MAP\n\n function serve\n".into(),
rendered_ids: vec!["repo://r/symbol/api.py/serve".into()],
rendered_entries: vec![],
omitted_ids: vec!["repo://r/symbol/api.py/internal".into()],
omissions: vec![SurfaceOmission {
count: 1,
kind: "function".into(),
reason: "token budget".into(),
}],
token_count: 7,
critical_drops: vec![],
};
let json = serde_json::to_string(&r).unwrap();
let back: SurfaceRenderResult = serde_json::from_str(&json).unwrap();
assert_eq!(back, r);
assert_eq!(back.rendered_ids[0], "repo://r/symbol/api.py/serve");
assert_eq!(back.omissions[0].count, 1);
}
#[test]
fn rank_node_carries_kind_and_name() {
let n = RankNode {
id: "repo://r/contract/c1".into(),
kind: kinds::CONTRACT.into(),
name: "c1".into(),
};
let json = serde_json::to_string(&n).unwrap();
let back: RankNode = serde_json::from_str(&json).unwrap();
assert_eq!(back.kind, "contract");
assert_eq!(back.name, "c1");
}
#[test]
fn context_artifact_content_hash_roundtrips_and_defaults() {
let a = ContextArtifact {
kind: "startup".into(),
epoch: "e1".into(),
renderer_version: "0.1.0".into(),
trust_policy: "floor=0.85".into(),
budget: ContextBudget::default(),
sha256: "abc".into(),
content_hash: "def".into(),
text: "body".into(),
};
let json = serde_json::to_string(&a).unwrap();
let back: ContextArtifact = serde_json::from_str(&json).unwrap();
assert_eq!(back.content_hash, "def");
let legacy = serde_json::json!({
"kind": "startup",
"epoch": "e1",
"renderer_version": "0.1.0",
"trust_policy": "floor=0.85",
"budget": ContextBudget::default(),
"sha256": "abc",
"text": "body",
});
let c: ContextArtifact = serde_json::from_value(legacy).unwrap();
assert_eq!(c.content_hash, "");
}
#[test]
fn predicate_and_kind_registries_are_complete() {
let (kind_ids, predicate_ids) = ontology_registries();
for required in [
predicates::DEFINES,
predicates::COMPOSES,
predicates::EXPORTS,
predicates::ANNOTATES,
predicates::REGISTERS,
predicates::INJECTS,
predicates::HANDLES_CALLBACK,
predicates::DECORATES,
predicates::OCCURS,
] {
assert!(
predicate_ids.contains(&required),
"predicates::ALL missing {required}"
);
}
for required in [kinds::FIELD, kinds::SCHEMA, kinds::TRUST_BOUNDARY, kinds::OCCURRENCE]
{
assert!(kind_ids.contains(&required), "kinds::ALL missing {required}");
}
let mut seen = std::collections::BTreeSet::new();
for p in predicate_ids {
assert!(seen.insert(*p), "duplicate predicate in ALL: {p}");
}
}
}