Rust Backend Patterns
Rust Backend Patterns
How to Use This Skill
- Review the patterns and examples below
- Apply the relevant patterns to your implementation
- Follow the best practices outlined in this skill
Expert skill for Rust/Actix-web development in the T2 backend (GCP-deployed API).
When to Use
✅ Use this skill when:
- Implementing new REST API endpoints (GET, POST, PUT, DELETE)
- Adding authentication middleware or authorization logic
- Creating custom error types and error handling patterns
- Writing repository methods for FDB data access
- Setting up CORS, rate limiting, or other middleware
- Structuring Actix-web handlers with proper dependency injection
- Writing integration tests for backend endpoints
- Need time savings: 50% faster endpoint implementation (40→20 min)
❌ Don't use this skill when:
- Working on frontend-only features (use React/TypeScript patterns)
- Debugging FDB queries (use foundationdb-queries skill)
- Deploying to GKE (use build-deploy-workflow skill)
- Writing production error handling (use production-patterns skill for circuit breakers)
Handler Pattern
// backend/src/handlers/*.rs
use actix_web::{web, HttpResponse};
use crate::db::FDBService;
use crate::middleware::Claims;
#[post("/api/v5/resource")]
async fn create_resource(
claims: Claims, // JWT auth - always required!
data: web::Json<CreateResourceRequest>,
fdb: web::Data<FDBService>,
) -> Result<HttpResponse, ApiError> {
// 1. Extract tenant from claims (auto-validated by middleware)
let tenant_id = claims.tenant_id;
// 2. Use repository pattern
let resource = fdb.resources()
.create_with_tenant(tenant_id, data.into_inner())
.await?;
// 3. Return success
Ok(HttpResponse::Created().json(resource))
}
Error Handling Pattern
// Use map_err() for TransactionCommitError
trx.commit().await.map_err(|e| {
ApiError::DatabaseError(format!("Failed to commit: {:?}", e))
})?;
Auth Middleware
ALL endpoints except /health and /ready require JWT:
// Automatically validates JWT and provides Claims
async fn handler(claims: Claims) -> Result<HttpResponse, ApiError> {
let tenant_id = claims.tenant_id; // ✅ Validated
let user_id = claims.user_id; // ✅ Validated
// ...
}
Repository Pattern
NEVER use FDB directly. Always use repositories:
// ✅ CORRECT
let user = fdb.users().get_by_id(&tenant_id, &user_id).await?;
// ❌ WRONG
let key = format!("/{}/users/{}", tenant_id, user_id);
let value = trx.get(&key.as_bytes()).await?;
Complete CRUD Endpoint Patterns
GET - Retrieve Single Resource
// backend/src/handlers/users.rs
use actix_web::{get, web, HttpResponse};
use crate::middleware::Claims;
use crate::db::repositories::UserRepository;
#[get("/api/v5/users/{user_id}")]
async fn get_user(
claims: Claims,
path: web::Path<Uuid>,
db: web::Data<Database>,
) -> Result<HttpResponse, ApiError> {
let user_id = path.into_inner();
let tenant_id = claims.tenant_id;
// Repository pattern with tenant isolation
match UserRepository::get(&db, &tenant_id, &user_id).await? {
Some(user) => Ok(HttpResponse::Ok().json(user)),
None => Err(ApiError::NotFound(format!("User {} not found", user_id))),
}
}
GET - List Resources with Pagination
#[derive(Deserialize)]
struct ListUsersQuery {
#[serde(default = "default_limit")]
limit: usize,
#[serde(default)]
offset: usize,
}
fn default_limit() -> usize { 50 }
#[get("/api/v5/users")]
async fn list_users(
claims: Claims,
query: web::Query<ListUsersQuery>,
db: web::Data<Database>,
) -> Result<HttpResponse, ApiError> {
let tenant_id = claims.tenant_id;
let users = UserRepository::list_by_tenant(&db, &tenant_id).await?;
// Apply pagination
let total = users.len();
let paginated: Vec<_> = users
.into_iter()
.skip(query.offset)
.take(query.limit)
.collect();
Ok(HttpResponse::Ok().json(json!({
"users": paginated,
"total": total,
"limit": query.limit,
"offset": query.offset,
})))
}
POST - Create Resource
#[derive(Deserialize, Validate)]
struct CreateUserRequest {
#[validate(email)]
email: String,
#[validate(length(min = 8))]
password: String,
#[validate(length(min = 1, max = 100))]
name: String,
}
#[post("/api/v5/users")]
async fn create_user(
claims: Claims,
data: web::Json<CreateUserRequest>,
db: web::Data<Database>,
) -> Result<HttpResponse, ApiError> {
// 1. Validate input
data.validate()
.map_err(|e| ApiError::ValidationError(format!("{}", e)))?;
// 2. Extract tenant from claims
let tenant_id = claims.tenant_id;
// 3. Create via repository
let user = UserRepository::create(&db, &tenant_id, &data.into_inner()).await?;
// 4. Return 201 Created
Ok(HttpResponse::Created().json(user))
}
PUT - Update Resource
#[derive(Deserialize, Validate)]
struct UpdateUserRequest {
#[validate(length(min = 1, max = 100))]
name: Option<String>,
#[validate(email)]
email: Option<String>,
}
#[put("/api/v5/users/{user_id}")]
async fn update_user(
claims: Claims,
path: web::Path<Uuid>,
data: web::Json<UpdateUserRequest>,
db: web::Data<Database>,
) -> Result<HttpResponse, ApiError> {
data.validate()
.map_err(|e| ApiError::ValidationError(format!("{}", e)))?;
let user_id = path.into_inner();
let tenant_id = claims.tenant_id;
// Check if user exists and belongs to tenant
let mut user = UserRepository::get(&db, &tenant_id, &user_id).await?
.ok_or_else(|| ApiError::NotFound(format!("User {} not found", user_id)))?;
// Apply updates
if let Some(name) = &data.name {
user.name = name.clone();
}
if let Some(email) = &data.email {
user.email = email.clone();
}
// Save changes
let updated_user = UserRepository::update(&db, &tenant_id, &user_id, user).await?;
Ok(HttpResponse::Ok().json(updated_user))
}
DELETE - Remove Resource
#[delete("/api/v5/users/{user_id}")]
async fn delete_user(
claims: Claims,
path: web::Path<Uuid>,
db: web::Data<Database>,
) -> Result<HttpResponse, ApiError> {
let user_id = path.into_inner();
let tenant_id = claims.tenant_id;
// Verify user exists before deleting
UserRepository::get(&db, &tenant_id, &user_id).await?
.ok_or_else(|| ApiError::NotFound(format!("User {} not found", user_id)))?;
// Delete
UserRepository::delete(&db, &tenant_id, &user_id).await?;
// Return 204 No Content
Ok(HttpResponse::NoContent().finish())
}
Custom Error Types
// backend/src/error.rs
use actix_web::{error::ResponseError, http::StatusCode, HttpResponse};
use derive_more::{Display, Error};
#[derive(Debug, Display, Error)]
pub enum ApiError {
#[display(fmt = "Not found: {}", _0)]
NotFound(String),
#[display(fmt = "Validation error: {}", _0)]
ValidationError(String),
#[display(fmt = "Unauthorized: {}", _0)]
Unauthorized(String),
#[display(fmt = "Forbidden: {}", _0)]
Forbidden(String),
#[display(fmt = "Database error: {}", _0)]
DatabaseError(String),
#[display(fmt = "Internal server error: {}", _0)]
InternalError(String),
}
impl ResponseError for ApiError {
fn error_response(&self) -> HttpResponse {
let (status, message) = match self {
ApiError::NotFound(msg) => (StatusCode::NOT_FOUND, msg),
ApiError::ValidationError(msg) => (StatusCode::BAD_REQUEST, msg),
ApiError::Unauthorized(msg) => (StatusCode::UNAUTHORIZED, msg),
ApiError::Forbidden(msg) => (StatusCode::FORBIDDEN, msg),
ApiError::DatabaseError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, "Database error"),
ApiError::InternalError(msg) => (StatusCode::INTERNAL_SERVER_ERROR, "Internal error"),
};
HttpResponse::build(status).json(json!({
"error": message,
"status": status.as_u16(),
}))
}
fn status_code(&self) -> StatusCode {
match self {
ApiError::NotFound(_) => StatusCode::NOT_FOUND,
ApiError::ValidationError(_) => StatusCode::BAD_REQUEST,
ApiError::Unauthorized(_) => StatusCode::UNAUTHORIZED,
ApiError::Forbidden(_) => StatusCode::FORBIDDEN,
ApiError::DatabaseError(_) | ApiError::InternalError(_) => StatusCode::INTERNAL_SERVER_ERROR,
}
}
}
// Convert FDB errors to ApiError
impl From<FdbError> for ApiError {
fn from(err: FdbError) -> Self {
match err {
FdbError::NotFound(msg) => ApiError::NotFound(msg),
FdbError::TransactionError(msg) => ApiError::DatabaseError(msg),
_ => ApiError::InternalError(format!("FDB error: {}", err)),
}
}
}
Middleware Patterns
CORS Configuration
// backend/src/main.rs
use actix_cors::Cors;
HttpServer::new(move || {
// CORS middleware - configure for production
let cors = Cors::default()
.allowed_origin("https://coditect.ai")
.allowed_origin("https://api.coditect.ai")
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
.allowed_headers(vec![
actix_web::http::header::AUTHORIZATION,
actix_web::http::header::CONTENT_TYPE,
])
.max_age(3600);
App::new()
.wrap(cors)
.wrap(middleware::Logger::default())
.configure(configure_routes)
})
Rate Limiting Middleware
// backend/src/middleware/rate_limit.rs
use actix_web::dev::{Service, ServiceRequest, ServiceResponse, Transform};
use std::sync::Arc;
use tokio::sync::Mutex;
use std::collections::HashMap;
use std::time::{Duration, Instant};
pub struct RateLimiter {
requests: Arc<Mutex<HashMap<String, Vec<Instant>>>>,
max_requests: usize,
window: Duration,
}
impl RateLimiter {
pub fn new(max_requests: usize, window: Duration) -> Self {
Self {
requests: Arc::new(Mutex::new(HashMap::new())),
max_requests,
window,
}
}
pub async fn check(&self, key: &str) -> Result<(), ApiError> {
let mut requests = self.requests.lock().await;
let now = Instant::now();
// Get or create entry
let entry = requests.entry(key.to_string()).or_insert_with(Vec::new);
// Remove expired requests
entry.retain(|&req_time| now.duration_since(req_time) < self.window);
// Check limit
if entry.len() >= self.max_requests {
return Err(ApiError::TooManyRequests(format!(
"Rate limit exceeded: {} requests per {:?}",
self.max_requests, self.window
)));
}
// Add current request
entry.push(now);
Ok(())
}
}
Testing Patterns
Unit Tests
#[cfg(test)]
mod tests {
use super::*;
use actix_web::test;
#[actix_web::test]
async fn test_create_user_success() {
let app = test::init_service(
App::new().service(create_user)
).await;
let req = test::TestRequest::post()
.uri("/api/v5/users")
.insert_header(("Authorization", "Bearer test_token"))
.set_json(&json!({
"email": "test@example.com",
"password": "securepass123",
"name": "Test User"
}))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::CREATED);
}
#[actix_web::test]
async fn test_create_user_validation_error() {
let app = test::init_service(
App::new().service(create_user)
).await;
let req = test::TestRequest::post()
.uri("/api/v5/users")
.insert_header(("Authorization", "Bearer test_token"))
.set_json(&json!({
"email": "invalid-email", // ❌ Invalid email
"password": "short", // ❌ Too short
"name": "Test User"
}))
.to_request();
let resp = test::call_service(&app, req).await;
assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
}
}
Integration Tests with Mock FDB
// tests/integration_test.rs
use actix_web::{test, App};
#[actix_web::test]
async fn test_user_crud_flow() {
// Setup mock FDB
let db = setup_test_db().await;
let app = test::init_service(
App::new()
.app_data(web::Data::new(db))
.service(create_user)
.service(get_user)
.service(update_user)
.service(delete_user)
).await;
// 1. Create user
let create_req = test::TestRequest::post()
.uri("/api/v5/users")
.insert_header(("Authorization", "Bearer test_token"))
.set_json(&json!({
"email": "test@example.com",
"password": "securepass123",
"name": "Test User"
}))
.to_request();
let create_resp = test::call_service(&app, create_req).await;
assert_eq!(create_resp.status(), StatusCode::CREATED);
let user: User = test::read_body_json(create_resp).await;
// 2. Get user
let get_req = test::TestRequest::get()
.uri(&format!("/api/v5/users/{}", user.user_id))
.insert_header(("Authorization", "Bearer test_token"))
.to_request();
let get_resp = test::call_service(&app, get_req).await;
assert_eq!(get_resp.status(), StatusCode::OK);
// 3. Update user
let update_req = test::TestRequest::put()
.uri(&format!("/api/v5/users/{}", user.user_id))
.insert_header(("Authorization", "Bearer test_token"))
.set_json(&json!({"name": "Updated Name"}))
.to_request();
let update_resp = test::call_service(&app, update_req).await;
assert_eq!(update_resp.status(), StatusCode::OK);
// 4. Delete user
let delete_req = test::TestRequest::delete()
.uri(&format!("/api/v5/users/{}", user.user_id))
.insert_header(("Authorization", "Bearer test_token"))
.to_request();
let delete_resp = test::call_service(&app, delete_req).await;
assert_eq!(delete_resp.status(), StatusCode::NO_CONTENT);
}
Integration with T2 Orchestrator
Orchestrator Phase 3: Backend Implementation
When the orchestrator coordinates backend feature development, it uses this skill for Actix-web endpoints:
Orchestrator Phase 3: Backend Implementation
├─ Use rust-backend-patterns for endpoint structure ← THIS SKILL
├─ Use foundationdb-queries for data persistence
├─ Use production-patterns for error handling
└─ Validate with TDD validator (endpoint tests)
Example Delegation:
"Use rust-backend-patterns skill to implement user profile CRUD endpoints with JWT auth."
Token Efficiency: Endpoint patterns save 50% implementation time (40→20 min) by providing:
- Ready-to-adapt CRUD templates
- Proven error handling patterns
- Middleware configuration examples
- Testing patterns with mocks
Troubleshooting
Issue 1: JWT Middleware Not Applied
Symptom: Endpoints accessible without JWT token
Cause: Middleware not registered in App configuration
Fix: Ensure JWT middleware is registered before routes
// WRONG: Middleware registered after routes
App::new()
.service(create_user) // ❌ No JWT validation
.wrap(JwtMiddleware::new());
// CORRECT: Middleware registered before routes
App::new()
.wrap(JwtMiddleware::new()) // ✅ Applied to all routes
.service(create_user);
Issue 2: CORS Errors in Browser
Symptom: Frontend requests blocked with CORS error
Cause: Missing or incorrect CORS configuration
Fix: Configure CORS with correct origins
let cors = Cors::default()
.allowed_origin("https://coditect.ai") // ✅ Production domain
.allowed_origin("http://localhost:5173") // ✅ Dev server
.allowed_methods(vec!["GET", "POST", "PUT", "DELETE"])
.allowed_headers(vec![
actix_web::http::header::AUTHORIZATION,
actix_web::http::header::CONTENT_TYPE,
]);
Issue 3: Request Body Deserialization Fails
Symptom:
Error: Json deserialize error: missing field `name`
Cause: Frontend sends different field names than backend expects
Fix: Use #[serde(rename)] or make fields optional
#[derive(Deserialize)]
struct CreateUserRequest {
#[serde(rename = "userName")] // ✅ Matches frontend
name: String,
// Or make optional
name: Option<String>,
}
Issue 4: Database Connection Pool Exhausted
Symptom:
Error: DatabaseError("Connection pool exhausted")
Cause: Too many concurrent requests or slow FDB transactions
Fix: Increase pool size or optimize transactions
// Increase pool size
let db = Database::new()
.max_connections(50) // ✅ Increased from default 10
.connect().await?;
// Or optimize transactions (keep them short)
let user = UserRepository::get(&db, &tenant_id, &user_id).await?;
// Don't do heavy processing inside transaction
process_user_data(&user); // ✅ Outside transaction
Issue 5: Validation Errors Not Returned
Symptom: Validation fails but client gets 500 Internal Error
Cause: Validation errors not mapped to ApiError
Fix: Use map_err to convert validation errors
data.validate()
.map_err(|e| ApiError::ValidationError(format!("{}", e)))?;
Multi-Context Window Support
This skill supports long-running Rust backend development across multiple context windows using Claude 4.5's enhanced state management capabilities.
State Tracking
Backend Implementation State (JSON):
{
"checkpoint_id": "ckpt_20251129_151000",
"endpoints_implemented": [
{"path": "/api/v5/users", "method": "GET", "status": "complete", "tests": "passing"},
{"path": "/api/v5/users/{id}", "method": "PUT", "status": "in_progress", "tests": "pending"},
{"path": "/api/v5/users/{id}", "method": "DELETE", "status": "pending", "tests": "not_started"}
],
"cargo_dependencies": ["actix-web", "validator", "serde_json"],
"test_status": {
"unit_tests": {"passed": 15, "failed": 1},
"integration_tests": {"passed": 8, "failed": 0}
},
"token_usage": 18000,
"created_at": "2025-11-29T15:10:00Z"
}
Progress Notes (Markdown):
# Rust Backend Progress - 2025-11-29
## Completed
- GET /api/v5/users endpoint with pagination
- POST /api/v5/users with validation
- Unit tests for user handlers (15/16 passing)
## In Progress
- PUT /api/v5/users/{id} - implementing update logic
- Fixing validation test failure (email format edge case)
## Next Actions
- Complete PUT endpoint implementation
- Add DELETE endpoint
- Fix email validation test
- Run cargo check and cargo test
Session Recovery
When starting a fresh context window after Rust backend work:
- Load Checkpoint State: Read
.coditect/checkpoints/rust-backend-latest.json - Review Progress Notes: Check
rust-backend-progress.mdfor context - Verify Cargo Status: Run
cargo checkto confirm compilation - Check Test Results: Run
cargo testto see current test status - Resume Implementation: Continue from last incomplete endpoint
Recovery Commands:
# 1. Check latest checkpoint
cat .coditect/checkpoints/rust-backend-latest.json | jq '.endpoints_implemented'
# 2. Review progress
tail -30 rust-backend-progress.md
# 3. Verify compilation
cargo check
# 4. Run tests
cargo test --lib
# 5. Check for TODOs
rg "todo!" backend/src/
State Management Best Practices
Checkpoint Files (JSON Schema):
- Store in
.coditect/checkpoints/rust-backend-{timestamp}.json - Include Cargo.toml dependency list
- Track test results per endpoint
- Record compilation status
Progress Tracking (Markdown Narrative):
- Maintain
rust-backend-progress.mdwith endpoint completion status - Document Rust-specific patterns used (Result<T,E>, error handling)
- Note test failures with stack traces
- List next endpoints to implement
Git Integration:
- Create checkpoint after each endpoint implementation
- Commit with conventional format:
feat(api): Add PUT /api/v5/users/{id} - Tag working states:
git tag backend-users-crud-complete
Progress Checkpoints
Natural Breaking Points:
- After each CRUD endpoint implemented and tested
- After Cargo.toml dependencies added
- After integration tests passing
- Before middleware modifications
- After all endpoints validated with manual testing
Checkpoint Creation Pattern:
// Automatic checkpoint after endpoint completion
if endpoints_complete >= 3 || test_failures > 0 {
create_checkpoint(CheckpointData {
endpoints: implemented_endpoints,
tests: test_results,
cargo_status: compilation_ok,
tokens: current_tokens
});
}
Example: Multi-Context CRUD Implementation
Context Window 1: GET & POST Endpoints
{
"checkpoint_id": "ckpt_users_read_write",
"phase": "create_read_complete",
"endpoints": ["GET /users", "POST /users"],
"tests_passed": 12,
"next_action": "Implement PUT and DELETE",
"token_usage": 11000
}
Context Window 2: PUT & DELETE Endpoints
# Load checkpoint
cat .coditect/checkpoints/ckpt_users_read_write.json
# Continue with update/delete operations
# Token savings: ~9000 tokens (no re-reading GET/POST code)
Token Savings Analysis:
- Without checkpoint: 20000 tokens (re-verify all endpoints)
- With checkpoint: 12000 tokens (resume from working state)
- Savings: 40% reduction (20000 → 12000 tokens)
Success Output
When successful, this skill MUST output:
✅ SKILL COMPLETE: rust-backend-patterns
Completed:
- [x] CRUD endpoints implemented (GET, POST, PUT, DELETE)
- [x] JWT authentication middleware applied
- [x] Repository pattern used for all DB access
- [x] Custom error types with proper ResponseError impl
- [x] Request validation with validator crate
- [x] CORS middleware configured
- [x] Unit tests passing (all endpoints)
- [x] Integration tests passing (full CRUD flow)
Endpoints Implemented:
- GET /api/v5/{resource}
- GET /api/v5/{resource}/{id}
- POST /api/v5/{resource}
- PUT /api/v5/{resource}/{id}
- DELETE /api/v5/{resource}/{id}
Test Results:
- Unit tests: {PASS_COUNT}/{TOTAL_COUNT} passing
- Integration tests: {INTEGRATION_PASS}/{INTEGRATION_TOTAL} passing
- Cargo check: 0 errors, {WARNING_COUNT} warnings
Outputs:
- backend/src/handlers/{resource}.rs created
- backend/src/db/repositories/{resource}.rs created
- tests/{resource}_test.rs created
Completion Checklist
Before marking this skill as complete, verify:
- All endpoints use
Claimsextractor for JWT validation - Repository pattern used (no direct FDB access in handlers)
- Error handling uses
map_err()for FDB transaction errors - Request structs have
#[derive(Deserialize, Validate)] - Validation called with
.validate()before processing - Tenant ID extracted from JWT claims
- CORS middleware configured with production domains
- Custom error types implement
ResponseErrortrait - HTTP status codes match REST conventions (200, 201, 204, 404, etc.)
- Unit tests cover all endpoint handlers
- Integration tests verify full CRUD flow
-
cargo checkpasses with no errors -
cargo testpasses all tests
Failure Indicators
This skill has FAILED if:
- ❌ Endpoint accessible without JWT token (middleware not applied)
- ❌ Handler uses direct FDB calls instead of repository
- ❌
TransactionCommitErrornot handled withmap_err() - ❌ Validation errors return 500 instead of 400
- ❌ CORS errors block frontend requests
- ❌ Request body deserialization fails silently
- ❌ Tenant isolation broken (wrong tenant's data returned)
- ❌ Database connection pool exhausted
- ❌ Cargo build fails with compilation errors
- ❌ Tests fail with panic or timeout
- ❌ API returns raw database errors to client
When NOT to Use
Do NOT use this skill when:
- Frontend-only features - Use React/TypeScript patterns skill instead
- FDB query debugging - Use foundationdb-queries skill for data layer
- GKE deployment - Use build-deploy-workflow skill for deployment
- WebSocket/streaming - This skill focuses on REST, not real-time protocols
- GraphQL API - Use graphql-rust-patterns skill (if available)
- Background jobs - Use task-queue-patterns skill for async processing
- Non-API backend code - Use rust-general-patterns for CLI/workers
Anti-Patterns (Avoid)
| Anti-Pattern | Problem | Solution |
|---|---|---|
| No JWT middleware | Endpoints publicly accessible | Always wrap routes with JwtMiddleware::new() |
| Direct FDB access in handlers | Tight coupling, hard to test | Use repository pattern exclusively |
| Unwrapping FDB errors | App panics on database failure | Use map_err() to convert to ApiError |
| Missing validation | Bad data enters database | Always call .validate() on request structs |
| Hardcoded CORS origins | Production requests blocked | Configure CORS from environment variable |
| Exposing internal errors | Security risk, info leak | Return generic errors to client, log details |
| No tenant isolation | Data leakage across tenants | Always filter by claims.tenant_id |
| Connection pool starvation | Slow endpoints block others | Keep FDB transactions short |
| Ignoring cargo warnings | Tech debt accumulates | Address warnings before deploying |
Principles
This skill embodies:
- #1 Recycle → Extend → Re-Use → Create - Proven CRUD patterns reused across endpoints
- #3 Keep It Simple - Repository pattern separates concerns cleanly
- #4 Separation of Concerns - Handlers, repositories, errors, validation all separate
- #5 Eliminate Ambiguity - Explicit error types, clear HTTP status codes
- #10 Security First - JWT required, tenant isolation enforced
- #11 Quality is Non-Negotiable - Tests required, cargo check must pass
- #13 Observability is Essential - Error logging, structured responses
Full Standard: CODITECT-STANDARD-AUTOMATION.md