kiro-rails
CLAUDE.md9 starsChanged 4 months agoMITView on GitHub
Project-specific values that override the generic defaults in the managed steering files. This is the ONLY steering file you should edit. All other steering files are managed by kiro-rails and will be overwritten on upgrade.
- Reads credentials
- Installs packages
- Commits and pushes
In this repository8 other files it gives its agents.
- auth-implementationSkill
- incident-responseSkill
- kiro-railsSkill
- review-guideSkill
- spec-archiveSkill
- spec-implementSkill
- spec-proposeSkill
- spec-verifySkill
The file
Show its rules and text
We split this CLAUDE.md into the rules it gives an agent
150 rules. About 0 are common, 104 are distinctive, and 46 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈104
Distinctive · estimate
This author's own choices. Worth a look.
46
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Distinctive: line 18
Delete sections you don't need
- Distinctive: line 44
Use JSONB over JSON for queryable structured data
- Distinctive: line 147
When any rule below conflicts with convenience, the rule wins.
- Very specific: line 164
Never change code outside the scope of the current task: No drive-by refactors. → change-discipline.md
- Very specific: line 168
Never modify an already-applied database migration: Create a new one. → database-conventions.md
- Distinctive: line 210
Schema first, code second: - before implementing any API endpoint, define the response type (Pydantic model, TypeScript interface, or Zod …
- Very specific: line 211
Never assume response shape: - don't write resp.data.message without verifying the backend actually returns {data: {message: ...}}.
- Very specific: line 214
Axios/fetch unwrapping: - document whether your API client unwraps responses.
- Distinctive: line 218
When connecting frontend to backend:
- Very specific: line 220
Log the actual response: - before writing rendering code, console.log or inspect the actual API response shape.
- Very specific: line 221
Handle nested envelopes: - if the API returns {data: {project: {...}}}, don't type the result as Project.
- Very specific: line 222
Array vs object: - verify: does the endpoint return [...] or {items: [...]} or {results: [...]}?
- Distinctive: line 223
Null vs undefined vs missing: - document which fields can be null, which are optional (may be missing from response), and which are always …
- Very specific: line 227
Structured error responses: - all API errors return a consistent shape: {error: string, detail?: string, code?: string}.
- Distinctive: line 228
Never expose internals: - no stack traces, file paths, SQL queries, or internal service names in error responses.
- Distinctive: line 234
Generous in dev: - start with 100+/min in development.
- Distinctive: line 253
Read any file in the repository
- Distinctive: line 254
Run linting, type checking, and tests
- Distinctive: line 256
Update documentation and changelog
- Very specific: line 266
Commit secrets, .env files, or credentials
- Distinctive: line 270
Remove or weaken existing tests (unless explicitly asked)
- Distinctive: line 280
Follow existing patterns from similar components: - before creating a new service, route, or component, find an existing one that does …
- Distinctive: line 281
Don't refactor while implementing: - if you notice code that could be improved but it's outside the current task, note it for later.
- Distinctive: line 282
When in doubt, be consistent: - if the codebase uses one pattern and the style guide says another, follow the codebase.
- Distinctive: line 291
No extra improvements: - do not refactor, optimize, or "clean up" code that is not part of the current task, even if it looks wrong.
- Distinctive: line 292
No unsolicited dependency updates: - don't upgrade packages, change configs, or modify tooling unless the task requires it.
- Distinctive: line 293
Scope creep is a bug: - if implementation reveals a needed change outside the current scope, document it as a separate task.
- Distinctive: line 294
Review your diff before committing: - every line in the diff should relate to the task.
- Distinctive: line 302
Two-fix limit: - if your second fix attempt for the same issue introduces yet another failure, STOP immediately.
- Distinctive: line 303
Map all paths: - before attempting fix #3, read the FULL integration context: all related code paths, all consumers, all edge cases.
- Distinctive: line 305
Document what you tried: - before the third attempt, write down: what fix #1 did, why it failed, what fix #2 did, why it failed.
- Distinctive: line 306
Ask for help: - if you cannot identify the root cause after mapping all paths, say so.
- Distinctive: line 314
Review all values: - default values, field names, identifiers, paths, URLs, error messages.
- Very specific: line 315
Check return types: - if you copied a function that returns isConnected: true, ask: is that the correct default HERE?
- Distinctive: line 316
Check message objects: - if constructing a new object based on a similar one, verify ALL required fields are included.
- Very specific: line 317
Check config references: - if the copied code references a config file, manifest, or package.json entry, verify that entry exists for the …
- Very specific: line 325
npm
filesarray: - after creating new directories or entry points, verify they're listed in package.json files. - Very specific: line 326
pyproject.toml
include: - after creating new Python modules, verify they're included in the package build. - Very specific: line 327
bin entries: - after creating CLI entry points, verify the bin field in package.json points to the correct file.
- Very specific: line 328
After adding any import: - verify the dependency is declared in the manifest (package.json or pyproject.toml).
- Distinctive: line 332
Every new dependency must have a functional justification tied to a concrete requirement, test need, or architectural concern.
- Distinctive: line 333
Do not add libraries speculatively.
- Very specific: line 334
Keep dependency manifests (pyproject.toml, package.json) lean and auditable.
- Distinctive: line 337
Check for overlap: - before adding a new package, verify no existing dependency already provides the same functionality.
- Distinctive: line 339
Pin versions explicitly: - never use floating version ranges in production dependencies.
- Distinctive: line 340
Audit before adding: - check the package's maintenance status, download count, last update date, and known vulnerabilities before adding it …
- Distinctive: line 346
No god classes, hidden coupling, or ambiguous ownership boundaries.
- Distinctive: line 347
Write code as if a different engineer will inherit the project under delivery pressure.
- Very specific: line 351
All commit-cadence, checkpoint, message-format, and branch rules live in one place: see git-and-focus-discipline.md (Part 3 - Commit …
- Very specific: line 360
When it exceeds 500 lines, roll over: rename to docs/changelogs/CHANGELOG.YYYY-MM-DD.md and start fresh
- Distinctive: line 361
Do NOT copy, summarize, or selectively preserve entries - just roll it over clean
- Distinctive: line 362
Write consolidated changelog entries grouped by feature
- Distinctive: line 366
Do not clutter the repository root with ad hoc notes, temporary design files, scratchpads, or undocumented artifacts.
- Very specific: line 367
All project documentation lives in docs/ (except README.md at root).
- Distinctive: line 371
No credentials in source control
- Distinctive: line 372
Use .env.example for configuration templates
- Distinctive: line 373
Run secret-exposure review before commits
- Distinctive: line 397
On attempt #2+ of any fix: Do NOT wait until the fix is done.
- Distinctive: line 426
Add new categories as they emerge.
- Distinctive: line 433
Test gaps: - add regression tests or smoke tests
- Distinctive: line 434
Process gaps: - update documentation or checklists
- Distinctive: line 438
When a pattern appears 3+ times:
- Distinctive: line 439
Write a concrete steering rule in the appropriate steering file
- Distinctive: line 440
Add a regression test if applicable
- Distinctive: line 442
Consider automation (hooks, linters, CI checks)
- Distinctive: line 453
Every piece of code must be commented so that a follow-on developer - or a coding agent picking up the project cold - can understand all …
- Distinctive: line 457
During code writing - NON-NEGOTIABLE: - every file you create or modify MUST have comments meeting this standard BEFORE you move to the …
- Distinctive: line 468
All comments must be written so that both human developers and AI coding agents can fully reconstruct the intent, constraints, and …
- Distinctive: line 469
No shorthand or project-internal jargon without explanation
- Distinctive: line 472
When a method exists because of a non-obvious requirement, say so
- Distinctive: line 476
Every file must start with a docstring/JSDoc block that includes:
- Distinctive: line 484
Every class - public, internal, or private - must have a docstring/JSDoc that includes:
- Distinctive: line 492
Every class property, instance variable, and TypeScript class member must be documented:
- Very specific: line 493
Python: use inline comments or Pydantic Field(description=...) for model fields
- Very specific: line 494
TypeScript: use JSDoc @property or inline comments above the declaration
- Distinctive: line 500
Every method and function must have a docstring/JSDoc, regardless of visibility (public, protected, private, internal helper):
- Distinctive: line 520
Do NOT use for:
- Distinctive: line 536
When code depends on or mirrors code in another module, add a cross-reference:
- Distinctive: line 543
Document why each value exists, not just what it is.
- Distinctive: line 554
Test your comments compile: - if you write a comment containing special characters, verify the file still parses.
- Very specific: line 649
If feature A needs something from feature B, it goes through B's public index.ts export, or it belongs in shared/.
- Distinctive: line 661
If a "shared" item accumulates feature-specific parameters or branches, it should be split back into feature-scoped copies
- Distinctive: line 667
Backend-only project: remove the frontend section entirely - don't leave dead structure
- Distinctive: line 668
Frontend-only project: remove the backend section entirely
- Very specific: line 670
Monorepo: use packages/ with each package following the same internal layer/feature structure
- Distinctive: line 672
Keep the principles (layer-first backend, feature-first frontend, graduation policy) even when the directory names change
- Distinctive: line 694
One database per project: - no sharing databases across projects
- Very specific: line 696
All connection strings in
.env: - never hardcode host, port, database name, or credentials - Very specific: line 704
Never prompt for passwords: - always read from .env
- Distinctive: line 724
Never modify already-applied migrations: - create new ones to alter schema
- Distinctive: line 730
No N+1 queries: - use eager loading, joins, or batch queries
- Distinctive: line 732
Parameterized queries only: - never string interpolation for SQL
- Distinctive: line 735
Don't fight the ORM: - if the ORM handles it, don't drop to raw SQL unless there's a measurable performance reason
- Distinctive: line 740
Keep transactions short: - don't hold transactions open across network calls or user interactions
- Distinctive: line 741
Handle rollback: - every transaction must have a clear rollback path on failure
- Distinctive: line 745
Use connection pooling in production - never open a new connection per request
- Very specific: line 753
Timestamps: - use timezone.utc for all database timestamps.
- Distinctive: line 754
Large result sets: - always paginate. Never return unbounded query results.
- Distinctive: line 762
Use advisory locks for distributed coordination
- Very specific: line 764
Use EXPLAIN ANALYZE to verify query plans for new endpoints
- Distinctive: line 774
Use for development, testing, and single-user applications
- Very specific: line 789
All documentation must be placed in the appropriate docs/ subdirectory based on its purpose:
- Very specific: line 837
DO NOT: Create documentation files in docs/ root, mix document types, or create new subdirectories without documenting their purpose.
- Very specific: line 839
DO: Place ADRs in docs/decisions/ with proper numbering, update this list if adding a new documentation category.
- Distinctive: line 848
Before substantive implementation begins, establish a structured plan:
- Distinctive: line 853
If implementation realities require a plan change: update the spec/roadmap, document the reason, record in changelog.
- Distinctive: line 861
UX acceptance criteria for any user-facing feature: - not just "show data" but how it's shown: hierarchy, spacing, interaction placement, …
- Distinctive: line 872
Every task has a concrete deliverable (file path, test name, endpoint)
- Distinctive: line 875
No vague tasks like "implement the feature" - break down to individual functions/components
- Very specific: line 881
Every ADR must be linked in docs/roadmap/roadmap.md at the sprint/milestone row where the decision was made.
- Distinctive: line 885
Only link ADRs that actually exist on disk
- Very specific: line 891
All docs live in docs/ directory (except README.md)
- Very specific: line 910
Use
mutateAsync+awaitfor dependent operations: - if operation B depends on operation A completing, use await mutateAsync() for A, … - Distinctive: line 912
Auth token timing: - never fire API requests that require auth before confirming the token is available.
- Distinctive: line 913
Sequence mutations that share state: - if two mutations modify the same resource, await the first before firing the second.
- Very specific: line 921
Never silently ignore errors: - every error must be raised, logged, or explicitly handled.
- Distinctive: line 922
Use specific error types: - not catch-all handlers. Each error type should clearly indicate what went wrong and where.
- Distinctive: line 925
Fix root causes, not symptoms: - if an error keeps occurring, fix the underlying issue rather than adding retry/fallback logic around it.
- Distinctive: line 926
External service calls: retry with backoff: - use exponential backoff for transient failures.
- Distinctive: line 927
API endpoints return proper HTTP status codes: - never return 200 for errors.
- Distinctive: line 928
Frontend errors: catch at boundaries: - use error boundaries (React) or equivalent.
- Distinctive: line 937
Pagination for all list endpoints: - never return unbounded result sets.
- Distinctive: line 938
No N+1 queries: - use eager loading, joins, or batch queries.
- Distinctive: line 940
Database indexes: - every column used in WHERE, JOIN, or ORDER BY clauses in frequent queries must have an index.
- Distinctive: line 942
Timeouts on all external calls: - every HTTP request, database query, and external service call must have an explicit timeout.
- Very specific: line 951
Use the application's dialog/modal system: - every dialog must render through the project's themed component (e.g., ConfirmDialog, …
- Distinctive: line 957
ZERO BLACKBOXES: Every pipeline, background process, and async workflow must be observable from day one.
- Distinctive: line 961
Structured logging: - every operation logs: what started, what inputs it received, what it produced, how long it took, whether it succeeded …
- Distinctive: line 962
Correlation IDs: - every pipeline run or background task gets a unique ID that flows through all log entries and database records.
- Very specific: line 963
State transitions: - every job/task has explicit states (PENDING → RUNNING → SUCCESS/FAILED/RETRYING) stored in the database.
- Distinctive: line 965
Error context: - when something fails, log the full context: what was being processed, what step failed, what the input was.
- Distinctive: line 966
Resumability: - if a pipeline crashes mid-run, it must be resumable from where it stopped via database checkpointing.
- Distinctive: line 967
Discuss architecture first: - before implementing any pipeline or background process, discuss the architecture: data flow, failure modes, …
- Distinctive: line 983
ALL hooks at the top of the component: - before any early returns, conditional logic, or variable declarations that depend on props.
- Very specific: line 985
Verify required providers exist: - before using useQuery, useParams, useNavigate, or any context hook, confirm the component is rendered …
- Very specific: line 986
Use
mutateAsync+awaitfor dependent operations: - never fire-and-forget with mutate() when a subsequent operation depends on the … - Distinctive: line 990
Document event capture/propagation: - when adding event handlers, note what events are captured, what propagation is stopped, and what …
- Distinctive: line 993
Escape key layering: - when multiple components listen for Escape (modals, drawers, dropdowns), implement a stack-based system.
- Very specific: line 994
Outside-click handlers + portals: - if a component renders via a portal (e.g., dropdown menu on document.body), the outside-click handler …
- Distinctive: line 999
Header/body alignment: - use the SAME layout strategy (grid or flex with identical gaps/gutters) for both header and body rows.
- Very specific: line 1000
Popover/dropdown positioning: - verify the positioning direction matches the trigger's screen location.
- Very specific: line 1005
When setting
staleTime: - document what invalidation scenarios exist. - Very specific: line 1006
After mutations, always invalidate: - call queryClient.invalidateQueries() for affected keys.
- Very specific: line 1007
Error + retry on 401: - if a query gets a 401, don't let staleTime prevent retry.
- Very specific: line 1008
Optimistic updates need rollback: - if using optimistic updates, always implement the onError rollback.
- Distinctive: line 1012
When extracting a shared component from an existing implementation, or when a new consumer adopts a shared component:
- Very specific: line 1014
Prop parity audit: - before declaring a consumer "done", list every prop the original passes and verify the new consumer passes equivalent …
- Distinctive: line 1016
Test with real data: - never verify a component extraction with empty/default data only.
- Very specific: line 1017
Layout wrapper parity: - verify the new consumer uses the same layout container (full-width, max-width, padding) as the original.
- Distinctive: line 1040
Before marking any UI component or feature task as done, verify:
≈ “Common” is an estimate: at least 1 in 5 of the 26,980 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
Project Rules (generated from kiro-rails steering by scripts/export-to-claude.sh)
Source of truth: .kiro/steering/*.md - edit there and re-run; do not edit this file.
Capability sync ledger (what maps to what, and fidelity): docs/references/kiro-claude-sync-ledger.md
inclusion: always
Project Overrides
Project-specific values that override the generic defaults in the managed steering files. This is the ONLY steering file you should edit. All other steering files are managed by kiro-rails and will be overwritten on upgrade.
<!-- ──────────────────────────────────────────────
INSTRUCTIONS:
- Uncomment and fill in sections relevant to your project
- Delete sections you don't need
- The installer can populate these values interactively
────────────────────────────────────────────── -->Tech Stack
<!-- Uncomment and set your stack:
- **Backend**: Python 3.12+ with FastAPI
- **Frontend**: TypeScript with React + Vite
- **Backend Dependencies**: uv with pyproject.toml
- **Frontend Dependencies**: npm with package.json
-->Dev Server Ports
<!-- Uncomment and set your ports:
- Backend: FastAPI + uvicorn on port 8000
- Frontend: Vite dev server on port 5173
-->Database Engine
<!-- Uncomment your engine and remove the others:
### PostgreSQL
- Host: localhost, Port: 5432
- Use JSONB over JSON for queryable structured data
- Enable pg_stat_statements for query monitoring
### MySQL
- Host: localhost, Port: 3306
- charset=utf8mb4, collation=utf8mb4_unicode_ci
- Enable strict mode (STRICT_TRANS_TABLES)
### SQLite
- Path: ./data/app.db
- Enable WAL mode for concurrent access
-->Migration Tool
<!-- Uncomment your migration tool:
- **Alembic** (SQLAlchemy) - migrations run with admin credentials via env.py
- **Prisma Migrate** - schema.prisma is the source of truth
- **Django Migrations** - manage.py migrate with admin DB URL
- **Knex** - knexfile.js reads from .env
-->Project-Specific Rules
- NEVER mention context exhaustion, context depth, or remaining context percentage. The user can see the figure. Do not warn, suggest stopping, or recommend "fresh sessions." Just keep working until told to stop or until you literally cannot produce output. No exceptions.
<!-- Add any rules specific to your project:
- Example: All API responses must include a `request_id` field
- Example: Feature flags are managed via LaunchDarkly, not .env
- Example: Use Celery for all background tasks
-->Domain Constants
<!-- List your project's domain-specific constant categories:
- Example: Order statuses (PENDING, CONFIRMED, SHIPPED, DELIVERED, CANCELLED)
- Example: User roles (ADMIN, MANAGER, MEMBER, VIEWER)
- Example: Subscription tiers (FREE, PRO, ENTERPRISE)
-->Code Style Overrides
<!-- Add project-specific code style rules:
- Example: Use datetime.now(timezone.utc) - never datetime.utcnow()
- Example: All service classes must inherit from BaseService
- Example: Use structlog for all logging, not stdlib logging
-->Design Tokens (UX Rubric Thresholds)
<!-- These values configure the thresholds in .kiro/steering/ux-console-idiom.md.
The rubric checks are fixed; these numbers are team-configurable.
Uncomment and set for your design system:
### Typography (D family)
- Body text size range: 13–14px
- Allowed font weights: 400, 600 (max 2)
- Body line-height range: 1.4–1.6
### Surfaces (S family)
- Content primacy: ≥60% of viewport height for primary content
### Consistency (K family)
- Max distinct border-radius values: 2
- Spacing scale base: 4px increments
- Icon style: outline (not mixed)
- Color palette: [list your token names or hex values]
### Save Model (V family)
- Save pattern: explicit (one of: explicit-button | auto-save | inline-confirm)
### Tables (T family)
- Max unbounded list items before pagination: 50
### Feedback timing
- Save confirmation deadline: 300ms
- Long operation progress threshold: 2s
### Date format
- Consistent date format: YYYY-MM-DD (or your team's choice)
-->Environment and Tooling
<!-- Uncomment and set your environment:
- Virtual environment: .venv in project root
- Package manager: uv (Python), npm (frontend)
- Linter: ruff (Python), eslint (frontend)
- Formatter: ruff format (Python), prettier (frontend)
-->inclusion: always description: The hard stops - what an agent must never do autonomously. The shortest, sharpest file in the steering set. Read it first.
Agent Boundaries
The hardest constraints, stated plainly. Everything here is elaborated elsewhere; this file exists so the rules are impossible to miss and impossible to "load past." When any rule below conflicts with convenience, the rule wins.
Never (without explicit user confirmation)
- Never commit directly to
main, force-push it, or rebase it. Every change goes on its own branch. →git-and-focus-discipline.md - Never end a session or turn with uncommitted work on a branch. Commit (feature
or
checkpoint:) or stash before the context boundary. →git-and-focus-discipline.md - Never resolve conflicts with
git checkout --ours ./--theirs .or any blanket strategy. Per-file only. →git-and-focus-discipline.md - Never modify a test to make it pass. Fix the implementation. A failing test is
information, not an obstacle. →
testing-standards.md - Never delete files, config, or
.env, change CI/CD, or alter shared infrastructure without explicit confirmation. →change-discipline.md - Never add, remove, or upgrade a dependency outside the task's stated need, and
never via
pip/requirements.txt(useuv). →change-discipline.md - Never commit secrets, credentials, or
.envcontents. →change-discipline.md - Never change code outside the scope of the current task. No drive-by refactors.
→
change-discipline.md - Never reach into another repository or kill a process you did not spawn.
→
session-isolation.md - Never modify an already-applied database migration. Create a new one.
→
database-conventions.md
Always
- Always branch first, and check for an existing branch in the area before
creating one (
scripts/branch-check.sh). →git-and-focus-discipline.md - Always checkpoint before a multi-file or multi-session task
(
git commit -m "checkpoint: before <task>"). →git-and-focus-discipline.md - Always commit at every meaningful checkpoint (compiles + affected tests pass +
lint clean). Do not let a passing state sit uncommitted. →
git-and-focus-discipline.md - Always merge-and-delete the branch as one motion when work lands on main.
→
git-and-focus-discipline.md - Always run a variant search after a bug fix. The reported instance is rarely
the only one. →
git-and-focus-discipline.md - Always stop and ask on a genuine ambiguity or conflict. Two minutes of
clarification beats twenty hours of rebuilding; a confident wrong guess is the
expensive failure mode. →
git-and-focus-discipline.md
When a new request arrives mid-task
File it to docs/backlog/INBOX.md and finish the current task first. Divert only
when the user explicitly orders it. → git-and-focus-discipline.md
inclusion: fileMatch fileMatchPattern: ["/api/", "/routes/", "/services//.ts", "/services//.py", "/schemas/", "**/hooks/use*.ts"]
API Contract Discipline
Rules for API boundary code. Loaded when working on routes, services, schemas, or data-fetching hooks.
Contract-First Development - MANDATORY
Define the response shape BEFORE implementing either side.
Rules
- Schema first, code second - before implementing any API endpoint, define the response type (Pydantic model, TypeScript interface, or Zod schema). Both frontend and backend implement against this contract.
- Never assume response shape - don't write
resp.data.messagewithout verifying the backend actually returns{data: {message: ...}}. Check the schema or test the endpoint. - Envelope consistency - decide once per project: do list endpoints return raw arrays or
{items: [...], total: N}? Document it. Never mix approaches. - Type the full response - including HTTP status, headers, and error shapes. Don't just type the happy path.
- Axios/fetch unwrapping - document whether your API client unwraps responses. If using Axios,
resp.datais the body. If your client auto-unwraps, the hook gets the body directly. Never guess.
Response Shape Verification - MANDATORY
When connecting frontend to backend:
- Log the actual response - before writing rendering code,
console.logor inspect the actual API response shape. Don't assume from the backend code. - Handle nested envelopes - if the API returns
{data: {project: {...}}}, don't type the result asProject. Type it as{data: {project: Project}}and extract. - Array vs object - verify: does the endpoint return
[...]or{items: [...]}or{results: [...]}? This is the #1 source of frontend crashes. - Null vs undefined vs missing - document which fields can be null, which are optional (may be missing from response), and which are always present.
Error Response Contract - MANDATORY
- Structured error responses - all API errors return a consistent shape:
{error: string, detail?: string, code?: string}. Never return raw exception messages. - Never expose internals - no stack traces, file paths, SQL queries, or internal service names in error responses. Use generic messages with correlation IDs for debugging.
- HTTP status codes are semantic - 400 for validation, 401 for auth, 403 for authorization, 404 for not found, 409 for conflict, 422 for unprocessable, 500 for server errors. Never return 200 with an error body.
Rate Limiting - GUIDANCE
- Per-endpoint, not global - rate limits should be scoped to specific endpoints, not applied as global middleware that blocks everything.
- Generous in dev - start with 100+/min in development. Tighten for production based on actual usage patterns.
- Return 429 with Retry-After - include the
Retry-Afterheader so clients know when to retry.
inclusion: always
Change Discipline
Rules governing what you can change, how changes must be scoped, dependency management, and commit hygiene.
Permission Boundaries - MANDATORY
Explicit rules for what may be changed, what requires approval, and what must never be touched.
✅ Always Allowed
- Read any file in the repository
- Run linting, type checking, and tests
- Edit source files within the scope of the current task
- Update documentation and changelog
⚠️ Ask First
- Adding or removing dependencies
- Database schema changes or new migrations
- Deleting files or directories
- Changing CI/CD configuration
- Modifying shared infrastructure code used by multiple services
🚫 Never
- Commit secrets,
.envfiles, or credentials - Force push to
mainor protected branches - Modify generated files (
dist/,build/, lock files unless updating deps) - Modify already-applied database migrations
- Remove or weaken existing tests (unless explicitly asked)
- Change code outside the scope of the current task
Consistency - Match Existing Patterns - MANDATORY
When touching existing code, matching the existing style is more important than "ideal" style.
Rules
- New code must look like it was written by the same author - match naming conventions, formatting, patterns, and idioms already present in the file/module.
- Follow existing patterns from similar components - before creating a new service, route, or component, find an existing one that does something similar and follow its structure.
- Don't refactor while implementing - if you notice code that could be improved but it's outside the current task, note it for later. Don't mix refactoring with feature work.
- When in doubt, be consistent - if the codebase uses one pattern and the style guide says another, follow the codebase. Consistency within a project trumps external standards.
Change Scope Discipline - MANDATORY
Change only what was asked for. No drive-by refactors, no unsolicited improvements.
Rules
- Minimal changes - modify as few lines as possible while correctly solving the problem. Every changed line must be justified by the task.
- No extra improvements - do not refactor, optimize, or "clean up" code that is not part of the current task, even if it looks wrong.
- No unsolicited dependency updates - don't upgrade packages, change configs, or modify tooling unless the task requires it.
- Scope creep is a bug - if implementation reveals a needed change outside the current scope, document it as a separate task. Don't silently expand the change.
- Review your diff before committing - every line in the diff should relate to the task. If something doesn't, revert it.
Fix Depth Rule - MANDATORY
If a fix introduces a new failure, STOP. Do not chain fixes blindly.
Rules
- Two-fix limit - if your second fix attempt for the same issue introduces yet another failure, STOP immediately.
- Map all paths - before attempting fix #3, read the FULL integration context: all related code paths, all consumers, all edge cases. Draw the complete picture.
- Root cause, not symptoms - each failed fix is evidence you're treating a symptom. Step back and identify the actual root cause.
- Document what you tried - before the third attempt, write down: what fix #1 did, why it failed, what fix #2 did, why it failed. The pattern reveals the real problem.
- Ask for help - if you cannot identify the root cause after mapping all paths, say so. Don't keep guessing.
Copy-Paste Verification - MANDATORY
After copying code from another context, verify EVERY field makes sense in the new context.
Rules
- Review all values - default values, field names, identifiers, paths, URLs, error messages. Each must be correct for the NEW context, not the source.
- Check return types - if you copied a function that returns
isConnected: true, ask: is that the correct default HERE? - Check message objects - if constructing a new object based on a similar one, verify ALL required fields are included. Don't drop fields.
- Check config references - if the copied code references a config file, manifest, or package.json entry, verify that entry exists for the new context.
Package Manifest Verification - MANDATORY
After creating any file that should be published or deployed, verify it's included in the manifest.
Rules
- npm
filesarray - after creating new directories or entry points, verify they're listed inpackage.jsonfiles. Runnpm pack --dry-runto confirm. - pyproject.toml
include- after creating new Python modules, verify they're included in the package build. - bin entries - after creating CLI entry points, verify the
binfield in package.json points to the correct file. - After adding any import - verify the dependency is declared in the manifest (package.json or pyproject.toml). Don't use undeclared packages.
Dependency Minimalism
- Every new dependency must have a functional justification tied to a concrete requirement, test need, or architectural concern.
- Do not add libraries speculatively. If the standard library or an existing dependency can do the job, use it.
- Keep dependency manifests (
pyproject.toml,package.json) lean and auditable. - Python dependencies are managed exclusively via uv and pyproject.toml. Never use
requirements.txt,pip install,pip freeze, orpoetry. Add deps withuv add <package>. The lockfile isuv.lock. - Justify every new dependency - the commit message or task notes must explain why this dependency is needed and why existing deps or stdlib can't do the job.
- Check for overlap - before adding a new package, verify no existing dependency already provides the same functionality.
- Prefer small, focused packages over large frameworks when only one feature is needed.
- Pin versions explicitly - never use floating version ranges in production dependencies. Lock files must be committed.
- Audit before adding - check the package's maintenance status, download count, last update date, and known vulnerabilities before adding it to the project.
Design Principles
- Favor composition over inheritance. Use inheritance only when it is semantically justified and materially improves the design.
- Public interfaces, domain models, service boundaries, and infrastructure adapters must be explicit and easy to reason about.
- No god classes, hidden coupling, or ambiguous ownership boundaries.
- Write code as if a different engineer will inherit the project under delivery pressure.
Commit Discipline
All commit-cadence, checkpoint, message-format, and branch rules live in one place:
see git-and-focus-discipline.md (Part 3 - Commit Discipline). In short: commit
every meaningful checkpoint (compiles + affected tests pass + lint clean), one logical
change per commit, Conventional-Commit messages, update the changelog for
behavior/structure changes, and never end a session with uncommitted work.
Changelog Rolling Policy
docs/changelogs/CHANGELOG.mdshould stay under 500 lines- When it exceeds 500 lines, roll over: rename to
docs/changelogs/CHANGELOG.YYYY-MM-DD.mdand start fresh - Do NOT copy, summarize, or selectively preserve entries - just roll it over clean
- Write consolidated changelog entries grouped by feature
Repository Hygiene
- Do not clutter the repository root with ad hoc notes, temporary design files, scratchpads, or undocumented artifacts.
- All project documentation lives in
docs/(exceptREADME.mdat root).
Security & Credentials
- No credentials in source control
- Use .env.example for configuration templates
- Run secret-exposure review before commits
- Credentials, API keys, and test accounts must be externalized into secure configuration - never inline
inclusion: always
Chokepoint Logging - MANDATORY
A chokepoint is any error, crash, or near-miss that required multiple attempts to fix or revealed a gap in process. Logging them creates institutional memory that prevents the same class of bug from recurring.
What Is a Chokepoint
Any error that:
- Required more than one attempt to fix
- Was caused by a pattern that could recur elsewhere
- Revealed a gap in tooling, testing, or process
- Broke production (even briefly)
When to Log
On attempt #2+ of any fix. Do NOT wait until the fix is done.
- BEFORE fixing (on attempt #2+): Append to
docs/engineering/chokepoint-log.md:### CP-NNN: description | file | attempt #N | gap: what was missing - AFTER green (before next task): Append resolution:
- **Root cause:** one sentence - **Resolution:** one sentence - **Pattern:** category tag - **Guardrail candidate:** proposed rule or test
Pattern Categories
| Tag | Meaning |
| ROUTE_ORDERING | Specific routes must precede parameterized catch-alls |
| CSS_OVERSIGHT | Missing scrollbar/overflow/responsive handling |
| LAYOUT_OVERFLOW | Flex/grid children exceeding viewport |
| QUERY_INVALIDATION | Mutation not invalidating all affected queries |
| TYPE_MISMATCH | API returns different shape than frontend expects |
| IMPORT_ERROR | Wrong export/import pattern causing runtime crash |
| DEPLOY_REGRESSION | Build deployed without smoke test |
| TOOL_MISUSE | Using wrong tool when a dedicated tool exists |
| STATE_SYNC | Multiple sources of truth drifting apart |
| RACE_CONDITION | Async operations executing in wrong order |
Add new categories as they emerge.
Periodic Analysis
At the end of each sprint (or every 20 chokepoints), review the log for:
- Recurring patterns - promote to steering rules
- Tooling gaps - file as feature requests or create scripts
- Test gaps - add regression tests or smoke tests
- Process gaps - update documentation or checklists
Guardrail Promotion
When a pattern appears 3+ times:
- Write a concrete steering rule in the appropriate steering file
- Add a regression test if applicable
- Mark the chokepoint entries as "promoted" with a link to the rule
- Consider automation (hooks, linters, CI checks)
inclusion: always
Code Commenting Standards
Every piece of code must be commented so that a follow-on developer - or a coding agent picking up the project cold - can understand all pieces without needing to ask the original author.
Enforcement
- During code writing - NON-NEGOTIABLE - every file you create or modify MUST have comments meeting this standard BEFORE you move to the next task. Do not write code first and "add comments later" - comments are part of the implementation, not an afterthought.
- Automated hook -
.kiro/hooks/comment-standards-check.kiro.hookintercepts git commits and verifies staged source files meet this standard. - Maintainability review - the maintainability review prompt explicitly checks for commenting compliance and flags undocumented code as HIGH severity.
- Linting rules - Enable docstring rules in your linter config.
Guiding Principle
Comments answer "why" and "how it fits", not "what it does". Every comment should assume the reader has zero prior context about the project.
Agent-Readability Requirement
All comments must be written so that both human developers and AI coding agents can fully reconstruct the intent, constraints, and architectural role of the code. This means:
- No shorthand or project-internal jargon without explanation
- Spell out acronyms on first use in each file
- State assumptions explicitly rather than relying on tribal knowledge
- When a method exists because of a non-obvious requirement, say so
Module-Level Docstrings / File-Level JSDoc
Every file must start with a docstring/JSDoc block that includes:
- A one-line summary of the module's purpose
- How this module fits into the larger architecture
- Key design decisions or tradeoffs (link to ADRs where applicable)
- For complex modules: a brief description of the internal flow
Class Docstrings
Every class - public, internal, or private - must have a docstring/JSDoc that includes:
- What the class represents and its responsibility boundary
- Why this class exists as a separate abstraction (justification)
- Key collaborators: what other classes/services it interacts with
- Thread-safety or lifecycle notes if applicable
Property and Attribute Documentation
Every class property, instance variable, and TypeScript class member must be documented:
- Python: use inline comments or Pydantic
Field(description=...)for model fields - TypeScript: use JSDoc
@propertyor inline comments above the declaration - State what the property holds, its valid range or constraints, and why it exists if non-obvious
- For Pydantic/dataclass fields: document each field's purpose, not just its type
Method and Function Docstrings - All Visibility Levels
Every method and function must have a docstring/JSDoc, regardless of visibility (public, protected, private, internal helper):
- Purpose: what it does and when it should be called
- Justification: why this method exists - what requirement, design decision, or constraint motivated it. If it could have been done differently, briefly note why this approach was chosen
- Args/Params: describe each parameter, its expected values, and edge cases
- Returns: what is returned and under what conditions
- Raises/Throws: exceptions that can be raised and when
- Side effects: any state mutations, I/O, or external calls
For trivial getters/setters, a one-line docstring is sufficient. The justification requirement applies to any method with non-obvious logic.
Inline Comments
Use for:
- Non-obvious design decisions and the reasoning behind them
- Cross-module relationships and data flow explanations
- Security rationale (why a check exists, what attack it prevents)
- Performance tradeoffs (why this algorithm, why this data structure)
- Backward compatibility notes
- Workarounds with references to the issue/bug they address
Do NOT use for:
- Restating what the code does (the code itself should be readable)
- Obvious variable assignments
- Standard library usage
Section Separators
For files with multiple logical sections, use comment block separators:
# ──────────────────────────────────────────────
# Section Name
# ──────────────────────────────────────────────Cross-Reference Comments
When code depends on or mirrors code in another module, add a cross-reference:
- "See also: ADR-005 for rationale"
- "The frontend mirrors these checks in auth.service.ts"
- "This constant must stay in sync with backend/src/core/config.py"
Enum and Constant Comments
Document why each value exists, not just what it is. If a constant's value was chosen for a specific reason (protocol requirement, performance threshold, external API contract), state that reason.
Comment-Safe Patterns - MANDATORY
Never write comments that break the parser.
Rules
- No unescaped
*/inside JSDoc/block comments - glob patterns like**/*.tscontain*/which prematurely closes the comment block. Use backticks or rephrase:`**/*.ts`or "all TypeScript files recursively". - No unescaped
/*that opens a nested comment - some languages don't support nested block comments. A stray/*inside a comment creates a parsing error. - Regex in comments must be in backticks - patterns like
/api\/v[0-9]+/can confuse parsers. Always wrap in inline code:`/api\/v[0-9]+/`. - Test your comments compile - if you write a comment containing special characters, verify the file still parses. A broken comment is worse than no comment.
inclusion: always
Code Organization
Runtime environment, folder structure, and development server configuration.
Runtime and Environment
Defaults below. Override in user-project-overrides.md for your stack.
- Backend: Python 3.12+ with FastAPI
- Frontend: TypeScript with React + Vite
- Backend Dependencies: uv with pyproject.toml
- Frontend Dependencies: npm with package.json
Folder Organization Principles - MANDATORY
These rules apply regardless of framework, language, or stack.
Backend: Domain-Grouped Within Layers
Organize backend code by layer first, domain second. Each layer directory (routes, models, schemas, services) contains subdirectories grouped by domain when the project has more than a handful of files per layer.
backend/src/
├── api/ # Route/controller layer
│ ├── v1/ # API version namespace
│ │ ├── auth.py
│ │ ├── users.py
│ │ └── products.py
│ └── deps.py # Shared route dependencies (auth, db session)
├── core/ # Configuration, security, database setup
├── common/ # Cross-cutting: middleware, logging, shared utils
├── models/ # ORM / data models
│ ├── auth/ # user.py, role.py, session.py
│ ├── products/ # product.py, category.py
│ └── base.py # Shared base model
├── schemas/ # Request/response schemas (Pydantic, Zod, etc.)
│ ├── auth/
│ ├── products/
│ └── common.py # Shared pagination, error response schemas
├── services/ # Business logic layer
│ ├── auth/
│ ├── products/
│ └── base.py # Base service class if applicable
├── constants/ # Domain constants - NEVER inline in model files
│ ├── auth.py
│ └── products.py
└── main.py # Application entry pointRules:
- Group by domain when a layer has 5+ files. Below that, flat is fine.
- Each domain subdirectory gets an
__init__.py(Python) orindex.ts(TS) that re-exports its public API. common/is for truly cross-cutting concerns (logging, middleware, error handling). Not a dumping ground.constants/holds all domain constants - never define them inline in model or service files.- Adapt layer names to your framework's conventions (e.g.,
views/for Django,handlers/for Go,controllers/for Express).
Frontend: Feature-Sliced Design
Organize frontend code by feature first. Each feature is a self-contained module with its own components, hooks, services, and types.
frontend/src/
├── app/ # Application root (routing, providers, layout)
├── features/ # Feature modules - one per domain
│ ├── auth/
│ │ ├── components/ # Feature-specific UI components
│ │ ├── hooks/ # Feature-specific hooks
│ │ ├── services/ # API calls for this feature
│ │ ├── types/ # Feature-specific type definitions
│ │ ├── utils/ # Feature-specific helpers
│ │ └── index.ts # Public API - ONLY export what other features need
│ ├── dashboard/
│ │ ├── components/
│ │ ├── hooks/
│ │ └── index.ts
├── shared/ # Cross-feature reusable code
│ ├── components/ # Generic UI (Button, Modal, Layout)
│ ├── hooks/ # Generic hooks (useDebounce, useLocalStorage)
│ ├── utils/ # Generic helpers (formatDate, cn())
│ └── types/ # Shared type definitions
├── services/ # Global API client setup, interceptors
└── types/ # App-wide type definitionsRules:
- Features never import from each other's internals - only through
index.ts. - If feature A needs something from feature B, it goes through B's public
index.tsexport, or it belongs inshared/. - Each feature's
index.tsis the only entry point. Internal files are private to the feature. - This pattern works for React, Vue, Svelte, Angular, or any component-based framework.
Shared vs Feature-Scoped - Graduation Policy
Code starts in the feature where it was first needed and graduates to shared/ only when reuse is proven:
- First use: lives inside the feature directory
- Second feature needs it: move it to
shared/and update both imports - Never preemptively put code in
shared/- that creates a junk drawer shared/items must be genuinely generic: no feature-specific logic, no domain assumptions, no hardcoded business rules- If a "shared" item accumulates feature-specific parameters or branches, it should be split back into feature-scoped copies
Customizing for Your Stack
The directory structures above are defaults. Adapt them to your stack:
- Backend-only project: remove the frontend section entirely - don't leave dead structure
- Frontend-only project: remove the backend section entirely
- Different framework: rename layers to match your framework's conventions (e.g., Django
views/, Gohandlers/, Railscontrollers/) - Monorepo: use
packages/with each package following the same internal layer/feature structure - No ORM: replace
models/with whatever your data layer uses (entities/,repositories/, etc.) - Keep the principles (layer-first backend, feature-first frontend, graduation policy) even when the directory names change
Local Development Servers
Defaults below. Override in user-project-overrides.md for your ports.
- Backend: default port 8000
- Frontend: default port 5173
inclusion: always
Database Conventions
Database architecture, credential management, migration rules, and ORM conventions.
Architecture Principles
- One database per project - no sharing databases across projects
- Separate test database - tests never touch the application database
- All connection strings in
.env- never hardcode host, port, database name, or credentials - Test database is disposable - tests may truncate/recreate tables freely
Credential Management
- Admin/root credentials are for migrations and schema changes only - stored in
.env - App user gets DML privileges only (SELECT, INSERT, UPDATE, DELETE) - no CREATE/DROP in production
- Test user gets full privileges on the test database only
- Never prompt for passwords - always read from
.env - Never use root for application connections - root is for admin operations only
.env Structure
# Admin (migrations, schema changes)
DB_ADMIN_URL=<engine>://<admin_user>:<admin_password>@localhost:<port>/<project>
# Application (runtime)
DATABASE_URL=<engine>://<project>_app:<app_password>@localhost:<port>/<project>
# Test
TEST_DATABASE_URL=<engine>://<project>_test:<test_password>@localhost:<port>/<project>_testMigration Rules
- Migrations run with admin credentials - they need DDL privileges
- Application code connects with the app user - limited to DML
- Never modify already-applied migrations - create new ones to alter schema
- No business logic in migrations - migrations are schema changes only
- Migrations must be reversible where possible - include downgrade/rollback steps
ORM Conventions
- No N+1 queries - use eager loading, joins, or batch queries
- Review ORM-generated SQL for new endpoints - check query plans for performance
- Parameterized queries only - never string interpolation for SQL
- Explicit timeouts on all database calls - no indefinite waits
- Index every column used in WHERE, JOIN, or ORDER BY for frequent queries - document index decisions
- Don't fight the ORM - if the ORM handles it, don't drop to raw SQL unless there's a measurable performance reason
Transaction Boundaries
- Explicit, not implicit - wrap multi-step operations in explicit transactions
- Keep transactions short - don't hold transactions open across network calls or user interactions
- Handle rollback - every transaction must have a clear rollback path on failure
Connection Pooling
- Use connection pooling in production - never open a new connection per request
- Set pool size based on expected concurrency and database max connections
- Configure idle connection timeout to prevent stale connections
- Monitor pool exhaustion - log when pool is near capacity
Common Pitfalls
- Soft deletes vs hard deletes - decide once per project, document in an ADR. Don't mix approaches.
- Timestamps - use
timezone.utcfor all database timestamps. Store as UTC, convert on display. - Large result sets - always paginate. Never return unbounded query results.
- Schema drift - the migration tool is the single source of truth for schema. Never modify schema manually.
Engine-Specific Notes
PostgreSQL
- Use JSONB over JSON for queryable structured data
- Use advisory locks for distributed coordination
- Enable
pg_stat_statementsfor query performance monitoring - Use
EXPLAIN ANALYZEto verify query plans for new endpoints
MySQL
- Set
charset=utf8mb4andcollation=utf8mb4_unicode_cias defaults - Enable strict mode (
STRICT_TRANS_TABLES) - Be aware of implicit type conversions in WHERE clauses
SQLite
- Enable WAL mode for concurrent read/write access
- Be aware of single-writer limitation - not suitable for high-concurrency production
- Use for development, testing, and single-user applications
inclusion: always
Documentation Standards
Documentation organization, spec quality standards, API versioning, and roadmap planning.
Documentation Organization
All documentation must be placed in the appropriate docs/ subdirectory based on its purpose:
docs/decisions/ (Architecture Decision Records)
- Format:
ADR-###-descriptive-name.md(e.g.,ADR-001-tech-stack.md) - Content: Context, decision, alternatives considered, consequences
- Immutability: ADRs are historical artifacts; status can change to "superseded" but content stays
docs/architecture/
- Purpose: Technical documentation of HOW the system works today
- Content: System structure, data flows, component interactions, implementation patterns
docs/roadmap/
- Purpose: Project planning and milestone tracking
- Content: Roadmaps, milestone plans, implementation schedules
docs/changelogs/
- Purpose: Historical record of changes to the system
- Content:
CHANGELOG.md(current), archived changelogs (CHANGELOG.YYYY-MM-DD.md)
docs/bugs/
- Purpose: Bug investigation and resolution documentation
- Format:
BUG-###-short-description.md
docs/ideas/
- Purpose: Feature exploration and research before promotion to specs
- Content: Freeform markdown exploring a feature concept
- Archive: Promoted ideas move to
docs/ideas/_archive/
docs/technical-debt/
- Purpose: Known technical debt items and remediation plans
docs/testing/
- Purpose: Testing strategy, test plans, coverage reports
docs/runbooks/
- Purpose: Operational guides and setup instructions
docs/references/
- Purpose: External documentation, research materials, API guides
docs/engineering/
- Purpose: Engineering process documentation, execution standards
docs/security/
- Purpose: Security review reports and findings log
Placement Rules
DO NOT: Create documentation files in docs/ root, mix document types, or create new subdirectories without documenting their purpose.
DO: Place ADRs in docs/decisions/ with proper numbering, update this list if adding a new documentation category.
API Versioning
- APIs must be versioned from the start of the project.
- Versioning strategy must be explicit, documented, and applied consistently.
Development Roadmap and Planning
- Before substantive implementation begins, establish a structured plan:
- Kiro Specs (preferred):
requirements.md,design.md, andtasks.mdunder.kiro/specs/{feature-name}/ - Manual Roadmap (for broader planning):
docs/roadmap/roadmap.mdwith milestones and TODO items
- Kiro Specs (preferred):
- Link specs to the roadmap at the appropriate timeline point.
- Execute work incrementally, one task at a time.
- If implementation realities require a plan change: update the spec/roadmap, document the reason, record in changelog.
Spec Quality Standards - NON-NEGOTIABLE
Requirements.md must include:
- Numbered user stories with acceptance criteria (testable, not vague)
- Non-functional requirements (performance, cost, reliability, accessibility)
- Explicit "out of scope" section to prevent scope creep
- UX acceptance criteria for any user-facing feature - not just "show data" but how it's shown: hierarchy, spacing, interaction placement, density expectations
Design.md must include:
- Architecture diagram showing service interactions
- Data model with actual SQL or Pydantic schemas (not hand-wavy descriptions)
- API contracts with request/response examples
- Frontend component tree with data flow
- For pipelines/background processes: observability design (what gets logged, what metrics are emitted, how to trace a request end-to-end)
Tasks.md must include:
- Phases with clear boundaries and checkpoint gates
- Every task has a concrete deliverable (file path, test name, endpoint)
- TDD structure: RED → GREEN → REFACTOR per step
- Security checkpoint at final phase
- No vague tasks like "implement the feature" - break down to individual functions/components
If a spec feels thin, it IS thin. Expand it before writing code.
ADR Roadmap Linking - MANDATORY
Every ADR must be linked in docs/roadmap/roadmap.md at the sprint/milestone row where the decision was made.
Rules:
- Link ADRs in chronological order within each row
- Only link ADRs that actually exist on disk
- Format:
[ADR-###](../decisions/ADR-###-descriptive-name.md)(relative path fromdocs/roadmap/)
Code Documentation
- See
code-commenting-standards.mdfor docstring and comment rules - All docs live in
docs/directory (exceptREADME.md)
inclusion: always
Error Handling & Performance
Standards for error handling, performance optimization, and themed UI dialogs.
Async Discipline - MANDATORY
Operations that depend on each other must be sequenced. Never fire-and-forget before a dependent operation.
Rules
- Use
mutateAsync+awaitfor dependent operations - if operation B depends on operation A completing, useawait mutateAsync()for A, notmutate().mutateis fire-and-forget; it does NOT wait for completion. - Never block async event loops with sync I/O - in Python asyncio, never call sync functions that take >100ms (HTTP requests, LLM calls, file I/O on large files) without
await asyncio.to_thread()orloop.run_in_executor(). - Auth token timing - never fire API requests that require auth before confirming the token is available. Check auth state before making dependent requests, not after a 401.
- Sequence mutations that share state - if two mutations modify the same resource, await the first before firing the second. Parallel mutations on shared state cause race conditions.
Error Handling Standards - MANDATORY
Errors must be explicit, contextual, and never silently swallowed.
Rules
- Never silently ignore errors - every error must be raised, logged, or explicitly handled. Empty
except:/catch {}blocks are forbidden. - Use specific error types - not catch-all handlers. Each error type should clearly indicate what went wrong and where.
- Error messages must include context - request parameters, status codes, what operation was being attempted, and what input caused the failure. No generic "something went wrong."
- No automatic fallbacks - code should either succeed or fail clearly. Fallbacks are only allowed when explicitly designed and documented. Silent fallbacks hide real problems.
- Fix root causes, not symptoms - if an error keeps occurring, fix the underlying issue rather than adding retry/fallback logic around it.
- External service calls: retry with backoff - use exponential backoff for transient failures. Raise the last error if all attempts fail. Log each retry attempt.
- API endpoints return proper HTTP status codes - never return 200 for errors. Use 4xx for client errors, 5xx for server errors, with structured error response bodies.
- Frontend errors: catch at boundaries - use error boundaries (React) or equivalent. Show user-friendly messages, log the full error for debugging.
Performance Guidelines - MANDATORY
Design for efficiency from the start. Performance is not an afterthought.
Rules
- Cache expensive operations - database queries, API calls, computed values. Use appropriate cache invalidation strategies.
- Pagination for all list endpoints - never return unbounded result sets. Default page size must be a named constant.
- No N+1 queries - use eager loading, joins, or batch queries. Review ORM-generated SQL for new endpoints.
- Lazy load heavy resources - large images, optional modules, below-the-fold content. Load on demand, not upfront.
- Database indexes - every column used in WHERE, JOIN, or ORDER BY clauses in frequent queries must have an index. Document index decisions.
- Bundle size awareness (frontend) - monitor bundle size. Use code splitting and tree shaking. Avoid importing entire libraries when only one function is needed.
- Timeouts on all external calls - every HTTP request, database query, and external service call must have an explicit timeout. No indefinite waits.
Themed Dialogs - MANDATORY
All confirmation dialogs, alerts, and informational popups must use the application's themed dialog components. No exceptions.
Rules
- Never use native browser dialogs -
window.alert(),window.confirm(), andwindow.prompt()are forbidden. They cannot be styled, break the visual experience, and behave inconsistently across browsers. - Use the application's dialog/modal system - every dialog must render through the project's themed component (e.g.,
ConfirmDialog,AlertDialog,Modal) so it inherits the design system's colors, typography, spacing, and animations. - Consistent UX across all interactions - destructive actions get a themed confirmation dialog with clear action labels (not "OK/Cancel"). Informational messages use themed toast/snackbar notifications. Error messages use themed error dialogs with context.
- Accessible by default - themed dialogs must trap focus, support Escape to close, and include proper ARIA roles (
role="dialog",aria-modal="true").
Observability-First Design - MANDATORY
ZERO BLACKBOXES. Every pipeline, background process, and async workflow must be observable from day one.
Rules for all background/async/pipeline code:
- Structured logging - every operation logs: what started, what inputs it received, what it produced, how long it took, whether it succeeded or failed. JSON structured logging, not print statements.
- Correlation IDs - every pipeline run or background task gets a unique ID that flows through all log entries and database records.
- State transitions - every job/task has explicit states (PENDING → RUNNING → SUCCESS/FAILED/RETRYING) stored in the database.
- Metrics - track: items processed, items failed, processing time, queue depth, retry count.
- Error context - when something fails, log the full context: what was being processed, what step failed, what the input was.
- Resumability - if a pipeline crashes mid-run, it must be resumable from where it stopped via database checkpointing.
- Discuss architecture first - before implementing any pipeline or background process, discuss the architecture: data flow, failure modes, retry strategy, observability hooks.
inclusion: fileMatch fileMatchPattern: ["/*.tsx", "/*.jsx"]
Frontend Patterns
Rules for React/JSX development. Loaded only when working on frontend component files.
React Hooks - NON-NEGOTIABLE
- ALL hooks at the top of the component - before any early returns, conditional logic, or variable declarations that depend on props. React requires the same hooks to run in the same order every render.
- Never place hooks after early returns - this causes "Rendered more hooks than during the previous render" crashes.
- Verify required providers exist - before using
useQuery,useParams,useNavigate, or any context hook, confirm the component is rendered inside the required provider (QueryClientProvider,Router, etc.). - Use
mutateAsync+awaitfor dependent operations - never fire-and-forget withmutate()when a subsequent operation depends on the result.mutateis fire-and-forget;mutateAsyncreturns a promise.
Event Propagation - MANDATORY
- Document event capture/propagation - when adding event handlers, note what events are captured, what propagation is stopped, and what other handlers exist on parent/child elements.
- React synthetic
stopPropagation()does NOT stop native DOM events - if you need to stop a native event (e.g., window-level Escape listener), usee.nativeEvent.stopImmediatePropagation()or manage an event stack. - Never spread DnD
{...listeners}on containers with click handlers - DnD listeners capture all pointer events. Apply them to a dedicated drag handle element instead. - Escape key layering - when multiple components listen for Escape (modals, drawers, dropdowns), implement a stack-based system. The topmost layer consumes the event; lower layers ignore it.
- Outside-click handlers + portals - if a component renders via a portal (e.g., dropdown menu on
document.body), the outside-click handler must check if the click target is inside the portal, not just inside the trigger's DOM tree.
CSS Layout - MANDATORY
- Flex containers: always set
min-h-0andoverflow-hiddenon intermediate containers. Withoutmin-h-0, flex children cannot shrink below their content size. - Header/body alignment - use the SAME layout strategy (grid or flex with identical gaps/gutters) for both header and body rows. Never mix CSS grid for headers with flex for rows.
- Popover/dropdown positioning - verify the positioning direction matches the trigger's screen location.
right: 0extends leftward (correct for right-side triggers).left: 0extends rightward (correct for left-side triggers). - Absolute positioning inside overflow containers - elements with
position: absoluteare clipped by ancestoroverflow: hidden. Use a portal or move the positioned element outside the overflow container.
Cache & Data Fetching - MANDATORY
- When setting
staleTime- document what invalidation scenarios exist. After any mutation that changes server state, verify the cache is invalidated or the query is refetched. - After mutations, always invalidate - call
queryClient.invalidateQueries()for affected keys. Don't rely on staleTime expiry for data freshness after writes. - Error + retry on 401 - if a query gets a 401, don't let
staleTimeprevent retry. Setretry: falsefor auth-dependent queries or clear the query cache on auth state change. - Optimistic updates need rollback - if using optimistic updates, always implement the
onErrorrollback. Don't leave stale optimistic state on failure.
Component Extraction & Reuse - MANDATORY
When extracting a shared component from an existing implementation, or when a new consumer adopts a shared component:
- Prop parity audit - before declaring a consumer "done", list every prop the original passes and verify the new consumer passes equivalent values. Empty props (
{},[],false) are the #1 source of "it compiles but looks wrong." - Compare against the source, not against "compiles" - the acceptance criterion is "behaves like the original", not "TypeScript is happy." Open both the source and the new consumer side-by-side.
- Test with real data - never verify a component extraction with empty/default data only. Use the same data the original consumer uses.
- Layout wrapper parity - verify the new consumer uses the same layout container (full-width, max-width, padding) as the original. A component inside
max-w-4xllooks nothing like the same component at full width. - Interactive behavior parity - hover states, click handlers, optimistic updates, undo/toast patterns. If the original has them, the new consumer needs them too (or explicitly documents why not).
Completion Verification - MANDATORY
tsc --noEmit + vite build passing is NECESSARY but NOT SUFFICIENT for UI work.
Build tools verify types, imports, and syntax. They do NOT verify:
- Visual output matches expectations
- Props carry correct values (not empty defaults)
- Layout fills the expected space
- Interactive behavior works
- The page looks like what the user asked for
Rules
- Never declare UI work "done" based solely on build output - you must verify the rendered result visually or explicitly state what you could not verify.
- When visual verification is unavailable - say so honestly. List specific things the user should check: "I cannot verify visually. Please confirm: (1) status pills show colors, (2) delete button appears on hover, (3) table fills full width." Never say "risk is minimal" without evidence.
- The user sees pixels, not types - a shared component architecture is worthless if the consumer passes wrong props. The last 5% (correct props, correct layout, correct behavior) is what makes the feature work.
- Speculation is not verification - "it should work" and "the risk is minimal" are not acceptable substitutes for actually checking. If you haven't verified, say "I haven't verified this."
Component Completeness - MANDATORY
Before marking any UI component or feature task as done, verify:
- Error state - what does the user see when the API call fails?
- Loading state - what does the user see while data is loading?
- Empty state - what does the user see when there's no data?
- Persistence - does any state need to survive page reload? If yes, where is it persisted?
- Destructive actions - do delete/remove actions have confirmation dialogs with undo?
- Themed components only - no native
<select>,window.alert(),window.confirm(), ortitleattributes. Use the design system.
UX Non-Negotiables - MANDATORY
These rules apply to ALL frontend layout and interaction decisions. Violations are bugs.
- Interaction locality - controls must be near the object they affect. Never place a section menu at the far-right edge when the section title is on the left. Actions go before or beside their target.
- Primary content gets primary space - descriptions, comments, and form fields are content, not metadata. They get dominant width and vertical breathing room. Never squeeze content below metadata.
- No decorative borders unless they encode state - thick colored borders with no functional meaning are visual noise. Use subtle background, shadow, or tokenized selection treatment.
- Minimum readable text size - body text minimum 14px. Content text (descriptions, comments) preferred 15-16px. Comments are content, not admin data.
- First-glance test - within 3 seconds, user must know: what am I viewing, what is most important, what can I do next.
- Density matches task type - data grids: compact is acceptable. Reading, comments, task detail, forms: spacious is required.
- Copy the reason, not just the surface - when borrowing a pattern from another product, preserve WHY it works (interaction model, hierarchy, locality). Don't copy the visual and break the interaction.
- No
justify-betweenfor section actions - section-level controls use leading or adjacent placement. Never push primary section controls to the far-right trailing slot unless they affect the entire page.
inclusion: always description: Git branching, commit cadence, and agent focus - branch isolation, defensive checkpoints, request triage, and merge-and-delete hygiene in one place
Git & Focus Discipline
This file governs one behavioral domain: how an agent uses git and how it stays focused on one task at a time. The two are inseparable - undisciplined focus (thrashing between tasks) is what entangles unrelated changes on the wrong branch, and undisciplined git (uncommitted work, branch sprawl) is what makes lost focus unrecoverable.
Two failure modes this file exists to prevent. Both are invisible when they happen and expensive much later:
- Branch sprawl & divergence - branches that are never merged-and-deleted pile up, silently diverge, and produce duplicate-but-different versions of the same file.
- Task thrashing & lost work - dropping the current job to chase a new request, or ending a session with work uncommitted, so the thread (and sometimes the code) evaporates.
The hard "never" rules referenced throughout live in
agent-boundaries.md- the shortest file in this set. Read it first; this file is the detailed "how".
Core Principle
Every concrete piece of work gets its own branch. Commit every meaningful checkpoint within that branch. Merge to main when it works, then delete the branch. Never mix unrelated work on the same branch.
main ──→ feat/A ──→ merge+delete ──→ fix/B ──→ merge+delete ──→ feat/C ──→ ...Part 1 - Branch Hygiene
A branch exists to isolate one task and then disappear. A branch that outlives its task is a liability. These rules come first because branch divergence is the most common and most damaging failure mode.
- One task, one branch. Never reuse a branch for a second, unrelated task.
- Check before you branch. Before creating a branch in a feature area, confirm
no branch already touches it:
If one exists, build on the latest of it (rebased on main) or merge it first. Do not create a parallel branch that will fork into a duplicate file.bash scripts/branch-check.sh <area> - Never start new work with a branch still open. Reach Definition of Done, or explicitly park the current branch, before creating another. Unmerged branch + new work = guaranteed divergence.
- Merge-and-delete is a single motion. The moment work merges to main, delete
the branch:
Do not keep it "just in case" - main has the history.git checkout main && git merge --no-ff feat/x && git branch -d feat/x - Detect collisions while you work. Run
bash scripts/branch-check.sh(no args) to list other unmerged branches editing the same files you are - the early-warning signal for silent divergence. - Prune merged branches regularly:
git branch --merged main | grep -v '^\*\|main' | xargs -r git branch -d - If two branches have already diverged on the same file, do not re-type or guess. Identify the superset version (the most complete, correct one), restore that single file onto the surviving branch, commit it immediately, then delete the dead branch. Uncommitted reconciliation is itself a root cause - commit it before doing anything else.
The anti-pattern this kills
~200 branches, almost none merged-and-deleted. Two independently touched the same feature area on the same day, producing duplicate-but-divergent files. The workflow said "merge then delete"; in practice they piled up and silently diverged.
Branch sprawl is not cosmetic. Every un-deleted branch is a chance for the next task to fork reality.
Parallel agent sessions
If more than one agent session may touch this repo (or a sibling tree) at once, each
session gets its own git worktree on its own branch - never a shared working
directory. scripts/branch-check.sh is the detective control (warns after the
fact); worktrees are the preventive control. See session-isolation.md for the
full cross-session safety rules.
Part 2 - Branch Types & Naming
| Situation | Branch type | Example |
| Building a new feature or spec | feat/ | feat/ingredient-analysis |
| Fixing a reported bug | fix/ | fix/bug-003-score-calc |
| UI/UX change only | ui/ | ui/dashboard-layout |
| Tests only | test/ | test/auth-regression |
| Tooling, config, deps | chore/ | chore/update-deps |
| Documentation only | docs/ | docs/adr-001-tech-stack |
| Refactor (no behaviour change) | refactor/ | refactor/extract-service |
Rules:
- Always kebab-case after the
/ - 3-5 words max
- Include BUG-### for bug fixes:
fix/bug-034-description - Include spec name for spec work:
feat/spec-name
Part 3 - Commit Discipline
A branch is not a place to hoard uncommitted work. Commit at every meaningful checkpoint so that no context boundary, crash, or bad next step can erase a good state. This is the discipline most agents lack by default.
What counts as a "meaningful checkpoint"
A commit-worthy checkpoint is reached when all three hold:
- The code compiles / imports cleanly (no syntax or import errors).
- The affected tests pass (the tests touching the code you just changed).
- There are no new lint errors introduced by the change.
The moment you reach that state, commit before moving to the next logical unit of work. Do not let a passing state sit uncommitted while you start the next piece - context compression, session end, or a bad subsequent step will erase it.
Defensive Checkpoints - MANDATORY
Before starting any task that will touch more than 3 files or span more than one session, create a labeled checkpoint commit first:
git add -A && git commit -m "checkpoint: before <task-description>"This creates a safe rollback point. A checkpoint is not a feature commit - it does not need to pass tests or meet the commit-message standard below. Its only job is to make the pre-task state recoverable. (This is the one sanctioned exception to "never commit failing code" - a checkpoint is explicitly flagged as such.)
Never end a session with uncommitted work - MANDATORY
Agent sessions end abruptly - context compaction, a closed terminal, a crash. Work left in the working tree but not in git is gone.
- At any natural stopping point (task done, turn ending, switching away), run
git status. If there are uncommitted changes on a branch, commit them (feature commit if at a checkpoint,checkpoint:commit otherwise) or stash them. - The
commit-checkpoint-on-stophook reminds you of this on agent stop, but the rule holds whether or not the hook fires.
One logical change per commit - MANDATORY
- One logical change per commit. When multiple files change for different reasons, prefer separate commits per reason. Do not bundle unrelated changes - it makes rollback impossible.
- Test before a feature commit. Never commit code that makes existing tests fail,
unless the commit is an explicitly-labeled
checkpoint:. - Review your diff before committing. Every changed line must relate to the
task (see
change-discipline.md).
Commit Message Format
<type>(<optional scope>): <short description>| Type | When |
feat | New feature |
fix | Bug fix |
chore | Tooling, deps, config |
docs | Documentation only |
test | Tests only |
refactor | No behaviour change |
ui | Frontend-only visual change |
perf | Performance improvement |
(checkpoint: is the one non-Conventional prefix, reserved for the defensive
rollback commits described above.)
Part 4 - The Standard Lifecycle
Standard Git Scripts
All git operations MUST use the scripts in scripts/ which pipe output through tee
to logs/. This ensures output is always captured.
| Script | Purpose | Log file |
./scripts/git-commit-push.sh "message" | Commit, merge to main, push | logs/git-commit-push.txt |
1. Start from main
git checkout main && git checkout -b feat/my-work2. Work and commit incrementally (at every meaningful checkpoint)
git add -A && git commit -m "feat: description of what changed"3. Merge to main when done
bash scripts/git-commit-push.sh "feat: description"4. Start the next piece of work from main
git checkout -b feat/next-thingPart 5 - Focus & The Request Queue Protocol
When a new request arrives while you are mid-task, the default is to FILE it, not DO it.
Triage, don't thrash
When the user sends a request while you have unfinished work in progress, classify it:
- Same task - a clarification, correction, or refinement of what you're already doing → fold it in and keep going.
- Explicit divert - the user clearly says to switch now ("stop", "drop that", "this is critical, fix it first") → switch, but commit or stash the current work first so it is not lost.
- Unrelated new request (the common case) → file it and continue.
How to file it
- Append the request to
docs/backlog/INBOX.md(create it if missing) with the date and what you were doing when it arrived. - Acknowledge in one line: "Noted [request] in the backlog - I'll finish [current task] first, then pick it up."
- Return to the task at hand. Do not start the new work.
Why this is strict
Switching tasks mid-stream is the root cause of:
- Half-finished features abandoned on branches.
- Unrelated changes bleeding into the wrong branch - you're on
feat/authfixing auth, an unrelated CSS or logic fix gets committed there too, and now two unrelated changes are entangled so neither can be reverted cleanly. - Lost context - the original task's plan evaporates and is never completed.
A filed request is never lost. An abandoned task usually is. Only the user may authorize a divert - you do not divert on your own judgment that the new thing is "quick."
Part 6 - Definition of Done
A task is not done when the code works. It is done when ALL of the following are true, in order:
- The change is complete and matches the request.
- Tests are written and passing (TDD - see
testing-standards.md). - The changelog and any affected docs are updated.
- The work is committed on its own branch.
- The branch is merged to main.
- The merged branch is deleted.
- The backlog is checked - drain the next
docs/backlog/INBOX.mditem or report what remains.
Only after step 7 do you start the next piece of work. "I'll commit it later" is how branches pile up and diverge.
Part 7 - Handling Bugs During Feature Work
Do NOT fix bugs on the current feature branch.
- Note it down - add to
docs/bugs/ - Finish or stash the current feature
- Create a dedicated fix branch from main
- Fix, test, merge
- Return to the feature branch and merge main
Bug Resolution Workflow - MANDATORY
When a bug is identified, follow this workflow in full:
- Assign a bug number - check
docs/bugs/for the highest existingBUG-###number and increment by 1 - Create the bug document -
docs/bugs/BUG-###-short-description.mdwith: ID, Severity, Status, Description, Reproduction steps, Root cause, Fix description, Files changed, Regression tests added - Fix the bug - minimal and targeted fix on a
fix/bug-###-descriptionbranch. Do not refactor unrelated code. - Variant search - NON-NEGOTIABLE - after identifying the root cause:
- Search for the same pattern at all other call sites in the codebase
- Search for the same vulnerability class elsewhere (e.g., if you found one SQL injection, search for all raw SQL)
- Document: "Variant search: checked [locations]. Found [N] additional instances."
- Fix ALL variants in the same branch, not just the reported instance
- The
variant-search-on-fix-branchhook reminds you of this whenever you work on afix/branch - the reported instance is rarely the only one.
- Add regression tests - NON-NEGOTIABLE - every bug fix requires both negative
AND positive regression tests:
- Negative test: reproduces the bug, must FAIL on unfixed code (RED phase)
- Positive test: confirms the fix, passes after fix (GREEN phase)
- Named after the bug:
test_bug###_<description>(Python) orit('BUG-###: <description>')(TypeScript) - Never mark a bug
FIXEDwithout regression tests committed in the same change
- Link to the roadmap - add a reference in the roadmap or spec tasks for traceability
- Update the changelog - add entry to
docs/changelogs/CHANGELOG.mdunder today's date - Update the bug document status - set
StatustoFIXED, fill inFixeddate and remaining fields
Part 8 - Spec-Driven Work
- Create
feat/<spec-name>branch from main - Complete ALL tasks in the spec on that branch
- Merge to main when the spec is complete
- ONLY THEN create the next spec's branch
Never start Phase 2 before Phase 1 is merged to main.
Part 9 - Forbidden Actions & Per-File Conflict Resolution
| Action | Why it's banned |
| Fixing a bug on a feature branch | Entangles unrelated changes |
| Creating a branch from another feature branch | Misses main's latest work |
| Committing directly to main | Bypasses branch isolation |
| Leaving a branch unmerged and starting new work | Creates divergence |
| Leaving a session with uncommitted work | Work evaporates at the context boundary |
| Mixing multiple features on one branch | Makes rollback impossible |
| Modifying a test to make it pass | Hides the real failure - fix the implementation |
git checkout --ours . or git checkout --theirs . | Blanket conflict resolution reverts work |
Force-pushing or rebasing main | Rewrites shared history |
Per-File Conflict Resolution - MANDATORY
FORBIDDEN:
git checkout --ours . # ← BANNED
git checkout --theirs . # ← BANNEDResolve each file individually based on which side has the correct work. On a genuine ambiguity you cannot resolve from the code, stop and ask - two minutes of clarification beats twenty hours of rebuilding. A confident wrong guess is the expensive failure mode.
Cross-references
agent-boundaries.md- the hard "never" rules in their shortest form.change-discipline.md- change-scope discipline (keep unrelated edits out of the diff), fix-depth rule, permission boundaries.session-isolation.md- cross-repo and cross-session safety, worktree usage.testing-standards.md- TDD cycle, regression-test requirements.scripts/branch-check.sh- branch collision detector used by the rules above.docs/backlog/INBOX.md- the request queue the focus protocol writes to.
inclusion: always
Import Path Rules
Absolute Rule: No Deep Relative Imports
NEVER use ../../ or deeper relative paths in any import statement. This applies to both Python backend and TypeScript frontend.
Relative imports beyond one level (../) create fragile, unreadable code that breaks on refactors. Use path aliases instead.
Python Backend
Use #src/ subpath imports or absolute package imports. Never ../../.
Allowed
# Absolute package imports (preferred)
from packages.common.models import Ingredient
from packages.evidence.src.scoring import ConsensusScorer
# Same-package relative (one level only, for tightly coupled files)
from .models import Ingredient
from ..core import configForbidden
# BANNED - deep relative imports
from ...common.models import Ingredient
from ....evidence.src.scoring import ConsensusScorerConfiguration
Each package's pyproject.toml declares itself as an installable package. Cross-package imports use the package name directly, resolved by uv workspaces.
TypeScript Frontend
Use @/ path alias for all imports. Never ../../.
Allowed
// Path alias (preferred for all cross-directory imports)
import { useAnalysis } from '@/features/analysis/hooks/useAnalysis'
import { Button } from '@/shared/components/Button'
import { api } from '@/services/api'
import type { Ingredient } from '@/types/ingredient'
// Same-directory relative (only for tightly coupled files in the same feature)
import { IngredientCard } from './IngredientCard'Forbidden
// BANNED - any relative import crossing directory boundaries
import { Button } from '../../shared/components/Button'
import { api } from '../../../services/api'Configuration
vite.config.ts:
resolve: {
alias: { '@': path.resolve(__dirname, './src') }
}tsconfig.json:
{
"compilerOptions": {
"baseUrl": ".",
"paths": { "@/*": ["src/*"] }
}
}Enforcement
- Code review catches violations
- ESLint
no-restricted-importsrule bans../..patterns in frontend - Ruff import sorting keeps Python imports clean
- When touching existing files with deep relative imports, fix them opportunistically
inclusion: auto description: File naming conventions for test files and source code organization
File Naming Conventions
Test Files Must Mirror Source File Names
Test files must use the exact same name as the source file they test, with the appropriate test suffix.
Backend (Python)
| Source file | Test file |
src/services/auth.py | tests/unit/test_auth.py |
src/models/user.py | tests/unit/test_user.py |
Frontend (TypeScript)
| Source file | Test file |
src/features/auth/auth.service.ts | tests/unit/auth.service.test.ts |
src/shared/hooks/useAuth.ts | tests/unit/useAuth.test.ts |
Rules
- Never mix conventions - the test file name is always derived from the source file name
- E2E tests use kebab-case descriptive names (e.g.
ingredient-analysis-flow.test.ts)
inclusion: always
Project-Specific Conventions
Rules specific to this project's codebase, tools, and architecture.
For project-specific overrides (tech stack, ports, database, code style), see user-project-overrides.md.
Git and Terminal Workflow
Terminal output capture can be unreliable in this environment. Always use the standard git scripts in scripts/ which pipe output through tee to logs/.
Domain Constants Strategy
Rule: All domain constants live in a dedicated constants directory - never inline in model files.
- Never define domain constants inline in a model file
- When adding a new feature with constants, create a dedicated constants file
Testing Execution
- Always stream test output: use
pytest -v --tb=shortwith NO pipes (| tail,| head,| grep) - Both positive AND negative test cases are required
Code Style
- Use
datetime.now(timezone.utc)- neverdatetime.utcnow()(deprecated) - Use parameterized queries for all SQL - never string interpolation
- Separation of concerns: keep services as separate classes
Architecture Decisions
- ADRs are required before major implementations - store them in
docs/decisions/
Environment and Tooling
- Use the project's virtual environment for all Python work. Never install packages globally.
- Never install packages in the global Python registry.
Command Output Logging - MANDATORY
ALL commands that produce output you need to analyze MUST be logged to files using tee.
Logging Pattern:
python -m pytest tests/ -v --tb=short 2>&1 | tee logs/test_results.logLog File Location:
- All command logs:
logs/
Reusable Utility Scripts - MANDATORY
When the first need arises for any repeated operation, write a parameterized reusable script with logging and error checking. Then use that script going forward. No ad-hoc multi-line shell commands for operations that will be repeated.
Rules
- First need = script. The moment you do something manually that could recur (restart a service, run a migration, deploy, seed data, check health), write a script in
scripts/. - Parameterized. Scripts accept arguments (service name, environment, etc.) - not hardcoded for one case.
- Verbose with logging. Scripts log what they're doing with timestamps. On failure, they print useful diagnostics.
- Error checking. Scripts use
set -uo pipefail, check exit codes, and fail loudly. - Reuse. Once a script exists, always use it. Never rewrite the same logic inline.
- Discoverable. Scripts live in
scripts/with descriptive names. Add a comment header explaining usage.
Anti-Patterns (BANNED)
# WRONG - ad-hoc multi-line restart
fuser -k 8060/tcp 2>/dev/null
sleep 2
cd /path/to/project && nohup .venv/bin/python -m uvicorn ...
echo $! > logs/server.pid
sleep 5
curl -s http://localhost:PORT/health
# CORRECT - one-liner using the script
bash scripts/restart-service.sh service-nameMinimize shell calls. If you find yourself making the same type of shell call 3+ times in a session (restart, health check, token fetch, DB query pattern), that is a candidate for a reusable script. Write it in scripts/, then use it going forward. TracePulse get_audit_trail can help identify repeated patterns.
inclusion: always
Reusable Architecture
Reusable component design, infrastructure abstraction, and centralized configuration.
Reusable Component Architecture - MANDATORY
Before building any new service, module, component, or helper, think reuse-first.
Design-Time Mindset
- Search before building - scan the codebase for existing implementations that solve the same or a similar problem. Duplication is a bug.
- Identify the generic core - every piece of logic has a domain-specific shell and a generic core. Extract the generic core as a standalone unit with clean inputs/outputs.
- Design for reuse, place locally - architect components with clean interfaces, dependency injection, and no hardcoded assumptions. But place them in the feature where they're first needed (per the Graduation Policy in
code-organization.md). The clean design makes future graduation toshared/trivial. - Pure functions by default - helper methods and utilities should be pure functions (no side effects, no hidden state, no implicit dependencies) wherever possible. Pure functions are trivially reusable and testable.
- Parameterize, don't specialize - if a component could serve multiple consumers with minor variations, accept those variations as parameters rather than forking the implementation.
When NOT to Make Something Reusable
- If it requires more than 3 domain-specific parameters to generalize, it's not ready - keep it feature-scoped
- If the "generic" version would be more complex than two specialized copies, don't abstract
- If only one consumer exists and no second consumer is foreseeable, don't over-engineer
Review Checkpoint
When designing any new module, answer these before writing code:
- Does something similar already exist in the codebase?
- What is the generic core vs. the domain-specific wrapper?
- Could another feature, service, or project use this with zero modification?
- Am I hardcoding anything that should be a parameter?
Infrastructure Abstraction - MANDATORY
Every external dependency gets an interface. Storage, email, payments, auth providers, AI models, notification channels - all must be accessed through an abstract interface with swappable backend implementations. No service should be hardwired to a specific vendor or infrastructure.
Adapter Pattern for External Services
Define an abstract interface for each infrastructure concern. Implement one adapter per backend. Application code depends only on the interface, never on a concrete implementation.
This applies to:
- File storage (local filesystem ↔ S3 ↔ GCS)
- Configuration persistence (file ↔ localStorage ↔ IndexedDB)
- Email/SMS (console logger ↔ SES ↔ SendGrid)
- Notifications (log ↔ SNS ↔ WebSocket)
- AI/ML providers (local model ↔ OpenAI ↔ Bedrock)
- Payment processing, auth providers, and any other external service
Factory + Config-Driven Instantiation
Backends are selected via configuration, never hardcoded. A factory function reads the config and returns the correct adapter:
storage = create_storage_backend(settings.STORAGE_BACKEND) # "local" | "s3"
email = create_email_backend(settings.EMAIL_BACKEND) # "console" | "ses"- The factory is the only place that knows about concrete implementations
- Application code receives the interface - it never imports a specific backend directly
- Switching providers is a config change, not a code change
Secure Defaults
All infrastructure adapters must enforce security at the interface level:
- Content-type validation - never trust client-provided MIME types on upload. Validate server-side.
- Size limits at the interface - enforce max file size in the abstract interface, not just the implementation. Every backend inherits the same limits.
- Path traversal prevention - sanitize all keys and paths. Reject
../, absolute paths, and null bytes. - Signed/expiring URLs - never expose raw storage paths (S3 keys, file paths). All download URLs must be signed with expiration.
- Least privilege - each adapter uses credentials scoped to its specific function. Storage adapters don't get database credentials.
Idempotency
- Uploads are idempotent - uploading the same key with the same content is a no-op
- Deletes are idempotent - deleting a non-existent object succeeds silently
- Config writes are idempotent - writing the same value is a no-op
- Design all infrastructure operations so they can be safely retried without side effects
Observability
Every infrastructure adapter must emit structured logs for every operation:
- What: operation type (upload, download, delete, send)
- Target: key, recipient, resource identifier
- Size: bytes transferred (where applicable)
- Duration: wall-clock time of the operation
- Outcome: success or failure with error category
- The abstract interface defines the logging contract - implementations inherit it, not duplicate it
Centralized Configuration & Constants - MANDATORY
ZERO embedded literals. All configuration values, magic numbers, string constants, and environment-dependent settings must live in dedicated, centralized locations - never scattered across modules.
State Persistence - MANDATORY
Every piece of state must have an explicit persistence strategy.
Rules
- Ask "what happens on reload?" - for any state introduced by a feature, explicitly decide: does it survive page reload? If yes, where is it stored?
- Module-level variables are ephemeral - they are destroyed on page reload, content script reload, or server restart. If state must persist, use: localStorage, sessionStorage, IndexedDB, chrome.storage, database, or URL params.
- Single source of truth - never store the same state in multiple locations (e.g., memory + localStorage + database). Pick one authoritative source and derive the rest.
- Document intentional ephemerality - if state is deliberately ephemeral (e.g., a hover state, animation frame), add a comment:
// Ephemeral: resets on reload (intentional). - Sync state on startup - if persistent state exists (localStorage, database), load it on component mount / app startup. Don't start with empty state and wait for a user action to hydrate.
Configuration Hierarchy
backend/src/
├── core/
│ └── config.py # App config: reads from env vars, .env, defaults
├── constants/ # Domain constants - organized by domain
│ ├── auth.py # Roles, token lifetimes, OAuth scopes
│ ├── products.py # Categories, statuses, limits
│ └── common.py # Shared constants (pagination defaults, date formats)frontend/src/
├── shared/
│ └── config/
│ ├── env.ts # Environment-dependent config (API URLs, feature flags)
│ └── constants.ts # App-wide constants
├── features/
│ └── auth/
│ └── constants.ts # Feature-scoped constants (only if used by this feature alone)Rules
- No string literals in business logic - URLs, status values, error messages, field names, limits, thresholds - all go in constants files or config.
- No magic numbers - every numeric value with business meaning gets a named constant with a comment explaining why that value was chosen.
- Config reads from environment - all environment-dependent values (DB URLs, API keys, feature flags, ports) go through a single config module that reads from
.env/ environment variables with typed defaults. - Constants are grouped by domain -
constants/auth.pyfor auth-related values,constants/products.pyfor product-related values. Not one giant constants file. - Feature-scoped constants stay in the feature - if a constant is only used within one feature, it lives in that feature's
constants.tsor at the top of the relevant module. It graduates toshared/orconstants/when a second consumer appears. - Enums over string literals - use enums (Python
Enum, TypeScriptenumoras const) for any value that has a fixed set of options. Never compare against raw strings. - Single source of truth - if the same value is needed by both frontend and backend, define it in the backend and expose it via API or shared schema. Never duplicate constants across the stack.
What Belongs Where
| Value type | Location |
| Environment-dependent (DB URL, API keys, ports) | core/config.py / shared/config/env.ts |
| Domain constants (statuses, roles, categories) | constants/<domain>.py / features/<domain>/constants.ts |
| Shared constants (pagination, date formats) | constants/common.py / shared/config/constants.ts |
| Error messages | constants/<domain>.py or a dedicated constants/errors.py |
| Feature flags | core/config.py (read from env) |
Periodic Review Policy
Purpose
This policy governs when and how automated code reviews are triggered during development. All reviews are powered by hooks in .kiro/hooks/. Results are stored in docs/security/ and docs/reviews/ respectively.
The security review system follows a three-tier model - one prompt, three hooks, three scopes - that matches review depth to development context. See docs/tiered-review-methodology.md for full rationale.
Quick Reference - Which Review, When
Use /review-guide for interactive help. The short version:
| You just... | Run this |
| Finished UI work | /review-ux-live (browser walk) or /review-ux-audit (from code) |
| Completed a feature | /review-code-security (Tier 2) + /review-code-maintainability |
| Changed auth/API code | /review-code-security + /review-api-contracts |
| Added dependencies | /review-dependency-risk |
| Wrote a spec | /review-spec-readiness |
| End of sprint | /review-code-security (Tier 3) + /review-test-quality |
| Shipping AI features | /review-ai-agent-surface |
| Worried about perf | /review-frontend-performance or /review-observability |
Full catalog: 17 prompts in .kiro/prompts/review-*.md
Threat Modeling - Recommended Before First Review
Before running your first Tier 2 or Tier 3 security review, build a threat model for the project using the AWS Labs Threat Modeling MCP Server. This produces a structured threat model (assets, trust boundaries, threat actors, data flows) that the security review prompts can reference to reduce false positives.
Setup: Add to .kiro/settings/mcp.json:
{
"mcpServers": {
"threat-modeling": {
"command": "uvx",
"args": ["threat-modeling-mcp-server"]
}
}
}Workflow: Run the threat modeling process → export to docs/security/THREAT_MODEL.md or .threatmodel/ → the security review prompt reads it before scanning.
Without a threat model, the reviewer infers trust boundaries from code alone, which is the primary cause of false positives.
Tier 1 - Pre-Commit (Every Commit)
Hook: security-tier1-precommit.json
Trigger: Automatically on every git commit
Scope: Staged files only
Fires when:
- Any
git commitcommand is detected - Any call to
scripts/git-commit-push.shis detected
Checks: Secrets, unsafe execution, auth bypass, missing input validation, PII in logs
Output: Inline block/warn response only - no SRR file generated
- CRITICAL and HIGH findings block the commit
- MEDIUM findings warn but allow
Tier 2 - Feature Complete
Hook: security-tier2-feature.json
Trigger: After feature completion or manual invocation
Scope: Files changed since the last SRR, plus new integrations, routes, and IAM definitions
Fires when:
- A new route, Lambda handler, or service is added and marked complete
- Any external integration (OAuth, third-party API, database, message queue) is wired up
- A new IAM role, policy, or cloud resource is defined
- Manually triggered when a feature branch is ready for review
Checks: All Tier 1 categories plus OWASP S1-S13, BOLA/IDOR, cryptographic quality, file upload security
Output: Full SRR report - docs/security/SRR-{###}-{YYYY-MM-DD}-T2.md
- SECURITY_LOG.md updated
- CRITICAL/HIGH findings create immediate fix tasks
- MEDIUM/LOW findings added to roadmap
Tier 3 - Sprint or Phase End
Hook: security-tier3-sprint.json
Trigger: Manual invocation at sprint or phase end
Scope: Full codebase
Fires when:
- End of each development sprint or phase
- Before any major release or deployment
- Manually triggered when systemic drift is suspected
- Any dependency manifest (
package.json,requirements.txt, etc.) has had significant changes across the sprint
Checks: All Tier 1 and Tier 2 categories plus supply chain (D1-D5), secure headers and CORS (S15), logging security (S16), rate limiting systemic review (S14-EXT), AI-generation artifact review, test coverage delta
Output: Full SRR report - docs/security/SRR-{###}-{YYYY-MM-DD}-T3.md
- SECURITY_LOG.md updated
- CRITICAL/HIGH findings create immediate fix tasks
- MEDIUM/LOW findings added to roadmap
- Dependency manifest snapshot recorded in
docs/security/dep-snapshot-{YYYY-MM-DD}.md
Maintainability Review
Trigger: Manual invocation at feature completion and sprint end Scope: Changed files at feature completion; full codebase at sprint end
Fires when:
- A feature or module is marked complete
- Before any major commit or PR
- At the end of each development sprint or phase
- Manually triggered when structural drift is suspected
Output: Full MRR report - docs/reviews/MRR-{###}-{YYYY-MM-DD}.md
- REVIEW_LOG.md updated
- Phase 1 quick wins added to active sprint backlog
- Phase 2 and Phase 3 items added to roadmap
UX Review - Live Browser Walk
Prompt: .kiro/prompts/review-ux-live.md
Agent: .kiro/agents/ux-reviewer.json (restricted-tool: browser MCP + read + script exec only)
Rubric: the "UX Console-Idiom Rubric" rules in CLAUDE.md (loaded on-demand, NOT always-on)
Trigger: Manual invocation at UI feature completion or pre-release
Scope: All user-facing routes (or subset specified by the invoker)
Fires when:
- A
ui/orfeat/branch with frontend changes is marked feature-complete - Before any release (per the release checklist in
versioning.md) - Manually triggered when UX regression is suspected
- After a significant design system or layout change
Does NOT fire:
- On every commit (too expensive - browser automation)
- On backend-only changes with no UI impact
- As always-on context (per research evidence on context-bloat cost)
Checks: Console-idiom rubric (9 families, 44 checks): Density & Type, Surfaces & Layout, Read-First Editing, Save Model, Tables & Lists, Empty States & Feedback, Copy & Correctness, Accessibility & States, Consistency & Tokens
Output: Full UXR report - docs/ux-reviews/UXR-{###}-{YYYY-MM-DD}.md
- Systemic findings + per-page findings with rubric IDs
- Prioritized plan (ship-now / fix-soon / defer)
- Mandatory Corrections/Retractions section
- Gate: PASS (zero Sev-1, no page below 70) or FAIL
Gate behavior:
- FAIL blocks release (same weight as a Tier 2 security finding)
- Sev-1 findings create immediate fix tasks
- Sev-2 findings added to next sprint backlog
Sequencing Rule
When both a security review and a maintainability review are due at the same checkpoint (e.g., end of sprint or feature complete), run in this order:
- Tier 2 or Tier 3 security review first
- Maintainability review second
Security findings may surface structural issues that the maintainability review should account for.
Adversarial Verification - Reducing False Positives
Security reviews use a two-pass approach to reduce false positives:
Pass 1: Discovery (built into the security prompt)
The code-security-reviewer agent scans the codebase and produces findings with the severity calibration rubric and self-verification pass built into the prompt.
Pass 2: Independent Verification (separate agent)
For Tier 2 and Tier 3 reviews, after the discovery pass produces findings, spawn the security-verifier agent to adversarially disprove them:
Instruction to include in T2/T3 review workflow:
After producing the SRR findings above, invoke the security-verifier agent
with ONLY the list of HIGH+ findings (no reasoning, no context from this review).
The verifier will independently search the codebase for compensating controls
and report DISPROVED / CONFIRMED / DOWNGRADE for each finding.
Update the SRR with the verification results before finalizing.When to use each approach
| Context | Verification approach |
| Tier 1 (pre-commit) | None - speed matters, findings are high-confidence patterns |
| Tier 2 (feature complete) | Self-verification pass (built into prompt) |
| Tier 3 (sprint end) | Self-verification + spawn security-verifier agent for HIGH+ findings |
| Pre-release or post-incident | Full independent verification - spawn verifier for ALL findings |
Why two agents matter
The same agent that finds an issue is biased toward confirming it (confirmation bias). A separate agent with no access to the discovery reasoning and instructions to disprove each finding roughly halves false positives (per Anthropic's "Using LLMs to Secure Source Code" research, May 2026).
Output Convention
| Review Type | Output Path | Naming Pattern |
| Security Tier 1 | Inline only | No file |
| Security Tier 2 | docs/security/ | SRR-{###}-{YYYY-MM-DD}-T2.md |
| Security Tier 3 | docs/security/ | SRR-{###}-{YYYY-MM-DD}-T3.md |
| AI Surface Review | docs/security/ | AISR-{###}-{YYYY-MM-DD}.md |
| Maintainability | docs/reviews/ | MRR-{###}-{YYYY-MM-DD}.md |
| UX Review | docs/ux-reviews/ | UXR-{###}-{YYYY-MM-DD}.md |
| Dep snapshot | docs/security/ | dep-snapshot-{YYYY-MM-DD}.md |
Report Numbering
- SRR numbers are sequential across all tiers: SRR-001, SRR-002, SRR-003, ...
- MRR numbers are sequential: MRR-001, MRR-002, MRR-003, ...
- UXR numbers are sequential: UXR-001, UXR-002, UXR-003, ...
- Always check existing files in
docs/security/,docs/reviews/, anddocs/ux-reviews/to determine the next number before creating a new report - The tier suffix (T2 or T3) is appended after the date, not the number
Folder Structure
.kiro/
- hooks/
- security-tier1-precommit.json
- security-tier2-feature.json
- security-tier3-sprint.json
- prompts/
- review-code-security.md
- review-code-maintainability.md
- steering/
- review-policy.md
docs/
- security/
- SECURITY_LOG.md
- dep-snapshot-{YYYY-MM-DD}.md
- SRR-{###}-{YYYY-MM-DD}-T2.md
- SRR-{###}-{YYYY-MM-DD}-T3.md
- reviews/
- REVIEW_LOG.md
- MRR-{###}-{YYYY-MM-DD}.md
- ux-reviews/
- UXR-{###}-{YYYY-MM-DD}.mdinclusion: always description: Operate only inside your own project - never reach into sibling repos, never touch git trees or processes you don't own
Session Isolation
Multiple agent sessions can run on one machine at the same time, sharing the filesystem and sibling git repositories. A session that reaches outside its own project corrupts another session's work. These rules keep each session in its lane.
This is the failure this file prevents:
A session launched for repo tactiq reached into the sibling planiq repo -
git -C /home/sourjya/coding/planiq reset --hard, a cherry-pick, a checkout, a PR - and corrupted the git state of a different session that was actively working planiq. Another session nearly rankillon its own host terminal.
The Boundary - MANDATORY
Your project root is the directory you were launched in. Everything you do stays inside it.
- Never operate on another repository. Do not
cdinto a sibling project, do not rungit -C <other-path> ..., and do not read/write/commit through absolute paths that point outside your project root. - Never infer cross-repo intent. If a task seems to need changes in another repository, STOP and tell the user to run that work in that repo's own session. Do not reach across on your own judgment - that is exactly how
git -C /other/repo reset --hardhappens. - Branches and PRs belong to one repo. Checking out a branch, cherry-picking, rebasing, or opening a PR in a repo that is not your project root is forbidden, even when asked indirectly ("also fix the thing in the other project").
Concurrent Sessions - MANDATORY
Assume another agent may be working in this tree, or a sibling tree, right now.
- Verify before destructive git. Before
reset --hard,checkout -f,clean -fd,cherry-pick,rebase, or branch deletion, confirm: (a) you are in your own project root, and (b) the current branch and working-tree state are what you expect. If the branch orHEADmoved under you, a foreign actor touched the tree - STOP and report, do not "fix" it blindly. - One worktree per concurrent session - the preventive control. When more than one agent session may touch this repo at once, each session gets its own git worktree on its own branch:
git worktree add ../<name> <branch>. Never switch branches in, or share, a single working tree across sessions - that is what collides onHEAD.scripts/branch-check.shis the detective control (warns after a collision); worktrees prevent it. - Run the guard.
bash scripts/session-guard.shrecords this session's lock and warns if another live session holds the tree or ifHEADdrifted unexpectedly.
Processes - MANDATORY
- Never kill a process you did not spawn. Your terminal, your MCP servers, and your parent session share one process tree - killing "the other agent" can be suicide (your own session) or sabotage (the user's other work).
- Identify before you signal. Before any
kill/pkill, map the process tree and confirm the target is something you started. When unsure, report the PIDs to the user and let them decide. Neverpkillby name pattern across the machine.
Cross-references
git-and-focus-discipline.md- in-repo branch discipline, commit cadence, and focus (this file is about cross-repo and cross-session safety).scripts/session-guard.sh- session lock + working-tree integrity check.
inclusion: always
Testing Standards
Test folder organization, task-first discipline, and test-driven development rules.
Test Folder Organization - STRICT
Directory Structure
tests/
├── unit/ # Fast, isolated tests - no I/O, no DB, no network
│ ├── auth/ # Mirrors backend/src domain structure
│ │ ├── test_auth_service.py
│ │ └── test_token_utils.py
│ ├── products/
│ │ ├── test_product_service.py
│ │ └── test_pricing.py
│ └── common/
│ └── test_pagination.py
├── integration/ # Tests that hit real DB, APIs, or external services
│ ├── test_auth_flow.py
│ └── test_product_api.py
├── e2e/ # End-to-end tests (Playwright, Cypress, etc.)
│ └── test_checkout_flow.py
├── property/ # Property-based tests (Hypothesis, fast-check)
│ └── test_pricing_properties.py
├── conftest.py # Root fixtures: DB sessions, auth helpers, test client
└── unit/conftest.py # Unit-specific fixtures (mocks, fakes)Rules
- NEVER create
__tests__/folders, co-located test files, or any alternative test directory structure - Unit test subdirectories mirror source domains - if the source has
services/auth/, tests go intests/unit/auth/ - One test file per source file -
auth_service.py→test_auth_service.py. Never bundle unrelated tests. - Test file naming mirrors source - prefix with
test_(Python) or suffix with.test.ts/.spec.ts(TypeScript) - Shared fixtures in conftest.py - never duplicate fixture setup across test files. Extract to the nearest
conftest.py. - Integration tests are separate from unit tests - unit tests must run without any external dependencies (DB, network, filesystem). Integration tests get their own directory.
- Create subdirectories as domains emerge - don't pre-create empty test directories. Add them when the first test for that domain is written.
Frontend Test Structure
frontend/tests/
├── unit/
│ ├── auth/
│ │ ├── LoginForm.test.tsx
│ │ └── useAuth.test.ts
│ ├── dashboard/
│ │ └── DashboardPage.test.tsx
│ └── shared/
│ └── Button.test.tsx
├── integration/
│ └── auth-flow.test.ts
├── e2e/
│ └── checkout.spec.ts
└── setup.ts # Test setup (mocks, providers, global config)Task-First Discipline - MANDATORY
CRITICAL: No code may be written without a task list. This is non-negotiable.
Where the task list lives:
- Full spec features:
.kiro/specs/{feature-name}/tasks.md - Bug fixes:
docs/bugs/BUG-###-description.md - Minor enhancements: create a minimal task list inline before starting
The task list must be followed in order:
- Read the full task list before writing any code
- Mark each task
[-](in progress) before starting it - Write tests FIRST (RED phase) per TDD rules
- Implement to make tests pass (GREEN phase)
- Mark each task
[x](complete) before moving to the next - Never skip tasks or reorder them without documenting why
Test-Driven Development (TDD) - MANDATORY
The TDD Cycle
- RED: Write a failing test first
- GREEN: Write minimal code to make it pass
- REFACTOR: Clean up while keeping tests green
TDD Workflow Rules
- NEVER write implementation code before writing its test
- NEVER skip the RED phase - you must see the test fail first
- Tests define the contract - implementation fulfills it
- Each test should fail for the right reason (missing functionality, not syntax errors)
Exceptions to TDD
The ONLY exceptions where you may write code before tests:
- Database migrations (but test the models they create)
- Configuration files (JSON, YAML, .env templates)
- Documentation (Markdown, ADRs)
- Build scripts and tooling configuration
Testing Requirements
- Every bug fix must include a regression test
- Minimum 80% code coverage for new code
- Both positive AND negative test cases required for every feature
inclusion: manual description: Console-idiom UX rubric - 9 check families with severity scoring for data-dense admin/SaaS interfaces. Load with /ux-console-idiom when reviewing or generating UI.
UX Console-Idiom Rubric
A checkable UX quality rubric for console-style applications - admin panels, dashboards, SaaS control planes, and data-dense internal tools. Each check has a unique ID, severity, and a binary pass/fail criterion.
Load this file when reviewing, auditing, or generating UI for console-type applications. It layers on top of WCAG 2.2 AA and Nielsen's 10 heuristics as the house standard - specific where they are general.
Customize: Override threshold values in user-project-overrides.md under the Design Tokens section. The IDs and severity model are fixed; the numeric thresholds are team-configurable.
Scoring Model
- Starting score: 100 per page/screen
- Sev-1 (blocking/data-loss/misleading): −15 each
- Sev-2 (idiom/flow break): −5 each
- Sev-3 (polish): −1 each
- Ship gate: zero Sev-1 findings AND no page below 70
Severity Definitions
| Level | Meaning | Examples |
| Sev-1 | Data loss, user blocked, or actively misleading | Modal with no Escape/close; save silently fails; destructive action without confirmation |
| Sev-2 | Breaks the console idiom or interrupts task flow | Multiple save patterns on one page; unreadable density; broken navigation state |
| Sev-3 | Polish issue, cosmetic inconsistency | Inconsistent border-radius; orphaned label; extra font weight |
D - Density & Type
Controls typographic consistency and information density.
| ID | Check | Sev | Criterion |
| D-1 | Body text size | 2 | Body text within the configured range (default: 13–14px). Outliers flagged. |
| D-2 | Font weight count | 2 | No more than 2 font weights in use (default: 400, 600). Each additional weight is a finding. |
| D-3 | Heading hierarchy | 3 | Heading sizes decrease monotonically (h1 > h2 > h3). No size inversions. |
| D-4 | Line height | 3 | Body line-height between 1.4–1.6. Below 1.3 is a Sev-2 density violation. |
| D-5 | Micro-label variant | 3 | Badges, tags, and status pills use a dedicated smaller type size, not body text. |
S - Surfaces & Layout
Controls spatial organization and container usage.
| ID | Check | Sev | Criterion |
| S-1 | Content primacy | 2 | Primary content (forms, descriptions, data) gets ≥60% of viewport height. Metadata is secondary. |
| S-2 | Section control placement | 2 | Section actions are adjacent to (leading or beside) their section header. Never trailing-far-right unless page-level. |
| S-3 | Consistent card/panel usage | 3 | Cards/panels of the same type use identical padding, border-radius, and shadow. |
| S-4 | Scroll containment | 2 | Each scrollable region is independently scrollable. No double-scrollbar or body-scroll-lock-while-panel-scrolls. |
| S-5 | Responsive breakpoints | 3 | Layout does not break or overflow between configured breakpoints. |
R - Read-First Editing
Controls the inline-edit and detail-view patterns.
| ID | Check | Sev | Criterion |
| R-1 | Read state is default | 2 | Detail views render in read mode first. Edit mode requires an explicit user action. |
| R-2 | Edit affordance visible | 2 | The path from read to edit is discoverable within 1 click/tap from the read view. |
| R-3 | Cancel discards cleanly | 2 | Cancelling an edit restores the exact prior read state. No partial saves, no stale form data. |
| R-4 | Dirty-state indicator | 3 | When a form has unsaved changes, a visual indicator (dot, label, or disabled-nav warning) is visible. |
V - Save Model
Controls how mutations are committed and confirmed.
| ID | Check | Sev | Criterion |
| V-1 | One save pattern per app | 2 | The entire application uses ONE save pattern (explicit Save button, auto-save, or inline confirm). Never mix. |
| V-2 | Save confirmation | 2 | Every successful save produces visible feedback (toast, inline check, status change) within 300ms. |
| V-3 | Save failure is loud | 1 | A failed save MUST surface an error the user can act on. Silent save failure is a Sev-1. |
| V-4 | Pending state during save | 3 | The save trigger shows a loading/pending state while the operation is in flight. No stale-looking button. |
T - Tables & Lists
Controls data-grid and list-view patterns.
| ID | Check | Sev | Criterion |
| T-1 | Header–body alignment | 2 | Table headers and body cells use the same layout strategy (grid or flex with identical columns). No alignment drift. |
| T-2 | Row height consistency | 3 | All rows in a scan-mode table have identical height. Variable-height rows break scan flow. |
| T-3 | Empty state | 2 | A table/list with zero items shows a purposeful empty state (message + primary action), never a blank white space. |
| T-4 | Pagination or virtual scroll | 2 | Lists with >50 items are paginated or virtualized. Never render unbounded DOM lists. |
| T-5 | Sort/filter discoverability | 3 | Sort and filter controls are visible without interaction (not hidden behind a menu) on data tables. |
E - Empty States & Feedback
Controls system status communication.
| ID | Check | Sev | Criterion |
| E-1 | Loading state | 2 | Every async-loaded section shows a skeleton or spinner. No blank flash before content appears. |
| E-2 | Error state | 2 | Every section that can fail shows a meaningful error (what happened + what to do next). No raw exception messages. |
| E-3 | Empty state with action | 2 | Empty sections explain what the area is for AND provide a primary creation action. |
| E-4 | Optimistic feedback | 3 | Actions that will succeed >99% of the time may show instant feedback (optimistic UI), with rollback on failure. |
| E-5 | Progress for long operations | 2 | Operations taking >2s show progress (determinate if possible, indeterminate otherwise). |
C - Copy & Correctness
Controls text quality and data display.
| ID | Check | Sev | Criterion |
| C-1 | Pluralization | 2 | All countable nouns pluralize correctly ("1 item" / "3 items", never "1 items"). |
| C-2 | Label/value pairing | 3 | Every data value has an adjacent label. No orphaned numbers or strings without context. |
| C-3 | Truncation with tooltip | 3 | Text that truncates (ellipsis) reveals full content on hover/focus via a tooltip or expansion. |
| C-4 | Consistent date format | 3 | A single date format is used throughout the app (configure in design tokens). |
| C-5 | User-generated names displayed as-is | 2 | User-provided names (project names, labels) display in their original case. Never slug-case or auto-transform. |
A - Accessibility & States
Controls WCAG compliance and interactive state coverage.
| ID | Check | Sev | Criterion |
| A-1 | Focus management | 1 | Modals and dialogs trap focus on open AND return focus to the trigger on close. Violation is Sev-1 if it blocks task completion. |
| A-2 | Contrast ratio | 2 | Text meets WCAG 2.2 SC 1.4.3 (4.5:1 normal, 3:1 large). UI components meet SC 1.4.11 (3:1). |
| A-3 | Keyboard navigation | 1 | Every interactive element is reachable and operable via keyboard. Tab order follows visual order. |
| A-4 | Escape closes overlays | 1 | Pressing Escape closes the topmost overlay (modal, dropdown, drawer). Missing Escape is Sev-1 if it traps the user. |
| A-5 | Named form controls | 2 | Every <input>, <select>, and <textarea> has a visible or aria-label. No unlabelled controls. |
| A-6 | Disabled state clarity | 3 | Disabled controls are visually distinct (opacity, cursor) and have a title/tooltip explaining WHY they are disabled. |
K - Consistency & Tokens
Controls design-system adherence and visual consistency.
| ID | Check | Sev | Criterion |
| K-1 | Border-radius set | 3 | At most N distinct border-radius values in use (default: 2). Additional values are findings. |
| K-2 | Color palette adherence | 2 | All colors used are from the configured design token palette. Off-palette colors are findings. |
| K-3 | Spacing scale | 3 | Padding and margin values align to the configured spacing scale (e.g., 4px increments). |
| K-4 | Icon style consistency | 3 | All icons use the same style family (outline OR filled, not mixed). |
| K-5 | Active/selected state | 2 | The current tab, nav item, or selection uses a consistent active-state treatment across the app. |
Using This Rubric
In review prompts
Reference this file and require findings to carry a rubric ID + severity:
Finding: D-2 (Sev-2) - 4 font weights in use (400, 500, 600, 700). Expected max 2.
Fix: Remove 500 and 700; consolidate to 400/600.In generation-time steering
Reference the design tokens in user-project-overrides.md to prevent writing non-compliant UI in the first place (prevention > detection).
In CI/report gates
Use the scoring model: a page with one Sev-1 finding automatically fails. A page at 65/100 fails. The report must carry the rubric ID for traceability.
Evidence
Findings SHOULD include quantitative evidence from scripts/style-survey.js (computed-style census) or accessibility-tree inspection. Screenshot-only findings are provisional until confirmed by live interaction.
Cross-references
user-project-overrides.md- team-specific threshold overrides (font size, weight count, radii count, spacing scale)ux-pattern-registry.md- reference layout patterns for common screen typesreview-ux-audit.md- review prompt that scores against this rubricreview-ux-live.md- live browser-walk protocol using this rubricscripts/style-survey.js- quantitative evidence collection script
inclusion: manual
UX Pattern Registry
Reference patterns for common screen types. Load with /ux-pattern-registry when designing or reviewing UI.
Before implementing any major screen, find the matching pattern below. If no pattern matches, propose a new entry before coding.
Task Detail View
Use when: User needs to read, edit, comment, or inspect a single task/item.
Layout:
- Large title at top, full width
- Metadata (status, assignee, dates) secondary - compact row or sidebar
- Description and comments get primary content space (dominant height)
- Activity/comments must not be visually minimized
Rules:
- Section controls adjacent to section headers (leading or beside)
- Content canvas gets 60%+ of viewport height
- Comments rendered at body text size (15-16px), not small/muted
- Subtle selection state - no thick decorative borders
Reference: Asana task detail, Linear issue detail, Notion page
Data Table / List View
Use when: User scans, filters, sorts, and selects from many items.
Layout:
- Compact density acceptable
- Column headers aligned with cell content (same layout strategy)
- Bulk actions near selection indicator
- Filters above or beside the table, not buried
Rules:
- Row height consistent - no variable-height rows in scan mode
- Clickable rows have hover state
- Header and body use identical grid/flex strategy (no alignment drift)
- Pagination or infinite scroll - never unbounded lists
Reference: GitHub issues list, Linear board, Airtable grid
Form / Settings Page
Use when: User fills in or modifies structured data.
Layout:
- Spacious - generous vertical gaps between field groups
- Labels above inputs (not beside, unless very short forms)
- Primary action (Save/Submit) at bottom, visually prominent
- Destructive actions (Delete) separated and de-emphasized
Rules:
- Field groups have clear section headers
- Validation errors inline, adjacent to the field
- No horizontal scrolling - form fits viewport width
- Tab order follows visual order
Reference: GitHub settings, Stripe dashboard settings
Dashboard / Overview
Use when: User needs a high-level summary with drill-down capability.
Layout:
- Cards or panels for distinct metric groups
- Most important metrics largest/topmost
- Secondary details accessible via click, not shown upfront
Rules:
- No more than 7±2 top-level items visible without scrolling
- Each card has a clear label and one primary number/status
- Drill-down affordance visible (arrow, link, hover state)
- Empty states for cards with no data
Reference: Vercel dashboard, Datadog overview, GitHub repo home
Modal / Dialog
Use when: User must complete a focused sub-task without losing page context.
Layout:
- Centered, max-width 600px for forms, 800px for content
- Title + close button at top
- Primary action bottom-right, cancel bottom-left
- Content scrolls if needed, buttons stay fixed
Rules:
- Focus trapped inside modal
- Escape closes (unless destructive action in progress)
- Backdrop click closes (unless form has unsaved changes)
- No nested modals - use a drawer or navigate instead
Reference: GitHub "New issue" dialog, Linear "Create issue"
Empty State
Use when: A screen or section has no data to display.
Layout:
- Centered in the content area
- Icon or illustration (optional, subtle)
- Clear message: what this area is for + what to do next
- Primary action button to create/add the first item
Rules:
- Never show a blank white space with no explanation
- Message uses plain language, not technical jargon
- Action button matches the primary creation flow
- Don't show filters/sort controls when there's nothing to filter
Reference: Notion empty page, Linear empty project
inclusion: auto description: Version numbering, git tagging, and release process
Versioning and Release Process
Version Scheme
Semantic Versioning: MAJOR.MINOR.PATCH
- MAJOR (1.0.0, 2.0.0): Breaking changes to public APIs, data formats, or user-facing contracts. Users must update their setup.
- MINOR (0.1.0, 0.2.0): New features, new capabilities. Backward compatible.
- PATCH (0.1.1, 0.1.2): Bug fixes, documentation updates, performance improvements. No new features.
Pre-1.0 Rules (Beta)
While in beta (0.x.y):
- Minor version bumps for feature additions
- Patch bumps for bug fixes
- Breaking changes are allowed without major bump (beta expectation)
- Each minor version gets a git tag
When to Tag
| Trigger | Version bump | Example |
| Bug fix that affects users | PATCH | 0.1.0 -> 0.1.1 |
| Security fix | PATCH (immediate) | 0.1.1 -> 0.1.2 |
| New feature | MINOR | 0.1.2 -> 0.2.0 |
| Breaking change (pre-1.0) | MINOR | 0.2.0 -> 0.3.0 |
| Stable release | MAJOR | 0.x.y -> 1.0.0 |
When NOT to Tag
- Documentation-only changes (no tag, no version bump)
- Internal refactors with no user-visible change
- Test additions
- CI/build changes
<!-- CUSTOMIZE: List all files that must be updated on version bump -->Files to Update on Version Bump
The git tag is the authoritative version. docs/changelogs/CHANGELOG.md is the
authoritative human-readable record. Every other version reference is a copy that
must be kept in sync with them.
Every file that hard-codes the version string must be listed here and bumped on every release. kiro-rails has no build manifest, but its two installers each carry a version constant that gates the "already installed / upgrade" decision, so they ARE version files and must stay in sync with the tag:
install.sh -> CURRENT_VERSION="0.18.0"
install.ps1 -> $CurrentVersion = "0.18.0"(These drifted to 0.15.0 while tags reached 0.17.3 precisely because this list did
not name them - KRL-13. A version reference not on this list is one that silently
never gets bumped.) If you fork kiro-rails into a project WITH a build manifest, add
pyproject.toml / package.json here too; if you strip the installers, remove this
block rather than pointing the checklist at a file that does not exist.
Verify tags and changelog agree before releasing:
git tag -l "v*" | sort -V | tail -1 # latest tag
grep -m1 -oE '^## [0-9-]+ - v[0-9.]+' docs/changelogs/CHANGELOG.md # latest documentedRelease Checklist
- All tests pass
- Lint clean
- Update version in all version files listed above (skip if the project has none)
- Update
docs/changelogs/CHANGELOG.md- move Unreleased items under the new version header with date - Regenerate the Claude bonus layer - the committed
.claude/tree is a generated artifact and MUST be refreshed before tagging:bash scripts/export-to-claude.sh && git add .claude. Verify it is in sync withbash scripts/check-claude-fresh.sh(must printOK). - Commit:
chore: release vX.X.X - Tag:
git tag -a vX.X.X -m "vX.X.X - brief description" - Push:
git push origin main --tags
Tag Format
v0.1.0 (not 0.1.0 - always prefix with v)
v0.2.0
v1.0.0Git Tag Commands
# Create annotated tag (always use annotated, not lightweight)
git tag -a v0.1.0 -m "v0.1.0 - description"
# Push tag
git push origin v0.1.0
# List tags
git tag -l "v*"
# Delete a tag (if you made a mistake)
git tag -d v0.1.0
git push origin --delete v0.1.0Does 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.

