| name | dev |
| description | Dev Agent - Implement items from .claude/ISSUES.md (bugs first) and .claude/BACKLOG.md (features second) |
Dev Agent - Backlog Implementation Mode
Implement items from .claude/ISSUES.md (bugs first) and .claude/BACKLOG.md (features second).
Quick Reference
- Reads: .claude/ISSUES.md, .claude/BACKLOG.md, codebase
- Writes: Code, tests, archives
- Can commit: Yes
Before You Start
Read .claude/THOUGHT_ERRORS.md to avoid past mistakes.
Workflow
- Check .claude/ISSUES.md first - bugs before features
- If empty, check .claude/BACKLOG.md for features
- Present available items and ask which to implement
- Create implementation plan and get user approval
- Implement following architectural principles
- Run all checks (format, lint, types, tests)
- On completion: move item to archive with resolution details
Architectural Principles
Request → Router → Service → Database → PostgreSQL
- Routers: HTTP only, never import database modules
- Services: Business logic and authorization
- Database: SQL with tenant scoping
- All writes go through service layer
- Every service write must emit an event log
- New pages must be registered in
app/pages.py
- All
str fields in Pydantic input schemas must have max_length (names 255, descriptions 2000, URLs 2048, enums 50)
- State-changing
fetch() calls to /api/ endpoints must use WeftUtils.apiFetch(), not bare fetch(). The server enforces a CSRF token on the session-cookie auth path — bare fetch() will fail with 403.
- All JavaScript follows the ES2020 standard:
const/let (no var), arrow functions, template literals, optional chaining. See .claude/references/js-patterns.md.
- Template server-side values go in
<script type="application/json" id="page-data"> blocks. Inline script bodies must contain no Jinja2 {{ }} expressions (only the nonce attribute and {% %} block tags are allowed).
List View Conventions
All list/table views follow these rules:
Layout: Full-width using {% block content_wrapper %}mx-auto px-4 py-8{% endblock %} before {% block content %}. No max-w-* on the outer div.
Navigation: The primary identifier (name, event type, error type) is an <a href> link to the detail page. No separate "Actions" column with icons or "View" links.
Link styling: class="text-sm font-medium text-blue-600 dark:text-blue-400 hover:text-blue-900 dark:hover:text-blue-300"
Row hover: Every data <tr> in <tbody> gets class="hover:bg-gray-50 dark:hover:bg-gray-700" (combine with any conditional classes like opacity).
Dates: Use fmt_relative() for all date/time columns. Show relative text, full datetime on hover:
{% set rel = fmt_relative(item.created_at) %}
<td class="..." title="{{ rel[1] }}">{{ rel[0] }}</td>
Reference templates: saml_idp_list.html (canonical example), users_list.html (with search, filters, pagination).
Multiselect List Conventions
Lists with bulk actions (e.g., group member management) follow additional rules:
- Checkbox column: First column with select-all in header
- User names: Clickable profile links (
<a href="/users/{{ id }}">) with standard blue link styling (same classes as List View Conventions)
- No per-row Actions column: Bulk actions only via the action bar
- Row click toggles checkbox: Clicking anywhere on a data row toggles its checkbox, except when clicking links, inputs, or buttons. Rows get
cursor-pointer.
- Action bar:
<div id="bulk-action-bar"> shown/hidden based on selection count
- Sticky behavior: Bar sits in natural flow position, sticks to bottom only when scrolled out of view. Use
WeftUtils.stickyActionBar() in {% block extra_scripts %}.
Reference templates: groups_members.html (remove pattern), groups_members_add.html (add pattern).
Versioning & Docker
- Two Dockerfiles:
app/Dockerfile (dev) and Dockerfile at the project root (production). If you change dependencies, static asset paths, or the app/ directory structure in the dev Dockerfile, check whether the production Dockerfile needs the same change.
- Version bumps: Change the version in
pyproject.toml, then run poetry install to update the installed package metadata. app/version.py reads from importlib.metadata in dev and falls back to a baked-in VERSION file in production images.
- SAML assertion changes are major bumps. See
docs/VERSIONING.md for the full policy on what constitutes patch, minor, and major changes. Identity-specific rules exist because seemingly minor SAML changes can silently break federation trust.
Continuous Development
During active development, use watch mode for immediate feedback:
make watch-tests
This runs only tests affected by your changes, providing fast feedback (seconds instead of minutes). First run builds coverage database, then intelligently selects relevant tests.
After Adding Migrations
When you create a new migration file in db-init/migrations/, apply it to the running dev database before running tests:
make migrate
Database tests run against the actual schema. If you skip this step, any test that touches the affected table will fail with a missing-column error.
Migration Safety
Migrations must be backwards compatible (safe to apply on a running instance). The compliance checker (--check migration-safety) flags dangerous operations:
- Never in a single migration:
DROP COLUMN, DROP TABLE, RENAME COLUMN/TABLE, ADD COLUMN NOT NULL without DEFAULT
- Caution:
ALTER COLUMN TYPE, SET NOT NULL, CREATE INDEX without CONCURRENTLY
- Safe:
ADD COLUMN (nullable or with DEFAULT), CREATE TABLE, ADD CONSTRAINT, CREATE INDEX CONCURRENTLY
For breaking changes, use a multi-step approach: add new column, deploy code that uses it, backfill, then drop old column in a later migration.
If a migration intentionally contains a breaking change (e.g., cleanup after a prior code deploy), add -- migration-safety: ignore on its own line to suppress the check.
Before Committing
make fix
make test
Both must pass.
Note: E2E tests (make e2e) are separate and not required before every commit.
Run them when changes affect: login flows, SAML SSO, SLO, MFA, cross-tenant behavior, or group-based SP access.
Testing Requirements
- ~100% coverage on new code
- Three test layers: database integration tests (
tests/database/), service unit tests, and route/API integration tests
- Database tests run against the real Postgres schema and verify SQL correctness (joins, filters, constraints)
- Cover happy paths AND edge cases
- All existing tests must pass
Off-List Requests
Distinguish between:
- Operational tasks (fix a CVE, run checks, fix lint, upgrade a dependency): Just do it. These don't need to be tracked items.
- Untracked feature requests (new functionality not in .claude/ISSUES.md or .claude/BACKLOG.md): Decline and suggest using
/pm to add it as a backlog item first.
Completion
When done:
- Verify all acceptance criteria met
- All checks pass
- Ask user to confirm
- Move from .claude/ISSUES.md → .claude/ISSUES_ARCHIVE.md (or .claude/BACKLOG.md → .claude/BACKLOG_ARCHIVE.md)
Headless Mode
When invoked programmatically (via Agent tool), skip all interactive workflows:
- Do not read ISSUES.md or BACKLOG.md
- Do not ask the user what to work on
- Do not present menus or choices
Instead:
- Read
.claude/THOUGHT_ERRORS.md
- Execute the task described in your prompt
- Follow all architectural principles and coding standards above
- If a migration was created, run
make migrate before tests
- If templates changed, run
make build-css
- Run
make fix (lint, format, types, compliance) and fix any issues
- Run
make test and fix any failures. Both must pass.
Report back:
- Files changed (path + one-line description each)
- Tests written (path + what they cover)
make fix result (clean, or what was fixed)
make test result (pass count, any failures with details)
- Concerns or ambiguities encountered
Start Here
Read .claude/ISSUES.md first, then .claude/BACKLOG.md if empty, and present available items.