{"repo": "chris-regnier/gavel", "files": {"CLAUDE.md": {"227995569ab2fb3b19bdd3549773a46b0b12449e": {"t": 1785746482.3215718, "n": 11653, "h": "b08c3d3c46aeecad", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # builds dist/gavel for current platform\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\ntask check # full CI parity: generate + lint + test + cross-compile (run before pushing)\ntask release:dry-run # rehearse the release pipeline locally (no tag, no push, no gh release)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool (after task build, binary is in dist/)\nOPENROUTER_API_KEY=... ./dist/gavel analyze --dir ./internal/input\n./dist/gavel judge # evaluate most recent analysis\n./dist/gavel judge --result # evaluate specific analysis\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nanalyze: Input Handler → BAML Analyzer → SARIF Assembler → FileStore (SARIF)\njudge: FileStore (SARIF) → Rego Evaluator → FileStore (Verdict)\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, output analysis summary JSON (id, findings count, scope, persona)\n\n**Data flow in `cmd/gavel/judge.go`:**\n1. Load tiered config (for telemetry)\n2. Resolve result ID (provided via `--result` or most recent from store)\n3. Read SARIF from store\n4. Evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Minimal ~50 word prompt optimized for small models. Focuses on bugs, error handling, security. See issue #35 for A/B evidence.\n- `code-reviewer-verbose`: Original ~250 word detailed prompt. Better for large models (Sonnet, GPT-4) that follow complex instructions well.\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\ntask release:dry-run # Full release rehearsal (test + build + changelog, no publish)\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n\n**Pinned CI tooling:**\n\n`baml-cli` and `goimports` are installed at pinned versions in every workflow via the `./.github/actions/install-tools` composite action. CI and release builds must be reproducible — do NOT reintroduce `@latest` in any workflow.\n\nTo upgrade a pin, edit the `default:` value of the relevant input in `.github/actions/install-tools/action.yml` (one-line change), then run `task generate && task check` locally. Keep `baml-cli-version` in sync with `github.com/boundaryml/baml` in `go.mod`, and `goimports-version` in sync with `golang.org/x/tools` in `go.sum`. See chris-regnier/gavel#88 for background.\n"}, "f976570019ceee00212b0a362fd56c87d0230a3e": {"t": 1785764798.9202013, "n": 11653, "h": "b08c3d3c46aeecad", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # builds dist/gavel for current platform\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\ntask check # full CI parity: generate + lint + test + cross-compile (run before pushing)\ntask release:dry-run # rehearse the release pipeline locally (no tag, no push, no gh release)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool (after task build, binary is in dist/)\nOPENROUTER_API_KEY=... ./dist/gavel analyze --dir ./internal/input\n./dist/gavel judge # evaluate most recent analysis\n./dist/gavel judge --result # evaluate specific analysis\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nanalyze: Input Handler → BAML Analyzer → SARIF Assembler → FileStore (SARIF)\njudge: FileStore (SARIF) → Rego Evaluator → FileStore (Verdict)\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, output analysis summary JSON (id, findings count, scope, persona)\n\n**Data flow in `cmd/gavel/judge.go`:**\n1. Load tiered config (for telemetry)\n2. Resolve result ID (provided via `--result` or most recent from store)\n3. Read SARIF from store\n4. Evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Minimal ~50 word prompt optimized for small models. Focuses on bugs, error handling, security. See issue #35 for A/B evidence.\n- `code-reviewer-verbose`: Original ~250 word detailed prompt. Better for large models (Sonnet, GPT-4) that follow complex instructions well.\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\ntask release:dry-run # Full release rehearsal (test + build + changelog, no publish)\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n\n**Pinned CI tooling:**\n\n`baml-cli` and `goimports` are installed at pinned versions in every workflow via the `./.github/actions/install-tools` composite action. CI and release builds must be reproducible — do NOT reintroduce `@latest` in any workflow.\n\nTo upgrade a pin, edit the `default:` value of the relevant input in `.github/actions/install-tools/action.yml` (one-line change), then run `task generate && task check` locally. Keep `baml-cli-version` in sync with `github.com/boundaryml/baml` in `go.mod`, and `goimports-version` in sync with `golang.org/x/tools` in `go.sum`. See chris-regnier/gavel#88 for background.\n"}, "bba7fb8103ea7aaaa2b009d7612f6c5e5b4dccc8": {"t": 1785764800.2092018, "n": 11470, "h": "5117104dd283ae92", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # builds dist/gavel for current platform\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\ntask check # full CI parity: generate + lint + test + cross-compile (run before pushing)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool (after task build, binary is in dist/)\nOPENROUTER_API_KEY=... ./dist/gavel analyze --dir ./internal/input\n./dist/gavel judge # evaluate most recent analysis\n./dist/gavel judge --result # evaluate specific analysis\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nanalyze: Input Handler → BAML Analyzer → SARIF Assembler → FileStore (SARIF)\njudge: FileStore (SARIF) → Rego Evaluator → FileStore (Verdict)\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, output analysis summary JSON (id, findings count, scope, persona)\n\n**Data flow in `cmd/gavel/judge.go`:**\n1. Load tiered config (for telemetry)\n2. Resolve result ID (provided via `--result` or most recent from store)\n3. Read SARIF from store\n4. Evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Minimal ~50 word prompt optimized for small models. Focuses on bugs, error handling, security. See issue #35 for A/B evidence.\n- `code-reviewer-verbose`: Original ~250 word detailed prompt. Better for large models (Sonnet, GPT-4) that follow complex instructions well.\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n\n**Pinned CI tooling:**\n\n`baml-cli` and `goimports` are installed at pinned versions in every workflow via the `./.github/actions/install-tools` composite action. CI and release builds must be reproducible — do NOT reintroduce `@latest` in any workflow.\n\nTo upgrade a pin, edit the `default:` value of the relevant input in `.github/actions/install-tools/action.yml` (one-line change), then run `task generate && task check` locally. Keep `baml-cli-version` in sync with `github.com/boundaryml/baml` in `go.mod`, and `goimports-version` in sync with `golang.org/x/tools` in `go.sum`. See chris-regnier/gavel#88 for background.\n"}, "6b4ed654064061166e8bd3e40f47702efab9b5b8": {"t": 1785764802.963548, "n": 10843, "h": "1a3847027cc83433", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # builds dist/gavel for current platform\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\ntask check # full CI parity: generate + lint + test + cross-compile (run before pushing)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool (after task build, binary is in dist/)\nOPENROUTER_API_KEY=... ./dist/gavel analyze --dir ./internal/input\n./dist/gavel judge # evaluate most recent analysis\n./dist/gavel judge --result # evaluate specific analysis\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nanalyze: Input Handler → BAML Analyzer → SARIF Assembler → FileStore (SARIF)\njudge: FileStore (SARIF) → Rego Evaluator → FileStore (Verdict)\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, output analysis summary JSON (id, findings count, scope, persona)\n\n**Data flow in `cmd/gavel/judge.go`:**\n1. Load tiered config (for telemetry)\n2. Resolve result ID (provided via `--result` or most recent from store)\n3. Read SARIF from store\n4. Evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Minimal ~50 word prompt optimized for small models. Focuses on bugs, error handling, security. See issue #35 for A/B evidence.\n- `code-reviewer-verbose`: Original ~250 word detailed prompt. Better for large models (Sonnet, GPT-4) that follow complex instructions well.\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "f7585c67a8fdb68f040135b3eda39ec1b5868ed3": {"t": 1785764804.3495033, "n": 10745, "h": "0a3193db26edd83a", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # builds dist/gavel for current platform\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool (after task build, binary is in dist/)\nOPENROUTER_API_KEY=... ./dist/gavel analyze --dir ./internal/input\n./dist/gavel judge # evaluate most recent analysis\n./dist/gavel judge --result # evaluate specific analysis\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nanalyze: Input Handler → BAML Analyzer → SARIF Assembler → FileStore (SARIF)\njudge: FileStore (SARIF) → Rego Evaluator → FileStore (Verdict)\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, output analysis summary JSON (id, findings count, scope, persona)\n\n**Data flow in `cmd/gavel/judge.go`:**\n1. Load tiered config (for telemetry)\n2. Resolve result ID (provided via `--result` or most recent from store)\n3. Read SARIF from store\n4. Evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Minimal ~50 word prompt optimized for small models. Focuses on bugs, error handling, security. See issue #35 for A/B evidence.\n- `code-reviewer-verbose`: Original ~250 word detailed prompt. Better for large models (Sonnet, GPT-4) that follow complex instructions well.\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "aa8bf5116606baa5034b6cec58992d703adc0835": {"t": 1785764805.7269719, "n": 10518, "h": "2bd557fc8f1b62bd", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # builds dist/gavel for current platform\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool (after task build, binary is in dist/)\nOPENROUTER_API_KEY=... ./dist/gavel analyze --dir ./internal/input\n./dist/gavel judge # evaluate most recent analysis\n./dist/gavel judge --result # evaluate specific analysis\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nanalyze: Input Handler → BAML Analyzer → SARIF Assembler → FileStore (SARIF)\njudge: FileStore (SARIF) → Rego Evaluator → FileStore (Verdict)\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, output analysis summary JSON (id, findings count, scope, persona)\n\n**Data flow in `cmd/gavel/judge.go`:**\n1. Load tiered config (for telemetry)\n2. Resolve result ID (provided via `--result` or most recent from store)\n3. Read SARIF from store\n4. Evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "b2d088541e63708946d0d8c4ee54f18e55e5dc7c": {"t": 1785764807.3557723, "n": 10455, "h": "76666799fab02a0e", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n./gavel judge # evaluate most recent analysis\n./gavel judge --result # evaluate specific analysis\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nanalyze: Input Handler → BAML Analyzer → SARIF Assembler → FileStore (SARIF)\njudge: FileStore (SARIF) → Rego Evaluator → FileStore (Verdict)\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, output analysis summary JSON (id, findings count, scope, persona)\n\n**Data flow in `cmd/gavel/judge.go`:**\n1. Load tiered config (for telemetry)\n2. Resolve result ID (provided via `--result` or most recent from store)\n3. Read SARIF from store\n4. Evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "9f8c7670df1aadcc9e40b4ed6d1f297fba1f3174": {"t": 1785764808.8224087, "n": 10132, "h": "b084bdac4ead0d96", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 19 default rules (15 regex + 4 AST) embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rules have a `type` field (`regex` or `ast`); regex rules have compiled patterns, AST rules reference a named check via `ast_check` with optional `ast_config`. Rule fields include CWE/OWASP references, confidence, and remediation guidance.\n- **AST checks** (`internal/astcheck/`): Tree-sitter-based structural analysis via `smacker/go-tree-sitter`. The `Check` interface (`Name() string`, `Run(tree, source, lang, config) []Match`) is registered in a `Registry`. `DefaultRegistry()` includes 4 checks: `function-length`, `nesting-depth`, `empty-handler`, `param-count`. Language detection (`Detect(path)`) maps file extensions to tree-sitter grammars for Go, Python, JS/TS, Java, C, and Rust. AST rules run in the instant tier alongside regex rules in `TieredAnalyzer.runPatternMatching()`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## AST Rules\n\nGavel uses tree-sitter (`smacker/go-tree-sitter`) for fast, structural code analysis in the instant tier alongside regex rules.\n\n**Implementation:**\n- `internal/astcheck/registry.go` - `Check` interface, `Match` struct, `Registry`\n- `internal/astcheck/language.go` - File extension → tree-sitter grammar mapping (`Detect()`)\n- `internal/astcheck/helpers.go` - Shared DFS traversal and function-node utilities\n- `internal/astcheck/defaults.go` - `DefaultRegistry()` wiring all checks\n- `internal/astcheck/{function_length,nesting_depth,empty_handler,param_count}.go` - Individual checks\n\n**Current AST checks (IDs AST001-AST004):**\n- `function-length` - Functions exceeding `max_lines` (default 50)\n- `nesting-depth` - Code blocks exceeding `max_depth` (default 4)\n- `empty-handler` - Empty error handlers (`if err != nil {}`, `except: pass`, empty `catch`)\n- `param-count` - Functions exceeding `max_params` (default 5); handles Go grouped params (`a, b int` = 2 params)\n\n**Supported languages:** Go, Python, JavaScript/JSX, TypeScript/TSX, Java, C/H, Rust\n\n**To add a new AST check:**\n1. Create `internal/astcheck/your_check.go` implementing `Check` interface\n2. Register in `internal/astcheck/defaults.go` `DefaultRegistry()`\n3. Add rule entry in `internal/rules/default_rules.yaml` with `type: ast` and `ast_check: your-check-name`\n4. Add tests in `internal/astcheck/astcheck_test.go`\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "58ccd2fbb823fc630c048a30c294d03c7b423819": {"t": 1785764810.2103682, "n": 8015, "h": "505080b4ef7975a7", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n1b. Load tiered rules (embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`, or `--rules-dir`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Vendable rules** (`internal/rules/`): 15 default rules embedded via `//go:embed default_rules.yaml`. `LoadRules(userDir, projectDir)` merges three tiers by rule ID (later wins): embedded defaults → `~/.config/gavel/rules/*.yaml` → `.gavel/rules/*.yaml`. The `--rules-dir` flag overrides the project rules directory. Rule fields include regex patterns, CWE/OWASP references, confidence, and remediation guidance.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "20efbdcc81b940dbabbea49605ff8a5605fa0d30": {"t": 1785764811.7486522, "n": 7482, "h": "e9d1b3069ab53c7b", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n- **Cache metadata & cross-environment sharing**: SARIF results include `gavel/cache_key` (deterministic hash of file content + policies + model + BAML templates) and `gavel/analyzer` metadata (provider, model, policies used). Cache keys enable sharing results across CI and local environments when analysis inputs match. Cache invalidation only occurs when LLM inputs change (file content, policy instructions, model, BAML templates), NOT when Rego policies or severity levels change (those only affect verdict evaluation, not SARIF generation).\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "004f974dc58e8ab860788ced18480ad5dfabcdff": {"t": 1785764813.851421, "n": 6935, "h": "6aefe9417283481a", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-haiku-4-5`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-haiku-4-5` (fast Haiku 4.5, cost-effective)\n- **Bedrock**: `anthropic.claude-haiku-4-5-v1:0` (fast Haiku 4.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-haiku-4-5 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-haiku-4-5-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 4.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 4.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "9aa6a6e10b669fa837711dead4a997d91f644b54": {"t": 1785764815.289839, "n": 6980, "h": "61cb4f05cb2c50b8", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-exp`, `anthropic/claude-3-5-haiku-20241022`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-3-5-haiku-20241022` (fast Haiku 3.5, cost-effective)\n- **Bedrock**: `anthropic.claude-3-5-haiku-20241022-v1:0` (fast Haiku 3.5 on AWS)\n- **OpenAI**: `o3-mini` (fast reasoning model)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-exp # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-3-5-haiku-20241022 # fast, cost-effective (recommended)\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-3-5-haiku-20241022-v1:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-5.3-codex # or gpt-5.2 for general use\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.6 (Feb 2026) > Sonnet 4.5 > OpenAI GPT-5.3-Codex\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku 3.5 > o3-mini\n- **Cost priority**: Ollama (free) > DeepSeek > o3-mini > Claude Haiku 3.5 > GPT-5 > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "7ecab2b0a1b8c91dca7a75aa3155dd8ea67794c7": {"t": 1785764816.9432888, "n": 6932, "h": "8e406ebec381ea78", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports multiple LLM providers:\n\n**Supported Providers:**\n1. **Ollama** (local, free): Requires Ollama running at configured base_url (default: `http://localhost:11434/v1`), model `gpt-oss:20b`\n2. **OpenRouter** (unified API): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n3. **Anthropic** (direct API): Requires `ANTHROPIC_API_KEY` env var, supports all Claude models\n4. **Bedrock** (AWS): Requires AWS credentials, supports Claude models on AWS Bedrock\n5. **OpenAI** (direct API): Requires `OPENAI_API_KEY` env var, supports GPT-4 and other OpenAI models\n\n**Fast Models for Quick Analysis:**\n- **Ollama**: `qwen2.5-coder:7b`, `deepseek-coder-v2:16b` (local, free, very fast)\n- **OpenRouter**: `google/gemini-2.0-flash-001`, `anthropic/claude-3.5-haiku`, `deepseek/deepseek-chat`\n- **Anthropic**: `claude-3-5-haiku-20241022` (fast, lower cost than Sonnet)\n- **Bedrock**: `anthropic.claude-3-5-haiku-*` (fast Haiku on AWS)\n- **OpenAI**: `gpt-4o-mini` (fast, cost-effective)\n\n**Provider Configuration:**\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\n# Ollama (local)\nprovider:\n name: ollama\n ollama:\n model: qwen2.5-coder:7b # fast local model\n base_url: http://localhost:11434/v1\n\n# OpenRouter (unified API)\nprovider:\n name: openrouter\n openrouter:\n model: google/gemini-2.0-flash-001 # very fast\n\n# Anthropic (direct API)\nprovider:\n name: anthropic\n anthropic:\n model: claude-sonnet-4-20250514 # or claude-3-5-haiku-20241022 for speed\n\n# AWS Bedrock\nprovider:\n name: bedrock\n bedrock:\n model: anthropic.claude-sonnet-4-5-v2:0\n region: us-east-1\n\n# OpenAI\nprovider:\n name: openai\n openai:\n model: gpt-4o # or gpt-4o-mini for speed\n```\n\n**Model Selection Guidance:**\n- **Quality priority**: Anthropic Claude Opus 4.5/4.6 > Sonnet 4/4.5 > OpenAI GPT-4o\n- **Speed priority**: Ollama local models > Gemini Flash > Claude Haiku > GPT-4o-mini\n- **Cost priority**: Ollama (free) > DeepSeek > GPT-4o-mini > Claude Haiku > GPT-4o > Claude Sonnet > Claude Opus\n\nSee `example-configs.yaml` for detailed provider examples with performance/cost comparisons.\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "31634ec57295b144d7a6b34a0f2e1d9b4b5a6b4b": {"t": 1785764818.4649434, "n": 5293, "h": "60e939415ea27e50", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports two LLM providers:\n- **Ollama** (default, local): Requires Ollama running at configured base_url (default: `http://localhost:11434`), model `gpt-oss:20b`\n- **OpenRouter**: Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\nprovider:\n name: ollama # or \"openrouter\"\n ollama:\n model: gpt-oss:20b\n base_url: http://localhost:11434\n openrouter:\n model: anthropic/claude-sonnet-4\n```\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n\n## Release Process\n\nGavel uses Task-based builds with GitHub Actions for multi-platform releases.\n\n**Creating a release:**\n```bash\n# Tag the release\ntask release VERSION=v0.2.0\n\n# Push the tag\ngit push origin v0.2.0\n```\n\n**Build workflow:**\n1. Linux and macOS runners build natively with CGO enabled\n2. Each platform produces amd64 and arm64 binaries\n3. Binaries are archived as `.tar.gz` with checksums\n4. Final job creates GitHub release with all artifacts\n\n**Local development build:**\n```bash\ntask build # Current platform only\ntask build:release # All architectures for current OS\n```\n\n**Requirements:**\n- CGO must be enabled (BAML dependency requires it)\n- macOS builds require Xcode tools\n- Linux builds use system GCC\n"}, "d54eaa1b716f23a9ad4758b06d37f57687d5018e": {"t": 1785764819.8918264, "n": 4548, "h": "02c39dccc6c554ef", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports two LLM providers:\n- **Ollama** (default, local): Requires Ollama running at configured base_url (default: `http://localhost:11434`), model `gpt-oss:20b`\n- **OpenRouter**: Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\nprovider:\n name: ollama # or \"openrouter\"\n ollama:\n model: gpt-oss:20b\n base_url: http://localhost:11434\n openrouter:\n model: anthropic/claude-sonnet-4\n```\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n"}, "61a3a51848bbf1ca7c9b16a59c30f6fd5dc6bf5f": {"t": 1785764821.1536584, "n": 4549, "h": "f39ec1fafc8f1085", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports two LLM providers:\n- **OpenRouter** (default): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n- **Ollama** (local): Requires Ollama running at configured base_url (default: `http://localhost:11434`), model `gpt-oss:20b`\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\nprovider:\n name: ollama # or \"openrouter\"\n ollama:\n model: gpt-oss:20b\n base_url: http://localhost:11434\n openrouter:\n model: anthropic/claude-sonnet-4\n```\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Personas\n\nGavel uses BAML to implement switchable analysis personas. Different personas provide\nspecialized expert perspectives: code quality, architecture, or security.\n\n**Implementation:**\n- `internal/analyzer/personas.go` - Persona prompt constants and selection logic\n- `internal/config/config.go` - Persona configuration field\n- `docs/personas-feature-design.md` - Full design document\n\n**To add a new persona:**\n1. Add prompt constant to `internal/analyzer/personas.go`\n2. Add case to `GetPersonaPrompt()` switch\n3. Add to valid personas map in `internal/config/config.go` validation\n4. Update documentation\n\n**Current personas:**\n- `code-reviewer` (default): Code quality, error handling, testability\n- `architect`: Scalability, API design, service boundaries\n- `security`: OWASP Top 10, auth/authz, injection vulnerabilities\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n"}, "01097725e215ef78bac2cc889308fbbb95dc6e33": {"t": 1785764823.4536283, "n": 3711, "h": "a7bf07409285c979", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports two LLM providers:\n- **Ollama** (default, local): Requires Ollama running at configured base_url (default: `http://localhost:11434`), model `gpt-oss:20b`\n- **OpenRouter**: Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\nprovider:\n name: ollama # or \"openrouter\"\n ollama:\n model: gpt-oss:20b\n base_url: http://localhost:11434\n openrouter:\n model: anthropic/claude-sonnet-4\n```\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n"}, "b5c3f986489cb3bab373daf8d7e68057d63f2acc": {"t": 1785764825.0741036, "n": 3712, "h": "37af2cd12dbe056f", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit).\n\nGavel supports two LLM providers:\n- **OpenRouter** (default): Requires `OPENROUTER_API_KEY` env var, model `anthropic/claude-sonnet-4`\n- **Ollama** (local): Requires Ollama running at configured base_url (default: `http://localhost:11434`), model `gpt-oss:20b`\n\nProvider selection is configured in `.gavel/policies.yaml` via the `provider` section:\n\n```yaml\nprovider:\n name: ollama # or \"openrouter\"\n ollama:\n model: gpt-oss:20b\n base_url: http://localhost:11434\n openrouter:\n model: anthropic/claude-sonnet-4\n```\n\nThe BAML client wrapper (`internal/analyzer/bamlclient.go`) dispatches to the appropriate generated client based on this config at runtime.\n\nAfter changing `.baml` files, run `task generate`. The LLM provider is selected via config, not environment variables.\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n"}, "7be52b0cf45d94587a894df268eb29bd445d39e1": {"t": 1785764826.7894506, "n": 3072, "h": "b7bffc93c18379dc", "c": "# CLAUDE.md\n\nThis file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.\n\n## Build & Development Commands\n\n```bash\ntask build # go build -o gavel ./cmd/gavel\ntask test # go test ./... -v\ntask lint # go vet ./...\ntask generate # baml-cli generate (regenerates baml_client/ from baml_src/)\n\n# Run a single test\ngo test ./internal/config/ -run TestMergeOverrides -v\n\n# Run integration test only\ngo test -run TestIntegration -v\n\n# Run the tool\nOPENROUTER_API_KEY=... ./gavel analyze --dir ./internal/input\n```\n\n## Architecture\n\nGavel is an AI-powered code analysis CLI that gates CI workflows (auto-merge, reject, human review) by analyzing code against configurable policies via an LLM, producing SARIF output, and evaluating it with Rego.\n\n**Pipeline:**\n```\nInput Handler → BAML Analyzer → SARIF Assembler → Rego Evaluator → Verdict\n ↓ ↓\n FileStore ←─────────────┘\n```\n\n**Data flow in `cmd/gavel/analyze.go`:**\n1. Load tiered config (system defaults → `~/.config/gavel/policies.yaml` → `.gavel/policies.yaml`)\n2. Read artifacts via input handler (files, unified diff, or directory walk)\n3. Format enabled policies into text, call BAML `AnalyzeCode` per artifact\n4. Convert findings to SARIF results with `gavel/` property extensions (recommendation, explanation, confidence)\n5. Deduplicate overlapping findings, assemble SARIF 2.1.0 log\n6. Store SARIF, evaluate with Rego, store verdict, output JSON\n\n## Key Design Decisions\n\n- **`BAMLClient` interface** (`internal/analyzer/analyzer.go`): All tests use a mock client. `BAMLLiveClient` (`bamlclient.go`) wraps the generated `baml_client.AnalyzeCode` function. The generated BAML types use `int64`/`RuleId`; the internal `Finding` type uses `int`/`RuleID`.\n- **Tiered config merging** (`internal/config/config.go`): Non-zero string fields override; `Enabled` bool always applies. `LoadFromFile` returns nil/nil for missing files.\n- **SARIF extensions**: All gavel-specific data lives in `Properties map[string]interface{}` with `gavel/` prefix keys.\n- **Rego evaluator** (`internal/evaluator/evaluator.go`): Default policy is embedded via `//go:embed default.rego`. Custom `.rego` files from a directory override it. Rego receives the full SARIF log as JSON input; it never sees source code.\n- **Storage** (`internal/store/`): `Store` interface with filesystem implementation. IDs are `-` directories under `.gavel/results/`.\n\n## BAML\n\nSource templates live in `baml_src/`. Generated Go client is in `baml_client/` (do not edit). After changing `.baml` files, run `task generate`. The LLM provider is OpenRouter (`OPENROUTER_API_KEY` env var), model `anthropic/claude-sonnet-4`.\n\n## Rego\n\nDefault gate policy is in `internal/evaluator/default.rego`. Package `gavel.gate`, queried for `data.gavel.gate.decision`. Returns \"reject\" (error + confidence > 0.8), \"merge\" (no results), or \"review\" (default). Uses `import rego.v1` syntax (OPA v1.13.1).\n"}}}}