Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

- **Bumped `rustls` 0.23.45 → 0.23.46 and `quinn-proto` 0.11.16 → 0.11.19** (RUSTSEC-2026-0338/0339/0340/0341/0342 and RUSTSEC-2026-0346/0347/0348/0349), plus the yanked `chacha20` 0.10.1 → 0.10.2. Lockfile-only patch bumps; this was failing the `Security Audit (CVE)` job on every PR.

### Changed

- **One `finalize` module owns the result tail of review, scan, commit and MCP (#648).** `engine::finalize` (`shape`, `shape_issues`, `blocks`, `exit_code`, `save_debt`, `save_history`) replaces six hand-copied versions of severity filtering, `should_block`, quality gate, exit code, debt snapshot and review history. Behavior of `cora review` (single and chunked) and `cora scan` is unchanged. Fixed along the way: the MCP `cora.review_diff` tool now honors its `min_severity` parameter and evaluates the quality gate on the reported findings; `cora commit` now records its review in the review history (`command = "commit"`).

## [0.18.0] - 2026-10-09

### Security
Expand Down
89 changes: 28 additions & 61 deletions src/commands/commit_cmd.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use colored::Colorize;
use std::io::{self, Write};

use crate::config::schema::Config;
use crate::engine::finalize::{self, HistoryEntry, ShapeOptions};
use crate::engine::llm;
use crate::engine::quality_gate;
use crate::engine::types::LLMConfig;
Expand Down Expand Up @@ -95,41 +96,34 @@ pub async fn execute_commit(
)
.await?;

// Filter by severity
let min_severity = config.hook.min_severity_level();
// Filter by severity, evaluate the quality gate
let mut filtered = response.clone();
filtered.issues.retain(|i| i.severity <= min_severity);

// Quality gate
let gate_result = if config.quality_gate.enabled {
let result = quality_gate::evaluate(&filtered.issues, &config.quality_gate);
Some(result)
} else {
None
};

// Check gate
let gate_failed = gate_result
.as_ref()
.is_some_and(|g| g.status == quality_gate::GateStatus::Fail);

// Save debt snapshot (best-effort)
if config.debt.enabled {
let (commit, branch) = get_git_context();
let snapshot = crate::engine::debt_tracker::DebtSnapshot::from_review(
&filtered.issues,
gate_result.as_ref(),
commit,
branch,
0,
None,
None,
);
crate::engine::debt_tracker::save_snapshot(
&snapshot,
config.debt.history_dir.as_deref(),
);
}
let shaped = finalize::shape(
&mut filtered,
config,
&ShapeOptions {
min_severity: config.hook.min_severity_level(),
filter: true,
gate: true,
ci: false,
},
);
let gate_result = shaped.gate.clone();
let gate_failed = shaped.gate_failed();

// Debt snapshot + review history (best-effort)
finalize::save_debt(config, &filtered.issues, gate_result.as_ref(), 0);
finalize::save_history(
&HistoryEntry {
command: "commit",
summary: &filtered.summary,
files_scanned: 0,
lines_scanned: 0,
tokens: response.tokens_used.as_ref(),
},
&filtered.issues,
&shaped,
);

if gate_failed && !opts.force {
// Print findings
Expand Down Expand Up @@ -457,33 +451,6 @@ fn do_commit(message: &str, quiet: bool) -> Result<i32> {
}
}

/// Get git context (cached).
fn get_git_context() -> (Option<String>, Option<String>) {
use std::sync::LazyLock;

static CONTEXT: LazyLock<(Option<String>, Option<String>)> = LazyLock::new(|| {
let commit = std::process::Command::new("git")
.args(["rev-parse", "--short", "HEAD"])
.output()
.ok()
.and_then(|o| {
if o.status.success() {
String::from_utf8(o.stdout)
.ok()
.map(|s| s.trim().to_string())
} else {
None
}
});

let branch = crate::git::get_current_branch().ok();

(commit, branch)
});

(CONTEXT.0.clone(), CONTEXT.1.clone())
}

/// Count files and lines in a diff.
fn diff_stats(diff: &str) -> (usize, usize) {
let mut files = 0usize;
Expand Down
Loading
Loading