diff --git a/CHANGELOG.md b/CHANGELOG.md index 2743dcb..e92d072 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,12 @@ # Changelog +## v1.1.1 - 2026-10-20 + +### Fixed + +- Matched baseline entries created under an older rule-semantics version now enter human review instead of remaining silently accepted. +- Benchmark environment data now resolves Git revision and worktree state from the benchmark project root, and records a clean worktree explicitly. + ## v1.1.0 - 2026-10-20 ### Added diff --git a/Cargo.lock b/Cargo.lock index dbb18db..f267a40 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -500,7 +500,7 @@ checksum = "1c133bc6a41be0d194c306b5506d15e6feeea7b1d6604bd3f8310dfb2ca96486" [[package]] name = "codeforge-baseline" -version = "1.1.0" +version = "1.1.1" dependencies = [ "chrono", "codeforge-protocol", @@ -514,12 +514,13 @@ dependencies = [ [[package]] name = "codeforge-benchmark" -version = "1.1.0" +version = "1.1.1" dependencies = [ "chrono", "codeforge-protocol", "codeforge-verification", "serde", + "tempfile", "thiserror 2.0.21", "tokio", "tracing", @@ -527,7 +528,7 @@ dependencies = [ [[package]] name = "codeforge-cli" -version = "1.1.0" +version = "1.1.1" dependencies = [ "anyhow", "clap", @@ -546,7 +547,7 @@ dependencies = [ [[package]] name = "codeforge-core" -version = "1.1.0" +version = "1.1.1" dependencies = [ "async-trait", "codeforge-benchmark", @@ -572,7 +573,7 @@ dependencies = [ [[package]] name = "codeforge-desktop" -version = "1.1.0" +version = "1.1.1" dependencies = [ "codeforge-baseline", "codeforge-core", @@ -591,7 +592,7 @@ dependencies = [ [[package]] name = "codeforge-diagnostics" -version = "1.1.0" +version = "1.1.1" dependencies = [ "codeforge-protocol", "serde", @@ -600,7 +601,7 @@ dependencies = [ [[package]] name = "codeforge-engines" -version = "1.1.0" +version = "1.1.1" dependencies = [ "async-trait", "codeforge-protocol", @@ -625,7 +626,7 @@ dependencies = [ [[package]] name = "codeforge-fleet" -version = "1.1.0" +version = "1.1.1" dependencies = [ "async-trait", "chrono", @@ -651,7 +652,7 @@ dependencies = [ [[package]] name = "codeforge-git" -version = "1.1.0" +version = "1.1.1" dependencies = [ "codeforge-protocol", "serde", @@ -663,7 +664,7 @@ dependencies = [ [[package]] name = "codeforge-plugin" -version = "1.1.0" +version = "1.1.1" dependencies = [ "codeforge-protocol", "semver", @@ -675,7 +676,7 @@ dependencies = [ [[package]] name = "codeforge-protocol" -version = "1.1.0" +version = "1.1.1" dependencies = [ "chrono", "serde", @@ -686,7 +687,7 @@ dependencies = [ [[package]] name = "codeforge-scheduler" -version = "1.1.0" +version = "1.1.1" dependencies = [ "tempfile", "thiserror 2.0.21", @@ -697,7 +698,7 @@ dependencies = [ [[package]] name = "codeforge-transform" -version = "1.1.0" +version = "1.1.1" dependencies = [ "chrono", "codeforge-protocol", @@ -713,7 +714,7 @@ dependencies = [ [[package]] name = "codeforge-verification" -version = "1.1.0" +version = "1.1.1" dependencies = [ "codeforge-protocol", "serde", @@ -725,7 +726,7 @@ dependencies = [ [[package]] name = "codeforge-workspace" -version = "1.1.0" +version = "1.1.1" dependencies = [ "codeforge-protocol", "ignore", diff --git a/Cargo.toml b/Cargo.toml index 00a5307..a550221 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -3,7 +3,7 @@ resolver = "2" members = ["crates/*", "apps/desktop/src-tauri"] [workspace.package] -version = "1.1.0" +version = "1.1.1" edition = "2024" rust-version = "1.90" license = "Apache-2.0" diff --git a/README.md b/README.md index 0c9e7c7..19703a2 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ autonomous coding agent. [![CI](https://github.com/styayur/codeforge-engine/actions/workflows/ci.yml/badge.svg)](https://github.com/styayur/codeforge-engine/actions/workflows/ci.yml) [![License: Apache-2.0](https://img.shields.io/badge/license-Apache--2.0-blue)](LICENSE) -[Documentation](docs/README.md) · [Latest stable release: v1.1.0](https://github.com/styayur/codeforge-engine/releases/tag/v1.1.0) · [Changelog](CHANGELOG.md) +[Documentation](docs/README.md) · [Latest stable release: v1.1.1](https://github.com/styayur/codeforge-engine/releases/tag/v1.1.1) · [Changelog](CHANGELOG.md) ## Why CodeForge @@ -75,7 +75,7 @@ missing required tools do not count as a pass. ## Installation Download the CLI archive for your platform from the -[v1.1.0 release](https://github.com/styayur/codeforge-engine/releases/tag/v1.1.0), +[v1.1.1 release](https://github.com/styayur/codeforge-engine/releases/tag/v1.1.1), verify it against `checksums.txt`, and extract `codeforge` (`codeforge.exe` on Windows). Desktop packages are available on the same page. Windows requires WebView2; Linux desktop builds require WebKitGTK. Packages do not install optional @@ -84,7 +84,7 @@ language tools. To build the CLI from the tagged source instead: ```bash -git clone --branch v1.1.0 --depth 1 https://github.com/styayur/codeforge-engine.git +git clone --branch v1.1.1 --depth 1 https://github.com/styayur/codeforge-engine.git cd codeforge-engine cargo install --locked --path crates/codeforge-cli codeforge --version @@ -121,7 +121,7 @@ repository. See [Verification](docs/VERIFICATION.md) and [baseline setup](docs/baseline.md); a baseline must be reviewed, not generated merely to make CI green. -Experimental commands included in the v1.1.0 package: +Experimental commands included in the v1.1.1 package: ```bash codeforge context . --query verification --limit 10 --max-bytes 32768 diff --git a/SECURITY.md b/SECURITY.md index 94d0273..0925f13 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -1,7 +1,7 @@ # Security policy Read the [security model](docs/SECURITY.md) for process, source and evidence -boundaries. CodeForge Engine v1.1.0 is the latest stable release. There is no +boundaries. CodeForge Engine v1.1.1 is the latest stable release. There is no long-term-support or security-response-time guarantee. Use [GitHub private vulnerability reporting](https://github.com/styayur/codeforge-engine/security/advisories/new) diff --git a/apps/desktop/package.json b/apps/desktop/package.json index 199507e..180fe15 100644 --- a/apps/desktop/package.json +++ b/apps/desktop/package.json @@ -1,7 +1,7 @@ { "name": "@codeforge/desktop", "private": true, - "version": "1.1.0", + "version": "1.1.1", "type": "module", "scripts": { "dev": "vite --host 127.0.0.1 --port 1420", diff --git a/apps/desktop/src-tauri/tauri.conf.json b/apps/desktop/src-tauri/tauri.conf.json index 778aeec..1300bb8 100644 --- a/apps/desktop/src-tauri/tauri.conf.json +++ b/apps/desktop/src-tauri/tauri.conf.json @@ -1,7 +1,7 @@ { "$schema": "https://schema.tauri.app/config/2", "productName": "CodeForge Engine", - "version": "1.1.0", + "version": "1.1.1", "identifier": "dev.codeforge.engine", "build": { "beforeDevCommand": "pnpm dev", diff --git a/crates/codeforge-baseline/src/lib.rs b/crates/codeforge-baseline/src/lib.rs index 033f41f..df83170 100644 --- a/crates/codeforge-baseline/src/lib.rs +++ b/crates/codeforge-baseline/src/lib.rs @@ -644,14 +644,23 @@ fn assessment_from_entry( entry: &BaselineEntry, match_kind: BaselineMatchKind, ) -> FindingAssessment { + let stale = is_rule_version_stale(entry); FindingAssessment { diagnostic_id: diagnostic.id.clone(), fingerprint: entry.fingerprint.clone(), rule_id: diagnostic.rule_id.clone(), path: entry.path.clone(), match_kind, - disposition: entry.disposition, - lifecycle: FindingLifecycle::Active, + disposition: if stale { + FindingDisposition::HumanReview + } else { + entry.disposition + }, + lifecycle: if stale { + FindingLifecycle::Stale + } else { + FindingLifecycle::Active + }, } } @@ -1077,6 +1086,43 @@ mod tests { assert_eq!(store.entries().len(), 2); } + #[test] + fn matched_entry_with_old_rule_version_requires_human_review() { + let temp = tempfile::tempdir().expect("tempdir"); + let path = temp.path().join("src/lib.rs"); + std::fs::create_dir_all(path.parent().expect("parent")).expect("src"); + std::fs::write(&path, "fn value() { let value = 1; }\n").expect("source"); + let diagnosis = diagnostic(&path, 1, "known issue"); + let fingerprint = fingerprint_diagnostic(&diagnosis, temp.path()); + let entry = BaselineEntry { + fingerprint: fingerprint.exact.clone(), + structural_fingerprint: Some(fingerprint.structural.clone()), + context_fingerprint: Some(fingerprint.context.clone()), + rule_id: diagnosis.rule_id.clone(), + path: PathBuf::from("src/lib.rs"), + symbol: fingerprint.symbol.clone(), + context_hash: Some(fingerprint.context_hash.clone()), + source_context: SourceContext::Production, + disposition: FindingDisposition::Accepted, + reason: Some("reviewed under an older rule".to_owned()), + first_seen: Utc::now(), + last_reviewed: Some(Utc::now()), + rule_version: Some("0".to_owned()), + lifecycle: FindingLifecycle::Active, + }; + let store = BaselineStore::from_file(BaselineFile { + schema_version: 1, + codeforge_version: env!("CARGO_PKG_VERSION").to_owned(), + entries: vec![entry], + }) + .expect("store"); + let assessments = store.classify_diagnostics(&[diagnosis], temp.path()); + assert_eq!(assessments.len(), 1); + assert_eq!(assessments[0].match_kind, BaselineMatchKind::Exact); + assert_eq!(assessments[0].lifecycle, FindingLifecycle::Stale); + assert_eq!(assessments[0].disposition, FindingDisposition::HumanReview); + } + #[test] fn prune_retains_stale_entries_and_requires_complete_scan() { let temp = tempfile::tempdir().expect("tempdir"); diff --git a/crates/codeforge-benchmark/Cargo.toml b/crates/codeforge-benchmark/Cargo.toml index 6feb87d..3f2898f 100644 --- a/crates/codeforge-benchmark/Cargo.toml +++ b/crates/codeforge-benchmark/Cargo.toml @@ -14,3 +14,6 @@ serde.workspace = true thiserror.workspace = true tokio.workspace = true tracing.workspace = true + +[dev-dependencies] +tempfile.workspace = true diff --git a/crates/codeforge-benchmark/src/lib.rs b/crates/codeforge-benchmark/src/lib.rs index 942db13..76bfd53 100644 --- a/crates/codeforge-benchmark/src/lib.rs +++ b/crates/codeforge-benchmark/src/lib.rs @@ -1,6 +1,7 @@ //! Benchmark execution and statistics. Results are only emitted from measured runs. use std::collections::BTreeMap; +use std::path::Path; use codeforge_protocol::{BenchmarkResult, TimingSummary}; use codeforge_verification::{CommandSpec, VerificationError, VerificationPipeline}; @@ -73,6 +74,17 @@ impl BenchmarkRunner { metric: impl Into, before_command: CommandSpec, after_command: CommandSpec, + ) -> Result { + self.compare_at(Path::new("."), metric, before_command, after_command) + .await + } + + pub async fn compare_at( + &self, + root: &Path, + metric: impl Into, + before_command: CommandSpec, + after_command: CommandSpec, ) -> Result { let command = Some(before_command.command_line()); let before = self.measure(before_command).await?; @@ -89,13 +101,17 @@ impl BenchmarkRunner { warmup: self.warmup, samples: self.samples, command, - environment: environment(), + environment: environment_at(root), timestamp: chrono::Utc::now(), }) } } pub fn environment() -> BTreeMap { + environment_at(Path::new(".")) +} + +pub fn environment_at(root: &Path) -> BTreeMap { let mut values = BTreeMap::new(); values.insert("os".to_owned(), std::env::consts::OS.to_owned()); values.insert("arch".to_owned(), std::env::consts::ARCH.to_owned()); @@ -107,34 +123,35 @@ pub fn environment() -> BTreeMap { if let Ok(cpus) = std::thread::available_parallelism() { values.insert("logical_cpus".to_owned(), cpus.get().to_string()); } - if let Some(commit) = command_output("git", &["rev-parse", "HEAD"]) { + if let Some(commit) = command_output(root, "git", &["rev-parse", "HEAD"]) { values.insert("git_commit".to_owned(), commit); } - if let Some(status) = command_output("git", &["status", "--porcelain"]) { + if let Some(status) = command_output(root, "git", &["status", "--porcelain"]) { values.insert( "git_worktree".to_owned(), if status.is_empty() { "clean" } else { "dirty" }.to_owned(), ); } - if let Some(rustc) = command_output("rustc", &["--version"]) { + if let Some(rustc) = command_output(root, "rustc", &["--version"]) { values.insert("rustc".to_owned(), rustc); } - if let Some(cargo) = command_output("cargo", &["--version"]) { + if let Some(cargo) = command_output(root, "cargo", &["--version"]) { values.insert("cargo".to_owned(), cargo); } values } -fn command_output(program: &str, args: &[&str]) -> Option { +fn command_output(root: &Path, program: &str, args: &[&str]) -> Option { let output = std::process::Command::new(program) .args(args) + .current_dir(root) .output() .ok()?; if !output.status.success() { return None; } let text = String::from_utf8_lossy(&output.stdout).trim().to_owned(); - (!text.is_empty()).then_some(text) + Some(text) } #[cfg(test)] @@ -158,10 +175,12 @@ mod tests { } #[test] - fn environment_records_version_and_platform() { - let values = environment(); + fn environment_records_version_and_target_root() { + let temp = tempfile::tempdir().expect("tempdir"); + let values = environment_at(temp.path()); assert_eq!(values["codeforge_version"], env!("CARGO_PKG_VERSION")); assert!(values.contains_key("os")); assert!(values.contains_key("arch")); + assert!(!values.contains_key("git_commit")); } } diff --git a/crates/codeforge-core/src/lib.rs b/crates/codeforge-core/src/lib.rs index e853cd3..ea0dcd3 100644 --- a/crates/codeforge-core/src/lib.rs +++ b/crates/codeforge-core/src/lib.rs @@ -425,7 +425,7 @@ impl CodeForgeEngine { summary, warmup: 1, samples, - environment: codeforge_benchmark::environment(), + environment: codeforge_benchmark::environment_at(self.root()), }) } @@ -509,7 +509,11 @@ impl CodeForgeEngine { ) { (Some(before), Some(after)) => { let runner = BenchmarkRunner::new(1, 3)?; - Some(runner.compare("optimization", before, after).await?) + Some( + runner + .compare_at(self.root(), "optimization", before, after) + .await?, + ) } _ => None, }; diff --git a/docs/README.md b/docs/README.md index f74778d..7900cb0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -1,7 +1,7 @@ # Documentation Start with the [project README](../README.md) for installation and a first scan. -v1.1.0 is the current stable release; Experimental capabilities are labeled +v1.1.1 is the current stable release; Experimental capabilities are labeled explicitly and remain opt-in. | Question | Guide | diff --git a/docs/ROADMAP.md b/docs/ROADMAP.md index ec5ee71..4a6f925 100644 --- a/docs/ROADMAP.md +++ b/docs/ROADMAP.md @@ -1,6 +1,6 @@ # Roadmap and maturity -## Stable workflows in v1.1.0 +## Stable workflows in v1.1.1 Native parsing/review, reviewed baselines, CLI and desktop preview/apply/undo, transaction history, configurable verification, SARIF export and independent @@ -10,7 +10,7 @@ existence does not make every proposed fix safe. ## Experimental capabilities On-demand context/impact and external SARIF path/CodeQL commands are included in -v1.1.0 as Experimental, opt-in paths. Impact resolution is name-based and bounded; +v1.1.1 as Experimental, opt-in paths. Impact resolution is name-based and bounded; test suggestions cannot replace a full test gate. Real CodeQL execution has not been certified. Desktop package build success alone does not establish end-to-end acceptance on every platform. @@ -20,7 +20,7 @@ acceptance on every platform. Evidence coverage metadata, baseline lifecycle improvements, local symbol/scope analysis, project verification orchestration, reproducible before/after benchmark comparison and executable architecture constraints require separate design and -test evidence. They are not silently promoted by the v1.1.0 release. +test evidence. They are not silently promoted by the v1.1.1 release. ## Future, not committed release scope diff --git a/docs/external-analysis.md b/docs/external-analysis.md index 028d89f..35f5b7a 100644 --- a/docs/external-analysis.md +++ b/docs/external-analysis.md @@ -1,6 +1,6 @@ # Optional external analysis -Experimental and included in the v1.1.0 package; never part of the default scan path. +Experimental and included in the v1.1.1 package; never part of the default scan path. `codeforge import-sarif report.sarif --root . --language rust` imports local SARIF 2.1.0 findings and code-flow paths as JSON. `diagnostic_index` associates diff --git a/docs/releases/v1.1.1.md b/docs/releases/v1.1.1.md new file mode 100644 index 0000000..ec031c7 --- /dev/null +++ b/docs/releases/v1.1.1.md @@ -0,0 +1,27 @@ +# CodeForge Engine v1.1.1 + +CodeForge Engine v1.1.1 is a stability patch for baseline rule migration and +benchmark provenance discovered while auditing v1.1.0. + +## Fixed + +- A baseline entry that still matches a current diagnostic but was reviewed under + an older rule-semantics version is now marked `Stale` and routed to human review. + It is no longer silently treated as an accepted suppression. +- Benchmark environment metadata now resolves Git revision and worktree state from + the benchmark project root rather than CodeForge's current working directory. +- Clean benchmark worktrees are recorded explicitly instead of being omitted. + +## Compatibility + +- Baseline schema remains 1 and existing baseline files remain readable. +- Review JSON and benchmark JSON changes are additive or correctional; no CLI + command is removed. +- SARIF and Fleet formats are unchanged. +- Rust 1.90 remains the declared MSRV. + +## Verification + +The patch is covered by a matched-stale baseline regression test and a target-root +benchmark environment test. Release artifacts are rebuilt, smoke-tested and +checksum-verified from the v1.1.1 tag candidate. diff --git a/docs/source-context.md b/docs/source-context.md index d811f52..784c6ff 100644 --- a/docs/source-context.md +++ b/docs/source-context.md @@ -1,6 +1,6 @@ # On-demand context and impact suggestions -Experimental and included in the v1.1.0 package; never part of the default scan path. +Experimental and included in the v1.1.1 package; never part of the default scan path. ```bash codeforge context . --query verification --limit 10 --max-bytes 32768