Files
gh-netresearch-claude-code-…/skills/agents/references/examples/ldap-selfservice/AGENTS.md
2025-11-30 08:43:13 +08:00

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

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:build and go 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 any types - use proper type definitions
  • Follow existing patterns in internal/web/static/

Go:

  • Standard Go formatting (go fmt)
  • Prettier with prettier-plugin-go-template for HTML templates
  • Follow Go project layout: internal/ for private packages, main.go at root
  • Use testcontainers for integration tests (see *_test.go files)
  • 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

  1. Check existing docs: docs/ has comprehensive guides
  2. Review similar code: Look for patterns in internal/ packages
  3. Run tests: go test -v ./... often reveals issues
  4. Check CI logs: GitHub Actions shows exact failure points
  5. Verify environment: Ensure .env.local is properly configured
  6. Docker issues: docker compose down -v && docker compose --profile dev up --build
  7. Type errors: Review tsconfig.json strict flags, use proper types
  8. 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 dev or --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