control-layer
CLAUDE.md96 starsChanged 30 days agoApache-2.0View on GitHub
Maintain shared skills in .claude/skills/. Codex discovers the same skills via .agents/skills -> ../.claude/skills; no per-skill links or inventory are needed. This is a public, open-source repository. Use fictional organizations, model aliases and illustrative prices in examples and test fixtures. Never include real customer identities, private aliases, negotiated terms, account identifiers or customer-specific operational details in code, docs, fixtures, commit messages, pull-request text or review comments. Keep private deployment paths, internal runbooks and customer investigations outside this repository. Describe built-in…
- Reads credentials
- Installs packages
We split this CLAUDE.md into the rules it gives an agent
54 rules. About 0 are common, 25 are distinctive, and 29 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈25
Distinctive · estimate
This author's own choices. Worth a look.
29
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 19
Check the diff and publication text for these details before committing or posting.
- Very specific: line 220
URL parameter: Add ?flags=demo to the URL (e.g., https://control-layer.pages.dev?flags=demo)
- Distinctive: line 226
MSW Service Worker: When demo mode is enabled, the app initializes a service worker that intercepts fetch requests
- Distinctive: line 274
RequestViewer: Read-only access to request logs and analytics
- Very specific: line 280
Use production-grade PostgreSQL database via DATABASE_URL environment variable
- Distinctive: line 311
All Rust tests require a running PostgreSQL database due to SQLx integration
- Very specific: line 312
Test cleanup scripts in scripts/ (drop-test-users.sh, drop-test-groups.sh) for Hurl tests
- Distinctive: line 321
Use unqualified names for imports, and put identifier imports at the top of the file.
- Very specific: line 329
Use specific error variants: Error::NotFound, Error::BadRequest, Error::InsufficientPermissions
- Very specific: line 342
Add #[tracing::instrument(skip_all)] to all handler functions for observability
- Very specific: line 343
Use #[utoipa::path(...)] macro for OpenAPI documentation
- Distinctive: line 344
Follow RESTful conventions: list (GET /resources), get (GET /resources/:id), create (POST /resources), update (PUT/PATCH /resources/:id), …
- Very specific: line 348
Always use the Repository pattern via db::handlers
- Distinctive: line 349
Create a transaction, instantiate repository, perform operations, commit
- Distinctive: line 350
Keep repository instantiation scoped in blocks to avoid borrow checker issues
- Very specific: line 355
Production: Use DbPools from sqlx-pool-router crate
- Very specific: line 359
Tests: Use TestDbPools from sqlx-pool-router crate
- Very specific: line 363
Handler Pattern: All handlers are generic over PoolProvider trait
- Very specific: line 365
Write operations: state.db.write().begin() for transactions
- Very specific: line 366
Read operations: state.db.read().acquire() for read-only queries
- Distinctive: line 372
When reading data immediately after writing it (e.g., verifying a file exists right after upload, or checking a batch right after …
- Very specific: line 375
Solution: Use .write() pool for both the write AND the subsequent read in read-after-write scenarios.
- Distinctive: line 379
General rule: If you write data and immediately need to read it within the same request handler, use the primary pool for both operations.
- Very specific: line 380
When to use replica: Only use .read() pool for queries that don't depend on recent writes in the same request, or for read-only endpoints …
- Distinctive: line 384
When clients make sequential API calls where one endpoint writes data and another endpoint reads it, both endpoints MUST use the same pool …
- Distinctive: line 387
Solution: If an endpoint creates/updates data that clients will immediately query via another endpoint, the read endpoint MUST also use the …
- Very specific: line 388
Example: After POST /batches writes to primary, GET /batches/:id must use primary pool for consistency (not replica).
- Distinctive: line 390
Acceptable lag: Only use replica pools for read endpoints when clients can tolerate eventual consistency (e.g., analytics dashboards, list …
- Distinctive: line 396
Test incrementally: write one test, make it pass, write the next test (not all at once)
- Distinctive: line 397
Prefer orthogonality and simplicity in test design - test one thing clearly per test
- Very specific: line 398
Use #[dwctl_test_macros::test] for dwctl unit tests to clone an isolated migrated database.
- Very specific: line 401
Use create_test_user(), create_test_app() helpers from test_utils module
- Distinctive: line 414
Use Vitest with describe/it blocks for unit tests
- Very specific: line 415
Test files colocated with source: Component.tsx → Component.test.tsx
- Distinctive: line 417
Prefer testing user-facing behavior over implementation details
- Distinctive: line 419
Query Scoping Pattern: When tests run in parallel, multiple component instances may exist in the DOM.
- Very specific: line 420
Use const { container } = render(...) to get the component's container
- Very specific: line 421
Use within(container).getByRole(...) instead of screen.getByRole(...)
- Very specific: line 422
Import within from @testing-library/react when using this pattern
- Distinctive: line 431
Use functional components with TypeScript
- Distinctive: line 432
Keep components focused and composable
- Very specific: line 437
Consistency over flashiness: Use existing components from components/ui/ where possible (based on Radix UI primitives)
- Very specific: line 439
Subtle animations only: Never animate without user behavior (no autoplay).
- Very specific: line 441
Color palette: Use the doubleword-* color scheme (neutrals, muted tones) defined in tailwind.config.js
- Very specific: line 443
Loading states: Use subtle spinners (animate-spin) only when needed, prefer skeleton states for content loading
- Distinctive: line 444
Mobile responsiveness: When working on the frontend, always keep mobile responsiveness in mind
- Very specific: line 450
All config lives in config.yaml with DWCTL_ environment variable overrides
- Very specific: line 451
Use double underscores for nested config: DWCTL_AUTH__NATIVE__ENABLED=false
- Distinctive: line 452
Document new config options in the config file with comments
- Distinctive: line 456
Write clear, descriptive commit messages
- Distinctive: line 459
Don't coauthor commits
- Very specific: line 460
Before commiting, run just lint rust, just test rust (if you've changed any rust code), just lint ts, and just test ts (if you've changed …
- Very specific: line 468
Use PostgreSQL LISTEN/NOTIFY for real-time cache invalidation (see sync/ module)
- Distinctive: line 469
Avoid N+1 queries - batch fetch related data when possible
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
Repository Skills
Maintain shared skills in .claude/skills/. Codex discovers the same skills via
.agents/skills -> ../.claude/skills; no per-skill links or inventory are needed.
Public repository confidentiality
This is a public, open-source repository. Use fictional organizations, model aliases and illustrative prices in examples and test fixtures. Never include real customer identities, private aliases, negotiated terms, account identifiers or customer-specific operational details in code, docs, fixtures, commit messages, pull-request text or review comments. Keep private deployment paths, internal runbooks and customer investigations outside this repository. Describe built-in features generically, with configuration that any installation can use. Check the diff and publication text for these details before committing or posting.
Project Overview
The Doubleword Control Layer (dwctl) is a high-performance AI model gateway that provides unified routing, management, and security for inference across multiple model providers. It's the world's fastest AI gateway with 450x less overhead than LiteLLM.
Architecture
The project consists of three main components in a Rust/TypeScript monorepo:
- dwctl (Rust): Core API server handling user/group/model management, authentication, and request routing
- fusillade (Rust): Batch processing system for HTTP requests with retry logic and per-model concurrency control
- dashboard (React/TypeScript): Web frontend for managing the control layer
Request Flow Architecture
The application handles two distinct request flows:
AI Proxy Requests (/ai/v1/*):
- Handled by the
onwardsrouting layer with synchronized cache of valid API keys - Cache updated in real-time via PostgreSQL LISTEN/NOTIFY
onwardsvalidates API key, maps model alias to inference endpoint, forwards request- Optional
outlet/outlet-postgresmiddleware logs request/response data - Credits deducted based on token usage
Management API Requests (/admin/api/v1/*):
- Authentication middleware validates credentials (session cookies, proxy headers, or API keys)
- Request reaches handler which performs authorization based on user roles
- Handlers interact with database through repository pattern
- Changes trigger PostgreSQL NOTIFY events to update
onwardsrouting cache
Database Layer
Uses SQLx with PostgreSQL following the Repository pattern:
Handlers (API) → Repositories (db::handlers) → Models (db::models) → PostgreSQL- Migrations live in
dwctl/migrations/(main schema) andfusillade-arsenal/migrations/(Fusillade).dwctl migrateapplies them;migrations.mode(runlocally,checkin deployments) decides whether the serving process applies them or only verifies compatibility. Every statement that changes the schema lives in a migration file; readdocs/migrations.mdbefore adding one, especially an index builtCONCURRENTLY - New main migrations must use UTC
YYYYMMDDhhmmss_description.sqlversions newer than the target branch's latest migration. Integer versions through 156 are frozen; never add another integer or fill a historical gap. Seedocs/migrations.md; migration tests enforce the numbering rule. - The
underwaycrate (background task queue) ships its own SQLx migrations that create theunderwayschema;just db-setupapplies these too, but they are also applied at runtime viaunderway::run_migrations - Schema uses PostgreSQL LISTEN/NOTIFY for real-time config updates
- Advisory locks for leader election - leader election is used to choose who runs the probe service, and (configurably) the batch daemon
- SQLx performs compile-time SQL validation (requires database connection during compilation)
Development Commands
Initial Setup
# Install prerequisites (macOS). `just check` verifies all of these:
# docker + compose plugin, hurl, psql, createdb, cargo, pnpm
brew install just hurl postgresql pnpm
brew install --cask docker # any Docker runtime works (Docker Desktop, OrbStack, colima)
# cargo comes from rustup, not Homebrew: https://rustup.rs
# Check all dependencies installed
just check
# Start Docker postgres (optimized for testing with fsync disabled)
just db-start
# Setup databases and run migrations
# Creates dwctl and fusillade databases, writes .env files
just db-setupImportant: Rust compilation requires a running PostgreSQL database due to SQLx's compile-time query verification.
Running Services
# Start backend (from project root)
just dev-native
# Start frontend dev server (from dashboard/)
pnpm run devjust dev-native is cargo run plus two things you need locally: it checks
whether Postgres is up before starting a build that would fail
without it, and it relaxes the session cookie for plain HTTP.
DWCTL_AUTH__NATIVE__SESSION__COOKIE_SECURE=false \
DWCTL_AUTH__NATIVE__SESSION__COOKIE_SAME_SITE=lax \
cargo runTesting
# Backend tests via nextest, followed by doctests (requires database)
just test rust
just test rust --watch # Watch mode
just test rust --coverage # With coverage
# Frontend tests
just test ts
just test ts --watch # Watch modeLinting and Formatting
# Lint
just lint rust
just lint ts
just lint ts --fix # Auto-fix issues
# Format
just fmt rust
just fmt ts
# CI pipeline (lint + test + coverage)
just ci rust
just ci tsDatabase Management
# Start/stop test postgres container
just db-start
just db-stop
just db-stop --remove # Also remove container
# Database connection settings (override with env vars)
# DB_HOST (default: localhost)
# DB_PORT (default: 5432)
# DB_USER (default: postgres)
# DB_PASS (default: password)
# Reset database migrations
cd dwctl
sqlx database reset -ySingle Test Execution
# Run specific Rust test
cargo test test_name
# Run specific Hurl test file
hurl --variables-file test.env tests/authenticated/specific-test.hurl
# Watch specific Rust test
cargo watch -x "test test_name"Configuration
The system uses config.yaml with environment variable overrides prefixed with DWCTL_. Nested config can be specified by joining keys with double underscore:
# Example: disable native auth
DWCTL_AUTH__NATIVE__ENABLED=false
# Set secret key
DWCTL_SECRET_KEY="your-secret-key"Important Config Sections
admin_email/admin_password: Initial admin user credentialsauth.native.enabled: Toggle username/password authenticationauth.proxy_header.enabled: Toggle proxy header authentication for SSOdatabase.type:external(default) orembedded(requires embedded-db feature)batches.enabled: Enable OpenAI-compatible batch processing APIbackground_services.batch_daemon.enabled: When batch daemon runs (leader,always,never)background_services.leader_election.enabled: Enable leader election for multi-instance deploymentsbackground_services.onwards_sync.enabled: Enable onwards config sync (syncs DB changes to AI proxy)background_services.probe_scheduler.enabled: Enable health probe scheduler
Demo Mode
The dashboard supports a demo mode for showcasing features without a live backend. Demo mode uses Mock Service Worker (MSW) to intercept API requests and return mock data.
Enabling Demo Mode
Demo mode can be enabled in three ways:
- URL parameter: Add
?flags=demoto the URL (e.g.,https://control-layer.pages.dev?flags=demo) - Settings page: Toggle demo mode in the Settings UI (persisted to localStorage)
- localStorage: Set
app-settingskey withfeatures.demo: true
How Demo Mode Works
- MSW Service Worker: When demo mode is enabled, the app initializes a service worker that intercepts fetch requests
- Mock handlers: Located in
dashboard/src/api/control-layer/mocks/handlers.ts, these handlers return static JSON data - Demo data: Mock data includes users, groups, models, endpoints, API keys, requests, batches, and transactions
- State persistence: Demo state (like user-group associations) is stored in localStorage and survives page refreshes
- DemoOnlyRoute: Some features may be wrapped in
DemoOnlyRoutecomponent to only show in demo mode
Demo Data Files
Mock data is stored in dashboard/src/api/control-layer/mocks/:
users.json,groups.json,endpoints.json,models.jsonapi-keys.json,transactions.jsonfiles.json,batches.json,batch-requests.json,file-requests.jsondemoState.ts: Manages stateful operations (adding/removing users from groups, etc.)
Feature Flags
Demo mode is part of the broader feature flag system. Available flags:
demo: Enable demo mode with mock datause_billing: Enable billing and cost management features
Flags can be combined: ?flags=demo,use_billing
Code Architecture
dwctl Structure
api/handlers/: HTTP request handlers for each resource (users, groups, deployments, etc.)api/models/: API request/response modelsauth/: Authentication middleware and permission checkingdb/handlers/: Repository implementations following Repository patterndb/models/: Database record structures matching table schemassync/: Real-time sync between database and onwards routing layer via LISTEN/NOTIFYrequest_logging/: Request/response logging powered by outlet/outlet-postgres
Key External Dependencies
onwards: High-performance routing layer for AI proxy requestsoutlet/outlet-postgres: Request/response logging middlewarefusillade: Batch processing system for async job execution
User Roles
The system uses additive RBAC where users can have multiple roles:
- StandardUser (required, cannot be removed): Base role enabling authentication, profile access, model usage, API key creation
- PlatformManager: Administrative access to most functionality (users, groups, models, settings) but NOT request logs
- RequestViewer: Read-only access to request logs and analytics
Role changes take effect immediately except for native auth JWT-based API access (requires re-login).
Production Deployment
- Use production-grade PostgreSQL database via
DATABASE_URLenvironment variable - Set secure random
DWCTL_SECRET_KEY(e.g.,openssl rand -base64 32) - Configure CORS for your frontend in
auth.security.cors.allowed_origins - Review user registration settings (
auth.native.allow_registration)
CI/CD
- GitHub Actions runs
just ci rustandjust ci ts - Rust CI requires Rust 1.88+ for SQLx compatibility
- SQLx prepare checks ensure offline query metadata is up to date:
cargo sqlx prepare --check --workspace - Frontend CI checks TypeScript compilation, linting, tests with coverage, and production build
Docker Build
Multi-platform builds use Docker Buildx:
# Build with specific tags
TAGS=latest PLATFORMS=linux/amd64 docker buildx bake --load
# Production multi-platform build
TAGS=v1.0.0 PLATFORMS=linux/amd64,linux/arm64 docker buildx bake --pushTesting Philosophy
- For Rust test changes, use the test-efficiency skill for isolated template databases, permission matrices, and timing slow tests.
- Backend:
cargo testprovides comprehensive API integration tests usingaxum-testto spin up the full API server and make ~real HTTP requests - Frontend:
pnpm testindashboard/runs Vitest unit tests for React components and utilities. Always scope queries usingwithin(container)instead ofscreento avoid querying multiple test instances in the DOM (see TypeScript/React Testing section for details) - Hurl and Playwright E2E tests exist but are currently minimal (not the main test suite)
- All Rust tests require a running PostgreSQL database due to SQLx integration
- Test cleanup scripts in
scripts/(drop-test-users.sh, drop-test-groups.sh) for Hurl tests - Tests run in parallel where possible for performance
Code Conventions & Best Practices
Rust
Import Style:
- Use unqualified names for imports, and put identifier imports at the top of the file. DON'T use fully qualified names unless absolutely necessary, to prevent name clashes.
- Organize imports in groups: std → external crates → internal modules
- Example:
use crate::errors::{Error, Result};then useResultdirectly, noterrors::Result
Error Handling:
- Return
Result<T, Error>from handlers (automatically converts to HTTP responses) - Use specific error variants:
Error::NotFound,Error::BadRequest,Error::InsufficientPermissions - Database errors wrap
DbErrorwhich auto-converts to appropriate HTTP status codes - Provide descriptive error messages focused on what went wrong, not implementation details
Background Error Logging:
- For failures off the request path (background tasks, pollers, daemon loops, sync jobs), use the
background_error!macro instead of a baretracing::error!/warn!. It emits the log and thedwctl_background_errors_totalmetric together, so the failure shows up on dashboards and alerts instead of being invisible. - Example:
crate::background_error!(component::WEBHOOK_DISPATCH, "delivery_create", Critical, error = %e, "Failed to create webhook delivery"); - Severity tiers:
Critical(logs at error, pages),Error(logs at error, no page),Warning(logs at warn, no page).componentandreasonmust be&'static strliterals. - Foreground handlers that already return a 5xx do NOT need this - those are counted by
dwctl_http_requests_total{status}.
API Handlers:
- Add
#[tracing::instrument(skip_all)]to all handler functions for observability - Use
#[utoipa::path(...)]macro for OpenAPI documentation - Follow RESTful conventions: list (GET /resources), get (GET /resources/:id), create (POST /resources), update (PUT/PATCH /resources/:id), delete (DELETE /resources/:id)
Database Access:
- Always use the Repository pattern via
db::handlers - Create a transaction, instantiate repository, perform operations, commit
- Keep repository instantiation scoped in blocks to avoid borrow checker issues
- Repositories are thin wrappers around transactions using
&mut Transaction
Pool Management:
- Production: Use
DbPoolsfromsqlx-pool-routercrate- Provides primary pool via
.write()for writes (transactions, mutations) - Provides replica pool via
.read()for read-only queries (when replica configured) - Falls back to primary pool if no replica configured
- Provides primary pool via
- Tests: Use
TestDbPoolsfromsqlx-pool-routercrate- Automatically creates a read-only replica from the test pool
- Enforces proper read/write separation during testing
- Tests use
#[sqlx::test]which provides aPgPool- this is automatically converted toTestDbPoolsinsetup_database()
- Handler Pattern: All handlers are generic over
PoolProvidertrait- Function signature:
async fn handler<P: PoolProvider>(State(state): State<AppState<P>>, ...) -> Result<...> - Write operations:
state.db.write().begin()for transactions - Read operations:
state.db.read().acquire()for read-only queries - This pattern ensures zero runtime overhead (monomorphization) while allowing production and test code to use different pool types
- Function signature:
- Never directly use
PgPoolin handler signatures - always use genericP: PoolProvider
Read-After-Write Consistency:
When reading data immediately after writing it (e.g., verifying a file exists right after upload, or checking a batch right after creation), always use the primary pool to avoid replication lag issues:
- Problem: Replica pools may lag behind the primary database by milliseconds to seconds. Reading from a replica immediately after a write to the primary can return stale data (e.g., "not found" errors for just-created records).
- Solution: Use
.write()pool for both the write AND the subsequent read in read-after-write scenarios. - Examples:
- File verification during batch creation: Use
get_file_from_primary_pool()instead ofget_file()(seebatches.rs:187-194) - Reading just-uploaded file metadata: Use primary pool (see
files.rs:567-570)
- File verification during batch creation: Use
- General rule: If you write data and immediately need to read it within the same request handler, use the primary pool for both operations.
- When to use replica: Only use
.read()pool for queries that don't depend on recent writes in the same request, or for read-only endpoints querying stable data.
CRITICAL - Inter-Endpoint Consistency:
When clients make sequential API calls where one endpoint writes data and another endpoint reads it, both endpoints MUST use the same pool (primary) to avoid replication lag issues:
- Problem: Client creates a resource via
POST /batches(writes to primary), then immediately callsGET /batches/:id(reads from replica) → 404 Not Found due to replication lag. - Solution: If an endpoint creates/updates data that clients will immediately query via another endpoint, the read endpoint MUST also use the primary pool.
- Example: After
POST /batcheswrites to primary,GET /batches/:idmust use primary pool for consistency (not replica). - Design principle: Choose pool routing based on typical client workflows, not just the individual endpoint's operation. If clients will chain requests (create → retrieve), both endpoints need primary pool.
- Acceptable lag: Only use replica pools for read endpoints when clients can tolerate eventual consistency (e.g., analytics dashboards, list views with "refresh" buttons, non-critical data).
Note: Even databases claiming "zero replication lag" (like Neon) can exhibit lag under load, especially with high concurrency (observed 100ms-1000ms+ lag during stress testing with 50-100 parallel requests).
Testing:
- Test incrementally: write one test, make it pass, write the next test (not all at once)
- Prefer orthogonality and simplicity in test design - test one thing clearly per test
- Use
#[dwctl_test_macros::test]for dwctl unit tests to clone an isolated migrated database. Retain#[sqlx::test]for migration/schema-startup tests and other crates; see the test-efficiency skill for choosing the required initial schema state. - Use
create_test_user(),create_test_app()helpers fromtest_utilsmodule - Integration tests use
axum_test::TestServerto make real HTTP requests
Documentation:
- Module-level documentation (
//!) explains purpose and architecture - Public APIs have doc comments (
///) with examples where helpful - Focus documentation on "why" and non-obvious behavior, not just "what"
TypeScript/React
Testing:
- Use Vitest with describe/it blocks for unit tests
- Test files colocated with source:
Component.tsx→Component.test.tsx - Focus on testing utility functions, context behavior, and complex component logic
- Prefer testing user-facing behavior over implementation details
- To select components, always use aria labels or roles rather than class names or tag names, to both improve accessibility and reduce brittleness
- Query Scoping Pattern: When tests run in parallel, multiple component instances may exist in the DOM. Always scope queries to avoid "Found multiple elements" errors:
- Use
const { container } = render(...)to get the component's container - Use
within(container).getByRole(...)instead ofscreen.getByRole(...) - Import
withinfrom@testing-library/reactwhen using this pattern - Exception - Portal-rendered elements: For elements that render outside the component container (modals, dialogs, dropdown menus, popovers), use
screeninstead ofwithin(container):- Modals and dialogs:
screen.getByRole("dialog") - Dropdown menus:
screen.getByRole("menu")andscreen.getByRole("menuitem") - Popover content:
screen.getByPlaceholderText(...)for inputs inside popovers - These elements use portals and render at the document root, so they won't be found within the component container
- Modals and dialogs:
- Use
Component Structure:
- Use functional components with TypeScript
- Keep components focused and composable
- Extract complex logic to custom hooks or utility functions
Design Principles:
- Consistency over flashiness: Use existing components from
components/ui/where possible (based on Radix UI primitives) - Avoid bright colors for emphasis: Rely on clear UX and layout instead of vibrant colors
- Subtle animations only: Never animate without user behavior (no autoplay); when animating, keep it subtle (e.g.,
transition-colors,transition-transform) - Pragmatic DRY: A little copying is better than slavish dedication to DRY - prefer clarity over abstraction for smaller chunks
- Color palette: Use the
doubleword-*color scheme (neutrals, muted tones) defined intailwind.config.js - Spacing & typography: Consistent use of Tailwind utilities; Space Grotesk font family
- Loading states: Use subtle spinners (
animate-spin) only when needed, prefer skeleton states for content loading - Mobile responsiveness: When working on the frontend, always keep mobile responsiveness in mind
General
Configuration:
- All config lives in
config.yamlwithDWCTL_environment variable overrides - Use double underscores for nested config:
DWCTL_AUTH__NATIVE__ENABLED=false - Document new config options in the config file with comments
Git Commits:
- Write clear, descriptive commit messages
- Focus on "why" not "what" (the diff shows what changed)
- Reference issue numbers when applicable
- Don't coauthor commits
- Before commiting, run
just lint rust,just test rust(if you've changed any rust code),just lint ts, andjust test ts(if you've changed any ts code) to ensure code quality. NEVER PUSH ANY CODE THAT DOESN'T PASS ALL LINTS AND TESTS.
Performance:
- Database queries should use appropriate indexes (check migrations)
- Use PostgreSQL LISTEN/NOTIFY for real-time cache invalidation (see
sync/module) - Avoid N+1 queries - batch fetch related data when possible
- To run sqlx migrations, navigate to the appropriate directory (dwctl, or fusillade/) and run
cargo sqlx migrate run. NEVER try to run sqlx migrate run --source ... --database-url from the root. - Instead of calling 'tokio::time::sleep' in tests, try to poll until the condition you're waiting for becomes true. Assert both against the state its in at first, then the state it changes to. Then the tests 1. Aren't slow - because they can change state immediately, and 2. test against the whole flow - both before and after the condition becomes true
In this repository5 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .github/copilot-instructions.mdCopilot instructions
- rust-test-efficiencySkill
- sqlx-index-migrationsSkill
- sqlx-queriesSkill
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

