|
| 1 | +# Agent Guidelines |
| 2 | + |
| 3 | +## Architecture Overview |
| 4 | +- **Deployment**: Standard HTTP server OR AWS Lambda (both supported) |
| 5 | +- **GitHub**: GitHub App required (JWT + installation token authentication) |
| 6 | +- **Features**: Okta group sync + PR compliance monitoring (both optional) |
| 7 | +- **Entry points**: |
| 8 | + - `cmd/server/main.go` - Standard HTTP server (VPS, container, K8s) |
| 9 | + - `cmd/lambda/main.go` - Lambda adapter (API Gateway + EventBridge) |
| 10 | + - `cmd/verify/main.go` - Integration tests with HTTP mock servers |
| 11 | + - `cmd/sample/main.go` - **DO NOT RUN** (requires live credentials) |
| 12 | +- **Packages**: |
| 13 | + - `internal/app/` - Core logic, configuration, and HTTP handlers (no AWS |
| 14 | + dependencies) |
| 15 | + - `internal/github/` - API client, webhooks, PR checks, team mgmt, auth |
| 16 | + - `internal/okta/` - API client, group sync |
| 17 | + - `internal/notifiers/` - Slack formatting for events and reports |
| 18 | + - `internal/errors/` - Sentinel errors |
| 19 | + |
| 20 | +## Build & Test |
| 21 | +- **Build server**: `make build-server` (creates `dist/server`) |
| 22 | +- **Build Lambda**: `make build-lambda` (creates `dist/bootstrap`) |
| 23 | +- **Run server locally**: `make server` |
| 24 | +- **Test all**: `make test` (runs with `-race -count=1`) |
| 25 | +- **Test single package**: `go test -race -count=1 ./internal/github` |
| 26 | +- **Test single function**: `go test -race -count=1 ./internal/okta -run |
| 27 | + TestGroupSync` |
| 28 | +- **Integration tests**: `make test-verify` (offline tests using HTTP mock |
| 29 | + servers) |
| 30 | +- **Verbose integration tests**: `make test-verify-verbose` (shows all HTTP |
| 31 | + requests during testing) |
| 32 | +- **Lint**: `go vet ./...` and `gofmt -l .` |
| 33 | + |
| 34 | +IMPORTANT: DO NOT run `go run cmd/sample/main.go` as it requires live |
| 35 | +credentials and makes real API calls to GitHub/Okta/Slack. |
| 36 | + |
| 37 | +## Code Style |
| 38 | +- **Imports**: stdlib, blank line, third-party, local (e.g., `internal/`) |
| 39 | +- **Naming**: `PascalCase` exports, `camelCase` private, `ALL_CAPS` env vars (prefixed `APP_`) |
| 40 | +- **Structs**: define types in package; constructors as `New()` or `NewTypeName()`; methods public (PascalCase) |
| 41 | +- **Formatting**: `gofmt` (tabs for indentation) |
| 42 | +- **Comments**: rare, lowercase, short; prefer self-documenting code |
| 43 | +- **Error handling**: return errors up stack; wrap with `fmt.Errorf` (see Error Handling below) |
| 44 | + |
| 45 | +## Error Handling |
| 46 | + |
| 47 | +### Error Message Format |
| 48 | +- **Style**: lowercase, action-focused, concise |
| 49 | +- **Pattern**: `"failed to {action} {object}: {context}"` |
| 50 | +- **Always include**: specific identifiers (PR numbers, team names, IDs) |
| 51 | +- **Examples**: |
| 52 | + - ✅ `"failed to fetch pr #123 from owner/repo: %w"` |
| 53 | + - ✅ `"failed to create team 'engineers' in org 'myorg': %w"` |
| 54 | + - ✅ `"required check 'ci' did not pass"` |
| 55 | + - ❌ `"PR is nil"` (no context) |
| 56 | + - ❌ `"Failed to Get Team"` (capitalized, generic) |
| 57 | + |
| 58 | +### Error Wrapping |
| 59 | +- Use `github.com/cockroachdb/errors` package for all error handling |
| 60 | +- **Wrap errors**: `errors.Wrap(err, "context")` or `errors.Wrapf(err, |
| 61 | + "failed to sync team '%s'", teamName)` |
| 62 | +- **Create new errors**: `errors.New("message")` or `errors.Newf("error: |
| 63 | + %s", context)` |
| 64 | +- Automatically captures stack traces for debugging Lambda issues |
| 65 | +- Preserve original error context while adding specific details |
| 66 | + |
| 67 | +### Sentinel Errors |
| 68 | +- Define common errors in `internal/errors/errors.go` |
| 69 | +- Each sentinel error is marked with a domain type (ValidationError, |
| 70 | + AuthError, APIError, ConfigError) |
| 71 | +- Domain markers enable error classification and monitoring |
| 72 | +- Use `errors.Is()` to check for sentinel errors in tests |
| 73 | +- Use `errors.HasType()` to check for error domains |
| 74 | +- Examples: `ErrMissingPRData`, `ErrInvalidSignature`, `ErrClientNotInit` |
| 75 | + |
| 76 | +### Stack Traces |
| 77 | +- Automatically captured when wrapping errors with cockroachdb/errors |
| 78 | +- No performance overhead unless error is formatted |
| 79 | +- Critical for debugging serverless Lambda executions |
| 80 | +- Use `errors.WithDetailf()` to add structured context to auth/API errors |
| 81 | + |
| 82 | +### Validation |
| 83 | +- Validate at parse time, not during processing |
| 84 | +- Webhook events validated in `ParseXxxEvent()` functions |
| 85 | +- Return detailed validation errors immediately |
| 86 | +- Prevents nil pointer issues downstream |
| 87 | + |
| 88 | +### Error Logging vs Returning |
| 89 | +- **Fatal errors** (config, init): return immediately |
| 90 | +- **Recoverable errors** (individual items in batch): collect and continue |
| 91 | +- **Optional features** (notifications): log only, don't fail parent operation |
| 92 | +- Lambda handlers: log detailed errors, return sanitized messages to client |
| 93 | + |
| 94 | +### Batch Operation Errors |
| 95 | +- Collect errors in result structs (e.g., `SyncReport.Errors`) |
| 96 | +- Continue processing remaining items |
| 97 | +- Return aggregated results with partial success |
| 98 | +- Helper methods: `HasErrors()`, `HasChanges()` |
| 99 | + |
| 100 | +## Authentication & Integration |
| 101 | + |
| 102 | +### GitHub |
| 103 | +- **Required**: GitHub App (JWT + installation tokens, automatic rotation) |
| 104 | +- **Auth flow**: JWT signed with private key → exchange for installation token → cached with auto-refresh |
| 105 | +- **Webhooks**: HMAC-SHA256 signature verification |
| 106 | + |
| 107 | +### Okta |
| 108 | +- OAuth 2.0 with private key authentication |
| 109 | +- **Required scopes**: `okta.groups.read` and `okta.users.read` |
| 110 | +- Sync uses slug-based GitHub Teams API |
| 111 | + |
| 112 | +### Slack |
| 113 | +- Optional notifications for PR events and sync reports |
| 114 | +- Configuration in `internal/notifiers/` |
| 115 | + |
| 116 | +## Markdown Style |
| 117 | +- **Line length**: Max 80 characters for prose text |
| 118 | +- **Exceptions**: Code blocks, links, ASCII art, tables |
| 119 | +- **Table alignment**: Align columns in plaintext for readability |
| 120 | +- **Wrapping**: Break at natural boundaries (commas, periods, conjunctions) |
| 121 | +- **Lists**: Indent continuation lines with 2 spaces |
| 122 | + |
| 123 | +NOTE: AGENTS.md itself is exempt from the 80-character line limit. |
0 commit comments