This repository has been archived on 2026-08-20. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
el-retired/_archive/rust-bootstrap/engrams/el-arch/src/rules/vbd.rs
T

102 lines
3.7 KiB
Rust

//! VBD (Volatility-Based Decomposition) and EBD (Experience-Based Decomposition) layer rules.
use crate::error::ArchDiagnostic;
use crate::rule::{ArchRule, FnContext};
// ── VBD-001: Accessor must not call Manager ───────────────────────────────────
/// VBD-001: @accessor functions must not call @manager functions.
/// Accessors are read-only, stable-interface components; they must not depend
/// on manager-layer orchestration logic.
pub struct AccessorMustNotCallManager;
impl ArchRule for AccessorMustNotCallManager {
fn name(&self) -> &str { "VBD-001" }
fn description(&self) -> &str {
"@accessor must not call @manager functions (accessor must not depend on manager layer)"
}
fn check(&self, ctx: &FnContext<'_>) -> Vec<ArchDiagnostic> {
if !ctx.has_annotation("accessor") {
return vec![];
}
ctx.body_calls
.iter()
.filter(|call| ctx.callee_has_annotation(&call.callee, "manager"))
.map(|call| ArchDiagnostic::error(
self.name(),
format!(
"accessor '{}' calls manager '{}' — accessors must not depend on the manager layer",
ctx.fn_name, call.callee
),
Some(ctx.fn_name.to_string()),
))
.collect()
}
}
// ── VBD-002: Experience must not directly call Experience ─────────────────────
/// VBD-002 / EBD-001: @experience functions must not call other @experience functions directly.
/// Experiences should communicate via events, not direct calls, to preserve
/// loose coupling between user-facing features.
pub struct ExperienceMustNotCallExperience;
impl ArchRule for ExperienceMustNotCallExperience {
fn name(&self) -> &str { "VBD-002" }
fn description(&self) -> &str {
"@experience must not call another @experience directly (use events instead)"
}
fn check(&self, ctx: &FnContext<'_>) -> Vec<ArchDiagnostic> {
if !ctx.has_annotation("experience") {
return vec![];
}
ctx.body_calls
.iter()
.filter(|call| ctx.callee_has_annotation(&call.callee, "experience"))
.map(|call| ArchDiagnostic::error(
self.name(),
format!(
"experience '{}' directly calls experience '{}' — use an event instead",
ctx.fn_name, call.callee
),
Some(ctx.fn_name.to_string()),
))
.collect()
}
}
// ── VBD-003: Experience should return Result<T, E> ────────────────────────────
/// VBD-003: @experience functions should return Result<T, E> for proper error propagation.
pub struct ExperienceShouldReturnResult;
impl ArchRule for ExperienceShouldReturnResult {
fn name(&self) -> &str { "VBD-003" }
fn description(&self) -> &str {
"@experience functions should return Result<T, E> for proper error handling"
}
fn check(&self, ctx: &FnContext<'_>) -> Vec<ArchDiagnostic> {
if !ctx.has_annotation("experience") {
return vec![];
}
// Warn if return type doesn't include "Result"
if !ctx.return_type_name.contains("Result") {
return vec![ArchDiagnostic::warning(
self.name(),
format!(
"experience '{}' returns '{}' instead of Result<T, E> — experiences should propagate errors",
ctx.fn_name, ctx.return_type_name
),
Some(ctx.fn_name.to_string()),
)];
}
vec![]
}
}