ADR-020-v4: AGENTS.md Standard Adoption - Part 2 (Technical)
Document Specification Block
Document: ADR-020-v4-agents-md-adoption-part2-technical
Version: 1.0.0
Purpose: Constrain AI implementation of AGENTS.md standard across CODITECT platform
Audience: AI agents, developers implementing the system
Date Created: 2025-08-30
Date Modified: 2025-08-30
Status: DRAFT
1. Technical Requirements
1.1 Constraints
- MUST create AGENTS.md file for every component directory
- MUST follow the standardized structure from agents.md specification
- MUST validate AGENTS.md files in CI/CD pipeline
- MUST NOT duplicate information already in ADRs or CLAUDE.md
- MUST keep files under 500 lines for optimal AI parsing
1.2 Dependencies
- ADR-001: Foundation Architecture (component structure)
- ADR-002: Multi-tenant Data Architecture (isolation patterns)
- ADR-015: Developer Experience (documentation standards)
- External: AGENTS.md standard (MIT licensed)
2. Component Architecture
2.1 System Components
2.2 Data Flow
3. Implementation Specifications
3.1 File Structure
# Required AGENTS.md structure
sections:
- name: "Component Overview"
required: true
max_lines: 20
- name: "Development Environment"
required: true
includes: [language, build, test, run]
- name: "Architecture"
required: true
includes: [structure, patterns, dependencies]
- name: "Testing Guidelines"
required: true
includes: [unit, integration, coverage]
- name: "Security Considerations"
required: true
- name: "Do's and Don'ts"
required: true
3.2 Validation Schema
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Serialize, Deserialize)]
pub struct AgentsMdSchema {
pub component_name: String,
pub sections: HashMap<String, Section>,
pub version: String,
pub last_updated: DateTime<Utc>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct Section {
pub title: String,
pub content: String,
pub required: bool,
pub max_lines: Option<usize>,
pub code_examples: Vec<CodeExample>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct CodeExample {
pub language: String,
pub description: String,
pub code: String,
}
3.3 CODI Integration
// codi/src/commands/agents.rs
use clap::Args;
use std::path::Path;
use anyhow::Result;
#[derive(Args, Debug)]
pub struct AgentsCommand {
#[command(subcommand)]
action: AgentsAction,
}
#[derive(Subcommand, Debug)]
enum AgentsAction {
/// Validate AGENTS.md file
Validate {
#[arg(short, long)]
path: PathBuf,
},
/// Generate AGENTS.md template
Generate {
#[arg(short, long)]
component: String,
#[arg(short, long)]
output: PathBuf,
},
/// Check all AGENTS.md files in project
Check,
}
impl AgentsCommand {
pub async fn execute(&self) -> Result<()> {
match &self.action {
AgentsAction::Validate { path } => self.validate_file(path).await,
AgentsAction::Generate { component, output } => {
self.generate_template(component, output).await
}
AgentsAction::Check => self.check_all_files().await,
}
}
async fn validate_file(&self, path: &Path) -> Result<()> {
let content = std::fs::read_to_string(path)?;
let validator = AgentsMdValidator::new();
match validator.validate(&content) {
Ok(report) => {
println!("✅ AGENTS.md validation passed");
println!("📊 Coverage: {:.1}%", report.coverage);
Ok(())
}
Err(errors) => {
eprintln!("❌ AGENTS.md validation failed:");
for error in errors {
eprintln!(" - {}", error);
}
Err(anyhow!("Validation failed"))
}
}
}
async fn generate_template(&self, component: &str, output: &Path) -> Result<()> {
let template = match component {
"api" => include_str!("../templates/agents-api.md"),
"frontend" => include_str!("../templates/agents-frontend.md"),
"cli" => include_str!("../templates/agents-cli.md"),
_ => include_str!("../templates/agents-generic.md"),
};
std::fs::write(output, template)?;
println!("✅ Generated AGENTS.md template at {}", output.display());
Ok(())
}
}
4. Core Implementation
4.1 Validator Implementation
use regex::Regex;
use std::collections::HashSet;
pub struct AgentsMdValidator {
required_sections: HashSet<String>,
section_pattern: Regex,
code_block_pattern: Regex,
}
impl AgentsMdValidator {
pub fn new() -> Self {
let mut required = HashSet::new();
required.insert("Component Overview".to_string());
required.insert("Development Environment".to_string());
required.insert("Architecture".to_string());
required.insert("Testing Guidelines".to_string());
required.insert("Security Considerations".to_string());
required.insert("Do's and Don'ts".to_string());
Self {
required_sections: required,
section_pattern: Regex::new(r"^#+\s+(.+)$").unwrap(),
code_block_pattern: Regex::new(r"```(\w+)?").unwrap(),
}
}
pub fn validate(&self, content: &str) -> Result<ValidationReport, Vec<String>> {
let mut errors = Vec::new();
let mut found_sections = HashSet::new();
let mut code_examples = 0;
let mut total_lines = 0;
for line in content.lines() {
total_lines += 1;
// Check for sections
if let Some(caps) = self.section_pattern.captures(line) {
let section = caps.get(1).unwrap().as_str();
found_sections.insert(section.to_string());
}
// Count code blocks
if self.code_block_pattern.is_match(line) {
code_examples += 1;
}
}
// Check required sections
for required in &self.required_sections {
if !found_sections.contains(required) {
errors.push(format!("Missing required section: {}", required));
}
}
// Check file size
if total_lines > 500 {
errors.push(format!(
"File too long: {} lines (max 500)",
total_lines
));
}
// Check for code examples
if code_examples < 3 {
errors.push(format!(
"Insufficient code examples: {} (min 3)",
code_examples
));
}
if errors.is_empty() {
Ok(ValidationReport {
coverage: (found_sections.len() as f32 / self.required_sections.len() as f32) * 100.0,
sections: found_sections.into_iter().collect(),
code_examples,
total_lines,
})
} else {
Err(errors)
}
}
}
#[derive(Debug)]
pub struct ValidationReport {
pub coverage: f32,
pub sections: Vec<String>,
pub code_examples: usize,
pub total_lines: usize,
}
4.2 CI/CD Integration
# .github/workflows/agents-md-validation.yml
name: AGENTS.md Validation
on:
pull_request:
paths:
- '**/AGENTS.md'
- 'components/**/AGENTS.md'
jobs:
validate:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Rust
uses: actions-rs/toolchain@v1
with:
toolchain: stable
- name: Build CODI
run: cargo build -p codi --release
- name: Validate AGENTS.md files
run: |
./target/release/codi agents check
- name: Comment on PR
if: failure()
uses: actions/github-script@v6
with:
script: |
github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: '❌ AGENTS.md validation failed. Please run `codi agents check` locally.'
})
5. Testing Requirements
5.1 Unit Tests
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_valid_agents_md() {
let valid_content = r#"
# Component Overview
This is a test component.
## Development Environment
- Language: Rust
- Build: cargo build
## Architecture
Simple service architecture.
## Testing Guidelines
Write tests first.
## Security Considerations
Follow OWASP guidelines.
## Do's and Don'ts
### Do's
- Write tests
### Don'ts
- Skip tests
```rust
fn example() {}
"#;
let validator = AgentsMdValidator::new();
let result = validator.validate(valid_content);
assert!(result.is_ok());
}
#[test]
fn test_missing_required_section() {
let invalid_content = r#"
Component Overview
This is missing other sections. "#;
let validator = AgentsMdValidator::new();
let result = validator.validate(invalid_content);
assert!(result.is_err());
if let Err(errors) = result {
assert!(errors.len() > 0);
assert!(errors[0].contains("Missing required section"));
}
}
#[test]
fn test_file_too_long() {
let mut long_content = String::new();
for i in 0..600 {
long_content.push_str(&format!("Line {}\n", i));
}
let validator = AgentsMdValidator::new();
let result = validator.validate(&long_content);
assert!(result.is_err());
}
}
### 5.2 Integration Tests
```rust
#[tokio::test]
async fn test_codi_agents_command() {
use tempfile::tempdir;
let dir = tempdir().unwrap();
let agents_file = dir.path().join("AGENTS.md");
// Generate template
let generate_cmd = AgentsCommand {
action: AgentsAction::Generate {
component: "api".to_string(),
output: agents_file.clone(),
},
};
assert!(generate_cmd.execute().await.is_ok());
assert!(agents_file.exists());
// Validate generated file
let validate_cmd = AgentsCommand {
action: AgentsAction::Validate {
path: agents_file,
},
};
assert!(validate_cmd.execute().await.is_ok());
}
6. Security Controls
6.1 Access Control
// AGENTS.md files are read-only in production
// Only modified through PR process
pub fn check_write_permission(user: &User, file: &Path) -> Result<()> {
if file.ends_with("AGENTS.md") && !user.has_role("maintainer") {
return Err(anyhow!("Only maintainers can modify AGENTS.md files"));
}
Ok(())
}
6.2 Content Validation
// Prevent injection attacks in AGENTS.md
pub fn sanitize_content(content: &str) -> String {
// Remove any executable code patterns
let cleaned = content
.replace("<script", "<script")
.replace("eval(", "eval(")
.replace("exec(", "exec(");
cleaned
}
7. Performance Specifications
7.1 Targets
- File parsing: < 100ms for 500 lines
- Validation: < 200ms per file
- Full project check: < 5 seconds
- Memory usage: < 10MB per file
7.2 Optimization Requirements
// Cache parsed AGENTS.md files
use lru::LruCache;
use std::sync::Arc;
use tokio::sync::RwLock;
pub struct AgentsMdCache {
cache: Arc<RwLock<LruCache<PathBuf, AgentsMdSchema>>>,
}
impl AgentsMdCache {
pub fn new(capacity: usize) -> Self {
Self {
cache: Arc::new(RwLock::new(LruCache::new(capacity))),
}
}
pub async fn get_or_parse(&self, path: &Path) -> Result<AgentsMdSchema> {
// Check cache first
{
let cache = self.cache.read().await;
if let Some(schema) = cache.get(path) {
return Ok(schema.clone());
}
}
// Parse file
let content = tokio::fs::read_to_string(path).await?;
let schema = parse_agents_md(&content)?;
// Update cache
{
let mut cache = self.cache.write().await;
cache.put(path.to_path_buf(), schema.clone());
}
Ok(schema)
}
}
8. Deployment Configuration
8.1 CODI Enhancement
# codi/cargo.toml additions
[dependencies]
regex = "1.10"
lru = "0.12"
[features]
default = ["agents-md"]
agents-md = []
8.2 Git Hooks
#!/bin/bash
# .git/hooks/pre-commit
# Validate AGENTS.md files before commit
set -e
# Check if any AGENTS.md files are being committed
if git diff --cached --name-only | grep -q 'AGENTS\.md$'; then
echo "Validating AGENTS.md files..."
# Run validation
if ! codi agents check; then
echo "❌ AGENTS.md validation failed"
echo "Please fix the issues and try again"
exit 1
fi
echo "✅ AGENTS.md validation passed"
fi
9. Monitoring & Observability
9.1 Metrics
use prometheus::{IntCounter, Histogram};
lazy_static! {
static ref AGENTS_MD_VALIDATIONS: IntCounter =
register_int_counter!(
"agents_md_validations_total",
"Total number of AGENTS.md validations"
).unwrap();
static ref AGENTS_MD_PARSE_TIME: Histogram =
register_histogram!(
"agents_md_parse_duration_seconds",
"Time to parse AGENTS.md files"
).unwrap();
}
9.2 Logging
use tracing::{info, warn, debug};
#[tracing::instrument]
pub async fn validate_agents_md(path: &Path) -> Result<()> {
info!(path = %path.display(), "Validating AGENTS.md");
let start = std::time::Instant::now();
let content = tokio::fs::read_to_string(path).await?;
debug!(size = content.len(), "Loaded file");
let validator = AgentsMdValidator::new();
match validator.validate(&content) {
Ok(report) => {
info!(
coverage = report.coverage,
sections = report.sections.len(),
"Validation passed"
);
AGENTS_MD_VALIDATIONS.inc();
AGENTS_MD_PARSE_TIME.observe(start.elapsed().as_secs_f64());
Ok(())
}
Err(errors) => {
warn!(errors = ?errors, "Validation failed");
Err(anyhow!("Validation failed with {} errors", errors.len()))
}
}
}
10. Constraints for AI Implementation
10.1 MUST Requirements
- MUST create AGENTS.md for every new component
- MUST validate all AGENTS.md files in CI/CD
- MUST follow the exact section structure
- MUST include at least 3 code examples per file
- MUST keep files under 500 lines
- MUST update AGENTS.md when component changes significantly
10.2 MUST NOT Requirements
- MUST NOT duplicate ADR content in AGENTS.md
- MUST NOT include secrets or credentials
- MUST NOT skip required sections
- MUST NOT use AGENTS.md for architectural decisions
- MUST NOT modify AGENTS.md without PR review
10.3 Test Coverage Requirements
- Validator unit tests: 100%
- Integration tests for all commands
- CI/CD pipeline tests
- Template generation tests
- Cache functionality tests