diff --git a/CHANGELOG.md b/CHANGELOG.md index 26d98b8..9d53cde 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,44 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added — Deeper, token-economical cross-file review context +## [0.7.0] - 2026-07-16 -- **Inbound caller (blast-radius) resolution.** Reviews now also resolve **who calls the changed code**, not just what the changed code calls. A changed function/type signature surfaces its call-sites across the repo so breaking changes can be flagged. Gated by new `review.context_chain.include_callers` (default `true`); uses gitignore-aware walking and is bounded (≤400 files, ≤3 call-sites/symbol), injecting only the call line + 1 line of context to stay cheap. -- **Definition extraction** (`extract_definitions_from_diff`) for Rust/Python/JS-TS/Go/Java-Kotlin — detects functions/types *declared* in the diff, which feed caller resolution. -- **Signature-only budget fallback.** When the token budget can't fit a full function/type body, a thin signature slice is injected instead of skipping the entry entirely (~3-5× more symbols under the same budget). +### Highlights -### Fixed — Cross-file context correctness & defaults +- **Deeper, token-economical cross-file review.** Reviews now resolve **who calls the changed code** (inbound / blast-radius), not just what the changed code calls — so breaking signature/type changes can be flagged. Bounded scanning + thin slices + a signature-only budget fallback keep token cost low. +- **Config is now validated at load time.** Out-of-range values (e.g. `temperature: 5`) and misspelled keys (`quailty_gate`) fail loudly instead of being silently ignored. +- **Markdown false positives suppressed.** Findings inside fenced code blocks (a `git push` in a fenced `bash` block flagged as SQL injection) are now dropped across all finding sources. +- **Performance, security, and correctness fixes** across the scan/review pipeline (10 perf bottlenecks, 2 CVE bumps, 8+ silent-corruption and best-practice bugs). -- **`review.rs` passed the wrong ignore list** to the context resolver (`ignore.rules` — finding-type strings — instead of `ignore.files` globs). The resolver could inject code from `node_modules/`/`target/`. Now uses `ignore.files`. -- **`context_chain.max_context_tokens` default raised 3000 → 5000**, and the resolver never scans gitignored build artifacts (caller scan uses the `ignore` crate). - -### Fixed — Minor Best-Practice Items (#334) - -- **Test-file detection no longer over-matches (#87).** `is_test_file` now uses path-segment awareness, so common words like `latest`, `aspect`, `attestation`, `protest` are no longer mistaken for test files. -- **Directory glob excludes are segment-boundary aware (#66).** A `src/` exclude no longer catches `mysrc/` or `docs/src-guide/`. -- **`max_findings` cutoff keeps the worst findings (#88).** Findings are sorted by severity before capping (Critical-first), so truncation drops the least important, not the highest severity. -- **Debt snapshot save failures are surfaced (#30).** Write failures emit a `warn!`-level log instead of being swallowed silently. -- **Token estimation no longer returns 0 for short content (#68).** Non-empty content estimates at least 1 token (was 0 under integer division). -- **Java wildcard imports preserved (#72).** `import com.example.*` keeps the `*` instead of truncating to `com.example`. -- **Rust module declarations extracted (#73).** `mod foo;` is now treated as a dependency symbol, matching the documented behavior. -- **DB size uses the real SQLite page size (#23).** `index_stats` queries `PRAGMA page_size` instead of assuming 4096 bytes. -- **`issue_type` serializes consistently (#48).** Serialized as `issue_type` (matching the field name); `type` kept as a deserialize alias. -- **Severity parsing avoids an allocation (#10).** `from_str_lossy` uses `eq_ignore_ascii_case`. - -### Fixed — Markdown False Positives (#329) +### Added -- **Findings inside Markdown fenced code blocks are now suppressed.** Code blocks (\`\`\` / `~~~`) in `.md`/`.mdx`/`.markdown` files are documentation examples, not executable code. A `git push` inside a ` ```bash ` block is no longer flagged as SQL injection. The filter covers all finding sources (security/secrets/rules scanners + LLM) and uses full hunk context (Add + Context lines) to track fence state, so it works even when only the code-block body was edited. +- **Inbound caller (blast-radius) resolution.** A new context-chain phase resolves call-sites of functions/types defined or modified in the diff, so breaking changes to their signatures surface their consumers. Gated by new `review.context_chain.include_callers` (default `true`); uses gitignore-aware walking and is bounded (≤400 files, ≤3 call-sites/symbol), injecting only the call line + 1 line of context. New `ContextPriority::CallerSite`. +- **Definition extraction** (`extract_definitions_from_diff`) for Rust/Python/JS-TS/Go/Java-Kotlin — detects functions/types *declared* in the diff, feeding caller resolution. Rust `mod foo;` and Java `import com.example.*` wildcards are now extracted correctly (#73, #72). +- **Signature-only budget fallback.** When the token budget can't fit a full function/type body, a thin signature slice (up to `{`) is injected instead of skipping the entry entirely (~3–5× more symbols under the same budget). +- **`Config::validate()`** (#94) — rejects out-of-range/unsupported values at load: `temperature` (0.0–2.0), `max_tokens`/`timeout` (≥1), `max_tokens_param`, `response_format`, `output.format`, `hook.mode`/`on_violation`/`min_severity`, and `provider.base_url` scheme. Multiple errors are aggregated into one message. +- **`Profile::validate()`** (#81) — focus `weight` must be 1–10, and `action`/`tone`/`detail_level` must be recognized values. +- **`deny_unknown_fields`** on all config sections (#80) — misspelled YAML keys are rejected at parse time. -### Fixed — Config Validation & Best Practices (#334) +### Changed -- **Quality gate no longer fails when disabled (#58).** `evaluate()` forced `GateResult::Pass` when `enabled: false`; it now short-circuits before applying thresholds so a disabled gate never blocks a merge. -- **Category actions are now validated enums (#57).** `quality_gate.categories.*.action` accepted any string, so a typo like `blok` silently became blocking. It is now a case-insensitive `CategoryAction` enum (`block`/`warn`/`ignore`); unknown values fail loudly at config load. -- **Config values are validated (#94).** `Config::validate()` now rejects out-of-range/unsupported values at load time: `temperature` (0.0–2.0), `max_tokens`/`timeout` (≥1), `max_tokens_param`, `response_format`, `output.format`, `hook.mode`/`on_violation`/`min_severity`, and `provider.base_url` scheme. Multiple errors are aggregated into one message. -- **Profile values are validated (#81).** Focus area `weight` must be 1–10, and `action`/`tone`/`detail_level` must be recognized values; all built-in profiles pass. +- **`CategoryAction` enum** (#57) — `quality_gate.categories.*.action` is now a case-insensitive enum (`block`/`warn`/`ignore`); a typo like `blok` fails loudly at config load instead of silently becoming blocking. +- **Disabled quality gate never fails** (#58) — `evaluate()` forces `Pass` when `enabled: false`. +- **`context_chain.max_context_tokens` default** raised 3000 → **5000**. +- **`issue_type`** serializes consistently as `issue_type` (#48); `type` retained as a deserialize alias. +- **`Severity::from_str_lossy`** uses `eq_ignore_ascii_case` (no allocation) (#10). -### Added — Config Safety +### Fixed -- **`deny_unknown_fields` on all config sections (#80).** Misspelled YAML keys (e.g. `quailty_gate`, `temprature`) are now rejected at parse time instead of being silently dropped. +- **Markdown fenced-code-block false positives** (#329) — findings inside fenced code blocks (triple-backtick / triple-tilde) in `.md`/`.mdx`/`.markdown` files are dropped across all finding sources (security/secrets/rules scanners + LLM). Fence state is tracked across full hunk context, so it works even when only the block body was edited. +- **Cross-file resolver used the wrong ignore list** — `review.rs` passed `ignore.rules` (finding-type strings) instead of `ignore.files` (`target/**`, `node_modules/**`); the resolver could inject build-artifact code. Now uses `ignore.files`. +- **Test-file detection over-match** (#87) — `is_test_file` is path-segment aware; `latest`, `aspect`, `attestation` are no longer mistaken for test files. +- **Directory glob excludes over-permissive** (#66) — `src/` matches only at segment boundaries (`mysrc/` no longer caught). +- **Token estimation** (#68) — non-empty content returns ≥1 token (was 0 under integer division). +- **DB size** (#23) — `index_stats` queries `PRAGMA page_size` instead of assuming 4096 bytes. +- **Project-sync workflow** — a merged PR referencing issues via `Refs #N` (not `Closes #N`) no longer fails the `sync` check. +- **10 scan/review performance bottlenecks** (#335) — precompiled regex, batched DB queries, early cutoffs, file-content cache, single reused Tokio runtime, single-transaction prune, etc. +- **Security:** bumped `anyhow` 1.0.102 → 1.0.103 (RUSTSEC-2026-0190) and `crossbeam-epoch` 0.9.18 → 0.9.20 (RUSTSEC-2026-0204). +- Various silent-data-corruption bugs resolved (#333): severity sort, security-findings fallback, deterministic debt-snapshot hashing, debt-trend math, config precedence, `context_chain` merge, and hook-install composition. ## [0.6.2] - 2026-06-21 diff --git a/Cargo.lock b/Cargo.lock index 092ea67..76fdb2b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -275,7 +275,7 @@ dependencies = [ [[package]] name = "cora-cli" -version = "0.6.2" +version = "0.7.0" dependencies = [ "anyhow", "assert_cmd", diff --git a/Cargo.toml b/Cargo.toml index 17e16a0..ecc615a 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "cora-cli" -version = "0.6.2" +version = "0.7.0" edition = "2024" description = "CLI-first AI code review — BYOK, diff/scan/branch, pre-commit hooks" license = "MIT" diff --git a/docs/changelog.md b/docs/changelog.md index 8122537..cc8c7ca 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -7,41 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] -### Added — Deeper cross-file review context (token-economical) +## [0.7.0] - 2026-07-16 -- **Caller/blast-radius resolution**: reviews now resolve who *calls* changed code (not just what changed code calls). `review.context_chain.include_callers` (default `true`); bounded scan, thin slices. -- **Definition extraction** (Rust/Python/JS-TS/Go/Java-Kotlin) feeds caller resolution. -- **Signature-only budget fallback**: injects a signature slice when the full body won't fit. +### Highlights -### Fixed - -- `review.rs` passed `ignore.rules` instead of `ignore.files` to the context resolver (could inject `node_modules`/`target` code). -- `context_chain.max_context_tokens` default 3000 → 5000. +- **Deeper, token-economical cross-file review** — reviews now resolve **who calls changed code** (blast radius), not just what changed code calls. Bounded scanning + thin slices + signature-only fallback keep token cost low. +- **Config validation at load time** — out-of-range values and misspelled keys fail loudly instead of being silently ignored. +- **Markdown false positives suppressed** — findings inside fenced code blocks dropped across all finding sources. +- **Perf + security + correctness fixes** across the pipeline (10 perf bottlenecks, 2 CVE bumps, silent-corruption & best-practice bugs). -### Fixed — Minor Best-Practice Items (#334) +### Added -- **#87** test-file detection uses path-segment awareness (no more `latest`/`aspect`/`attestation` false matches). -- **#66** directory glob excludes are segment-boundary aware (`src/` ≠ `mysrc/`). -- **#88** `max_findings` cutoff sorts severity-first so truncation drops least-important findings. -- **#30** debt snapshot save failures emit `warn!`. -- **#68** token estimation returns ≥1 for non-empty content. -- **#72** Java `import com.example.*` keeps the wildcard. -- **#73** Rust `mod foo;` extracted as a dependency. -- **#23** DB size queries `PRAGMA page_size` instead of assuming 4096. -- **#48** `issue_type` serializes consistently; `type` kept as deserialize alias. -- **#10** severity parsing avoids an allocation. +- **Caller (blast-radius) resolution** — `review.context_chain.include_callers` (default `true`); gitignore-aware, bounded ≤400 files / ≤3 call-sites per symbol. New `ContextPriority::CallerSite`. +- **Definition extraction** (Rust/Python/JS-TS/Go/Java-Kotlin) feeds caller resolution; Rust `mod foo;` and Java `import com.example.*` now extracted (#73, #72). +- **Signature-only budget fallback** — injects a signature slice when the full body won't fit. +- **`Config::validate()`** (#94) + **`Profile::validate()`** (#81) — reject out-of-range/unsupported values at load. +- **`deny_unknown_fields`** on all config sections (#80). -### Fixed — Markdown False Positives (#329) +### Changed -- **Findings inside Markdown fenced code blocks are now suppressed.** A `git push` inside a ` ```bash ` block in an `.md` file is no longer flagged as SQL injection. Covers all finding sources (security/secrets/rules + LLM); tracks fence state across full hunk context (Add + Context lines). +- **`CategoryAction` enum** (#57) — case-insensitive `block`/`warn`/`ignore`; typos fail loudly. +- **Disabled quality gate never fails** (#58). +- **`context_chain.max_context_tokens`** default 3000 → **5000**. +- **`issue_type`** serializes consistently (#48); `type` kept as deserialize alias. +- **`Severity::from_str_lossy`** avoids an allocation (#10). -### Fixed — Config Validation & Best Practices (#334) +### Fixed -- **Quality gate no longer fails when disabled (#58).** `evaluate()` now forces `GateResult::Pass` when `enabled: false` instead of applying thresholds. -- **Category actions are now validated enums (#57).** `quality_gate.categories.*.action` is a case-insensitive `CategoryAction` enum (`block`/`warn`/`ignore`); unknown values fail loudly at config load instead of silently becoming blocking. -- **Config values are validated (#94).** `Config::validate()` rejects out-of-range/unsupported values at load: `temperature` (0.0–2.0), `max_tokens`/`timeout` (≥1), `max_tokens_param`, `response_format`, `output.format`, `hook.*`, and `provider.base_url`. -- **Profile values are validated (#81).** Focus `weight` must be 1–10; `action`/`tone`/`detail_level` must be recognized. -- **`deny_unknown_fields` on all config sections (#80).** Misspelled YAML keys are rejected at parse time. +- **Markdown fenced-code-block false positives** (#329) dropped across all finding sources. +- **Cross-file resolver** now uses `ignore.files` (not `ignore.rules`) — no longer injects `node_modules`/`target` code. +- **Test-file detection** (#87) and **glob excludes** (#66) no longer over-match (`latest`/`aspect`/`mysrc/`). +- **Token estimation** (#68), **DB size** (#23). +- **Project-sync workflow** no longer fails on merged PRs using `Refs #N`. +- **10 performance bottlenecks** (#335); **silent data-corruption bugs** (#333). +- **Security:** `anyhow` 1.0.102 → 1.0.103, `crossbeam-epoch` 0.9.18 → 0.9.20. ## [0.6.2] - 2026-06-21 diff --git a/docs/configuration.md b/docs/configuration.md index 6632ca4..a23337f 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -26,6 +26,8 @@ Settings are resolved in this order (highest priority first): 5. **Auto-detect** — Provider-specific env vars (`OPENAI_API_KEY`, `ZAI_API_KEY`, etc.) 6. **Built-in defaults** — Sensible defaults for all settings +After all sources are merged, **config values are validated at load time**: out-of-range values (e.g. `temperature: 5`), unsupported formats (`output.format: prety`), and misspelled keys (`quailty_gate`, `temprature`) fail loudly with a clear message instead of being silently ignored. This applies to `temperature` (0.0–2.0), `max_tokens`/`timeout` (≥1), `max_tokens_param`, `response_format`, `output.format`, `hook.mode`/`on_violation`/`min_severity`, `provider.base_url`, and profile `weight`/`action`/`tone`/`detail_level`. + ### API Key Resolution 1. `--api-key` flag (one-shot) @@ -152,6 +154,35 @@ cora uses two mechanisms to prevent the LLM from fabricating findings: - **File path injection** — Actual file paths are embedded in the prompt, anchoring the LLM to real files in the diff. - **Post-parse filtering** — After parsing, any reported file paths or line numbers that don't exist in the actual diff are discarded. +## Cross-File Review Context + +To review a change accurately, cora injects **cross-file context** alongside the diff — it doesn't review the diff in isolation. This is deterministic (no extra LLM calls) and bounded by a token budget so cost stays predictable. + +Two axes are resolved: + +- **Outbound** — what the changed code *calls/imports* (function/type definitions the diff references). +- **Inbound (blast radius)** — *who calls the changed code*. If a PR modifies a function signature or type, its call-sites across the repo are surfaced so breaking changes can be flagged. + +When the budget can't fit a full definition, a thin **signature slice** is injected instead of skipping it, so more symbols fit under the same budget. + +```yaml +review: + context_chain: + enabled: true # master switch for cross-file context + max_context_tokens: 5000 # budget (~4 chars/token) for injected context + follow_depth: 1 # outbound resolution depth (1 = direct refs only) + include_tests: true # resolve test files via naming convention + include_callers: true # resolve callers of changed code (blast radius) +``` + +| Field | Default | Notes | +|-------|---------|-------| +| `enabled` | `true` | Disable to review the diff only. | +| `max_context_tokens` | `5000` | Approx. 20 KB of code injected. | +| `follow_depth` | `1` | Outbound recursion depth (`1` = direct references). | +| `include_tests` | `true` | Map changed source to its test files. | +| `include_callers` | `true` | Inbound caller resolution. Scans source files (gitignore-aware — `target/`, `node_modules/` are never scanned), bounded to ≤400 files and ≤3 call-sites per symbol. | + ## Quality Gate Quality gate evaluates review findings against configurable thresholds to produce a **PASS/FAIL** result. This is useful for CI enforcement — block merges when code quality drops below your standards. @@ -190,6 +221,8 @@ quality_gate: - **block** — exceed threshold = gate FAIL (exit code 2) - **warn** — report but don't fail gate - **ignore** — skip entirely + + Actions are validated enums — case-insensitive (`block`, `Block`, `BLOCK` all work), and an unknown value (e.g. `blok`) fails at config load instead of silently becoming blocking. A disabled gate (`enabled: false`) never fails. 4. Overall gate status: **PASSED** or **FAILED** ### CLI Output