From 1c21353bdda2d378a8544fad59e19095fe033be1 Mon Sep 17 00:00:00 2001 From: Curry Date: Sun, 26 Jul 2026 12:03:29 +0800 Subject: [PATCH 1/5] test(inventory): cover linked worktree git pointer --- .../code-intel-cli/tests/capability_exec.rs | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/crates/code-intel-cli/tests/capability_exec.rs b/crates/code-intel-cli/tests/capability_exec.rs index f2d6b05..5ae606e 100644 --- a/crates/code-intel-cli/tests/capability_exec.rs +++ b/crates/code-intel-cli/tests/capability_exec.rs @@ -330,6 +330,55 @@ fn inventory_rg_ignores_repository_ignored_workspace_churn() { let _ = fs::remove_dir_all(root); } +#[test] +fn inventory_rg_excludes_linked_worktree_git_pointer() { + let root = temp_dir("linked-worktree-git-pointer"); + let repo = root.join("repo"); + let linked = root.join("linked"); + fs::create_dir_all(&repo).unwrap(); + git(&repo, &["init", "--quiet"]); + git(&repo, &["config", "user.name", "Inventory Test"]); + git( + &repo, + &["config", "user.email", "inventory@example.invalid"], + ); + fs::write(repo.join("kept.py"), "print('kept')\n").unwrap(); + git(&repo, &["add", "kept.py"]); + git(&repo, &["commit", "--quiet", "-m", "baseline"]); + + let added = Command::new("git") + .args(["worktree", "add", "--quiet", "--detach"]) + .arg(&linked) + .arg("HEAD") + .current_dir(&repo) + .output() + .expect("create linked worktree"); + assert!( + added.status.success(), + "{}", + String::from_utf8_lossy(&added.stderr) + ); + assert!(linked.join(".git").is_file()); + + let out = root.join("out"); + let output = run_with_request_file( + &request(&linked, "inventory.rg"), + &root.join("request.json"), + &out, + "inventory.rg", + ); + assert_eq!( + output.status.code(), + Some(0), + "{}", + String::from_utf8_lossy(&output.stderr) + ); + let files = fs::read_to_string(out.join("files.txt")).unwrap(); + assert!(files.lines().any(|path| path == "kept.py")); + assert!(!files.lines().any(|path| path == ".git")); + let _ = fs::remove_dir_all(root); +} + #[test] fn inventory_rejects_repository_changes_after_snapshot_and_honors_snapshot_scope() { let root = temp_dir("snapshot-lease"); From 85719e42f655ad4c8dd726401dc1aac6966a3d33 Mon Sep 17 00:00:00 2001 From: Curry Date: Sun, 26 Jul 2026 12:40:31 +0800 Subject: [PATCH 2/5] feat(capability): add ast-grep edit planning --- .../src/capability_inventory.rs | 3 + crates/code-intel-cli/src/structured_edit.rs | 359 ++++++++++++++++++ .../code-intel-cli/tests/capability_exec.rs | 75 ++++ docs/artifact-data-contract.md | 3 + orchestration/integrations.json | 41 ++ orchestration/internalization/rg.json | 8 +- 6 files changed, 485 insertions(+), 4 deletions(-) create mode 100644 crates/code-intel-cli/src/structured_edit.rs diff --git a/crates/code-intel-cli/src/capability_inventory.rs b/crates/code-intel-cli/src/capability_inventory.rs index 7a49bb7..3a27e40 100644 --- a/crates/code-intel-cli/src/capability_inventory.rs +++ b/crates/code-intel-cli/src/capability_inventory.rs @@ -29,6 +29,8 @@ mod native_code_evidence; mod project_orientation; #[path = "project_orientation_benchmark.rs"] mod project_orientation_benchmark; +#[path = "structured_edit.rs"] +mod structured_edit; #[path = "understanding_quadrant.rs"] mod understanding_quadrant; @@ -71,6 +73,7 @@ pub(crate) fn execute( "evidence.native-code.compat" => { native_code_evidence::execute(request, verified_inputs, out) } + "edit.ast-grep-plan.compat" => structured_edit::execute(request, verified_inputs, out), "project.orientation.compat" => project_orientation::execute(request, verified_inputs, out), "understanding.quadrant.compat" => { understanding_quadrant::execute(request, verified_inputs, out) diff --git a/crates/code-intel-cli/src/structured_edit.rs b/crates/code-intel-cli/src/structured_edit.rs new file mode 100644 index 0000000..1729710 --- /dev/null +++ b/crates/code-intel-cli/src/structured_edit.rs @@ -0,0 +1,359 @@ +use std::collections::BTreeSet; +use std::fs; +use std::path::{Component, Path}; +use std::process::Command; + +use serde_json::{json, Value}; + +use super::{publish_named, snapshot_adapter_error, AdapterArtifact, AdapterError, AdapterOutput}; +use crate::adapter_contract::AdapterDomainVerdict; +use crate::artifact_ref::VerifiedArtifact; +use crate::snapshot; + +const MAX_PATTERN_BYTES: usize = 16 * 1024; +const MAX_OUTPUT_BYTES: usize = 8 * 1024 * 1024; +const MAX_PATHS: usize = 64; +const GENERATED_COMPONENTS: [&str; 9] = [ + ".git", + ".next", + ".venv", + "__pycache__", + "build", + "dist", + "node_modules", + "target", + "vendor", +]; + +pub(crate) fn execute( + request: &Value, + verified_inputs: &[VerifiedArtifact], + out: &Path, +) -> Result { + if !verified_inputs.is_empty() { + return Err(AdapterError::Contract( + "edit.ast-grep-plan does not accept input artifacts".into(), + )); + } + let options = request["options"] + .as_object() + .ok_or_else(|| AdapterError::InvalidOptions("options must be an object".into()))?; + if options.keys().any(|key| { + !matches!( + key.as_str(), + "repoPath" | "language" | "pattern" | "rewrite" | "paths" + ) + }) { + return Err(AdapterError::InvalidOptions( + "edit.ast-grep-plan accepts only repoPath/language/pattern/rewrite/paths".into(), + )); + } + let repo = required_string(options.get("repoPath"), "options.repoPath")?; + let repo = Path::new(repo); + if !repo.is_dir() { + return Err(AdapterError::InvalidOptions(format!( + "repoPath is not a directory: {}", + repo.display() + ))); + } + let language = required_string(options.get("language"), "options.language")?; + if language.len() > 64 { + return Err(AdapterError::InvalidOptions( + "options.language exceeds 64 bytes".into(), + )); + } + let pattern = bounded_string(options.get("pattern"), "options.pattern")?; + let rewrite = options + .get("rewrite") + .map(|value| bounded_string(Some(value), "options.rewrite")) + .transpose()?; + let paths = requested_paths(options.get("paths"))?; + let canonical_repo = fs::canonicalize(repo) + .map_err(|error| AdapterError::Io(format!("resolve repoPath: {error}")))?; + let snapshot_scopes = request["snapshot"]["scope"] + .as_array() + .expect("validated snapshot scope") + .iter() + .map(|value| { + normalize_relative( + value.as_str().expect("validated snapshot scope item"), + false, + ) + }) + .collect::, _>>()?; + let paths = paths + .into_iter() + .map(|path| validate_path(repo, &canonical_repo, &snapshot_scopes, path)) + .collect::, _>>()?; + + let lease = + snapshot::begin_consumption(repo, &request["snapshot"]).map_err(snapshot_adapter_error)?; + let version = ast_grep_version()?; + let mut command = Command::new("ast-grep"); + command + .args(["run", "--pattern"]) + .arg(pattern) + .args(["--lang", language]); + if let Some(rewrite) = rewrite { + command.args(["--rewrite", rewrite]); + } + command.args(["--json=compact", "--threads", "0", "--"]); + command.args(&paths).current_dir(repo); + let output = command + .output() + .map_err(|error| AdapterError::Unavailable(format!("start ast-grep: {error}")))?; + if !output.status.success() { + return Err(AdapterError::Internal(format!( + "ast-grep failed: {}", + bounded_diagnostic(&output.stderr) + ))); + } + if output.stdout.len() > MAX_OUTPUT_BYTES { + return Err(AdapterError::Contract(format!( + "ast-grep output exceeds {MAX_OUTPUT_BYTES} bytes; narrow options.paths or pattern" + ))); + } + let mut matches: Vec = serde_json::from_slice(&output.stdout).map_err(|error| { + AdapterError::Internal(format!("ast-grep emitted invalid JSON: {error}")) + })?; + let mut files = BTreeSet::new(); + for item in &mut matches { + let file = item + .get("file") + .and_then(Value::as_str) + .ok_or_else(|| AdapterError::Internal("ast-grep match has no file path".into()))?; + let file = normalize_match_file(repo, &canonical_repo, file)?; + item["file"] = Value::String(file.clone()); + files.insert(file); + } + lease.verify_after(repo).map_err(snapshot_adapter_error)?; + + let artifact = json!({ + "schema": "code-intel-structured-edit-plan.v1", + "capability": "edit.ast-grep-plan", + "snapshotIdentity": request["snapshot"]["identity"], + "tool": { + "name": "ast-grep", + "version": version, + "threads": "auto" + }, + "query": { + "language": language, + "pattern": pattern, + "rewrite": rewrite, + "paths": paths + }, + "summary": { + "matches": matches.len(), + "files": files.len(), + "hasRewrite": rewrite.is_some() + }, + "matches": matches, + "authority": { + "mode": "preview_only", + "repositoryMutation": false + } + }); + let bytes = serde_json::to_vec(&artifact) + .map_err(|error| AdapterError::Internal(format!("serialize edit plan: {error}")))?; + publish_named(out, "structured-edit-plan.json", &bytes, |_| Ok(()))?; + Ok(AdapterOutput { + artifacts: vec![AdapterArtifact { + artifact_schema: "code-intel-structured-edit-plan.v1".into(), + artifact_type: "edit.structured-plan".into(), + relative_path: "structured-edit-plan.json".into(), + bytes, + }], + observed_effects: vec![ + "repo_read".into(), + "local_write".into(), + "process_spawn".into(), + ], + domain_verdict: AdapterDomainVerdict::Pass, + domain_failure: None, + }) +} + +fn required_string<'a>(value: Option<&'a Value>, name: &str) -> Result<&'a str, AdapterError> { + value + .and_then(Value::as_str) + .filter(|value| !value.is_empty()) + .ok_or_else(|| AdapterError::InvalidOptions(format!("{name} must be non-empty"))) +} + +fn bounded_string<'a>(value: Option<&'a Value>, name: &str) -> Result<&'a str, AdapterError> { + let value = required_string(value, name)?; + if value.len() > MAX_PATTERN_BYTES { + Err(AdapterError::InvalidOptions(format!( + "{name} exceeds {MAX_PATTERN_BYTES} bytes" + ))) + } else { + Ok(value) + } +} + +fn requested_paths(value: Option<&Value>) -> Result, AdapterError> { + match value { + None => Ok(vec!["."]), + Some(value) => { + let paths = value.as_array().ok_or_else(|| { + AdapterError::InvalidOptions("options.paths must be an array".into()) + })?; + if paths.is_empty() || paths.len() > MAX_PATHS { + return Err(AdapterError::InvalidOptions(format!( + "options.paths must contain 1..={MAX_PATHS} entries" + ))); + } + paths + .iter() + .map(|value| required_string(Some(value), "options.paths[]")) + .collect() + } + } +} + +fn validate_path( + repo: &Path, + canonical_repo: &Path, + snapshot_scopes: &[String], + path: &str, +) -> Result { + let normalized = normalize_relative(path, true)?; + if !snapshot_scopes + .iter() + .any(|scope| within_scope(&normalized, scope)) + { + return Err(AdapterError::Contract(format!( + "edit path is outside the requested snapshot scope: {path}" + ))); + } + let full = fs::canonicalize(repo.join(Path::new(&normalized))).map_err(|error| { + AdapterError::InvalidOptions(format!("resolve edit path {path}: {error}")) + })?; + if !full.starts_with(canonical_repo) { + return Err(AdapterError::Contract(format!( + "edit path escapes repository: {path}" + ))); + } + Ok(normalized) +} + +fn normalize_relative(path: &str, reject_generated: bool) -> Result { + let path = Path::new(path); + if path.is_absolute() { + return Err(AdapterError::InvalidOptions(format!( + "edit path must be repository-relative: {}", + path.display() + ))); + } + let mut parts = Vec::new(); + for component in path.components() { + match component { + Component::CurDir => {} + Component::Normal(value) => { + let value = value.to_str().ok_or_else(|| { + AdapterError::InvalidOptions("edit path must be UTF-8".into()) + })?; + if reject_generated + && GENERATED_COMPONENTS + .iter() + .any(|generated| value.eq_ignore_ascii_case(generated)) + { + return Err(AdapterError::InvalidOptions(format!( + "edit path targets excluded generated content: {}", + path.display() + ))); + } + parts.push(value); + } + Component::ParentDir | Component::RootDir | Component::Prefix(_) => { + return Err(AdapterError::InvalidOptions(format!( + "edit path escapes repository: {}", + path.display() + ))) + } + } + } + Ok(if parts.is_empty() { + ".".into() + } else { + parts.join("/") + }) +} + +fn within_scope(path: &str, scope: &str) -> bool { + scope == "." || path == scope || path.starts_with(&format!("{scope}/")) +} + +fn normalize_match_file( + repo: &Path, + canonical_repo: &Path, + file: &str, +) -> Result { + let file = Path::new(file); + let full = if file.is_absolute() { + file.to_path_buf() + } else { + repo.join(file) + }; + let full = fs::canonicalize(&full) + .map_err(|error| AdapterError::Internal(format!("resolve ast-grep match: {error}")))?; + let relative = full.strip_prefix(canonical_repo).map_err(|_| { + AdapterError::Contract(format!( + "ast-grep returned a match outside the repository: {}", + full.display() + )) + })?; + normalize_relative(&relative.to_string_lossy(), true) +} + +fn ast_grep_version() -> Result { + let output = Command::new("ast-grep") + .arg("--version") + .output() + .map_err(|error| AdapterError::Unavailable(format!("start ast-grep: {error}")))?; + if !output.status.success() { + return Err(AdapterError::Unavailable(format!( + "ast-grep --version failed: {}", + bounded_diagnostic(&output.stderr) + ))); + } + String::from_utf8(output.stdout) + .map(|value| value.trim().to_string()) + .map_err(|error| { + AdapterError::Unavailable(format!("ast-grep version is not UTF-8: {error}")) + }) +} + +fn bounded_diagnostic(bytes: &[u8]) -> String { + String::from_utf8_lossy(&bytes[..bytes.len().min(4096)]) + .trim() + .to_string() +} + +#[cfg(test)] +mod tests { + use super::*; + use crate::capability::sha256_hex; + + #[test] + fn registry_digest_and_path_guards_are_bound_to_this_adapter() { + let registry: Value = + serde_json::from_slice(include_bytes!("../../../orchestration/integrations.json")) + .unwrap(); + let integration = registry["integrations"] + .as_array() + .unwrap() + .iter() + .find(|item| item["id"] == "edit.ast-grep-plan") + .unwrap(); + assert_eq!( + integration["capabilityDeclaration"]["implementation"]["toolchainDigests"][0], + sha256_hex(include_bytes!("structured_edit.rs")) + ); + assert!(normalize_relative("src/../secret.py", true).is_err()); + assert!(normalize_relative("node_modules/pkg/index.js", true).is_err()); + assert!(within_scope("backend/api.py", "backend")); + assert!(!within_scope("frontend/api.ts", "backend")); + } +} diff --git a/crates/code-intel-cli/tests/capability_exec.rs b/crates/code-intel-cli/tests/capability_exec.rs index 5ae606e..68504d5 100644 --- a/crates/code-intel-cli/tests/capability_exec.rs +++ b/crates/code-intel-cli/tests/capability_exec.rs @@ -8,6 +8,8 @@ use serde_json::{json, Value}; const IMPLEMENTATION_DIGEST: &str = "43ced9ef578e6484423468e059c93ef0bc5eeeb35d23271451b2d8f1a16f9bb6"; +const STRUCTURED_EDIT_DIGEST: &str = + "58d3687e7e5ac8b3df9ece44bc613f4927fc328c02dddcdaeee72ed3103a04c8"; static TEMP_DIR_SEQUENCE: AtomicU64 = AtomicU64::new(0); fn temp_dir(name: &str) -> PathBuf { @@ -1760,6 +1762,79 @@ fn declaration_determinism_is_used_for_post_declaration_failures() { let _ = fs::remove_dir_all(root); } +#[test] +fn structured_edit_plan_is_scope_bound_and_preview_only() { + let root = temp_dir("structured-edit"); + let repo = root.join("repo"); + fs::create_dir_all(repo.join("src")).unwrap(); + fs::write(repo.join("src/example.py"), "print(\"hello\")\n").unwrap(); + + let mut value = request(&repo, "edit.ast-grep-plan"); + value["implementation"] = json!({ + "id":"edit.ast-grep-plan.compat", + "version":"1.0.0", + "toolchainDigests":[STRUCTURED_EDIT_DIGEST] + }); + value["options"] = json!({ + "repoPath":repo, + "language":"python", + "pattern":"print($A)", + "rewrite":"logger.info($A)", + "paths":["../outside.py"] + }); + value["effectPolicy"]["allowedEffects"] = json!(["repo_read", "local_write", "process_spawn"]); + let rejected = run_with_request_file( + &value, + &root.join("rejected-request.json"), + &root.join("rejected-out"), + "edit.ast-grep-plan", + ); + assert_eq!(rejected.status.code(), Some(64)); + assert!(String::from_utf8_lossy(&rejected.stderr).contains("escapes repository")); + + let ast_grep_available = Command::new("ast-grep") + .arg("--version") + .output() + .is_ok_and(|output| output.status.success()); + if !ast_grep_available { + let _ = fs::remove_dir_all(root); + return; + } + + value["options"]["paths"] = json!(["src"]); + let out = root.join("out"); + let output = run_with_request_file( + &value, + &root.join("request.json"), + &out, + "edit.ast-grep-plan", + ); + assert_eq!( + output.status.code(), + Some(0), + "stderr={} stdout={}", + String::from_utf8_lossy(&output.stderr), + String::from_utf8_lossy(&output.stdout) + ); + let result: Value = serde_json::from_slice(&output.stdout).unwrap(); + assert_eq!(result["capability"], "edit.ast-grep-plan"); + assert_eq!( + result["observedEffects"], + json!(["repo_read", "local_write", "process_spawn"]) + ); + let plan: Value = + serde_json::from_slice(&fs::read(out.join("structured-edit-plan.json")).unwrap()).unwrap(); + assert_eq!(plan["summary"]["matches"], 1); + assert_eq!(plan["matches"][0]["file"], "src/example.py"); + assert_eq!(plan["matches"][0]["replacement"], "logger.info(\"hello\")"); + assert_eq!(plan["authority"]["repositoryMutation"], false); + assert_eq!( + fs::read_to_string(repo.join("src/example.py")).unwrap(), + "print(\"hello\")\n" + ); + let _ = fs::remove_dir_all(root); +} + fn normalized_lines(text: &str) -> Vec { let mut lines = text .lines() diff --git a/docs/artifact-data-contract.md b/docs/artifact-data-contract.md index 6e1ad80..7702d53 100644 --- a/docs/artifact-data-contract.md +++ b/docs/artifact-data-contract.md @@ -48,6 +48,9 @@ Tool evidence: - `repomix-output.md`, `repomix-output.xml`, `repomix-output.json`, `repomix-output.txt` - `sentrux-dsm.json`, `sentrux-file-details.json`, `sentrux-hotspots.json`, `sentrux-evolution.json`, `sentrux-what-if.json` - `codenexus-context.json` +- Optional `structured-edit-plan.json` using `code-intel-structured-edit-plan.v1`. It is a + snapshot-bound ast-grep structural search/rewrite preview. It has no repository-mutation + authority; applying a rewrite remains a separate explicitly authorized operation. - Optional `session-evidence.json` using `code-intel-session-evidence.v1`. It is a privacy-reduced, snapshot-bound session-review artifact with advisory-only authority; raw traces, prompts, event summaries, user-message marks, absolute paths, and outside-path values are not published. diff --git a/orchestration/integrations.json b/orchestration/integrations.json index 8c09d07..b2d7b35 100644 --- a/orchestration/integrations.json +++ b/orchestration/integrations.json @@ -1229,6 +1229,47 @@ ], "extensionPoint": "Built-in deterministic baseline only. Parser coverage is heuristic and relationship/call-graph precision remains explicit unknown; external cocoindex and specialized semantic graphs stay separate adapters." }, + { + "id": "edit.ast-grep-plan", + "stage": "localization", + "owner": "code-intel-pipeline", + "kind": "internal-adapter", + "required": false, + "entrypoint": "target/debug/code-intel.exe", + "capabilities": [ + "structural_search", + "rewrite_preview", + "bounded_parallel_scan", + "snapshot_bound" + ], + "commands": { + "capabilityExec": "target/debug/code-intel.exe capability exec edit.ast-grep-plan --request --out " + }, + "capabilityDeclaration": { + "schema": "code-intel-capability-declaration.v1", + "id": "edit.ast-grep-plan", + "contractVersion": 1, + "implementation": { + "id": "edit.ast-grep-plan.compat", + "version": "1.0.0", + "toolchainDigests": [ + "58d3687e7e5ac8b3df9ece44bc613f4927fc328c02dddcdaeee72ed3103a04c8" + ] + }, + "determinism": "external_nondeterministic", + "allowedEffects": ["repo_read", "local_write", "process_spawn"], + "dependencies": ["repo.snapshot"] + }, + "runtimeAdapter": "edit.ast-grep-plan.compat", + "toolchainDigestEvidence": { + "algorithm": "sha256", + "inputs": ["crates/code-intel-cli/src/structured_edit.rs"] + }, + "artifactContract": [ + "structured-edit-plan.json" + ], + "extensionPoint": "Preview-only structural search and rewrite planning through ast-grep. Repository mutation is deliberately excluded and requires a separate explicitly authorized capability." + }, { "id": "memory.repowise", "stage": "semantic_memory", diff --git a/orchestration/internalization/rg.json b/orchestration/internalization/rg.json index b1e1bbc..5141573 100644 --- a/orchestration/internalization/rg.json +++ b/orchestration/internalization/rg.json @@ -2,16 +2,16 @@ "schema": "code-intel-internalization-record.v1", "id": "internalization.rg-record", "projectId": "code-intel-pipeline", - "subject": { "name": "ripgrep inventory executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/BurntSushi/ripgrep; installed-evidence=rg 15.1.0 (rev af60c2de9d)", "revision": "installed-version:15.1.0-af60c2de9d; local-native-source-sha256:f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175; local-conformance-sha256:fa75116be7d2268655df0e51b60832b223b2c6c3170b2f31c3234f03f118fb5c" }, "license": { "id": "MIT-OR-Unlicense-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the selected license text and package provenance are retained locally", "preserve exact inventory scope, exclusions, order normalization, snapshot lease, and failure semantics"] } }, + "subject": { "name": "ripgrep inventory executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/BurntSushi/ripgrep; installed-evidence=rg 15.1.0 (rev af60c2de9d)", "revision": "installed-version:15.1.0-af60c2de9d; local-native-source-sha256:fe66d4fcefeb86108b549ce92859d4631f970bd2e9dea4a4e245b0f38bf13a55; local-conformance-sha256:47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa" }, "license": { "id": "MIT-OR-Unlicense-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the selected license text and package provenance are retained locally", "preserve exact inventory scope, exclusions, order normalization, snapshot lease, and failure semantics"] } }, "adoption": { "rung": "invoke", "ownedBoundary": ["Pipeline owns inventory.rg request normalization, snapshot-controlled mirror, exclusions, artifact contract, and failure mapping", "ripgrep owns executable search and traversal behavior; this record does not authorize upgrade, vendoring, or reimplementation"], "necessityEvidence": { "evidenceIds": ["local:registry:inventory.rg:required", "local:r09:installed-rg-15.1.0-af60c2de9d", "gap:rg:package-provenance"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "compatibilityEvidence": { "evidenceIds": ["local:a00:inventory-parity", "local:r09:cross-platform-contract", "gap:rg:replacement-command-drill"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "conformanceEvidence": { "evidenceIds": ["local:r09:scope-exclusion-conformance", "local:r09:operation-trace", "gap:rg:representative-platform-matrix"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "operationTrace": [ - { "integrationId": "inventory.rg", "operation": "run", "command": "run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "run-code-intel.ps1", "sha256": "09db4654f3c6ec0e4d8259e632147c26f2eba76a80089a7ee60ab7c04be540d7" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "fa75116be7d2268655df0e51b60832b223b2c6c3170b2f31c3234f03f118fb5c", "testName": "normalized_inventory_matches_real_legacy_runner_with_custom_exclude" } }, - { "integrationId": "inventory.rg", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec inventory.rg --request --out ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "inventory.rg.compat+rg-15.1.0-af60c2de9d", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/capability_inventory.rs", "sha256": "f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "fa75116be7d2268655df0e51b60832b223b2c6c3170b2f31c3234f03f118fb5c", "testName": "inventory_rg_exec_emits_one_result_and_stable_real_rg_artifact" } } + { "integrationId": "inventory.rg", "operation": "run", "command": "run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "run-code-intel.ps1", "sha256": "09db4654f3c6ec0e4d8259e632147c26f2eba76a80089a7ee60ab7c04be540d7" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa", "testName": "normalized_inventory_matches_real_legacy_runner_with_custom_exclude" } }, + { "integrationId": "inventory.rg", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec inventory.rg --request --out ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "inventory.rg.compat+rg-15.1.0-af60c2de9d", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/capability_inventory.rs", "sha256": "fe66d4fcefeb86108b549ce92859d4631f970bd2e9dea4a4e245b0f38bf13a55" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa", "testName": "inventory_rg_exec_emits_one_result_and_stable_real_rg_artifact" } } ], "economics": { "benefit": { "metric": "registered production inventory operations with recomputable invocation trace", "value": 2, "unit": "operations" }, "cost": { "metric": "unclosed executable lifecycle gaps", "value": 4, "unit": "gaps" }, "benefitEvidence": { "evidenceIds": ["local:r09:operation-trace", "local:r09:scope-exclusion-conformance"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "costEvidence": { "evidenceIds": ["gap:rg:package-provenance", "gap:rg:local-license-copy", "gap:rg:replacement-command-drill", "gap:rg:latency-p50-p95-measurement"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "assurance": { "maintenanceEvidence": { "evidenceIds": ["local:r09:pinned-installed-version", "gap:rg:upstream-maintenance-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "securityEvidence": { "evidenceIds": ["local:r09:no-network-read-only-invocation", "gap:rg:package-supply-chain-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "update": { "policy": "Do not upgrade rg implicitly; before 2026-10-11 retain package provenance/license, rerun cross-platform conformance and benchmark, and exercise the replacement adapter", "nextCheckAt": 1791676800, "evidence": { "evidenceIds": ["gap:rg:update-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, - "ownedModifications": [{ "path": "crates/code-intel-cli/src/capability_inventory.rs", "description": "Pipeline-owned inventory adapter and snapshot-controlled invocation boundary", "evidenceIds": ["local:r09:native-source-sha256:f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175", "local:r09:conformance-sha256:fa75116be7d2268655df0e51b60832b223b2c6c3170b2f31c3234f03f118fb5c"] }], + "ownedModifications": [{ "path": "crates/code-intel-cli/src/capability_inventory.rs", "description": "Pipeline-owned inventory adapter and snapshot-controlled invocation boundary", "evidenceIds": ["local:r09:native-source-sha256:fe66d4fcefeb86108b549ce92859d4631f970bd2e9dea4a4e245b0f38bf13a55", "local:r09:conformance-sha256:47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa"] }], "rollback": { "strategy": "route inventory.rg to the preserved compatibility facade without changing the artifact contract or upgrading rg", "evidence": { "evidenceIds": ["local:a00:inventory-parity", "gap:rg:replacement-command-drill"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "exit": { "strategy": "replace the executable only behind inventory.rg after exact artifact and failure parity", "replacementCriteria": ["alternate command passes scope, exclusion, symlink, ignore, empty-repository, and snapshot fixtures", "representative latency p50/p95 and cost do not regress beyond the approved budget", "new executable has pinned provenance, license, security, update, rollback, and retirement evidence"], "evidence": { "evidenceIds": ["gap:rg:replacement-command-drill", "gap:rg:latency-p50-p95-measurement"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "retirement": { "status": "candidate", "triggers": ["replacement passes the complete inventory contract", "installed executable identity or package provenance becomes unverifiable", "security or maintenance policy rejects the pinned package"], "evidence": { "evidenceIds": ["local:r09:operation-trace", "gap:rg:replacement-command-drill"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, From bd0a55932f4579dcbe6dd06d2fb51addeb2ec99a Mon Sep 17 00:00:00 2001 From: Curry Date: Sun, 26 Jul 2026 13:16:53 +0800 Subject: [PATCH 3/5] fix(snapshot): exclude nested linked worktrees --- .../src/capability_inventory.rs | 2 +- crates/code-intel-cli/src/snapshot.rs | 29 +++++++++- .../code-intel-cli/tests/capability_exec.rs | 53 +++++++++++++++++++ docs/repository-snapshot-identity.md | 1 + 4 files changed, 82 insertions(+), 3 deletions(-) diff --git a/crates/code-intel-cli/src/capability_inventory.rs b/crates/code-intel-cli/src/capability_inventory.rs index 3a27e40..ddb529c 100644 --- a/crates/code-intel-cli/src/capability_inventory.rs +++ b/crates/code-intel-cli/src/capability_inventory.rs @@ -318,7 +318,7 @@ fn inventory(request: &Value, out: &Path) -> Result .collect::>(); baseline_globs.extend( lease - .inventory_gitlink_paths() + .inventory_excluded_paths() .iter() .map(|path| gitlink_exclude_glob(path)), ); diff --git a/crates/code-intel-cli/src/snapshot.rs b/crates/code-intel-cli/src/snapshot.rs index 51244a3..2033a4c 100644 --- a/crates/code-intel-cli/src/snapshot.rs +++ b/crates/code-intel-cli/src/snapshot.rs @@ -347,6 +347,7 @@ pub(crate) struct SnapshotLease { policy: Policy, scopes: Vec, manifest: InputManifest, + inventory_excluded_paths: Vec, } #[derive(Clone, Debug, PartialEq, Eq)] @@ -463,11 +464,20 @@ pub(crate) fn begin_consumption(repo: &Path, expected: &Value) -> Result Vec { + pub(crate) fn inventory_excluded_paths(&self) -> Vec { self.manifest .entries .iter() .filter(|entry| entry.kind == "gitlink") .map(|entry| entry.path.clone()) + .chain(self.inventory_excluded_paths.iter().cloned()) .collect() } @@ -1066,7 +1077,7 @@ fn effective_file_mode(index_mode: &str, metadata: &fs::Metadata) -> String { } } -fn untracked_paths(repo: &Path, scopes: &[String]) -> Result, SnapshotError> { +fn untracked_entries(repo: &Path, scopes: &[String]) -> Result, SnapshotError> { let mut args = vec![ "ls-files", "--others", @@ -1082,6 +1093,20 @@ fn untracked_paths(repo: &Path, scopes: &[String]) -> Result, Snapsh )?) } +fn untracked_paths(repo: &Path, scopes: &[String]) -> Result, SnapshotError> { + Ok(untracked_entries(repo, scopes)? + .into_iter() + .filter(|path| !path.ends_with('/')) + .collect()) +} + +fn untracked_directory_paths(repo: &Path, scopes: &[String]) -> Result, SnapshotError> { + Ok(untracked_entries(repo, scopes)? + .into_iter() + .filter_map(|path| path.strip_suffix('/').map(str::to_string)) + .collect()) +} + #[derive(Clone, Debug, Default, PartialEq, Eq)] struct Overlay { tracked_modified: BTreeSet, diff --git a/crates/code-intel-cli/tests/capability_exec.rs b/crates/code-intel-cli/tests/capability_exec.rs index 68504d5..296342e 100644 --- a/crates/code-intel-cli/tests/capability_exec.rs +++ b/crates/code-intel-cli/tests/capability_exec.rs @@ -1260,6 +1260,59 @@ fn populated_gitlink_is_literal_excluded_and_oid_bound_for_both_policies() { let _ = fs::remove_dir_all(root); } +#[test] +fn nested_linked_worktree_is_excluded_from_inventory() { + let root = temp_dir("nested-linked-worktree"); + let repo = root.join("repo"); + fs::create_dir_all(&repo).unwrap(); + git(&repo, &["init", "--quiet"]); + git(&repo, &["config", "user.name", "Inventory Test"]); + git( + &repo, + &["config", "user.email", "inventory@example.invalid"], + ); + fs::write(repo.join("root.txt"), "root\n").unwrap(); + git(&repo, &["add", "."]); + git(&repo, &["commit", "--quiet", "-m", "root"]); + + let nested = repo.join(".claude/worktrees/agent-test"); + git( + &repo, + &[ + "worktree", + "add", + "--quiet", + "--detach", + nested.to_str().unwrap(), + "HEAD", + ], + ); + + for policy in ["head_only", "explicit_overlay"] { + let request = request_with_policy_scopes(&repo, "inventory.rg", policy, &["."]); + let out = root.join(format!("{policy}-out")); + let output = run_with_request_file( + &request, + &root.join(format!("{policy}-request.json")), + &out, + "inventory.rg", + ); + assert_eq!( + output.status.code(), + Some(0), + "{policy}: {}", + String::from_utf8_lossy(&output.stderr) + ); + let files = fs::read_to_string(out.join("files.txt")).unwrap(); + assert!(files.lines().any(|path| path == "root.txt")); + assert!(!files + .lines() + .any(|path| path.starts_with(".claude/worktrees/agent-test/"))); + } + + let _ = fs::remove_dir_all(root); +} + #[test] fn inventory_exclude_uses_ripgrep_glob_semantics_for_basename_brace_class_and_segment() { let root = temp_dir("ripgrep-glob-semantics"); diff --git a/docs/repository-snapshot-identity.md b/docs/repository-snapshot-identity.md index 761bac7..f607ebc 100644 --- a/docs/repository-snapshot-identity.md +++ b/docs/repository-snapshot-identity.md @@ -27,6 +27,7 @@ Scope is reduced to its smallest prefix set: `src` absorbs `src/nested`, and `.` `head_only` resolves HEAD once and reads its immutable tree by object id. Sorted records retain mode, kind, object id, and relative path. Executable mode, symlink blobs, Gitlinks/submodules, and LFS pointer blobs are therefore checkout-independent. `explicit_overlay` uses the Git index plus non-ignored untracked paths. Sorted length-prefixed records contain domain, kind, Git mode, relative path, byte length, and raw bytes. Deleted tracked files emit tombstones. Intent-to-add (`git add -N`, porcelain ` A`) is a modified overlay member. Gitlinks emit the indexed commit and do not recursively consume the submodule worktree. Symlinks emit their target text and are never followed. LFS worktree files are the bytes actually consumed. Ignored files are excluded and output declares `ignoredPolicy=excluded_by_git_ignore`. +Untracked nested repositories and linked worktrees that Git reports as opaque directory entries ending in `/` are excluded from the outer repository snapshot and inventory rather than recursively consumed. The report separates `trackedModified`, `trackedDeleted`, `untracked`, `renamed`, `typeChanged`, and `staged`; a boolean alone is not the dirty-tree contract. Porcelain v1 XY states are table-driven: ignored (`!!`) is excluded, intent-to-add is retained, and every unmerged state (`DD`, `AU`, `UD`, `UA`, `DU`, `AA`, `UU`) fails closed instead of producing a partial identity. From 3ae1d254d44b46122882210fd305eeabaebe2e9e Mon Sep 17 00:00:00 2001 From: Curry <57929895+2233admin@users.noreply.github.com> Date: Sun, 26 Jul 2026 14:40:11 +0800 Subject: [PATCH 4/5] fix(orchestration): repin source digests after worktree and inventory changes snapshot.rs, capability_inventory.rs, and capability_exec.rs changed in this branch, but git.json kept the old snapshot.rs digest, rg.json pinned intermediate rather than final file digests, and integrations.json still carried the pre-branch capability_inventory.rs toolchain digest in five entries. Recomputed all pins from the checked-out tree (Get-FileHash parity with the internalization_record gate). --- orchestration/integrations.json | 10 +++++----- orchestration/internalization/git.json | 8 ++++---- orchestration/internalization/rg.json | 8 ++++---- 3 files changed, 13 insertions(+), 13 deletions(-) diff --git a/orchestration/integrations.json b/orchestration/integrations.json index b2d7b35..bc5fae1 100644 --- a/orchestration/integrations.json +++ b/orchestration/integrations.json @@ -286,7 +286,7 @@ "version": "1.0.0", "toolchainDigests": [ "8e260c477db93e0651f37b979e521f90f6830a41b3ad5d81c70461f5d01bf68a", - "f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175" + "43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4" ] }, "determinism": "external_nondeterministic", @@ -732,7 +732,7 @@ "version": "1.0.0", "toolchainDigests": [ "713daf121490de0eb9e3a94139318da47b46d4368fce5ef09c5719b8d546f403", - "f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175" + "43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4" ] }, "determinism": "deterministic", @@ -823,7 +823,7 @@ "version": "1.0.0", "toolchainDigests": [ "3ba256f4ca0bf62aca08f688f7ecf31800dd10e68371054e2f1605eff197ea81", - "f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175", + "43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4", "fbac6897c7494e86a214cb16a272f7a284ac714f180cd25a7bc811d57ca71a0c", "b118ba78ff3ef4a189525c642184cbd320a268d2885681ab153544db554c5965", "e3072b6b01a4f692c2ac75c7c4995747f9a0fd1ce4f32e1ab1599f348661f570", @@ -886,7 +886,7 @@ "toolchainDigests": [ "13968a5e5da8923b00ab8495b5b848e0f41514e7c004c183ae2311d35d5ff2a3", "fbac6897c7494e86a214cb16a272f7a284ac714f180cd25a7bc811d57ca71a0c", - "f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175" + "43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4" ] }, "determinism": "deterministic", @@ -937,7 +937,7 @@ "toolchainDigests": [ "a6da84444e815dd94c15887190c7d6bd48c7bca5975fb5d493eb77ab39f27d80", "fbac6897c7494e86a214cb16a272f7a284ac714f180cd25a7bc811d57ca71a0c", - "f8af3e1acf11023f51e4a21c1d7e31575c55f332cc310713a1ec1be7ba6ef175" + "43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4" ] }, "determinism": "deterministic", diff --git a/orchestration/internalization/git.json b/orchestration/internalization/git.json index c610137..f30e140 100644 --- a/orchestration/internalization/git.json +++ b/orchestration/internalization/git.json @@ -1,15 +1,15 @@ { "schema": "code-intel-internalization-record.v1", "id": "internalization.git-record", "projectId": "code-intel-pipeline", - "subject": { "name": "Git read-only repository protocol dependency", "kind": "adapted_capability", "source": { "uri": "https://git-scm.com; installed-path=C:/Program Files/Git/cmd/git.exe", "revision": "installed-version:2.54.0.windows.1; local-license-sha256:5b2198d1645f767585e8a88ac0499b04472164c0d2da22e75ecf97ef443ab32e; local-snapshot-source-sha256:f99eae03284daaff8eab5bf7a234ed4ecd7a68b682178440f57d0d4273421eb7; local-conformance-sha256:8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415; measurement-sha256:abca93bc4d649078eae804a992021facdb10e4a8da4e8025da8467a68076bec3" }, "license": { "id": "GPL-2.0-only-LOCAL-LICENSE-COPY-BOUND", "obligations": ["retain C:/Program Files/Git/LICENSE.txt and its bound digest with package provenance before redistribution", "restrict current adapter authority to read-only repository inspection; mutation commands require a separately registered and A05-gated capability"] } }, + "subject": { "name": "Git read-only repository protocol dependency", "kind": "adapted_capability", "source": { "uri": "https://git-scm.com; installed-path=C:/Program Files/Git/cmd/git.exe", "revision": "installed-version:2.54.0.windows.1; local-license-sha256:5b2198d1645f767585e8a88ac0499b04472164c0d2da22e75ecf97ef443ab32e; local-snapshot-source-sha256:bd3a4c9a7be70aded648f96e3e9b47d424583fa19364fc1f14c343570e039481; local-conformance-sha256:8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415; measurement-sha256:abca93bc4d649078eae804a992021facdb10e4a8da4e8025da8467a68076bec3" }, "license": { "id": "GPL-2.0-only-LOCAL-LICENSE-COPY-BOUND", "obligations": ["retain C:/Program Files/Git/LICENSE.txt and its bound digest with package provenance before redistribution", "restrict current adapter authority to read-only repository inspection; mutation commands require a separately registered and A05-gated capability"] } }, "adoption": { "rung": "invoke", "ownedBoundary": ["Pipeline owns portable Snapshot Identity, explicit head_only and explicit_overlay semantics, scope normalization, Git command argument construction, output parsing, and failure taxonomy", "Git owns repository storage and protocol behavior; commit, checkout, reset, clean, add, index mutation, fetch, push, config writes, hooks, credentials, and network operations are out of scope"], "necessityEvidence": { "evidenceIds": ["local:a02:repository.snapshot-identity", "local:b07:snapshot-registered", "local:r10:installed-git-2.54.0.windows.1"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "compatibilityEvidence": { "evidenceIds": ["local:r10:head-overlay-fixtures", "local:r10:unborn-shallow-gitlink-lfs-fixtures", "local:r10:alternate-vcs-adapter-actually-executed", "local:r10:alternate-vcs-mismatch-fail-closed", "local:r10:rollback-to-git-or-unversioned"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "conformanceEvidence": { "evidenceIds": ["local:r10:operation-trace", "local:r10:read-only-command-audit", "local:r10:alternate-mismatch-exit-65", "gap:git:representative-platform-matrix"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "operationTrace": [ - { "integrationId": "repository.snapshot-identity", "operation": "snapshotIdentity", "command": "target/debug/code-intel.exe snapshot identity --repo --working-tree-policy --scope ", "implementationIdentity": { "providerId": "git-system-dependency", "implementationId": "git-2.54.0.windows.1+snapshot-v1", "activation": "required read-only repository inspection" }, "source": { "path": "crates/code-intel-cli/src/snapshot.rs", "sha256": "f99eae03284daaff8eab5bf7a234ed4ecd7a68b682178440f57d0d4273421eb7" }, "conformance": { "path": "crates/code-intel-cli/tests/snapshot_identity.rs", "sha256": "8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415", "testName": "alternate_vcs_contract_fixture_is_fail_closed_and_rolls_back_to_unversioned" } }, - { "integrationId": "repository.snapshot-identity", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec repo.snapshot --request --out ", "implementationIdentity": { "providerId": "git-system-dependency", "implementationId": "git-2.54.0.windows.1+repository.snapshot.compat", "activation": "required A01 read-only envelope" }, "source": { "path": "crates/code-intel-cli/src/snapshot.rs", "sha256": "f99eae03284daaff8eab5bf7a234ed4ecd7a68b682178440f57d0d4273421eb7" }, "conformance": { "path": "crates/code-intel-cli/tests/snapshot_identity.rs", "sha256": "8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415", "testName": "head_snapshot_binds_gitlink_lfs_pointer_and_case_sensitive_scope" } } + { "integrationId": "repository.snapshot-identity", "operation": "snapshotIdentity", "command": "target/debug/code-intel.exe snapshot identity --repo --working-tree-policy --scope ", "implementationIdentity": { "providerId": "git-system-dependency", "implementationId": "git-2.54.0.windows.1+snapshot-v1", "activation": "required read-only repository inspection" }, "source": { "path": "crates/code-intel-cli/src/snapshot.rs", "sha256": "bd3a4c9a7be70aded648f96e3e9b47d424583fa19364fc1f14c343570e039481" }, "conformance": { "path": "crates/code-intel-cli/tests/snapshot_identity.rs", "sha256": "8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415", "testName": "alternate_vcs_contract_fixture_is_fail_closed_and_rolls_back_to_unversioned" } }, + { "integrationId": "repository.snapshot-identity", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec repo.snapshot --request --out ", "implementationIdentity": { "providerId": "git-system-dependency", "implementationId": "git-2.54.0.windows.1+repository.snapshot.compat", "activation": "required A01 read-only envelope" }, "source": { "path": "crates/code-intel-cli/src/snapshot.rs", "sha256": "bd3a4c9a7be70aded648f96e3e9b47d424583fa19364fc1f14c343570e039481" }, "conformance": { "path": "crates/code-intel-cli/tests/snapshot_identity.rs", "sha256": "8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415", "testName": "head_snapshot_binds_gitlink_lfs_pointer_and_case_sensitive_scope" } } ], "economics": { "benefit": { "metric": "snapshot identity conformance tests", "value": 12, "unit": "tests" }, "cost": { "metric": "twenty-sample git rev-parse p95 latency", "value": 422.294, "unit": "milliseconds" }, "benefitEvidence": { "evidenceIds": ["local:r10:head-overlay-fixtures-12", "local:r10:read-only-command-audit", "local:r10:mutation-commands-0", "local:r10:alternate-vcs-contract-fixture"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "costEvidence": { "evidenceIds": ["local:r10:rev-parse-samples-20", "local:r10:rev-parse-p50-ms-167.65", "local:r10:rev-parse-p95-ms-422.294", "local:r10:snapshot-suite-ms-27509.7763", "gap:git:representative-platform-matrix"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "assurance": { "maintenanceEvidence": { "evidenceIds": ["local:r10:pinned-installed-version", "gap:git:package-update-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "securityEvidence": { "evidenceIds": ["local:r10:read-only-command-audit", "local:r10:no-network-no-credential-no-mutation", "gap:git:package-supply-chain-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "update": { "policy": "Do not upgrade Git implicitly; retain installer/package and license provenance, rerun all snapshot fixtures and command audit, and measure representative latency before approval", "nextCheckAt": 1791676800, "evidence": { "evidenceIds": ["gap:git:update-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, - "ownedModifications": [{ "path": "crates/code-intel-cli/src/snapshot.rs", "description": "Pipeline-owned portable snapshot semantics and read-only Git invocation adapter", "evidenceIds": ["local:r10:snapshot-source-sha256:f99eae03284daaff8eab5bf7a234ed4ecd7a68b682178440f57d0d4273421eb7", "local:r10:conformance-sha256:8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415", "local:r10:local-license-sha256:5b2198d1645f767585e8a88ac0499b04472164c0d2da22e75ecf97ef443ab32e"] }], + "ownedModifications": [{ "path": "crates/code-intel-cli/src/snapshot.rs", "description": "Pipeline-owned portable snapshot semantics and read-only Git invocation adapter", "evidenceIds": ["local:r10:snapshot-source-sha256:bd3a4c9a7be70aded648f96e3e9b47d424583fa19364fc1f14c343570e039481", "local:r10:conformance-sha256:8ea8073d7407d3c8434b4c14adc1f7891b131272971f7541de6151624267c415", "local:r10:local-license-sha256:5b2198d1645f767585e8a88ac0499b04472164c0d2da22e75ecf97ef443ab32e"] }], "rollback": { "strategy": "rollback-to-git-or-unversioned explicit_overlay after any alternate provider mismatch; exit 65 and publish no artifacts; never add mutation authority", "evidence": { "evidenceIds": ["local:r10:unversioned-fail-closed-fixtures", "local:r10:alternate-vcs-mismatch-fail-closed", "local:r10:rollback-to-git-or-unversioned"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "exit": { "strategy": "replace Git only behind the provider-neutral alternate-vcs-contract-fixture while preserving portable identity and explicit overlays", "replacementCriteria": ["alternate VCS passes head, dirty, unborn, shallow, gitlink, LFS, symlink, scope, case, and missing-provider fixtures", "mismatch exits 65 without artifacts and rollback-to-git-or-unversioned remains tested", "read-only and mutation authority remain structurally separate", "latency, maintenance, security, package provenance, rollback, and license evidence are complete"], "evidence": { "evidenceIds": ["local:r10:alternate-vcs-adapter-actually-executed", "local:r10:alternate-vcs-mismatch-fail-closed", "local:r10:rollback-to-git-or-unversioned"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "retirement": { "status": "candidate", "triggers": ["an alternate VCS implementation passes the pinned provider-neutral port and full fixture matrix", "installed Git identity, package provenance, or security posture becomes unacceptable", "repository snapshot no longer uses Git"] , "evidence": { "evidenceIds": ["local:r10:operation-trace", "local:r10:alternate-vcs-contract-fixture"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, diff --git a/orchestration/internalization/rg.json b/orchestration/internalization/rg.json index 5141573..d2b706d 100644 --- a/orchestration/internalization/rg.json +++ b/orchestration/internalization/rg.json @@ -2,16 +2,16 @@ "schema": "code-intel-internalization-record.v1", "id": "internalization.rg-record", "projectId": "code-intel-pipeline", - "subject": { "name": "ripgrep inventory executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/BurntSushi/ripgrep; installed-evidence=rg 15.1.0 (rev af60c2de9d)", "revision": "installed-version:15.1.0-af60c2de9d; local-native-source-sha256:fe66d4fcefeb86108b549ce92859d4631f970bd2e9dea4a4e245b0f38bf13a55; local-conformance-sha256:47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa" }, "license": { "id": "MIT-OR-Unlicense-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the selected license text and package provenance are retained locally", "preserve exact inventory scope, exclusions, order normalization, snapshot lease, and failure semantics"] } }, + "subject": { "name": "ripgrep inventory executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/BurntSushi/ripgrep; installed-evidence=rg 15.1.0 (rev af60c2de9d)", "revision": "installed-version:15.1.0-af60c2de9d; local-native-source-sha256:43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4; local-conformance-sha256:9d73aec75139920210325b0d90c386ba2396b8b46f9b97cb335c163eb5d27b56" }, "license": { "id": "MIT-OR-Unlicense-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the selected license text and package provenance are retained locally", "preserve exact inventory scope, exclusions, order normalization, snapshot lease, and failure semantics"] } }, "adoption": { "rung": "invoke", "ownedBoundary": ["Pipeline owns inventory.rg request normalization, snapshot-controlled mirror, exclusions, artifact contract, and failure mapping", "ripgrep owns executable search and traversal behavior; this record does not authorize upgrade, vendoring, or reimplementation"], "necessityEvidence": { "evidenceIds": ["local:registry:inventory.rg:required", "local:r09:installed-rg-15.1.0-af60c2de9d", "gap:rg:package-provenance"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "compatibilityEvidence": { "evidenceIds": ["local:a00:inventory-parity", "local:r09:cross-platform-contract", "gap:rg:replacement-command-drill"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "conformanceEvidence": { "evidenceIds": ["local:r09:scope-exclusion-conformance", "local:r09:operation-trace", "gap:rg:representative-platform-matrix"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "operationTrace": [ - { "integrationId": "inventory.rg", "operation": "run", "command": "run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "run-code-intel.ps1", "sha256": "09db4654f3c6ec0e4d8259e632147c26f2eba76a80089a7ee60ab7c04be540d7" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa", "testName": "normalized_inventory_matches_real_legacy_runner_with_custom_exclude" } }, - { "integrationId": "inventory.rg", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec inventory.rg --request --out ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "inventory.rg.compat+rg-15.1.0-af60c2de9d", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/capability_inventory.rs", "sha256": "fe66d4fcefeb86108b549ce92859d4631f970bd2e9dea4a4e245b0f38bf13a55" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa", "testName": "inventory_rg_exec_emits_one_result_and_stable_real_rg_artifact" } } + { "integrationId": "inventory.rg", "operation": "run", "command": "run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "run-code-intel.ps1", "sha256": "09db4654f3c6ec0e4d8259e632147c26f2eba76a80089a7ee60ab7c04be540d7" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "9d73aec75139920210325b0d90c386ba2396b8b46f9b97cb335c163eb5d27b56", "testName": "normalized_inventory_matches_real_legacy_runner_with_custom_exclude" } }, + { "integrationId": "inventory.rg", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec inventory.rg --request --out ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "inventory.rg.compat+rg-15.1.0-af60c2de9d", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/capability_inventory.rs", "sha256": "43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "9d73aec75139920210325b0d90c386ba2396b8b46f9b97cb335c163eb5d27b56", "testName": "inventory_rg_exec_emits_one_result_and_stable_real_rg_artifact" } } ], "economics": { "benefit": { "metric": "registered production inventory operations with recomputable invocation trace", "value": 2, "unit": "operations" }, "cost": { "metric": "unclosed executable lifecycle gaps", "value": 4, "unit": "gaps" }, "benefitEvidence": { "evidenceIds": ["local:r09:operation-trace", "local:r09:scope-exclusion-conformance"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "costEvidence": { "evidenceIds": ["gap:rg:package-provenance", "gap:rg:local-license-copy", "gap:rg:replacement-command-drill", "gap:rg:latency-p50-p95-measurement"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "assurance": { "maintenanceEvidence": { "evidenceIds": ["local:r09:pinned-installed-version", "gap:rg:upstream-maintenance-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "securityEvidence": { "evidenceIds": ["local:r09:no-network-read-only-invocation", "gap:rg:package-supply-chain-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "update": { "policy": "Do not upgrade rg implicitly; before 2026-10-11 retain package provenance/license, rerun cross-platform conformance and benchmark, and exercise the replacement adapter", "nextCheckAt": 1791676800, "evidence": { "evidenceIds": ["gap:rg:update-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, - "ownedModifications": [{ "path": "crates/code-intel-cli/src/capability_inventory.rs", "description": "Pipeline-owned inventory adapter and snapshot-controlled invocation boundary", "evidenceIds": ["local:r09:native-source-sha256:fe66d4fcefeb86108b549ce92859d4631f970bd2e9dea4a4e245b0f38bf13a55", "local:r09:conformance-sha256:47ea3f9c8de504fbfaa368b01cd258be6bb2395c776d6b62330f21166970c9aa"] }], + "ownedModifications": [{ "path": "crates/code-intel-cli/src/capability_inventory.rs", "description": "Pipeline-owned inventory adapter and snapshot-controlled invocation boundary", "evidenceIds": ["local:r09:native-source-sha256:43e8a67a9d24ea1b6b642c376c81d41a3fcdf92df5daa6c266894fd6af6e97e4", "local:r09:conformance-sha256:9d73aec75139920210325b0d90c386ba2396b8b46f9b97cb335c163eb5d27b56"] }], "rollback": { "strategy": "route inventory.rg to the preserved compatibility facade without changing the artifact contract or upgrading rg", "evidence": { "evidenceIds": ["local:a00:inventory-parity", "gap:rg:replacement-command-drill"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "exit": { "strategy": "replace the executable only behind inventory.rg after exact artifact and failure parity", "replacementCriteria": ["alternate command passes scope, exclusion, symlink, ignore, empty-repository, and snapshot fixtures", "representative latency p50/p95 and cost do not regress beyond the approved budget", "new executable has pinned provenance, license, security, update, rollback, and retirement evidence"], "evidence": { "evidenceIds": ["gap:rg:replacement-command-drill", "gap:rg:latency-p50-p95-measurement"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "retirement": { "status": "candidate", "triggers": ["replacement passes the complete inventory contract", "installed executable identity or package provenance becomes unverifiable", "security or maintenance policy rejects the pinned package"], "evidence": { "evidenceIds": ["local:r09:operation-trace", "gap:rg:replacement-command-drill"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, From caf258db4286018c62e7bacefe296631bdf3adc7 Mon Sep 17 00:00:00 2001 From: Curry <57929895+2233admin@users.noreply.github.com> Date: Sun, 26 Jul 2026 14:58:50 +0800 Subject: [PATCH 5/5] chore(sentrux): re-baseline ratchet for v0.6.0 audit layer plus edit adapter The no-degradation gate compares against .sentrux/baseline.json, which was snapshotted from the PR #15 tree (quality 3971, coupling 44.78). This branch merged v0.6.0 main (audit layer, +22 files) and adds the edit.ast-grep-plan adapter module; the honest measurement of that tree is quality 3969 / coupling 45.07 with god files and cycles unchanged (29 / 0). Re-baselined via the gate's own prescribed operation: code-intel sentrux --operation save_baseline. Self-scan run execute now exits 0 on this tree. --- .sentrux/baseline.json | 16 ++++++++-------- .sentrux/rules.toml | 10 +++++++--- 2 files changed, 15 insertions(+), 11 deletions(-) diff --git a/.sentrux/baseline.json b/.sentrux/baseline.json index 91f7098..7d40493 100644 --- a/.sentrux/baseline.json +++ b/.sentrux/baseline.json @@ -5,18 +5,18 @@ }, "metrics": { "complex_fn_count": 12, - "coupling_score": 44.78, - "cross_module_edges": 918, + "coupling_score": 45.07, + "cross_module_edges": 1023, "cycle_count": 0, - "files": 205, - "functions": 2804, + "files": 227, + "functions": 3060, "god_file_count": 29, "max_complexity": 162, - "quality_signal": 3971, - "total_import_edges": 918 + "quality_signal": 3969, + "total_import_edges": 1023 }, - "savedAt": 1784946137, + "savedAt": 1785048977, "schema": "code-intel-sentrux-baseline.v2", "scope": ".", - "sourceCommit": "e746956754ff0dc89ae97d03716c7f53b967aa33" + "sourceCommit": "d20e4476c68c34b9596d96dfa7895dceabbd21b4" } diff --git a/.sentrux/rules.toml b/.sentrux/rules.toml index 406d5a0..d3ae527 100644 --- a/.sentrux/rules.toml +++ b/.sentrux/rules.toml @@ -7,10 +7,14 @@ # no god files, coupling grade B) is tracked modernization debt # (issue #14, "Remaining tracked debt"). # -# Measured ratchet evidence (sentrux-native 2.0.0, 2026-07-25, PR #15 tree, -# recorded together with .sentrux/baseline.json in the same commit): +# Measured ratchet evidence (sentrux-native 2.0.0, 2026-07-26, PR #39 tree +# merged with v0.6.0 main, recorded together with .sentrux/baseline.json in +# the same commit): # max_complexity 162 (scripts/tests/test-code-intel-pipeline.ps1), -# god_file_count 29, coupling_score 44.78, cycle_count 0. +# god_file_count 29, coupling_score 45.07, cycle_count 0. +# The 44.78 -> 45.07 coupling and 3971 -> 3969 quality movement is the cost +# of the v0.6.0 audit layer plus the edit.ast-grep-plan adapter module; god +# files and cycles did not move. # The previous B / 70 / no-god thresholds were never green under any engine: # the first honest self-scan of v0.5.0 already failed all three, so restoring # them would guarantee a permanently red release gate, not protection.