Skip to main content

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", "&lt;script")
.replace("eval(", "eval&#40;")
.replace("exec(", "exec&#40;");

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

  1. MUST create AGENTS.md for every new component
  2. MUST validate all AGENTS.md files in CI/CD
  3. MUST follow the exact section structure
  4. MUST include at least 3 code examples per file
  5. MUST keep files under 500 lines
  6. MUST update AGENTS.md when component changes significantly

10.2 MUST NOT Requirements

  1. MUST NOT duplicate ADR content in AGENTS.md
  2. MUST NOT include secrets or credentials
  3. MUST NOT skip required sections
  4. MUST NOT use AGENTS.md for architectural decisions
  5. 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