use std::fmt::Write as _;
const MAX_CHANGED_PATHS_IN_PROMPT: usize = 80;
#[derive(Clone, Debug, Eq, PartialEq)]
pub(super) struct ReviewContext {
paths: Vec<ChangedPath>,
areas: Vec<ReviewArea>,
}
impl ReviewContext {
pub(super) fn from_changed_files(changed_files: &str) -> Self {
let paths = changed_files
.lines()
.filter_map(ChangedPath::new)
.collect::<Vec<_>>();
let areas = REVIEW_AREAS
.iter()
.copied()
.filter(|area| paths.iter().any(|path| area.matches_path(path.as_str())))
.collect::<Vec<_>>();
Self { paths, areas }
}
pub(super) fn changed_files_for_prompt(&self) -> String {
let mut files = String::new();
for path in self.paths.iter().take(MAX_CHANGED_PATHS_IN_PROMPT) {
let _result = writeln!(&mut files, "- {}", path.prompt_text());
}
if files.is_empty() {
return String::from("- (none)");
}
let omitted = self.paths.len().saturating_sub(MAX_CHANGED_PATHS_IN_PROMPT);
if omitted > 0 {
let _result = writeln!(
&mut files,
"- ... {omitted} more changed path(s); inspect the saved diff snapshot for the full list."
);
}
files
}
pub(super) fn source_records_for_prompt(&self) -> String {
if self.areas.is_empty() {
return String::from(
"No source-record hint was selected. Inspect the diff and nearby source first.",
);
}
let mut guidance = format!(
"Review-context hints selected from all {} changed path(s). Inspect the diff first; use these only when they help judge a touched invariant.\n",
self.paths.len()
);
for area in &self.areas {
let _result = writeln!(
&mut guidance,
"- {}: records: {}; invariants: {}",
area.title(),
area.records().join(", "),
area.invariants()
);
}
guidance
}
}
#[derive(Clone, Debug, Eq, PartialEq)]
struct ChangedPath {
path: String,
prompt_text: String,
}
impl ChangedPath {
fn new(path: &str) -> Option<Self> {
if path.is_empty() || path.contains('\0') {
return None;
}
Some(Self {
path: path.to_owned(),
prompt_text: escape_path_for_prompt(path),
})
}
fn as_str(&self) -> &str {
&self.path
}
fn prompt_text(&self) -> &str {
&self.prompt_text
}
}
fn escape_path_for_prompt(path: &str) -> String {
let mut escaped = String::with_capacity(path.len());
for character in path.chars() {
for escaped_character in character.escape_default() {
escaped.push(escaped_character);
}
}
escaped
}
const REVIEW_AREAS: &[ReviewArea] = &[
ReviewArea::Elenchus,
ReviewArea::PluginRuntime,
ReviewArea::EditorCore,
ReviewArea::Ecs,
ReviewArea::Vim,
ReviewArea::Filesystem,
ReviewArea::Config,
ReviewArea::SupplyChain,
ReviewArea::Tests,
ReviewArea::ProgressNotes,
];
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
enum ReviewArea {
Elenchus,
PluginRuntime,
EditorCore,
Ecs,
Vim,
Filesystem,
Config,
SupplyChain,
Tests,
ProgressNotes,
}
impl ReviewArea {
fn matches_path(self, path: &str) -> bool {
match self {
Self::Elenchus => {
path.starts_with("src/elenchus/")
|| path.starts_with("helen/src/elenchus/")
|| path == "tests/elenchus_flow.rs"
|| path == "helen/tests/elenchus_flow.rs"
|| path == "scripts/elenchus.sh"
|| path == "helen/scripts/elenchus.sh"
|| path == "AGENTS.md"
}
Self::PluginRuntime => is_plugin_runtime_path(path),
Self::EditorCore => {
path.starts_with("src/buffer/")
|| path.starts_with("src/domain/")
|| path.starts_with("src/editor/")
|| path.starts_with("src/render/")
|| path.starts_with("src/text_stream/")
|| path == "docs/arch.md"
|| path == "docs/target_arch.md"
}
Self::Ecs => path.starts_with("src/ecs/"),
Self::Vim => path.starts_with("src/vim/") || path.starts_with("src/adapters/bevy/vim/"),
Self::Filesystem => {
path.starts_with("src/fs_utils/")
|| path == "docs/authority.md"
|| path == "docs/supply-chain.md"
}
Self::Config => path.starts_with("src/config/"),
Self::SupplyChain => {
matches!(path, "Cargo.toml" | "Cargo.lock" | "docs/supply-chain.md")
}
Self::Tests => path.starts_with("tests/") || path.contains("/tests.rs"),
Self::ProgressNotes => {
path == "docs/next_steps.md"
|| path == "docs/elenchus_helen_extraction.md"
|| path.ends_with("_todos.md")
|| path.ends_with("_notes.md")
|| path.starts_with("archive/")
}
}
}
const fn title(self) -> &'static str {
match self {
Self::Elenchus => "Elenchus gate",
Self::PluginRuntime => "Plugin/Wasm boundary",
Self::EditorCore => "Editor core",
Self::Ecs => "ECS owner boundary",
Self::Vim => "Vim semantics",
Self::Filesystem => "Filesystem authority",
Self::Config => "Configuration",
Self::SupplyChain => "Supply chain",
Self::Tests => "Tests",
Self::ProgressNotes => "Progress notes",
}
}
const fn records(self) -> &'static [&'static str] {
match self {
Self::Elenchus => &[
"AGENTS.md",
".codex/review-policy.md",
"src/elenchus/",
"helen/src/elenchus/",
],
Self::PluginRuntime => &[
"docs/arch.md",
"docs/target_arch.md",
"docs/authority.md",
"docs/plugins/runtime.md",
"docs/plugins/security.md",
"docs/plugins/testing.md",
"docs/supply-chain.md",
],
Self::EditorCore | Self::Ecs => &["docs/arch.md", "docs/target_arch.md"],
Self::Vim => &["docs/arch.md"],
Self::Filesystem => &["docs/arch.md", "docs/authority.md", "docs/supply-chain.md"],
Self::Config => &["docs/arch.md", "docs/authority.md"],
Self::SupplyChain => &["docs/supply-chain.md"],
Self::Tests => &["docs/plugins/testing.md when plugin behavior is touched"],
Self::ProgressNotes => &["the changed note file", "the source records it cites"],
}
}
const fn invariants(self) -> &'static str {
match self {
Self::Elenchus => {
"preserve the gate contract: clean index, exact diff snapshot, local checks, read-only review, risk pause, and commit only after pass"
}
Self::PluginRuntime => {
"untrusted guests, default-deny capabilities, typed proof values, bounded resources, explicit commit points, redacted diagnostics"
}
Self::EditorCore => {
"pure Rust semantics first, UTF-8 and revision invariants owned by buffer/text types, Bevy only adapts owner decisions"
}
Self::Ecs => {
"typed requests cross into owner systems; stale targets produce typed redacted rejections"
}
Self::Vim => {
"grammar and modal semantics stay pure and deterministic before Bevy dispatch"
}
Self::Filesystem => {
"normalize and validate paths at trust boundaries; keep raw paths out of status and diagnostics unless explicitly intended"
}
Self::Config => {
"config is an authority boundary; defaults must fail closed and validation should produce typed proofs"
}
Self::SupplyChain => {
"dependency and Wasm-loading changes need reviewed trust, pinning, and default-deny behavior"
}
Self::Tests => {
"tests should protect externally visible behavior and boundary invariants, not implementation trivia"
}
Self::ProgressNotes => {
"notes must accurately summarize the diff and checks without creating a second architecture source"
}
}
}
}
fn is_plugin_runtime_path(path: &str) -> bool {
if path.ends_with("_todos.md") || path.ends_with("_notes.md") {
return false;
}
path.starts_with("src/plugin/")
|| path.starts_with("src/ecs/plugin")
|| matches!(
path,
"src/ecs/events/plugin.rs"
| "src/ecs/events/edit.rs"
| "docs/arch.md"
| "docs/target_arch.md"
| "docs/authority.md"
| "docs/supply-chain.md"
)
|| path.starts_with("docs/plugins/")
|| path.starts_with("tests/goldens/plugin/")
}
#[cfg(test)]
mod tests {
use super::ReviewContext;
use std::fmt::Write as _;
#[test]
fn elenchus_progress_note_does_not_trigger_plugin_runtime_context() {
let context = ReviewContext::from_changed_files(
"src/elenchus/review.rs\nsrc/plugin/refactor_todos.md\n",
);
let guidance = context.source_records_for_prompt();
assert!(guidance.contains("Elenchus gate"));
assert!(guidance.contains("Progress notes"));
assert!(!guidance.contains("Plugin/Wasm boundary"));
}
#[test]
fn helen_elenchus_paths_select_elenchus_context() {
let context = ReviewContext::from_changed_files(
"helen/src/elenchus/mod.rs\nhelen/scripts/elenchus.sh\nhelen/tests/elenchus_flow.rs\n",
);
let guidance = context.source_records_for_prompt();
assert!(guidance.contains("Elenchus gate"));
}
#[test]
fn plugin_runtime_path_selects_plugin_source_records() {
let context = ReviewContext::from_changed_files("src/plugin/runtime/wasmtime.rs\n");
let guidance = context.source_records_for_prompt();
assert!(guidance.contains("Plugin/Wasm boundary"));
assert!(guidance.contains("docs/plugins/runtime.md"));
assert!(guidance.contains("default-deny capabilities"));
}
#[test]
fn changed_files_are_rendered_without_source_hints_for_unclassified_paths() {
let context = ReviewContext::from_changed_files("README.md\n");
assert_eq!(context.changed_files_for_prompt(), "- README.md\n");
assert_eq!(
context.source_records_for_prompt(),
"No source-record hint was selected. Inspect the diff and nearby source first."
);
}
#[test]
fn changed_files_prompt_is_bounded_but_context_uses_all_paths() {
let mut changed_files = String::new();
for index in 0..80 {
let _result = writeln!(&mut changed_files, "notes/{index}.md");
}
changed_files.push_str("src/plugin/runtime/wasmtime.rs\n");
let context = ReviewContext::from_changed_files(&changed_files);
let files = context.changed_files_for_prompt();
let guidance = context.source_records_for_prompt();
assert!(files.contains("- notes/79.md\n"));
assert!(!files.contains("src/plugin/runtime/wasmtime.rs"));
assert!(files.contains(
"- ... 1 more changed path(s); inspect the saved diff snapshot for the full list.\n"
));
assert!(guidance.contains("selected from all 81 changed path(s)"));
assert!(guidance.contains("Plugin/Wasm boundary"));
}
#[test]
fn changed_path_prompt_escapes_control_characters() {
let context = ReviewContext::from_changed_files("src/elenchus/review.rs\tcopy\n");
assert_eq!(
context.changed_files_for_prompt(),
"- src/elenchus/review.rs\\tcopy\n"
);
}
}