8.6 KiB
Agent Guidelines
Precedence: Nearest AGENTS.md wins. This is the root file with global defaults.
Project: LDAP Selfservice Password Changer — hybrid Go + TypeScript web application with WCAG 2.2 AAA accessibility compliance.
Quick Navigation
- internal/AGENTS.md - Go backend services
- internal/web/AGENTS.md - TypeScript frontend & Tailwind CSS
Global Defaults
Project Overview
Self-service password change/reset web app for LDAP/ActiveDirectory with email-based password reset, rate limiting, and strict accessibility standards. Single binary deployment with embedded assets.
Stack: Go 1.25 + Fiber, TypeScript (ultra-strict), Tailwind CSS 4, Docker multi-stage builds, pnpm 10.18
Key characteristics:
- Docker-first: All dev/CI must work via Docker
- Accessibility: WCAG 2.2 AAA compliance (7:1 contrast, keyboard nav, screen readers)
- Type-safe: Go with testcontainers, TypeScript with all strict flags
- Security-focused: LDAPS, rate limiting, token-based reset, no password storage
Setup
Prerequisites: Docker + Docker Compose (required), Go 1.25+, Node.js 24+, pnpm 10.18+ (for native dev)
# Clone and setup environment
git clone <repo>
cd ldap-selfservice-password-changer
cp .env.local.example .env.local # Edit with your LDAP config
# Docker (recommended)
docker compose --profile dev up
# Native development
pnpm install
go mod download
pnpm dev # Runs concurrent TS watch, CSS watch, and Go with hot-reload
See docs/development-guide.md for comprehensive setup.
Build & Test Commands
Package manager: pnpm (specified in package.json: pnpm@10.18.1)
# Build everything
pnpm build # Build frontend assets + Go binary
# Frontend only
pnpm build:assets # TypeScript + Tailwind CSS
pnpm js:build # TypeScript compile + minify
pnpm css:build # Tailwind CSS + PostCSS
# Development (watch mode)
pnpm dev # Concurrent: TS watch, CSS watch, Go hot-reload
pnpm js:dev # TypeScript watch
pnpm css:dev # CSS watch
pnpm go:dev # Go with nodemon hot-reload
# Tests
go test -v ./... # All Go tests with verbose output
go test ./internal/... # Specific package tests
# Formatting
pnpm prettier --write . # Format TS, Go templates, config files
pnpm prettier --check . # Check formatting (CI)
# Type checking
pnpm js:build # TypeScript strict compilation (no emit in dev)
go build -v ./... # Go compilation + type checking
CI commands (from .github/workflows/check.yml):
- Type check:
pnpm js:buildandgo build -v ./... - Format check:
pnpm prettier --check . - Tests:
go test -v ./...
Code Style
TypeScript:
- Ultra-strict tsconfig:
strict: true,noUncheckedIndexedAccess,exactOptionalPropertyTypes,noPropertyAccessFromIndexSignature - Prettier formatting: 120 char width, semicolons, double quotes, 2-space tabs
- No
anytypes - use proper type definitions - Follow existing patterns in
internal/web/static/
Go:
- Standard Go formatting (
go fmt) - Prettier with
prettier-plugin-go-templatefor HTML templates - Follow Go project layout:
internal/for private packages,main.goat root - Use testcontainers for integration tests (see
*_test.gofiles) - Error wrapping with context
General:
- Composition over inheritance
- SOLID, KISS, DRY, YAGNI principles
- Law of Demeter: minimize coupling
- No secrets in VCS (use .env.local, excluded from git)
Security
- No secrets in git: Use
.env.local(gitignored), never commit LDAP credentials - LDAPS required: Production must use encrypted LDAP connections
- Rate limiting: 3 requests/hour per IP (configurable via
RATE_LIMIT_*env vars) - Token security: Cryptographic random tokens with configurable expiry
- Input validation: Strict validation on all user inputs (see
internal/validators/) - Dependency scanning: Renovate enabled, review changelogs for major updates
- No PII logging: Redact sensitive data in logs
- Run as non-root: Dockerfile uses UID 65534 (nobody)
PR/Commit Checklist
✅ Before commit:
- Run
pnpm prettier --write .(format all) - Run
pnpm js:build(TypeScript strict check) - Run
go test ./...(all tests pass) - Run
go build(compilation check) - No secrets in changed files
- Update docs if behavior changed
- WCAG 2.2 AAA compliance maintained (if UI changed)
✅ Commit format: Conventional Commits
type(scope): description
Examples:
feat(auth): add password reset via email
fix(validators): correct password policy regex
docs(api): update JSON-RPC examples
chore(deps): update pnpm to v10.18.1
No Claude attribution in commit messages.
✅ PR requirements:
- All CI checks pass (types, formatting, tests)
- Keep PRs small (~≤300 net LOC if possible)
- Include ticket ID if applicable:
fix(rate-limit): ISSUE-123: fix memory leak - Update relevant docs in same PR (README, AGENTS.md, docs/)
Good vs Bad Examples
✅ Good - TypeScript strict types:
interface PasswordPolicy {
minLength: number;
requireNumbers: boolean;
}
function validatePassword(password: string, policy: PasswordPolicy): boolean {
const hasNumber = /\d/.test(password);
return password.length >= policy.minLength && (!policy.requireNumbers || hasNumber);
}
❌ Bad - Using any or unsafe access:
function validatePassword(password: any, policy: any) {
// ❌ any types
return password.length >= policy.minLength; // ❌ unsafe access
}
✅ Good - Go error handling:
func connectLDAP(config LDAPConfig) (*ldap.Conn, error) {
conn, err := ldap.DialURL(config.URL)
if err != nil {
return nil, fmt.Errorf("failed to connect to LDAP at %s: %w", config.URL, err)
}
return conn, nil
}
❌ Bad - Ignoring errors:
func connectLDAP(config LDAPConfig) *ldap.Conn {
conn, _ := ldap.DialURL(config.URL) // ❌ ignoring error
return conn // ❌ may return nil
}
✅ Good - Accessible UI:
<button type="submit" aria-label="Submit password change" class="bg-blue-600 hover:bg-blue-700 focus:ring-4">
Change Password
</button>
❌ Bad - Inaccessible UI:
<div onclick="submit()">Submit</div>
❌ not keyboard accessible, wrong semantics
When Stuck
- Check existing docs: docs/ has comprehensive guides
- Review similar code: Look for patterns in
internal/packages - Run tests:
go test -v ./...often reveals issues - Check CI logs: GitHub Actions shows exact failure points
- Verify environment: Ensure
.env.localis properly configured - Docker issues:
docker compose down -v && docker compose --profile dev up --build - Type errors: Review
tsconfig.jsonstrict flags, use proper types - Accessibility: See docs/accessibility.md for WCAG 2.2 AAA guidelines
House Rules
Docker-First Philosophy:
- All dev and CI must work via Docker Compose
- Native setup is optional convenience, not requirement
- Use profiles in compose.yml:
--profile devor--profile test
Documentation Currency:
- Update docs in same PR as code changes
- No drift between code and documentation
- Keep README, AGENTS.md, and docs/ synchronized
Testing Standards:
- Aim for ≥80% coverage on changed code
- Use testcontainers for integration tests (see existing
*_test.go) - For bugfixes: write failing test first (TDD)
- Tests must pass before PR approval
Scope Discipline:
- Build only what's requested
- No speculative features
- MVP first, iterate based on feedback
- YAGNI: You Aren't Gonna Need It
Accessibility Non-Negotiable:
- WCAG 2.2 AAA compliance required
- 7:1 contrast ratios for text
- Full keyboard navigation support
- Screen reader tested (VoiceOver/NVDA)
- See docs/accessibility.md
Commit Practices:
- Atomic commits: one logical change per commit
- Conventional Commits format enforced
- Never commit secrets or
.env.local - Keep PRs focused and reviewable
Type Safety:
- TypeScript: No
any, all strict flags enabled - Go: Leverage type system, avoid
interface{} - Validate inputs at boundaries
Dependency Management:
- Renovate auto-updates enabled
- Major version updates require changelog review
- Use Context7 MCP or official docs for migrations
- Keep pnpm-lock.yaml and go.sum committed