From d709f56cadb2793b6fed0587bc62671df4280344 Mon Sep 17 00:00:00 2001 From: Curry Date: Tue, 18 Aug 2026 23:25:52 +0800 Subject: [PATCH 01/27] feat(sentrux): add capability matrix contract --- ...l-sentrux-capability-matrix.v1.schema.json | 49 ++++ .../sentrux-capability-matrix.v1.json | 244 ++++++++++++++++++ tests/test_sentrux_capability_matrix.py | 85 ++++++ 3 files changed, 378 insertions(+) create mode 100644 orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json create mode 100644 orchestration/sentrux-capability-matrix.v1.json create mode 100644 tests/test_sentrux_capability_matrix.py diff --git a/orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json b/orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json new file mode 100644 index 00000000..d5f887e3 --- /dev/null +++ b/orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json @@ -0,0 +1,49 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "https://code-intel.local/schemas/code-intel-sentrux-capability-matrix.v1.schema.json", + "title": "Code Intel Sentrux Capability Matrix v1", + "type": "object", + "additionalProperties": false, + "required": ["schema", "contractVersion", "coverageStatus", "sources", "completionPolicy", "capabilities"], + "properties": { + "schema": { "const": "code-intel-sentrux-capability-matrix.v1" }, + "contractVersion": { "const": 1 }, + "coverageStatus": { "enum": ["partial", "complete"] }, + "purpose": { "type": "string", "minLength": 1 }, + "sources": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "completionPolicy": { + "type": "object", + "additionalProperties": false, + "required": ["requiredStatesForComplete", "forbiddenSilentStates", "rule"], + "properties": { + "requiredStatesForComplete": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "forbiddenSilentStates": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "rule": { "type": "string", "minLength": 1 } + } + }, + "capabilities": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "additionalProperties": false, + "required": ["id", "operation", "aliases", "scope", "trigger", "route", "currentState", "requiredForRelease", "inputs", "artifacts", "decisionConsumers", "failureSemantics", "platforms"], + "properties": { + "id": { "type": "string", "pattern": "^sentrux\\.[a-z0-9_]+$" }, + "operation": { "type": "string", "pattern": "^[a-z0-9_]+$" }, + "aliases": { "type": "array", "items": { "type": "string", "pattern": "^[a-z0-9_]+$" }, "uniqueItems": true }, + "scope": { "type": "string", "minLength": 1 }, + "trigger": { "type": "string", "minLength": 1 }, + "route": { "type": "string", "minLength": 1 }, + "currentState": { "enum": ["authoritative_automatic", "native_manual", "compatibility_only", "declared_only"] }, + "requiredForRelease": { "type": "boolean" }, + "inputs": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "artifacts": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "decisionConsumers": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, + "failureSemantics": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "platforms": { "type": "array", "items": { "enum": ["windows", "macos", "linux"] }, "minItems": 1, "uniqueItems": true } + } + } + } + } +} diff --git a/orchestration/sentrux-capability-matrix.v1.json b/orchestration/sentrux-capability-matrix.v1.json new file mode 100644 index 00000000..98984b0a --- /dev/null +++ b/orchestration/sentrux-capability-matrix.v1.json @@ -0,0 +1,244 @@ +{ + "schema": "code-intel-sentrux-capability-matrix.v1", + "contractVersion": 1, + "coverageStatus": "partial", + "purpose": "Single inventory of Sentrux capabilities, their canonical operation, automation route, artifacts, consumers, and remaining gaps.", + "sources": [ + "legacy/Invoke-SentruxAgentTool.ps1", + "crates/code-intel-cli/src/sentrux.rs", + "crates/code-intel-cli/src/builtin_provider_evidence.rs", + "orchestration/integrations.json" + ], + "completionPolicy": { + "requiredStatesForComplete": ["authoritative_automatic"], + "forbiddenSilentStates": ["declared_only", "compatibility_only"], + "rule": "Every applicable capability must have an automatic route, a validated artifact, and at least one downstream decision consumer before a complete release is allowed." + }, + "capabilities": [ + { + "id": "sentrux.scan", + "operation": "scan", + "aliases": ["sentrux_scan"], + "scope": "core_analysis", + "trigger": "repository_run", + "route": "rust_native_manual", + "currentState": "native_manual", + "requiredForRelease": true, + "inputs": ["repository.snapshot"], + "artifacts": ["sentrux-scan.v1"], + "decisionConsumers": [], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.health", + "operation": "health", + "aliases": ["sentrux_health"], + "scope": "core_analysis", + "trigger": "repository_run", + "route": "rust_native_manual", + "currentState": "native_manual", + "requiredForRelease": true, + "inputs": ["sentrux.scan.v1"], + "artifacts": ["sentrux-health.v1"], + "decisionConsumers": [], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.dsm", + "operation": "dsm", + "aliases": ["sentrux_dsm"], + "scope": "core_analysis", + "trigger": "repository_run", + "route": "rust_native_manual", + "currentState": "native_manual", + "requiredForRelease": true, + "inputs": ["repository.snapshot"], + "artifacts": ["sentrux-dsm.v1", "sentrux-hotspots.v1", "sentrux-file-details.v1"], + "decisionConsumers": [], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.git_stats", + "operation": "git_stats", + "aliases": ["sentrux_git_stats"], + "scope": "history", + "trigger": "repository_run", + "route": "legacy_compatibility", + "currentState": "compatibility_only", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "git.history"], + "artifacts": ["sentrux-git-stats.v1"], + "decisionConsumers": [], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.evolution", + "operation": "evolution", + "aliases": [], + "scope": "history", + "trigger": "repository_run", + "route": "legacy_compatibility", + "currentState": "compatibility_only", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "git.history"], + "artifacts": ["sentrux-evolution.v1"], + "decisionConsumers": [], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.test_gaps", + "operation": "test_gaps", + "aliases": ["sentrux_test_gaps"], + "scope": "test_selection", + "trigger": "repository_run", + "route": "legacy_compatibility", + "currentState": "compatibility_only", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "sentrux.dsm.v1", "test.inventory"], + "artifacts": ["sentrux-test-gaps.v1"], + "decisionConsumers": [], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.what_if", + "operation": "what_if", + "aliases": [], + "scope": "change_prediction", + "trigger": "change_or_pr", + "route": "legacy_compatibility", + "currentState": "compatibility_only", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "change.set", "sentrux.dsm.v1"], + "artifacts": ["sentrux-what-if.v1"], + "decisionConsumers": [], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.check_rules", + "operation": "check_rules", + "aliases": ["rules"], + "scope": "rules", + "trigger": "repository_run", + "route": "rust_native_manual", + "currentState": "native_manual", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "sentrux.rules"], + "artifacts": ["sentrux-check-rules.v1"], + "decisionConsumers": [], + "failureSemantics": ["domain_fail", "failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.check", + "operation": "check", + "aliases": [], + "scope": "rules", + "trigger": "repository_run", + "route": "provider.sentrux-adapt", + "currentState": "authoritative_automatic", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "sentrux.rules", "sentrux.baseline"], + "artifacts": ["code-intel-sentrux-command-observation.v1", "code-intel-structural-evidence-payload.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "pr_gate", "release_gate"], + "failureSemantics": ["domain_fail", "failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.gate", + "operation": "gate", + "aliases": [], + "scope": "rules", + "trigger": "repository_run", + "route": "provider.sentrux-adapt", + "currentState": "authoritative_automatic", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "sentrux.rules", "sentrux.baseline"], + "artifacts": ["code-intel-sentrux-command-observation.v1", "code-intel-structural-evidence-payload.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "pr_gate", "release_gate"], + "failureSemantics": ["domain_fail", "failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.baseline_save", + "operation": "gate_save", + "aliases": ["save_baseline", "baseline"], + "scope": "rules", + "trigger": "bootstrap_or_explicit_approval", + "route": "rust_native_manual", + "currentState": "native_manual", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "sentrux.rules"], + "artifacts": ["sentrux-baseline.v1"], + "decisionConsumers": ["sentrux.gate"], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.session_start", + "operation": "session_start", + "aliases": [], + "scope": "agent_session", + "trigger": "agent_edit_session_start", + "route": "legacy_compatibility", + "currentState": "compatibility_only", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "session.scope"], + "artifacts": ["sentrux-session-start.v1"], + "decisionConsumers": ["sentrux.session_end"], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.session_end", + "operation": "session_end", + "aliases": [], + "scope": "agent_session", + "trigger": "agent_edit_session_end", + "route": "legacy_compatibility", + "currentState": "compatibility_only", + "requiredForRelease": true, + "inputs": ["sentrux.session_start.v1", "repository.snapshot", "working_tree"], + "artifacts": ["sentrux-session-end.v1"], + "decisionConsumers": ["pr_gate", "release_gate"], + "failureSemantics": ["domain_fail", "failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.rescan", + "operation": "rescan", + "aliases": [], + "scope": "agent_session", + "trigger": "stale_or_changed_snapshot", + "route": "legacy_compatibility", + "currentState": "compatibility_only", + "requiredForRelease": true, + "inputs": ["repository.snapshot", "sentrux.session_start.v1"], + "artifacts": ["sentrux-rescan.v1"], + "decisionConsumers": ["sentrux.session_end"], + "failureSemantics": ["failed", "unavailable", "unknown"], + "platforms": ["windows", "macos", "linux"] + }, + { + "id": "sentrux.provider_discovery", + "operation": "provider_discovery", + "aliases": ["pro_status", "plugin_list", "plugin_validate"], + "scope": "installation", + "trigger": "doctor_or_install_smoke", + "route": "provider_probe", + "currentState": "declared_only", + "requiredForRelease": true, + "inputs": ["installation.layout"], + "artifacts": ["sentrux-provider-capabilities.v1"], + "decisionConsumers": ["doctor", "run_planner", "install_smoke", "release_gate"], + "failureSemantics": ["unavailable", "failed", "unknown"], + "platforms": ["windows", "macos", "linux"] + } + ] +} diff --git a/tests/test_sentrux_capability_matrix.py b/tests/test_sentrux_capability_matrix.py new file mode 100644 index 00000000..975b7aa4 --- /dev/null +++ b/tests/test_sentrux_capability_matrix.py @@ -0,0 +1,85 @@ +from __future__ import annotations + +import json +import re +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +MATRIX_PATH = ROOT / "orchestration" / "sentrux-capability-matrix.v1.json" +INTEGRATIONS_PATH = ROOT / "orchestration" / "integrations.json" +RUST_SENTRUX_PATH = ROOT / "crates" / "code-intel-cli" / "src" / "sentrux.rs" +LEGACY_SENTRY_PATH = ROOT / "legacy" / "Invoke-SentruxAgentTool.ps1" + + +class SentruxCapabilityMatrixTests(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.matrix = json.loads(MATRIX_PATH.read_text(encoding="utf-8")) + cls.integrations = json.loads(INTEGRATIONS_PATH.read_text(encoding="utf-8")) + cls.rust_source = RUST_SENTRUX_PATH.read_text(encoding="utf-8") + cls.legacy_source = LEGACY_SENTRY_PATH.read_text(encoding="utf-8") + + def test_matrix_has_a_single_identity_for_every_capability_and_alias(self) -> None: + capabilities = self.matrix["capabilities"] + ids = [item["id"] for item in capabilities] + self.assertEqual(len(ids), len(set(ids))) + + aliases: list[str] = [] + for item in capabilities: + aliases.extend(item["aliases"]) + self.assertNotIn(item["operation"], item["aliases"]) + self.assertEqual(len(aliases), len(set(aliases))) + self.assertTrue(set(ids).isdisjoint(aliases)) + + def test_all_declared_structure_sentrux_capabilities_are_mapped(self) -> None: + structure = next( + item + for item in self.integrations["integrations"] + if item["id"] == "structure.sentrux" + ) + operations = { + capability + for capability in structure["capabilities"] + if capability != "rules" + } + mapped = { + alias + for item in self.matrix["capabilities"] + for alias in [item["operation"], *item["aliases"]] + } + self.assertTrue(operations <= mapped) + self.assertIn("rules", mapped) + + def test_rust_operations_are_present_in_the_matrix(self) -> None: + expected = {"dsm", "scan", "health", "check", "check_rules", "gate", "gate_save"} + matrix_operations = {item["operation"] for item in self.matrix["capabilities"]} + self.assertTrue(expected <= matrix_operations) + for operation in expected: + with self.subTest(operation=operation): + self.assertIn(f'"{operation}"', self.rust_source) + + def test_legacy_operations_and_aliases_are_present_in_the_matrix(self) -> None: + validate_set = re.search(r"ValidateSet\(([^)]*)\)", self.legacy_source) + self.assertIsNotNone(validate_set) + declared = set(re.findall(r'"([a-z_]+)"', validate_set.group(1))) + mapped = { + value + for item in self.matrix["capabilities"] + for value in [item["operation"], *item["aliases"]] + } + self.assertTrue(declared <= mapped) + + def test_partial_coverage_is_explicit_until_all_release_capabilities_are_automatic(self) -> None: + self.assertEqual(self.matrix["coverageStatus"], "partial") + required = [item for item in self.matrix["capabilities"] if item["requiredForRelease"]] + self.assertTrue(any(item["currentState"] != "authoritative_automatic" for item in required)) + self.assertEqual( + set(self.matrix["completionPolicy"]["forbiddenSilentStates"]), + {"declared_only", "compatibility_only"}, + ) + + +if __name__ == "__main__": + unittest.main() From 392fc73ac8882318ca9376376637a4ee13d650c5 Mon Sep 17 00:00:00 2001 From: Curry Date: Tue, 18 Aug 2026 23:45:07 +0800 Subject: [PATCH 02/27] feat(sentrux): add capability matrix audit command --- crates/code-intel-cli/src/cli/legacy.rs | 2 + crates/code-intel-cli/src/main.rs | 1 + crates/code-intel-cli/src/sentrux.rs | 32 +- .../src/sentrux_capabilities.rs | 405 ++++++++++++++++++ 4 files changed, 424 insertions(+), 16 deletions(-) create mode 100644 crates/code-intel-cli/src/sentrux_capabilities.rs diff --git a/crates/code-intel-cli/src/cli/legacy.rs b/crates/code-intel-cli/src/cli/legacy.rs index 1309b291..e3f9dbc5 100644 --- a/crates/code-intel-cli/src/cli/legacy.rs +++ b/crates/code-intel-cli/src/cli/legacy.rs @@ -1058,6 +1058,7 @@ pub(super) fn cmd_sentrux(args: &Args) -> Result<()> { sentrux::run(&sentrux::Options { operation: args.operation.as_deref(), repo: args.repo.as_deref(), + json: args.json, no_ratchet: args.no_ratchet, }) } @@ -1147,6 +1148,7 @@ Commands: lint hardcoded-paths [] [--json] route|routes [--action List|Plan|Validate] [--provider repowise|understand] [--operation ] [--repo ] [--json] sentrux [--no-ratchet] + sentrux capabilities [] [--json] (read-only capability matrix audit) (--no-ratchet: `check` only, skip the .sentrux/baseline.json ratchet) capability exec --request --out [--artifact-root ] [--manifest ] model inventory-validate --request [--out ] diff --git a/crates/code-intel-cli/src/main.rs b/crates/code-intel-cli/src/main.rs index 019b67ea..1056a511 100644 --- a/crates/code-intel-cli/src/main.rs +++ b/crates/code-intel-cli/src/main.rs @@ -62,6 +62,7 @@ mod run_error; mod runtime_ci_evidence; mod sentrux; mod sentrux_analysis; +mod sentrux_capabilities; mod sentrux_gate; mod session_evidence; mod snapshot; diff --git a/crates/code-intel-cli/src/sentrux.rs b/crates/code-intel-cli/src/sentrux.rs index ef892bf7..c72febf7 100644 --- a/crates/code-intel-cli/src/sentrux.rs +++ b/crates/code-intel-cli/src/sentrux.rs @@ -1,4 +1,5 @@ use crate::sentrux_analysis; +use crate::sentrux_capabilities; use crate::sentrux_gate; use crate::Result; use std::path::Path; @@ -6,27 +7,26 @@ use std::path::Path; pub struct Options<'a> { pub operation: Option<&'a str>, pub repo: Option<&'a Path>, - /// CLI `--no-ratchet`. Only consulted by the `check` operation; see - /// `sentrux_gate::run_check_aligned`. + pub json: bool, pub no_ratchet: bool, } -/// Structural operations served by the built-in engine. The PATH-resolved -/// `sentrux` binary is no longer consulted here: the gate verdict must be a -/// function of the snapshot and this binary. External Sentrux implementations -/// remain reachable through the provider `toolPathPrefix` seam. -/// -/// `check` evaluates both `.sentrux/rules.toml` and, by default, the -/// `.sentrux/baseline.json` ratchet that `gate` evaluates -- the same two -/// verdicts the authoritative `evidence.sentrux` DAG node always computes -/// (issue #106: before this, `check` only ran the static rules, so -/// `code-intel sentrux check` could report green while `code-intel run -/// execute` failed on the same tree). Pass `--no-ratchet` to see only the -/// static verdict; the output says so explicitly. `check_rules` is the -/// unconditional static-only alias kept for parity with the legacy PS1 -/// tool's `check_rules` operation. pub fn run(options: &Options<'_>) -> Result<()> { let operation = options.operation.ok_or("sentrux requires an operation")?; + + if operation == "capabilities" { + let repo = match options.repo { + Some(repo) => repo.canonicalize().map_err(|error| { + format!( + "sentrux capabilities repository '{}' is unavailable: {error}", + repo.display() + ) + })?, + None => std::env::current_dir()?, + }; + return sentrux_capabilities::run_capabilities(&repo, options.json); + } + let repo = options.repo.ok_or("sentrux requires a repo/path")?; let repo = repo.canonicalize()?; match operation { diff --git a/crates/code-intel-cli/src/sentrux_capabilities.rs b/crates/code-intel-cli/src/sentrux_capabilities.rs new file mode 100644 index 00000000..4adfb2f4 --- /dev/null +++ b/crates/code-intel-cli/src/sentrux_capabilities.rs @@ -0,0 +1,405 @@ +use crate::Result; +use serde_json::{Map, Value}; +use std::collections::{BTreeMap, BTreeSet}; +use std::fs; +use std::path::Path; + +const CAPABILITY_MATRIX_RELATIVE_PATH: &str = "orchestration/sentrux-capability-matrix.v1.json"; +const CAPABILITY_MATRIX_SCHEMA: &str = "code-intel-sentrux-capability-matrix.v1"; + +pub(crate) fn run_capabilities(repo: &Path, json: bool) -> Result<()> { + let matrix = load_capability_matrix(repo)?; + let audit = capability_audit(&matrix)?; + + if json { + println!("{}", serde_json::to_string_pretty(&audit)?); + } else { + let coverage = audit + .get("coverage") + .ok_or("sentrux capabilities audit omitted coverage")?; + println!( + "capability coverage: {} ({}/{} required capabilities covered)", + coverage["status"].as_str().unwrap_or("unknown"), + coverage["requiredCovered"].as_u64().unwrap_or(0), + coverage["required"].as_u64().unwrap_or(0), + ); + println!("complete: {}", audit["complete"].as_bool().unwrap_or(false)); + for capability in audit["capabilities"] + .as_array() + .ok_or("sentrux capabilities audit omitted capabilities")? + { + let consumers = capability["decisionConsumers"] + .as_array() + .map(|values| { + values + .iter() + .filter_map(Value::as_str) + .collect::>() + .join(",") + }) + .unwrap_or_default(); + println!( + "- {} operation={} currentState={} route={} decisionConsumers=[{}]", + capability["id"].as_str().unwrap_or(""), + capability["operation"].as_str().unwrap_or(""), + capability["currentState"].as_str().unwrap_or(""), + capability["route"].as_str().unwrap_or(""), + consumers, + ); + } + } + Ok(()) +} + +fn load_capability_matrix(repo: &Path) -> Result { + let path = repo.join(CAPABILITY_MATRIX_RELATIVE_PATH); + let text = fs::read_to_string(&path).map_err(|error| { + format!( + "sentrux capabilities matrix is missing or unreadable at '{}': {error}", + path.display() + ) + })?; + let matrix = serde_json::from_str(text.trim_start_matches('\u{feff}')).map_err(|error| { + format!( + "sentrux capabilities matrix JSON error at '{}': {error}", + path.display() + ) + })?; + validate_capability_matrix(&matrix, &path)?; + Ok(matrix) +} + +fn capability_audit(matrix: &Value) -> Result { + let object = matrix + .as_object() + .ok_or("sentrux capabilities matrix schema/header error: root must be an object")?; + let capabilities = object["capabilities"] + .as_array() + .ok_or("sentrux capabilities matrix schema/header error: capabilities must be an array")?; + let policy = object["completionPolicy"].as_object().ok_or( + "sentrux capabilities matrix schema/header error: completionPolicy must be an object", + )?; + let required_states = policy["requiredStatesForComplete"] + .as_array() + .ok_or("sentrux capabilities matrix schema/header error: required completion states must be an array")? + .iter() + .filter_map(Value::as_str) + .collect::>(); + + let mut state_counts = BTreeMap::::new(); + let mut required = 0u64; + let mut covered = 0u64; + let mut required_covered = 0u64; + let mut output_capabilities = Vec::with_capacity(capabilities.len()); + + for capability in capabilities { + let item = capability.as_object().ok_or( + "sentrux capabilities matrix schema/header error: capability must be an object", + )?; + let current_state = item["currentState"].as_str().ok_or( + "sentrux capabilities matrix schema/header error: currentState must be a string", + )?; + let is_covered = required_states.contains(current_state); + *state_counts.entry(current_state.to_string()).or_default() += 1; + if is_covered { + covered += 1; + } + if item["requiredForRelease"].as_bool().unwrap_or(false) { + required += 1; + if is_covered { + required_covered += 1; + } + } + output_capabilities.push(serde_json::json!({ + "id": item["id"], + "operation": item["operation"], + "currentState": item["currentState"], + "route": item["route"], + "decisionConsumers": item["decisionConsumers"], + })); + } + + let complete = capabilities + .iter() + .filter(|item| item["requiredForRelease"].as_bool().unwrap_or(false)) + .all(|item| { + let state_allowed = item["currentState"] + .as_str() + .is_some_and(|state| required_states.contains(state)); + let has_artifact = item["artifacts"] + .as_array() + .is_some_and(|values| !values.is_empty()); + let has_consumer = item["decisionConsumers"] + .as_array() + .is_some_and(|values| !values.is_empty()); + state_allowed && has_artifact && has_consumer + }); + + let coverage = serde_json::json!({ + "status": object["coverageStatus"], + "total": capabilities.len(), + "required": required, + "covered": covered, + "requiredCovered": required_covered, + "byState": state_counts, + }); + Ok(serde_json::json!({ + "schema": "code-intel-sentrux-capability-audit.v1", + "coverage": coverage, + "capabilities": output_capabilities, + "complete": complete, + })) +} + +fn validate_capability_matrix(matrix: &Value, path: &Path) -> Result<()> { + let object = matrix + .as_object() + .ok_or_else(|| matrix_error(path, "root must be an object"))?; + let schema = object + .get("schema") + .and_then(Value::as_str) + .ok_or_else(|| matrix_error(path, "schema header must be a string"))?; + if schema != CAPABILITY_MATRIX_SCHEMA { + return Err(matrix_error( + path, + &format!("schema header must be '{CAPABILITY_MATRIX_SCHEMA}', got '{schema}'"), + )); + } + if object.get("contractVersion").and_then(Value::as_u64) != Some(1) { + return Err(matrix_error(path, "contractVersion header must be 1")); + } + if object + .get("coverageStatus") + .and_then(Value::as_str) + .is_none() + { + return Err(matrix_error(path, "coverageStatus header must be a string")); + } + + let policy = object + .get("completionPolicy") + .and_then(Value::as_object) + .ok_or_else(|| matrix_error(path, "completionPolicy header must be an object"))?; + string_array( + policy, + "requiredStatesForComplete", + path, + "completionPolicy", + )?; + string_array(policy, "forbiddenSilentStates", path, "completionPolicy")?; + if policy.get("rule").and_then(Value::as_str).is_none() { + return Err(matrix_error(path, "completionPolicy.rule must be a string")); + } + + let capabilities = object + .get("capabilities") + .and_then(Value::as_array) + .ok_or_else(|| matrix_error(path, "capabilities must be an array"))?; + if capabilities.is_empty() { + return Err(matrix_error(path, "capabilities must not be empty")); + } + let mut ids = BTreeSet::new(); + let mut operations = BTreeSet::new(); + let mut aliases = BTreeSet::new(); + for (index, capability) in capabilities.iter().enumerate() { + let item = capability.as_object().ok_or_else(|| { + matrix_error(path, &format!("capabilities[{index}] must be an object")) + })?; + for field in ["id", "operation", "currentState", "route"] { + if item.get(field).and_then(Value::as_str).is_none() { + return Err(matrix_error( + path, + &format!("capabilities[{index}].{field} must be a string"), + )); + } + } + let id = item["id"].as_str().expect("validated capability id"); + if !ids.insert(id) { + return Err(matrix_error( + path, + &format!("duplicate capability id '{id}'"), + )); + } + let operation = item["operation"].as_str().expect("validated operation"); + if !operations.insert(operation) { + return Err(matrix_error( + path, + &format!("duplicate capability operation '{operation}'"), + )); + } + for alias in string_array(item, "aliases", path, &format!("capabilities[{index}]"))? { + if !aliases.insert(alias.clone()) || ids.contains(alias.as_str()) { + return Err(matrix_error( + path, + &format!("duplicate capability alias '{alias}'"), + )); + } + } + if item + .get("requiredForRelease") + .and_then(Value::as_bool) + .is_none() + { + return Err(matrix_error( + path, + &format!("capabilities[{index}].requiredForRelease must be a boolean"), + )); + } + for field in ["artifacts", "decisionConsumers"] { + string_array(item, field, path, &format!("capabilities[{index}]"))?; + } + } + Ok(()) +} + +fn string_array( + object: &Map, + field: &str, + path: &Path, + context: &str, +) -> Result> { + let values = object.get(field).and_then(Value::as_array).ok_or_else(|| { + matrix_error( + path, + &format!("{context}.{field} must be an array of strings"), + ) + })?; + values + .iter() + .enumerate() + .map(|(index, value)| { + value.as_str().map(ToString::to_string).ok_or_else(|| { + matrix_error( + path, + &format!("{context}.{field}[{index}] must be a string"), + ) + }) + }) + .collect() +} + +fn matrix_error(path: &Path, message: &str) -> Box { + format!( + "sentrux capabilities matrix schema/header error at '{}': {message}", + path.display() + ) + .into() +} + +#[cfg(test)] +mod tests { + use super::{capability_audit, load_capability_matrix, validate_capability_matrix}; + use serde_json::json; + use std::fs; + use std::path::{Path, PathBuf}; + + fn temp_repo(label: &str) -> PathBuf { + let nonce = std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock") + .as_nanos(); + let path = std::env::temp_dir().join(format!( + "code-intel-sentrux-capabilities-{label}-{}-{nonce}", + std::process::id() + )); + fs::create_dir_all(path.join("orchestration")).expect("create matrix fixture"); + path + } + + fn write_matrix(repo: &Path, contents: &str) { + fs::write( + repo.join("orchestration/sentrux-capability-matrix.v1.json"), + contents, + ) + .expect("write matrix fixture"); + } + + #[test] + fn current_matrix_reports_partial_coverage_without_claiming_execution() { + let manifest_dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + let repo = manifest_dir + .parent() + .and_then(Path::parent) + .expect("repository root"); + let matrix = load_capability_matrix(repo).expect("load repository matrix"); + let audit = capability_audit(&matrix).expect("render audit"); + assert_eq!(audit["coverage"]["status"], "partial"); + assert_eq!(audit["complete"], false); + assert!(audit["capabilities"][0].get("id").is_some()); + assert!(audit["capabilities"][0].get("operation").is_some()); + assert!(audit["capabilities"][0].get("currentState").is_some()); + assert!(audit["capabilities"][0].get("route").is_some()); + assert!(audit["capabilities"][0].get("decisionConsumers").is_some()); + } + + #[test] + fn missing_matrix_is_an_explicit_error() { + let repo = temp_repo("missing"); + let error = load_capability_matrix(&repo).expect_err("missing matrix must fail"); + assert!(error.to_string().contains("missing or unreadable")); + let _ = fs::remove_dir_all(repo); + } + + #[test] + fn malformed_matrix_json_is_an_explicit_error() { + let repo = temp_repo("json"); + write_matrix(&repo, "{not-json"); + let error = load_capability_matrix(&repo).expect_err("malformed matrix must fail"); + assert!(error.to_string().contains("JSON error")); + let _ = fs::remove_dir_all(repo); + } + + #[test] + fn invalid_matrix_header_is_an_explicit_error() { + let repo = temp_repo("header"); + write_matrix( + &repo, + &serde_json::to_string(&json!({ + "schema": "wrong-schema", + "contractVersion": 1, + "coverageStatus": "partial", + "completionPolicy": { + "requiredStatesForComplete": ["authoritative_automatic"], + "forbiddenSilentStates": ["declared_only"], + "rule": "rule" + }, + "capabilities": [] + })) + .expect("serialize matrix fixture"), + ); + let error = load_capability_matrix(&repo).expect_err("invalid header must fail"); + assert!(error.to_string().contains("schema/header error")); + assert!(error.to_string().contains("schema header")); + let _ = fs::remove_dir_all(repo); + } + + #[test] + fn minimal_valid_matrix_obeys_completion_policy_fields() { + let matrix = json!({ + "schema": "code-intel-sentrux-capability-matrix.v1", + "contractVersion": 1, + "coverageStatus": "complete", + "completionPolicy": { + "requiredStatesForComplete": ["authoritative_automatic"], + "forbiddenSilentStates": ["declared_only"], + "rule": "rule" + }, + "capabilities": [{ + "id": "sentrux.example", + "operation": "example", + "aliases": [], + "currentState": "authoritative_automatic", + "route": "provider.sentrux-adapt", + "requiredForRelease": true, + "artifacts": ["example.v1"], + "decisionConsumers": ["release_gate"] + }] + }); + validate_capability_matrix(&matrix, Path::new("fixture.json")) + .expect("minimal matrix should validate"); + assert_eq!( + capability_audit(&matrix).expect("render audit")["complete"], + true + ); + } +} From 0f311dbcae34e7d5d076c583f511847019bfd356 Mon Sep 17 00:00:00 2001 From: Curry Date: Tue, 18 Aug 2026 23:53:29 +0800 Subject: [PATCH 03/27] feat(sentrux): publish capability evidence contract --- crates/code-intel-cli/src/artifact_ref.rs | 123 ++++++++++ .../src/builtin_provider_evidence.rs | 216 +++++++++++++++++- ...sentrux-capability-artifact.v1.schema.json | 194 ++++++++++++++++ ...test_sentrux_capability_artifact_schema.py | 130 +++++++++++ 4 files changed, 651 insertions(+), 12 deletions(-) create mode 100644 orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json create mode 100644 tests/test_sentrux_capability_artifact_schema.py diff --git a/crates/code-intel-cli/src/artifact_ref.rs b/crates/code-intel-cli/src/artifact_ref.rs index 1fb891a5..d023413b 100644 --- a/crates/code-intel-cli/src/artifact_ref.rs +++ b/crates/code-intel-cli/src/artifact_ref.rs @@ -289,6 +289,14 @@ fn diagnosis_family_contract(schema: &str, artifact_type: &str) -> Option { + Some(ArtifactContract { + artifact_schema: "code-intel-sentrux-capability-artifact.v1", + artifact_type: "provider.sentrux.capability-artifact", + max_bytes: 8 * 1024 * 1024, + validate_payload: validate_sentrux_capability_artifact, + }) + } ("code-intel-hospital.v1", "diagnosis.hospital") => Some(ArtifactContract { artifact_schema: "code-intel-hospital.v1", artifact_type: "diagnosis.hospital", @@ -561,6 +569,69 @@ fn sentrux_command_result_is_valid(command: &Value, id: &str) -> bool { && command["stderr"].is_string() } +fn validate_sentrux_capability_artifact(bytes: &[u8]) -> Result<(), String> { + let value = parse_contract_json(bytes, "Sentrux capability artifact")?; + exact_object_keys( + &value, + &[ + "schema", + "contractVersion", + "capabilityId", + "operation", + "runId", + "snapshotIdentity", + "provider", + "status", + "authority", + "inputs", + "outputs", + "failure", + "freshness", + "decisionConsumers", + ], + "Sentrux capability artifact", + )?; + if value["schema"] != "code-intel-sentrux-capability-artifact.v1" + || value["contractVersion"] != 1 + || !value["capabilityId"] + .as_str() + .is_some_and(|id| !id.is_empty() && id.starts_with("sentrux.")) + || !value["operation"] + .as_str() + .is_some_and(|operation| !operation.is_empty()) + || !value["runId"] + .as_str() + .is_some_and(|run_id| !run_id.is_empty()) + || !value["snapshotIdentity"].as_str().is_some_and(valid_digest) + || !value["provider"].is_object() + || !matches!( + value["status"].as_str(), + Some( + "succeeded" | "degraded" | "unavailable" | "skipped" | "not_applicable" | "failed" + ) + ) + || !matches!( + value["authority"].as_str(), + Some("authoritative" | "fallback" | "compatibility" | "declared_only") + ) + || !value["inputs"].is_object() + || !value["outputs"].is_object() + || if value["status"] == "succeeded" { + !value["failure"].is_null() + } else { + !value["failure"].is_object() + } + || !value["freshness"].is_object() + || !value["decisionConsumers"].is_array() + { + return Err("Sentrux capability artifact header or envelope fields are invalid".into()); + } + if value["status"] == "succeeded" && value["outputs"].as_object().is_none_or(|v| v.is_empty()) { + return Err("successful Sentrux capability artifact must contain outputs".into()); + } + Ok(()) +} + fn validate_retirement_manifest(bytes: &[u8]) -> Result<(), String> { let value = parse_contract_json(bytes, "retirement manifest")?; exact_object_keys( @@ -3827,6 +3898,58 @@ mod tests { } } + #[test] + fn sentrux_capability_artifact_contract_accepts_success_and_requires_failure_details() { + let reference = json!({ + "artifactSchema":"code-intel-sentrux-capability-artifact.v1", + "type":"provider.sentrux.capability-artifact" + }); + let contract = registered_contract(&reference).expect("Sentrux artifact is registered"); + let base = json!({ + "schema":"code-intel-sentrux-capability-artifact.v1", + "contractVersion":1, + "capabilityId":"sentrux.scan", + "operation":"scan", + "runId":"run-1", + "snapshotIdentity":"a".repeat(64), + "provider":{ + "mode":"builtin", + "id":"sentrux.builtin", + "version":"1.0.0", + "digest":"b".repeat(64) + }, + "status":"succeeded", + "authority":"authoritative", + "inputs":{}, + "outputs":{"artifacts":[]}, + "failure":null, + "freshness":{ + "status":"current", + "evaluatedAt":"2026-08-18T00:00:00Z", + "consumedSnapshotIdentity":"a".repeat(64) + }, + "decisionConsumers":["release_gate"] + }); + (contract.validate_payload)(&serde_json::to_vec(&base).unwrap()) + .expect("successful Sentrux artifact with null failure must pass"); + + let mut failed = base.clone(); + failed["status"] = json!("failed"); + failed["failure"] = json!({ + "kind":"provider_error", + "message":"command failed", + "retryable":true + }); + (contract.validate_payload)(&serde_json::to_vec(&failed).unwrap()) + .expect("failed Sentrux artifact with failure details must pass"); + + failed["failure"] = Value::Null; + assert!( + (contract.validate_payload)(&serde_json::to_vec(&failed).unwrap()).is_err(), + "failed Sentrux artifact must not silently omit failure details" + ); + } + fn deletion_file(path: &str, base: &str, result: &str, added: Vec<&str>) -> Value { json!({ "path":path, diff --git a/crates/code-intel-cli/src/builtin_provider_evidence.rs b/crates/code-intel-cli/src/builtin_provider_evidence.rs index 654c58cc..776e1824 100644 --- a/crates/code-intel-cli/src/builtin_provider_evidence.rs +++ b/crates/code-intel-cli/src/builtin_provider_evidence.rs @@ -29,6 +29,8 @@ mod graph; mod graph_adapter; #[path = "sentrux_adapter.rs"] mod sentrux_adapter; +#[path = "sentrux_analysis.rs"] +mod sentrux_analysis; #[path = "sentrux_gate.rs"] mod sentrux_gate; @@ -46,6 +48,15 @@ const CODENEXUS_EFFECTS: [&str; 4] = [ "read_sentrux_artifacts", "write_compatibility_artifact", ]; +const SENTRUX_CAPABILITY_COMMANDS: [(&str, &str); 7] = [ + ("sentrux.gate", "gate"), + ("sentrux.check", "check"), + ("sentrux.scan", "scan"), + ("sentrux.health", "health"), + ("sentrux.dsm", "dsm"), + ("sentrux.check_rules", "check_rules"), + ("sentrux.gate_save", "gate_save"), +]; pub(super) fn graph_admission( request: &Value, @@ -134,8 +145,8 @@ pub(super) fn sentrux_admission( let lease = snapshot::begin_consumption(repo, &request["snapshot"]).map_err(AdapterError::Contract)?; let collected_at = now()?; - let gate = run_sentrux(repo, tool_path_prefix, "gate")?; - let check = run_sentrux(repo, tool_path_prefix, "check")?; + let (gate, check, capability_observations) = + collect_sentrux_capabilities(repo, tool_path_prefix)?; lease.verify_after(repo).map_err(AdapterError::Contract)?; let observed_at = now()?.max(collected_at); let identity = snapshot_identity(request)?; @@ -197,15 +208,18 @@ pub(super) fn sentrux_admission( .map_err(AdapterError::Contract)?; let payload = json!({ "schema":"code-intel-evidence-payload.v1", - "data":{"structuralEvidence":{ - "schema":"code-intel-structural-evidence-payload.v1", - "snapshotIdentity":identity, - "provider":first["port"]["provider"], - "provenance":payload_provenance(request), - "effects":first["port"]["effects"], - "completeness":first["port"]["completeness"], - "rules":first["port"]["rules"] - }} + "data":{ + "structuralEvidence":{ + "schema":"code-intel-structural-evidence-payload.v1", + "snapshotIdentity":identity, + "provider":first["port"]["provider"], + "provenance":payload_provenance(request), + "effects":first["port"]["effects"], + "completeness":first["port"]["completeness"], + "rules":first["port"]["rules"] + }, + "sentruxCapabilities":capability_observations + } }); fs::create_dir(out) .map_err(|error| AdapterError::Io(format!("create Sentrux provider output: {error}")))?; @@ -441,6 +455,130 @@ struct SentruxCommand { governed: bool, } +fn collect_sentrux_capabilities( + repo: &Path, + tool_path_prefix: Option<&Path>, +) -> Result<(SentruxCommand, SentruxCommand, Vec), AdapterError> { + let mut gate = None; + let mut check = None; + let mut observations = Vec::with_capacity(SENTRUX_CAPABILITY_COMMANDS.len()); + for &(capability_id, subcommand) in &SENTRUX_CAPABILITY_COMMANDS { + if subcommand == "gate_save" { + observations.push(json!({ + "capabilityId":capability_id, + "operation":subcommand, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "status":"not_run", + "verdict":"unknown", + "command":Value::Null, + "failure":{ + "kind":"explicit_mutation_required", + "message":"gate_save writes the repository baseline and requires explicit authority" + } + })); + continue; + } + let command = match run_sentrux(repo, tool_path_prefix, subcommand) { + Ok(command) => command, + Err(error) if matches!(subcommand, "gate" | "check") => return Err(error), + Err(error) => { + observations.push(json!({ + "capabilityId":capability_id, + "operation":subcommand, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "status":"failed", + "verdict":"unknown", + "command":Value::Null, + "failure":{ + "kind":adapter_error_kind(&error), + "message":format!("{error:?}") + } + })); + continue; + } + }; + observations.push(capability_observation( + capability_id, + subcommand, + tool_path_prefix, + &command, + )); + match subcommand { + "gate" => gate = Some(command), + "check" => check = Some(command), + _ => {} + } + } + Ok(( + gate.ok_or_else(|| AdapterError::Internal("Sentrux gate observation is missing".into()))?, + check + .ok_or_else(|| AdapterError::Internal("Sentrux check observation is missing".into()))?, + observations, + )) +} + +fn capability_observation( + capability_id: &str, + subcommand: &str, + tool_path_prefix: Option<&Path>, + command: &SentruxCommand, +) -> Value { + let (status, verdict, failure) = if command.success { + ("succeeded", "pass", json!({"kind":"none"})) + } else if !command.governed { + ("succeeded", "unknown", json!({"kind":"none"})) + } else { + ( + "failed", + "fail", + json!({ + "kind":"command_failed", + "message":command_failure_message(command) + }), + ) + }; + json!({ + "capabilityId":capability_id, + "operation":subcommand, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "status":status, + "verdict":verdict, + "command":command_evidence(subcommand, command), + "failure":failure + }) +} + +fn sentrux_provider_mode(tool_path_prefix: Option<&Path>) -> &'static str { + if tool_path_prefix.is_some() { + "external" + } else { + "builtin_lite" + } +} + +fn adapter_error_kind(error: &AdapterError) -> &'static str { + match error { + AdapterError::Unavailable(_) => "provider_unavailable", + AdapterError::Contract(_) => "contract_error", + AdapterError::InvalidOptions(_) => "invalid_options", + AdapterError::Internal(_) => "provider_error", + AdapterError::Io(_) => "io_error", + } +} + +fn command_failure_message(command: &SentruxCommand) -> String { + command + .stderr + .lines() + .chain(command.stdout.lines()) + .find(|line| !line.trim().is_empty()) + .map(str::trim) + .unwrap_or("Sentrux command reported a failing verdict") + .chars() + .take(1024) + .collect() +} + impl SentruxCommand { fn from_native(run: sentrux_gate::EngineRun, subcommand: &str) -> Self { Self { @@ -527,8 +665,14 @@ fn run_sentrux( } None => { let run = match subcommand { - "gate" => sentrux_gate::run_gate(repo, false), + "dsm" => { + return json_command(sentrux_analysis::analyze(repo), subcommand); + } + "scan" => return json_command(sentrux_gate::scan_json(repo), subcommand), + "health" => return json_command(sentrux_health_json(repo), subcommand), + "check_rules" => sentrux_gate::run_check(repo), "check" => sentrux_gate::run_check(repo), + "gate" => sentrux_gate::run_gate(repo, false), other => { return Err(AdapterError::Internal(format!( "unsupported built-in Sentrux subcommand: {other}" @@ -549,6 +693,54 @@ fn run_sentrux( Ok(command) } +fn json_command( + value: Result, + subcommand: &str, +) -> Result { + let value = + value.map_err(|error| AdapterError::Internal(format!("Sentrux {subcommand}: {error}")))?; + let stdout = serde_json::to_string_pretty(&value).map_err(|error| { + AdapterError::Internal(format!("serialize Sentrux {subcommand}: {error}")) + })?; + Ok(SentruxCommand { + argv: vec![ + "code-intel".into(), + "sentrux".into(), + subcommand.into(), + ".".into(), + ], + exit_code: Some(0), + success: true, + stdout, + stderr: String::new(), + violations: Vec::new(), + governed: true, + }) +} + +fn sentrux_health_json(repo: &Path) -> Result { + let metrics = sentrux_gate::scan_json(repo)?; + let god_files = metrics["god_file_count"].as_i64().unwrap_or(0); + let complex = metrics["complex_fn_count"].as_i64().unwrap_or(0); + let coupling = metrics["coupling_score"].as_f64().unwrap_or(0.0); + let bottleneck = if god_files > 0 { + "god_files" + } else if complex > 0 { + "complexity" + } else if coupling > 20.0 { + "coupling" + } else { + "none" + }; + Ok(json!({ + "status":"ok", + "tool":sentrux_gate::ENGINE_ID, + "quality_signal":metrics["quality_signal"], + "files":metrics["files"], + "bottleneck":bottleneck + })) +} + fn external_command(path: &Path) -> Command { #[cfg(windows)] if path diff --git a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json new file mode 100644 index 00000000..836efe0e --- /dev/null +++ b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json @@ -0,0 +1,194 @@ +{ + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "code-intel-sentrux-capability-artifact.v1", + "title": "Code Intel Sentrux capability artifact envelope v1", + "description": "Snapshot-bound, non-silent result envelope shared by every Sentrux capability.", + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "contractVersion", + "capabilityId", + "operation", + "runId", + "snapshotIdentity", + "provider", + "status", + "authority", + "inputs", + "outputs", + "failure", + "freshness", + "decisionConsumers" + ], + "properties": { + "schema": { "const": "code-intel-sentrux-capability-artifact.v1" }, + "contractVersion": { "const": 1 }, + "capabilityId": { "$ref": "#/$defs/identifier" }, + "operation": { "$ref": "#/$defs/identifier" }, + "runId": { "$ref": "#/$defs/nonEmpty" }, + "snapshotIdentity": { "$ref": "#/$defs/sha256" }, + "provider": { "$ref": "#/$defs/provider" }, + "status": { + "enum": [ + "succeeded", + "degraded", + "unavailable", + "skipped", + "not_applicable", + "failed" + ] + }, + "authority": { + "enum": ["authoritative", "fallback", "compatibility", "declared_only"] + }, + "inputs": { "type": "object" }, + "outputs": { + "type": "object", + "properties": { + "artifacts": { + "type": "array", + "items": { "$ref": "#/$defs/artifactRef" }, + "uniqueItems": true + } + } + }, + "failure": { + "oneOf": [ + { "type": "null" }, + { "$ref": "#/$defs/failure" } + ] + }, + "freshness": { "$ref": "#/$defs/freshness" }, + "decisionConsumers": { + "type": "array", + "minItems": 1, + "uniqueItems": true, + "items": { "$ref": "#/$defs/identifier" } + } + }, + "allOf": [ + { + "if": { "properties": { "status": { "const": "succeeded" } } }, + "then": { "properties": { "failure": { "type": "null" } } }, + "else": { "properties": { "failure": { "$ref": "#/$defs/failure" } } } + }, + { + "if": { "properties": { "status": { "const": "unavailable" } } }, + "then": { + "properties": { + "failure": { + "type": "object", + "properties": { "kind": { "const": "provider_unavailable" } } + } + } + } + }, + { + "if": { "properties": { "status": { "const": "skipped" } } }, + "then": { + "properties": { + "failure": { + "type": "object", + "properties": { "kind": { "const": "skipped" } } + } + } + } + }, + { + "if": { "properties": { "status": { "const": "not_applicable" } } }, + "then": { + "properties": { + "failure": { + "type": "object", + "properties": { "kind": { "const": "not_applicable" } } + } + } + } + } + ], + "$defs": { + "nonEmpty": { + "type": "string", + "minLength": 1 + }, + "identifier": { + "type": "string", + "pattern": "^[a-z0-9][a-z0-9._-]*$" + }, + "sha256": { + "type": "string", + "pattern": "^[a-f0-9]{64}$" + }, + "provider": { + "type": "object", + "additionalProperties": false, + "required": ["mode", "id", "version", "digest"], + "properties": { + "mode": { + "enum": ["external", "builtin", "lite_fallback", "legacy_compatibility"] + }, + "id": { "$ref": "#/$defs/identifier" }, + "version": { "$ref": "#/$defs/nonEmpty" }, + "digest": { "$ref": "#/$defs/sha256" } + } + }, + "artifactRef": { + "type": "object", + "additionalProperties": false, + "required": [ + "schema", + "artifactSchema", + "type", + "path", + "sha256", + "consumedSnapshotIdentity" + ], + "properties": { + "schema": { "const": "code-intel-artifact-ref.v1" }, + "artifactSchema": { "$ref": "#/$defs/nonEmpty" }, + "type": { "const": "provider.sentrux.capability-artifact" }, + "path": { "$ref": "#/$defs/nonEmpty" }, + "sha256": { "$ref": "#/$defs/sha256" }, + "consumedSnapshotIdentity": { "$ref": "#/$defs/sha256" } + } + }, + "failure": { + "type": "object", + "additionalProperties": false, + "required": ["kind", "message", "retryable"], + "properties": { + "kind": { + "enum": [ + "provider_unavailable", + "provider_error", + "config_error", + "local_tool_error", + "snapshot_mismatch", + "stale_evidence", + "degraded", + "skipped", + "not_applicable", + "blocked_policy", + "unknown" + ] + }, + "message": { "$ref": "#/$defs/nonEmpty" }, + "retryable": { "type": "boolean" }, + "details": { "type": "object" } + } + }, + "freshness": { + "type": "object", + "additionalProperties": false, + "required": ["status", "evaluatedAt", "consumedSnapshotIdentity"], + "properties": { + "status": { + "enum": ["current", "stale", "snapshot_mismatch", "unknown"] + }, + "evaluatedAt": { "type": "string", "format": "date-time" }, + "consumedSnapshotIdentity": { "$ref": "#/$defs/sha256" } + } + } + } +} diff --git a/tests/test_sentrux_capability_artifact_schema.py b/tests/test_sentrux_capability_artifact_schema.py new file mode 100644 index 00000000..2a7cfcc3 --- /dev/null +++ b/tests/test_sentrux_capability_artifact_schema.py @@ -0,0 +1,130 @@ +from __future__ import annotations + +import json +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +SCHEMA_PATH = ( + ROOT + / "orchestration" + / "schemas" + / "code-intel-sentrux-capability-artifact.v1.schema.json" +) + + +class SentruxCapabilityArtifactSchemaTests(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.schema = json.loads(SCHEMA_PATH.read_text(encoding="utf-8")) + + def test_envelope_is_closed_and_requires_shared_fields(self) -> None: + self.assertEqual(self.schema["type"], "object") + self.assertFalse(self.schema["additionalProperties"]) + self.assertEqual( + set(self.schema["required"]), + { + "schema", + "contractVersion", + "capabilityId", + "operation", + "runId", + "snapshotIdentity", + "provider", + "status", + "authority", + "inputs", + "outputs", + "failure", + "freshness", + "decisionConsumers", + }, + ) + + def test_status_and_authority_are_explicit_enums_without_silent_skip(self) -> None: + status = self.schema["properties"]["status"]["enum"] + authority = self.schema["properties"]["authority"]["enum"] + + self.assertIn("skipped", status) + self.assertNotIn("silent_skip", authority) + self.assertNotIn("none", authority) + self.assertEqual( + set(status), + {"succeeded", "degraded", "unavailable", "skipped", "not_applicable", "failed"}, + ) + self.assertEqual( + set(authority), + {"authoritative", "fallback", "compatibility", "declared_only"}, + ) + + def test_provider_and_freshness_bind_evidence_to_a_snapshot(self) -> None: + provider = self.schema["$defs"]["provider"] + freshness = self.schema["$defs"]["freshness"] + + self.assertEqual( + set(provider["required"]), {"mode", "id", "version", "digest"} + ) + self.assertEqual( + self.schema["$id"], "code-intel-sentrux-capability-artifact.v1" + ) + self.assertEqual( + self.schema["properties"]["schema"]["const"], + "code-intel-sentrux-capability-artifact.v1", + ) + self.assertEqual( + set(freshness["required"]), + {"status", "evaluatedAt", "consumedSnapshotIdentity"}, + ) + self.assertEqual( + set(provider["properties"]["mode"]["enum"]), + {"external", "builtin", "lite_fallback", "legacy_compatibility"}, + ) + + def test_non_success_requires_failure_and_special_states_name_the_failure(self) -> None: + branches = self.schema["allOf"] + outcome_branch = branches[0] + unavailable_branch = branches[1] + skipped_branch = branches[2] + not_applicable_branch = branches[3] + + self.assertEqual( + outcome_branch["if"]["properties"]["status"]["const"], "succeeded" + ) + self.assertEqual(outcome_branch["then"]["properties"]["failure"]["type"], "null") + self.assertEqual( + outcome_branch["else"]["properties"]["failure"]["$ref"], + "#/$defs/failure", + ) + self.assertEqual( + unavailable_branch["then"]["properties"]["failure"]["properties"]["kind"]["const"], + "provider_unavailable", + ) + self.assertEqual( + skipped_branch["then"]["properties"]["failure"]["properties"]["kind"]["const"], + "skipped", + ) + self.assertEqual( + not_applicable_branch["then"]["properties"]["failure"]["properties"]["kind"]["const"], + "not_applicable", + ) + + def test_artifact_type_is_fixed_inside_outputs(self) -> None: + artifact_ref = self.schema["$defs"]["artifactRef"] + + self.assertEqual( + artifact_ref["properties"]["type"]["const"], + "provider.sentrux.capability-artifact", + ) + self.assertIn("artifacts", self.schema["properties"]["outputs"]["properties"]) + + def test_decision_consumers_cannot_be_empty(self) -> None: + consumers = self.schema["properties"]["decisionConsumers"] + + self.assertEqual(consumers["type"], "array") + self.assertEqual(consumers["minItems"], 1) + self.assertTrue(consumers["uniqueItems"]) + + +if __name__ == "__main__": + unittest.main() From d63f591b3a9c0338e69c656a5ed6b974c9451cf9 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:10:22 +0800 Subject: [PATCH 04/27] feat(sentrux): expose capability artifacts to diagnosis --- .../src/builtin_provider_evidence.rs | 234 +++------------ .../code-intel-cli/src/hospital_diagnosis.rs | 13 +- .../src/sentrux_capability_artifacts.rs | 268 ++++++++++++++++++ 3 files changed, 319 insertions(+), 196 deletions(-) create mode 100644 crates/code-intel-cli/src/sentrux_capability_artifacts.rs diff --git a/crates/code-intel-cli/src/builtin_provider_evidence.rs b/crates/code-intel-cli/src/builtin_provider_evidence.rs index 776e1824..1b5acba8 100644 --- a/crates/code-intel-cli/src/builtin_provider_evidence.rs +++ b/crates/code-intel-cli/src/builtin_provider_evidence.rs @@ -31,6 +31,8 @@ mod graph_adapter; mod sentrux_adapter; #[path = "sentrux_analysis.rs"] mod sentrux_analysis; +#[path = "sentrux_capability_artifacts.rs"] +mod sentrux_capability_artifacts; #[path = "sentrux_gate.rs"] mod sentrux_gate; @@ -48,15 +50,6 @@ const CODENEXUS_EFFECTS: [&str; 4] = [ "read_sentrux_artifacts", "write_compatibility_artifact", ]; -const SENTRUX_CAPABILITY_COMMANDS: [(&str, &str); 7] = [ - ("sentrux.gate", "gate"), - ("sentrux.check", "check"), - ("sentrux.scan", "scan"), - ("sentrux.health", "health"), - ("sentrux.dsm", "dsm"), - ("sentrux.check_rules", "check_rules"), - ("sentrux.gate_save", "gate_save"), -]; pub(super) fn graph_admission( request: &Value, @@ -146,14 +139,17 @@ pub(super) fn sentrux_admission( snapshot::begin_consumption(repo, &request["snapshot"]).map_err(AdapterError::Contract)?; let collected_at = now()?; let (gate, check, capability_observations) = - collect_sentrux_capabilities(repo, tool_path_prefix)?; + sentrux_capability_artifacts::collect_sentrux_capabilities(repo, tool_path_prefix)?; lease.verify_after(repo).map_err(AdapterError::Contract)?; let observed_at = now()?.max(collected_at); let identity = snapshot_identity(request)?; let command_observation = json!({ "schema":"code-intel-sentrux-command-observation.v1", "snapshotIdentity":identity, - "commands":[command_evidence("gate", &gate), command_evidence("check", &check)] + "commands":[ + command_evidence("gate", &gate), + command_evidence("check", &check) + ] }); let command_observation_bytes = serde_json::to_vec(&command_observation).map_err(|error| { AdapterError::Internal(format!("serialize Sentrux command observation: {error}")) @@ -206,6 +202,14 @@ pub(super) fn sentrux_admission( }); let first = sentrux_adapter::translate(&native, observed_at, MAX_AGE_SECONDS) .map_err(AdapterError::Contract)?; + let run_id = format!("sentrux-{}-{}", identity, std::process::id()); + let (capability_artifacts, capability_refs) = + sentrux_capability_artifacts::build_capability_artifacts( + &capability_observations, + identity, + &run_id, + tool_path_prefix, + )?; let payload = json!({ "schema":"code-intel-evidence-payload.v1", "data":{ @@ -218,7 +222,8 @@ pub(super) fn sentrux_admission( "completeness":first["port"]["completeness"], "rules":first["port"]["rules"] }, - "sentruxCapabilities":capability_observations + "sentruxCapabilities":capability_observations, + "capabilityArtifactRefs":capability_refs } }); fs::create_dir(out) @@ -232,6 +237,11 @@ pub(super) fn sentrux_admission( .map_err(|error| AdapterError::Internal(format!("serialize Sentrux payload: {error}")))?; fs::write(out.join("sentrux-payload.json"), &payload_bytes) .map_err(|error| AdapterError::Io(format!("write Sentrux payload: {error}")))?; + for artifact in &capability_artifacts { + fs::write(out.join(&artifact.relative_path), &artifact.bytes).map_err(|error| { + AdapterError::Io(format!("write Sentrux capability artifact: {error}")) + })?; + } let mut native = native; native["payload"] = payload_ref("sentrux-payload.json", &payload_bytes, identity); let adapter = sentrux_adapter::translate(&native, observed_at, MAX_AGE_SECONDS) @@ -260,6 +270,7 @@ pub(super) fn sentrux_admission( bytes: command_observation_bytes, }, ]); + output.artifacts.extend(capability_artifacts); Ok(output) } @@ -455,130 +466,6 @@ struct SentruxCommand { governed: bool, } -fn collect_sentrux_capabilities( - repo: &Path, - tool_path_prefix: Option<&Path>, -) -> Result<(SentruxCommand, SentruxCommand, Vec), AdapterError> { - let mut gate = None; - let mut check = None; - let mut observations = Vec::with_capacity(SENTRUX_CAPABILITY_COMMANDS.len()); - for &(capability_id, subcommand) in &SENTRUX_CAPABILITY_COMMANDS { - if subcommand == "gate_save" { - observations.push(json!({ - "capabilityId":capability_id, - "operation":subcommand, - "providerMode":sentrux_provider_mode(tool_path_prefix), - "status":"not_run", - "verdict":"unknown", - "command":Value::Null, - "failure":{ - "kind":"explicit_mutation_required", - "message":"gate_save writes the repository baseline and requires explicit authority" - } - })); - continue; - } - let command = match run_sentrux(repo, tool_path_prefix, subcommand) { - Ok(command) => command, - Err(error) if matches!(subcommand, "gate" | "check") => return Err(error), - Err(error) => { - observations.push(json!({ - "capabilityId":capability_id, - "operation":subcommand, - "providerMode":sentrux_provider_mode(tool_path_prefix), - "status":"failed", - "verdict":"unknown", - "command":Value::Null, - "failure":{ - "kind":adapter_error_kind(&error), - "message":format!("{error:?}") - } - })); - continue; - } - }; - observations.push(capability_observation( - capability_id, - subcommand, - tool_path_prefix, - &command, - )); - match subcommand { - "gate" => gate = Some(command), - "check" => check = Some(command), - _ => {} - } - } - Ok(( - gate.ok_or_else(|| AdapterError::Internal("Sentrux gate observation is missing".into()))?, - check - .ok_or_else(|| AdapterError::Internal("Sentrux check observation is missing".into()))?, - observations, - )) -} - -fn capability_observation( - capability_id: &str, - subcommand: &str, - tool_path_prefix: Option<&Path>, - command: &SentruxCommand, -) -> Value { - let (status, verdict, failure) = if command.success { - ("succeeded", "pass", json!({"kind":"none"})) - } else if !command.governed { - ("succeeded", "unknown", json!({"kind":"none"})) - } else { - ( - "failed", - "fail", - json!({ - "kind":"command_failed", - "message":command_failure_message(command) - }), - ) - }; - json!({ - "capabilityId":capability_id, - "operation":subcommand, - "providerMode":sentrux_provider_mode(tool_path_prefix), - "status":status, - "verdict":verdict, - "command":command_evidence(subcommand, command), - "failure":failure - }) -} - -fn sentrux_provider_mode(tool_path_prefix: Option<&Path>) -> &'static str { - if tool_path_prefix.is_some() { - "external" - } else { - "builtin_lite" - } -} - -fn adapter_error_kind(error: &AdapterError) -> &'static str { - match error { - AdapterError::Unavailable(_) => "provider_unavailable", - AdapterError::Contract(_) => "contract_error", - AdapterError::InvalidOptions(_) => "invalid_options", - AdapterError::Internal(_) => "provider_error", - AdapterError::Io(_) => "io_error", - } -} - -fn command_failure_message(command: &SentruxCommand) -> String { - command - .stderr - .lines() - .chain(command.stdout.lines()) - .find(|line| !line.trim().is_empty()) - .map(str::trim) - .unwrap_or("Sentrux command reported a failing verdict") - .chars() - .take(1024) - .collect() -} - impl SentruxCommand { fn from_native(run: sentrux_gate::EngineRun, subcommand: &str) -> Self { Self { @@ -599,12 +486,6 @@ impl SentruxCommand { fn from_external(output: Output, subcommand: &str) -> Self { let stdout = String::from_utf8_lossy(&output.stdout).into_owned(); - // External engines only expose text; keep the per-line failure - // messages so downstream diagnosis is never target-blind. Bounded at - // the producer: an over-verbose external engine must degrade the - // details, never turn the domain verdict into a contract failure. - const MAX_EXTERNAL_VIOLATIONS: usize = 32; - const MAX_EXTERNAL_MESSAGE: usize = 1024; let violations = if output.status.success() { Vec::new() } else { @@ -613,20 +494,11 @@ impl SentruxCommand { .filter_map(|line| line.strip_prefix("- ")) .map(str::trim) .filter(|message| !message.is_empty()) - .take(MAX_EXTERNAL_VIOLATIONS) - .map(|message| { - let mut bounded = String::new(); - for character in message.chars() { - if bounded.len() + character.len_utf8() > MAX_EXTERNAL_MESSAGE { - break; - } - bounded.push(character); - } - Violation { - rule: format!("sentrux_{subcommand}"), - message: bounded, - targets: Vec::new(), - } + .take(32) + .map(|message| Violation { + rule: format!("sentrux_{subcommand}"), + message: message.chars().take(1024).collect(), + targets: Vec::new(), }) .collect() }; @@ -637,8 +509,6 @@ impl SentruxCommand { stdout, stderr: String::from_utf8_lossy(&output.stderr).into_owned(), violations, - // External engines expose no governance signal, so their exit code - // is the only verdict available and is taken at face value. governed: true, } } @@ -665,13 +535,11 @@ fn run_sentrux( } None => { let run = match subcommand { - "dsm" => { - return json_command(sentrux_analysis::analyze(repo), subcommand); - } + "dsm" => return json_command(sentrux_analysis::analyze(repo), subcommand), "scan" => return json_command(sentrux_gate::scan_json(repo), subcommand), "health" => return json_command(sentrux_health_json(repo), subcommand), "check_rules" => sentrux_gate::run_check(repo), - "check" => sentrux_gate::run_check(repo), + "check" => sentrux_gate::run_check_aligned(repo, true), "gate" => sentrux_gate::run_gate(repo, false), other => { return Err(AdapterError::Internal(format!( @@ -702,6 +570,11 @@ fn json_command( let stdout = serde_json::to_string_pretty(&value).map_err(|error| { AdapterError::Internal(format!("serialize Sentrux {subcommand}: {error}")) })?; + if stdout.len() > MAX_COMMAND_EVIDENCE_BYTES { + return Err(AdapterError::Contract(format!( + "Sentrux {subcommand} output exceeds the bounded evidence limit" + ))); + } Ok(SentruxCommand { argv: vec![ "code-intel".into(), @@ -756,7 +629,7 @@ fn external_command(path: &Path) -> Command { } fn resolve_sentrux(prefix: &Path) -> Result { - sentrux_names() + ["sentrux.exe", "sentrux.cmd", "sentrux.bat", "sentrux"] .iter() .map(|name| prefix.join(name)) .find(|path| path.is_file()) @@ -768,14 +641,6 @@ fn resolve_sentrux(prefix: &Path) -> Result { }) } -fn sentrux_names() -> &'static [&'static str] { - if cfg!(windows) { - &["sentrux.exe", "sentrux.cmd", "sentrux.bat", "sentrux"] - } else { - &["sentrux"] - } -} - fn command_evidence(subcommand: &str, command: &SentruxCommand) -> Value { json!({ "id":subcommand, @@ -787,37 +652,16 @@ fn command_evidence(subcommand: &str, command: &SentruxCommand) -> Value { }) } -/// Translates one Sentrux command into an authoritative rule verdict. -/// -/// An ungoverned command carries no structural verdict: `check` without -/// `.sentrux/rules.toml` evaluated no rule, and `gate` without -/// `.sentrux/baseline.json` had no prior measurement to detect a regression -/// against. Its nonzero exit code is an operator affordance ("save a -/// baseline"), not evidence that an admitted rule failed, so reporting `fail` -/// here made every never-baselined repository read as an architecture gate -/// failure in diagnosis. The raw exit code and stdout stay verbatim in the -/// command observation artifact, which is where the ungoverned state is -/// auditable. fn command_rule(kind: &str, command: &SentruxCommand) -> Value { let verdict = if command.success || !command.governed { "pass" } else { "fail" }; - let mut rule = json!({ - "kind":kind, - "status":"evaluated", - "verdict":verdict, - "failure":{"kind":"none"} - }); + let mut rule = + json!({"kind":kind,"status":"evaluated","verdict":verdict,"failure":{"kind":"none"}}); if verdict == "fail" && !command.violations.is_empty() { - rule["details"] = json!({ - "violations":command - .violations - .iter() - .map(Violation::to_json) - .collect::>() - }); + rule["details"] = json!({"violations":command.violations.iter().map(Violation::to_json).collect::>()}); } rule } diff --git a/crates/code-intel-cli/src/hospital_diagnosis.rs b/crates/code-intel-cli/src/hospital_diagnosis.rs index 5f8404b3..50c7af64 100644 --- a/crates/code-intel-cli/src/hospital_diagnosis.rs +++ b/crates/code-intel-cli/src/hospital_diagnosis.rs @@ -34,6 +34,7 @@ struct Signals { modernization_debt: bool, top_target: Option, failing_rules: Vec, + sentrux_capability_refs: Vec, admissions: BTreeMap, } @@ -55,6 +56,7 @@ impl Default for Signals { modernization_debt: false, top_target: None, failing_rules: Vec::new(), + sentrux_capability_refs: Vec::new(), admissions: BTreeMap::new(), } } @@ -260,6 +262,15 @@ fn consume_admission(input: &VerifiedArtifact, signals: &mut Signals) -> Result< ); } } + if let Some(refs) = data.get("capabilityArtifactRefs").and_then(Value::as_array) { + require_provider_modality(provider, "structural_evidence")?; + if !signals.sentrux_capability_refs.is_empty() { + return Err(AdapterError::Contract( + "duplicate admitted Sentrux capability artifacts".into(), + )); + } + signals.sentrux_capability_refs = refs.clone(); + } if let Some(native) = data.get("nativeCode") { require_provider_modality(provider, "native_code")?; if signals.native_seen { @@ -426,7 +437,7 @@ fn diagnose(request: &Value, s: &Signals, audit: Option<&AuditReport>) -> Value "diagnosis":{"findings":[diagnosis],"impression":diagnosis,"risk":status,"evidence":evidence}, "treatment":{"plan":treatment,"follow_up":["Rerun diagnosis.hospital with current admitted evidence."]}, "protocols":[], - "tools":{}, + "tools":{"sentruxCapabilities":s.sentrux_capability_refs}, "surgery_plan":{ "schema":"code-intel-surgery-plan.v1", "status":surgery_status, diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs new file mode 100644 index 00000000..22817bd1 --- /dev/null +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -0,0 +1,268 @@ +use std::path::Path; + +use serde_json::{json, Value}; + +use crate::adapter_contract::{AdapterArtifact, AdapterError}; +use crate::capability::{rfc3339_now, sha256_hex}; + +use super::{command_evidence, run_sentrux, SentruxCommand}; + +const SENTRUX_CAPABILITY_COMMANDS: [(&str, &str); 7] = [ + ("sentrux.gate", "gate"), + ("sentrux.check", "check"), + ("sentrux.scan", "scan"), + ("sentrux.health", "health"), + ("sentrux.dsm", "dsm"), + ("sentrux.check_rules", "check_rules"), + ("sentrux.gate_save", "gate_save"), +]; + +pub(super) fn collect_sentrux_capabilities( + repo: &Path, + tool_path_prefix: Option<&Path>, +) -> Result<(SentruxCommand, SentruxCommand, Vec), AdapterError> { + let mut gate = None; + let mut check = None; + let mut observations = Vec::with_capacity(SENTRUX_CAPABILITY_COMMANDS.len()); + for &(capability_id, subcommand) in &SENTRUX_CAPABILITY_COMMANDS { + if subcommand == "gate_save" { + observations.push(json!({ + "capabilityId":capability_id, + "operation":subcommand, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "status":"not_run", + "verdict":"unknown", + "command":Value::Null, + "failure":{ + "kind":"explicit_mutation_required", + "message":"gate_save writes the repository baseline and requires explicit authority" + } + })); + continue; + } + let command = match run_sentrux(repo, tool_path_prefix, subcommand) { + Ok(command) => command, + Err(error) if matches!(subcommand, "gate" | "check") => return Err(error), + Err(error) => { + observations.push(json!({ + "capabilityId":capability_id, + "operation":subcommand, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "status":"failed", + "verdict":"unknown", + "command":Value::Null, + "failure":{ + "kind":adapter_error_kind(&error), + "message":format!("{error:?}") + } + })); + continue; + } + }; + observations.push(capability_observation( + capability_id, + subcommand, + tool_path_prefix, + &command, + )); + match subcommand { + "gate" => gate = Some(command), + "check" => check = Some(command), + _ => {} + } + } + Ok(( + gate.ok_or_else(|| AdapterError::Internal("Sentrux gate observation is missing".into()))?, + check + .ok_or_else(|| AdapterError::Internal("Sentrux check observation is missing".into()))?, + observations, + )) +} + +pub(super) fn build_capability_artifacts( + observations: &[Value], + snapshot_identity: &str, + run_id: &str, + tool_path_prefix: Option<&Path>, +) -> Result<(Vec, Vec), AdapterError> { + let provider = sentrux_capability_provider(tool_path_prefix); + let mut artifacts = Vec::with_capacity(observations.len()); + let mut refs = Vec::with_capacity(observations.len()); + for observation in observations { + let capability_id = observation["capabilityId"].as_str().ok_or_else(|| { + AdapterError::Contract("Sentrux capability observation has no capabilityId".into()) + })?; + let operation = observation["operation"].as_str().ok_or_else(|| { + AdapterError::Contract("Sentrux capability observation has no operation".into()) + })?; + let raw_status = observation["status"].as_str().unwrap_or("failed"); + let status = match raw_status { + "not_run" => "not_applicable", + "succeeded" | "degraded" | "unavailable" | "skipped" | "failed" => raw_status, + other => { + return Err(AdapterError::Contract(format!( + "unsupported Sentrux capability status: {other}" + ))) + } + }; + let artifact = json!({ + "schema":"code-intel-sentrux-capability-artifact.v1", + "contractVersion":1, + "capabilityId":capability_id, + "operation":operation, + "runId":run_id, + "snapshotIdentity":snapshot_identity, + "provider":provider.clone(), + "status":status, + "authority":if status == "not_applicable" { "declared_only" } else { "authoritative" }, + "inputs":{"snapshotIdentity":snapshot_identity}, + "outputs":{ + "command":observation["command"], + "verdict":observation["verdict"] + }, + "failure":capability_failure(observation, status), + "freshness":{ + "status":"current", + "evaluatedAt":rfc3339_now(), + "consumedSnapshotIdentity":snapshot_identity + }, + "decisionConsumers":sentrux_decision_consumers(capability_id) + }); + let bytes = serde_json::to_vec(&artifact).map_err(|error| { + AdapterError::Internal(format!("serialize Sentrux capability artifact: {error}")) + })?; + let relative_path = format!( + "sentrux-capability-{}.json", + capability_id.replace('.', "-") + ); + refs.push(json!({ + "schema":"code-intel-artifact-ref.v1", + "artifactSchema":"code-intel-sentrux-capability-artifact.v1", + "type":"provider.sentrux.capability-artifact", + "path":relative_path, + "sha256":sha256_hex(&bytes), + "consumedSnapshotIdentity":snapshot_identity + })); + artifacts.push(AdapterArtifact { + artifact_schema: "code-intel-sentrux-capability-artifact.v1".into(), + artifact_type: "provider.sentrux.capability-artifact".into(), + relative_path, + bytes, + }); + } + Ok((artifacts, refs)) +} + +fn sentrux_capability_provider(tool_path_prefix: Option<&Path>) -> Value { + if tool_path_prefix.is_some() { + json!({ + "mode":"external", + "id":"sentrux.command-adapter", + "version":"1.0.0", + "digest":sha256_hex(include_bytes!("builtin_provider_evidence.rs")) + }) + } else { + json!({ + "mode":"builtin", + "id":super::sentrux_gate::ENGINE_ID, + "version":super::sentrux_gate::ENGINE_VERSION, + "digest":sha256_hex(include_bytes!("sentrux_gate.rs")) + }) + } +} + +fn capability_failure(observation: &Value, status: &str) -> Value { + if status == "succeeded" { + return Value::Null; + } + let raw_kind = observation["failure"]["kind"].as_str().unwrap_or("unknown"); + let kind = match raw_kind { + "explicit_mutation_required" | "not_applicable" => "not_applicable", + "provider_unavailable" => "provider_unavailable", + "contract_error" | "invalid_options" => "config_error", + "io_error" => "local_tool_error", + _ => "provider_error", + }; + let message = observation["failure"]["message"] + .as_str() + .or_else(|| observation["failure"]["kind"].as_str()) + .unwrap_or("Sentrux capability did not complete") + .to_string(); + json!({ + "kind":kind, + "message":message, + "retryable":kind == "provider_unavailable" || kind == "local_tool_error" + }) +} + +fn sentrux_decision_consumers(capability_id: &str) -> Value { + match capability_id { + "sentrux.gate" | "sentrux.check" => { + json!(["diagnosis.hospital", "pr_gate", "release_gate"]) + } + "sentrux.gate_save" => json!(["sentrux.gate"]), + _ => json!(["evidence.sentrux", "diagnosis.hospital"]), + } +} + +fn capability_observation( + capability_id: &str, + subcommand: &str, + tool_path_prefix: Option<&Path>, + command: &SentruxCommand, +) -> Value { + let (status, verdict, failure) = if command.success { + ("succeeded", "pass", json!({"kind":"none"})) + } else if !command.governed { + ("succeeded", "unknown", json!({"kind":"none"})) + } else { + ( + "failed", + "fail", + json!({ + "kind":"command_failed", + "message":command_failure_message(command) + }), + ) + }; + json!({ + "capabilityId":capability_id, + "operation":subcommand, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "status":status, + "verdict":verdict, + "command":command_evidence(subcommand, command), + "failure":failure + }) +} + +fn sentrux_provider_mode(tool_path_prefix: Option<&Path>) -> &'static str { + if tool_path_prefix.is_some() { + "external" + } else { + "builtin_lite" + } +} + +fn adapter_error_kind(error: &AdapterError) -> &'static str { + match error { + AdapterError::Unavailable(_) => "provider_unavailable", + AdapterError::Contract(_) => "contract_error", + AdapterError::InvalidOptions(_) => "invalid_options", + AdapterError::Internal(_) => "provider_error", + AdapterError::Io(_) => "io_error", + } +} + +fn command_failure_message(command: &SentruxCommand) -> String { + command + .stderr + .lines() + .chain(command.stdout.lines()) + .find(|line| !line.trim().is_empty()) + .map(str::trim) + .unwrap_or("Sentrux command reported a failing verdict") + .chars() + .take(1024) + .collect() +} From 1a449d8ff5abfc43faea3cb8017f91d9895d4e7f Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:16:51 +0800 Subject: [PATCH 05/27] feat(sentrux): surface capability evidence in impact --- crates/code-intel-cli/src/change_impact.rs | 75 +++++++++++++++++++ crates/code-intel-cli/tests/dag_run.rs | 10 +++ .../code-intel-change-impact.v1.schema.json | 18 +++++ 3 files changed, 103 insertions(+) diff --git a/crates/code-intel-cli/src/change_impact.rs b/crates/code-intel-cli/src/change_impact.rs index 0c0387ec..f57e4cfd 100644 --- a/crates/code-intel-cli/src/change_impact.rs +++ b/crates/code-intel-cli/src/change_impact.rs @@ -6,6 +6,9 @@ use serde_json::{json, Value}; use crate::committed_evidence::{self, CommittedEvidence, EvidenceError}; use crate::impact_graph::{impacted_files, reverse_import_graph, select_tests, test_commands}; +const SENTRUX_CAPABILITY_ARTIFACT_SCHEMA: &str = "code-intel-sentrux-capability-artifact.v1"; +const SENTRUX_CAPABILITY_ARTIFACT_TYPE: &str = "provider.sentrux.capability-artifact"; + pub(crate) fn run_raw(raw: &[String]) -> i32 { // Leaf adapter only — controllers wrap the execute_* paths with typed // authority receipts and must not be imported here (import cycle). @@ -294,6 +297,7 @@ fn build_result( }) }) .collect::>(); + let (sentrux_evidence_refs, sentrux_evidence) = sentrux_evidence(evidence, stale); let mut result = json!({ "schema":"code-intel-change-impact.v1", "repo":cli.repo, @@ -304,6 +308,8 @@ fn build_result( "freshness":freshness, "changed":changed, "evidenceRefs":[files_ref,imports_ref], + "sentruxEvidenceRefs":sentrux_evidence_refs, + "sentruxEvidence":sentrux_evidence, "impact":{ "files":impact_rows, "resolvedImportEdges":resolved_edges, @@ -344,6 +350,48 @@ fn build_result( Ok(ChangeImpactResult { value: result }) } +/// Project only manifest refs whose payloads were verified by +/// `committed_evidence::load`. This deliberately does not inspect provider +/// stdout or re-run Sentrux: change impact is a committed-snapshot consumer. +fn sentrux_evidence(evidence: &CommittedEvidence, stale: bool) -> (Vec, Value) { + let refs = evidence + .refs + .iter() + .zip(evidence.verified.iter()) + .filter(|(reference, _)| { + reference["artifactSchema"] == SENTRUX_CAPABILITY_ARTIFACT_SCHEMA + && reference["type"] == SENTRUX_CAPABILITY_ARTIFACT_TYPE + }) + .map(|(reference, _)| reference.clone()) + .collect::>(); + if refs.is_empty() { + return ( + refs, + json!({ + "status":"unknown", + "diagnostics":["No verified Sentrux capability artifact refs are present in the committed evidence; Sentrux-specific impact and test-gap signals are advisory/unknown."], + }), + ); + } + if stale { + ( + refs, + json!({ + "status":"advisory", + "diagnostics":["Sentrux capability refs are verified against the committed snapshot, but this impact result is stale-advisory."], + }), + ) + } else { + ( + refs, + json!({ + "status":"available", + "diagnostics":[], + }), + ) + } +} + fn normalize_relative(path: &str) -> Result { let path = path.replace('\\', "/"); if path.is_empty() @@ -371,3 +419,30 @@ pub(crate) enum ImpactError { Contract(String), HostIo(String), } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn missing_sentrux_capability_refs_are_explicitly_unknown() { + let evidence = CommittedEvidence { + entry: Value::Null, + refs: Vec::new(), + verified: Vec::new(), + run_root: std::path::PathBuf::new(), + }; + + let (refs, projection) = sentrux_evidence(&evidence, false); + + assert!(refs.is_empty()); + assert_eq!(projection["status"], "unknown"); + assert!(projection["diagnostics"] + .as_array() + .expect("missing evidence diagnostic array") + .iter() + .any(|diagnostic| diagnostic + .as_str() + .is_some_and(|text| text.contains("advisory/unknown")))); + } +} diff --git a/crates/code-intel-cli/tests/dag_run.rs b/crates/code-intel-cli/tests/dag_run.rs index 368d0176..60adeec3 100644 --- a/crates/code-intel-cli/tests/dag_run.rs +++ b/crates/code-intel-cli/tests/dag_run.rs @@ -341,6 +341,16 @@ fn production_dag_output_commits_and_enters_the_authoritative_index() { assert_eq!(impact["schema"], "code-intel-change-impact.v1"); assert_eq!(impact["runOutcome"], "completed"); assert_eq!(impact["freshness"]["status"], "current"); + assert_eq!(impact["sentruxEvidence"]["status"], "available"); + assert!(!impact["sentruxEvidenceRefs"].as_array().unwrap().is_empty()); + assert!(impact["sentruxEvidenceRefs"] + .as_array() + .unwrap() + .iter() + .all(|reference| { + reference["artifactSchema"] == "code-intel-sentrux-capability-artifact.v1" + && reference["type"] == "provider.sentrux.capability-artifact" + })); assert_eq!( impact["testSelection"]["files"], json!(["tests/lib_test.rs"]) diff --git a/orchestration/schemas/code-intel-change-impact.v1.schema.json b/orchestration/schemas/code-intel-change-impact.v1.schema.json index 2faf1ef0..9423d497 100644 --- a/orchestration/schemas/code-intel-change-impact.v1.schema.json +++ b/orchestration/schemas/code-intel-change-impact.v1.schema.json @@ -45,6 +45,24 @@ "maxItems": 2, "items": { "$ref": "code-intel-run-manifest.v1.schema.json#/definitions/artifactRef" } }, + "sentruxEvidenceRefs": { + "type": "array", + "items": { "$ref": "code-intel-run-manifest.v1.schema.json#/definitions/artifactRef" }, + "uniqueItems": true + }, + "sentruxEvidence": { + "type": "object", + "additionalProperties": false, + "required": ["status", "diagnostics"], + "properties": { + "status": { "enum": ["available", "advisory", "unknown"] }, + "diagnostics": { + "type": "array", + "items": { "type": "string", "minLength": 1 }, + "uniqueItems": true + } + } + }, "impact": { "type": "object", "additionalProperties": false, From ad6b8c117e859c9894ae51d9f9bba9f0d72dcd02 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:18:15 +0800 Subject: [PATCH 06/27] feat(sentrux): project capability evidence in reports --- crates/code-intel-cli/src/artifacts_report.rs | 333 ++++++++++++++++++ 1 file changed, 333 insertions(+) diff --git a/crates/code-intel-cli/src/artifacts_report.rs b/crates/code-intel-cli/src/artifacts_report.rs index 18397d92..f69229a0 100644 --- a/crates/code-intel-cli/src/artifacts_report.rs +++ b/crates/code-intel-cli/src/artifacts_report.rs @@ -1,3 +1,4 @@ +use std::collections::BTreeSet; use std::fs; use std::path::{Path, PathBuf}; @@ -36,9 +37,11 @@ pub(super) fn report(repo: &Path, artifact_root: Option<&Path>, json: bool) -> R let hospital = required_report_artifact(&run_root, &manifest, "diagnosis.hospital")?; let hospital_markdown = required_report_artifact(&run_root, &manifest, "diagnosis.hospital-view")?; + let hospital_value = read_json_artifact(&hospital.path, "diagnosis.hospital")?; let hospital_text = fs::read_to_string(&hospital_markdown.path)?; let agent_code_slice_ranking = optional_report_artifact(&run_root, &manifest, "code_evidence.agent_slice")?; + let sentrux_evidence = project_sentrux_evidence(&run_root, &manifest, &hospital_value); let out = serde_json::json!({ "schema": "code-intel-report.v1", @@ -47,6 +50,7 @@ pub(super) fn report(repo: &Path, artifact_root: Option<&Path>, json: bool) -> R "hospital": hospital.to_json(), "hospitalMarkdown": hospital_markdown.to_json(), "agentCodeSliceRanking": agent_code_slice_ranking.as_ref().map(ReportArtifact::to_json), + "sentruxEvidence": sentrux_evidence, }); if json { println!("{}", serde_json::to_string_pretty(&out)?); @@ -59,6 +63,12 @@ pub(super) fn report(repo: &Path, artifact_root: Option<&Path>, json: bool) -> R if let Some(ranking) = &agent_code_slice_ranking { println!("agentCodeSliceRanking: {}", ranking.path.display()); } + println!( + "sentruxEvidence: {}", + out["sentruxEvidence"]["status"] + .as_str() + .unwrap_or("unknown") + ); println!(); println!("--- hospital.md ---"); print!("{hospital_text}"); @@ -69,6 +79,215 @@ pub(super) fn report(repo: &Path, artifact_root: Option<&Path>, json: bool) -> R Ok(()) } +fn read_json_artifact(path: &Path, artifact_type: &str) -> Result { + let bytes = fs::read(path)?; + serde_json::from_slice(&bytes).map_err(|error| { + format!( + "committed `{artifact_type}` artifact {} is not valid JSON: {error}", + path.display() + ) + .into() + }) +} + +/// Project Sentrux evidence only when the Hospital's references are also +/// present in the committed run manifest and the referenced capability +/// artifact verifies against that manifest. The report must remain useful on +/// older or partial runs, but it must never turn an absent or untrusted +/// capability reference into a successful result. +fn project_sentrux_evidence(run_root: &Path, manifest: &Value, hospital: &Value) -> Value { + let manifest_refs = manifest_capability_refs(manifest); + let hospital_refs = match hospital.pointer("/tools/sentruxCapabilities") { + Some(Value::Array(refs)) => refs, + Some(_) => { + return serde_json::json!({ + "status": if manifest_refs.is_empty() { "unknown" } else { "degraded" }, + "manifestCapabilityRefs": manifest_refs.len(), + "hospitalCapabilityRefs": 0, + "capabilities": [], + "missingReferences": manifest_refs.iter().map(|reference| reference_descriptor(reference)).collect::>(), + "unverifiedReferences": [], + "reason": "Hospital tools.sentruxCapabilities is not an array" + }); + } + None => { + let status = if manifest_refs.is_empty() { + "unknown" + } else { + "degraded" + }; + return serde_json::json!({ + "status": status, + "manifestCapabilityRefs": manifest_refs.len(), + "hospitalCapabilityRefs": 0, + "capabilities": [], + "missingReferences": manifest_refs.iter().map(|reference| reference_descriptor(reference)).collect::>(), + "unverifiedReferences": [], + "reason": if manifest_refs.is_empty() { + "Hospital did not publish Sentrux capability references and the manifest contains none" + } else { + "Manifest contains Sentrux capability references but Hospital did not project them" + } + }); + } + }; + + let manifest_keys = manifest_refs + .iter() + .filter_map(|reference| reference_key(reference).map(|key| (key, *reference))) + .collect::>(); + let mut projected_keys = BTreeSet::new(); + let mut verified = Vec::new(); + let mut unverified = Vec::new(); + + for reference in hospital_refs { + let Some(key) = reference_key(reference) else { + unverified.push(serde_json::json!({ + "reference": reference, + "reason": "Hospital Sentrux capability reference is malformed" + })); + continue; + }; + if !projected_keys.insert(key.clone()) { + unverified.push(serde_json::json!({ + "reference": reference, + "reason": "Hospital Sentrux capability reference is duplicated" + })); + continue; + } + let Some((_, manifest_reference)) = manifest_keys + .iter() + .find(|(candidate, _)| *candidate == key) + else { + unverified.push(serde_json::json!({ + "reference": reference, + "reason": "Hospital Sentrux capability reference is not present in the committed manifest" + })); + continue; + }; + + let Some(snapshot) = manifest["snapshotIdentity"].as_str() else { + unverified.push(serde_json::json!({ + "reference": reference, + "reason": "Committed manifest has no snapshot identity" + })); + continue; + }; + let artifact = match crate::artifact_ref::registered_contract(manifest_reference).and_then( + |contract| { + crate::artifact_ref::verify_artifact_ref( + run_root, + snapshot, + contract, + manifest_reference, + ) + }, + ) { + Ok(artifact) => artifact, + Err(error) => { + unverified.push(serde_json::json!({ + "reference": reference, + "reason": format!("Committed Sentrux capability artifact failed verification: {error:?}") + })); + continue; + } + }; + let payload: Value = match serde_json::from_slice(artifact.bytes()) { + Ok(payload) => payload, + Err(error) => { + unverified.push(serde_json::json!({ + "reference": reference, + "reason": format!("Verified Sentrux capability artifact is not JSON: {error}") + })); + continue; + } + }; + verified.push(serde_json::json!({ + "capabilityId": payload["capabilityId"], + "operation": payload["operation"], + "status": payload["status"], + "authority": payload["authority"], + "verdict": payload.pointer("/outputs/verdict").cloned().unwrap_or(Value::String("unknown".into())), + "provider": payload["provider"], + "artifact": reference_descriptor(manifest_reference), + "verification": "committed_manifest_and_payload" + })); + } + + let missing = manifest_refs + .iter() + .filter(|reference| { + reference_key(reference).is_some_and(|key| !projected_keys.contains(&key)) + }) + .map(|reference| reference_descriptor(reference)) + .collect::>(); + let has_degraded_capability = verified.iter().any(|capability| { + !matches!(capability["status"].as_str(), Some("succeeded")) + || matches!(capability["verdict"].as_str(), Some("fail" | "unknown")) + }); + let status = if verified.is_empty() { + "unknown" + } else if !unverified.is_empty() || !missing.is_empty() || has_degraded_capability { + "degraded" + } else { + "verified" + }; + serde_json::json!({ + "status": status, + "manifestCapabilityRefs": manifest_refs.len(), + "hospitalCapabilityRefs": hospital_refs.len(), + "capabilities": verified, + "missingReferences": missing, + "unverifiedReferences": unverified, + "reason": if status == "verified" { + "All Hospital Sentrux capability references are manifest-bound and payload-verified" + } else if status == "degraded" { + "Sentrux evidence is partially verified; missing, unverified, or non-success capabilities are listed" + } else { + "No Hospital Sentrux capability reference could be verified" + } + }) +} + +fn manifest_capability_refs(manifest: &Value) -> Vec<&Value> { + manifest["nodes"] + .as_object() + .into_iter() + .flat_map(|nodes| nodes.values()) + .flat_map(|node| node["artifacts"].as_array().into_iter().flatten()) + .filter(|reference| { + reference["type"] == "provider.sentrux.capability-artifact" + && reference["artifactSchema"] == "code-intel-sentrux-capability-artifact.v1" + }) + .collect() +} + +fn reference_key(reference: &Value) -> Option { + [ + "schema", + "artifactSchema", + "type", + "path", + "sha256", + "consumedSnapshotIdentity", + ] + .into_iter() + .map(|field| reference[field].as_str()) + .collect::>>() + .map(|fields| fields.join("\u{0}")) +} + +fn reference_descriptor(reference: &Value) -> Value { + serde_json::json!({ + "schema": reference["schema"], + "artifactSchema": reference["artifactSchema"], + "type": reference["type"], + "path": reference["path"], + "sha256": reference["sha256"], + "consumedSnapshotIdentity": reference["consumedSnapshotIdentity"] + }) +} + fn required_report_artifact( run_root: &Path, manifest: &Value, @@ -151,3 +370,117 @@ fn latest_committed_run(repo_artifacts: &Path) -> Result<(PathBuf, Value)> { ) .into()) } + +#[cfg(test)] +mod tests { + use super::*; + use serde_json::json; + use std::time::{SystemTime, UNIX_EPOCH}; + + fn unique_temp_dir() -> PathBuf { + let stamp = SystemTime::now() + .duration_since(UNIX_EPOCH) + .expect("clock should be after epoch") + .as_nanos(); + std::env::temp_dir().join(format!("code-intel-report-sentrux-{stamp}")) + } + + fn capability_fixture(root: &Path) -> (String, Value) { + let snapshot = "a".repeat(64); + let payload = json!({ + "schema":"code-intel-sentrux-capability-artifact.v1", + "contractVersion":1, + "capabilityId":"sentrux.scan", + "operation":"scan", + "runId":"run-1", + "snapshotIdentity":snapshot, + "provider":{ + "mode":"builtin", + "id":"sentrux.builtin", + "version":"1.0.0", + "digest":"b".repeat(64) + }, + "status":"succeeded", + "authority":"authoritative", + "inputs":{"snapshotIdentity":snapshot}, + "outputs":{"verdict":"pass"}, + "failure":null, + "freshness":{ + "status":"current", + "evaluatedAt":"2026-08-19T00:00:00Z", + "consumedSnapshotIdentity":snapshot + }, + "decisionConsumers":["diagnosis.hospital"] + }); + let bytes = serde_json::to_vec(&payload).expect("fixture should serialize"); + let relative_path = "objects/sha256/sentrux-scan"; + let path = root.join(relative_path); + fs::create_dir_all(path.parent().expect("fixture has a parent")) + .expect("fixture directory should be created"); + fs::write(path, bytes.clone()).expect("fixture artifact should be written"); + let reference = json!({ + "schema":"code-intel-artifact-ref.v1", + "artifactSchema":"code-intel-sentrux-capability-artifact.v1", + "type":"provider.sentrux.capability-artifact", + "path":relative_path, + "sha256":crate::capability::sha256_hex(&bytes), + "consumedSnapshotIdentity":snapshot + }); + (snapshot, reference) + } + + #[test] + fn report_projects_verified_refs_and_marks_untrusted_refs_degraded() { + let root = unique_temp_dir(); + fs::create_dir_all(&root).expect("fixture root should be created"); + let (snapshot, reference) = capability_fixture(&root); + let mut untrusted = reference.clone(); + untrusted["sha256"] = json!("c".repeat(64)); + let manifest = json!({ + "snapshotIdentity":snapshot, + "nodes":{"evidence.sentrux":{"artifacts":[reference]}} + }); + let hospital = json!({ + "tools":{"sentruxCapabilities":[reference, untrusted]} + }); + + let evidence = project_sentrux_evidence(&root, &manifest, &hospital); + + assert_eq!(evidence["status"], "degraded"); + assert_eq!(evidence["manifestCapabilityRefs"], 1); + assert_eq!(evidence["hospitalCapabilityRefs"], 2); + assert_eq!(evidence["capabilities"].as_array().unwrap().len(), 1); + assert_eq!(evidence["capabilities"][0]["capabilityId"], "sentrux.scan"); + assert_eq!( + evidence["capabilities"][0]["verification"], + "committed_manifest_and_payload" + ); + assert_eq!( + evidence["unverifiedReferences"].as_array().unwrap().len(), + 1 + ); + + let _ = fs::remove_dir_all(root); + } + + #[test] + fn report_marks_missing_hospital_projection_unknown_or_degraded() { + let root = unique_temp_dir(); + fs::create_dir_all(&root).expect("fixture root should be created"); + let (snapshot, reference) = capability_fixture(&root); + let manifest = json!({ + "snapshotIdentity":snapshot, + "nodes":{"evidence.sentrux":{"artifacts":[reference]}} + }); + + let degraded = project_sentrux_evidence(&root, &manifest, &json!({"tools":{}})); + assert_eq!(degraded["status"], "degraded"); + assert_eq!(degraded["missingReferences"].as_array().unwrap().len(), 1); + + let unknown = project_sentrux_evidence(&root, &json!({"nodes":{}}), &json!({})); + assert_eq!(unknown["status"], "unknown"); + assert!(unknown["capabilities"].as_array().unwrap().is_empty()); + + let _ = fs::remove_dir_all(root); + } +} From 3a71adb5eca2e974fa73127914a7ecbcb815680e Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:18:16 +0800 Subject: [PATCH 07/27] ci(sentrux): gate pull requests on capability closure --- .github/workflows/pr-gate.yml | 89 +++++++++++++++++++++++++++++++++++ 1 file changed, 89 insertions(+) diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml index cdedb14a..273f67df 100644 --- a/.github/workflows/pr-gate.yml +++ b/.github/workflows/pr-gate.yml @@ -227,3 +227,92 @@ jobs: exit 1 fi echo "agent-approved label present — gate green" + + sentrux-capability-gate: + # The PR gate consumes only the committed Run Commit boundary. It never + # treats Sentrux stdout as authority: `artifact index` revalidates the + # completion marker, content-addressed manifest, and every Artifact Ref. + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Setup Rust + shell: pwsh + run: | + rustup toolchain install + rustup show active-toolchain + + - name: Build Rust CLI + shell: pwsh + run: cargo build -p code-intel --release --locked + + - name: Run authoritative Sentrux capability pipeline + shell: pwsh + run: | + $binary = "./target/release/code-intel" + $staging = Join-Path $env:RUNNER_TEMP "code-intel-pr-sentrux-staging" + $authority = Join-Path $env:RUNNER_TEMP "code-intel-pr-sentrux-authority" + $finalName = "pr-sentrux-$env:GITHUB_RUN_ID" + New-Item -ItemType Directory -Force -Path $authority | Out-Null + & $binary run execute --repo . --out $staging --authority-root $authority --final-name $finalName --manifest orchestration/integrations.json --doctor-require-repowise false + if ($LASTEXITCODE -ne 0) { + throw "authoritative Sentrux pipeline failed with exit ${LASTEXITCODE}; inspect the uploaded run closure evidence" + } + + $repoName = (Get-Item .).Name + $runRoot = Join-Path (Join-Path $authority $repoName) $finalName + $index = Join-Path $authority "index.json" + & $binary artifact index --artifact-root $authority --output $index --operation rebuild + if ($LASTEXITCODE -ne 0) { throw "committed artifact index validation failed" } + + $markerPath = Join-Path $runRoot "run-complete.json" + if (-not (Test-Path -LiteralPath $markerPath -PathType Leaf)) { throw "Sentrux run has no completion marker: $markerPath" } + $marker = Get-Content -Raw -LiteralPath $markerPath | ConvertFrom-Json + if ($marker.schema -ne "code-intel-run-commit.v1") { throw "unexpected completion marker schema: $($marker.schema)" } + $manifestPath = Join-Path $runRoot ($marker.manifest.path -replace '/', [IO.Path]::DirectorySeparatorChar) + $manifest = Get-Content -Raw -LiteralPath $manifestPath | ConvertFrom-Json + if ($manifest.schema -ne "code-intel-run-manifest.v1" -or $manifest.outcome -ne "completed") { throw "Sentrux run manifest is not completed and authoritative" } + if ($manifest.snapshotIdentity -ne $marker.snapshotIdentity) { throw "completion marker and run manifest snapshot identities differ" } + + $indexValue = Get-Content -Raw -LiteralPath $index | ConvertFrom-Json + $entry = @($indexValue.entries | Where-Object { $_.repo -eq $repoName -and $_.run -eq $finalName }) + if ($entry.Count -ne 1) { throw "artifact index did not admit exactly one completed PR run" } + $refs = @($entry[0].artifactRefs) + $hospitalRefs = @($refs | Where-Object { $_.type -eq "diagnosis.hospital" }) + if ($hospitalRefs.Count -ne 1) { throw "completed run is missing its verified final hospital report Artifact Ref" } + $capabilityRefs = @($refs | Where-Object { $_.artifactSchema -eq "code-intel-sentrux-capability-artifact.v1" -and $_.type -eq "provider.sentrux.capability-artifact" }) + if ($capabilityRefs.Count -eq 0) { throw "completed run has no verified Sentrux capability Artifact Refs" } + + $matrix = Get-Content -Raw -LiteralPath "orchestration/sentrux-capability-matrix.v1.json" | ConvertFrom-Json + $matrixById = @{} + foreach ($capability in @($matrix.capabilities)) { $matrixById[$capability.id] = $capability } + $observed = @{} + foreach ($ref in $capabilityRefs) { + $payloadPath = Join-Path $runRoot ($ref.path -replace '/', [IO.Path]::DirectorySeparatorChar) + $payload = Get-Content -Raw -LiteralPath $payloadPath | ConvertFrom-Json + if ($payload.schema -ne "code-intel-sentrux-capability-artifact.v1" -or $payload.snapshotIdentity -ne $marker.snapshotIdentity) { throw "Sentrux capability artifact is not schema-valid and snapshot-bound: $($ref.path)" } + if (-not $matrixById.ContainsKey($payload.capabilityId)) { throw "Sentrux capability is absent from the capability matrix: $($payload.capabilityId)" } + if ($matrixById[$payload.capabilityId].operation -ne $payload.operation) { throw "Sentrux capability operation disagrees with the matrix: $($payload.capabilityId)" } + if ($observed.ContainsKey($payload.capabilityId)) { throw "duplicate Sentrux capability artifact: $($payload.capabilityId)" } + $observed[$payload.capabilityId] = $payload + if ($payload.status -ne "succeeded" -and $null -eq $payload.failure) { throw "non-success Sentrux capability has no explicit failure: $($payload.capabilityId)" } + } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) + foreach ($id in $required) { + if (-not $observed.ContainsKey($id)) { throw "required automatic Sentrux capability is missing from the committed run: $id" } + if ($observed[$id].status -ne "succeeded") { throw "required automatic Sentrux capability did not succeed: $id ($($observed[$id].status))" } + } + Write-Host "Sentrux capability closure verified: $($capabilityRefs.Count) capability refs; required automatic=$($required.Count); matrix coverage=$($matrix.coverageStatus)" + + - name: Upload Sentrux closure evidence + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: sentrux-pr-closure + path: | + ${{ runner.temp }}/code-intel-pr-sentrux-authority + if-no-files-found: ignore From ec737b6a5d31d3795bc195d67230581be6b7a79c Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:24:14 +0800 Subject: [PATCH 08/27] fix(report): match capability refs by content identity --- crates/code-intel-cli/src/artifacts_report.rs | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/crates/code-intel-cli/src/artifacts_report.rs b/crates/code-intel-cli/src/artifacts_report.rs index f69229a0..a526e9fd 100644 --- a/crates/code-intel-cli/src/artifacts_report.rs +++ b/crates/code-intel-cli/src/artifacts_report.rs @@ -267,7 +267,6 @@ fn reference_key(reference: &Value) -> Option { "schema", "artifactSchema", "type", - "path", "sha256", "consumedSnapshotIdentity", ] @@ -440,8 +439,10 @@ mod tests { "snapshotIdentity":snapshot, "nodes":{"evidence.sentrux":{"artifacts":[reference]}} }); + let mut hospital_reference = reference.clone(); + hospital_reference["path"] = json!("sentrux-capability-scan.json"); let hospital = json!({ - "tools":{"sentruxCapabilities":[reference, untrusted]} + "tools":{"sentruxCapabilities":[hospital_reference, untrusted]} }); let evidence = project_sentrux_evidence(&root, &manifest, &hospital); From a254515d1f10ff1d132331801a6e29e0f0e50fbf Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:24:19 +0800 Subject: [PATCH 09/27] ci(release): verify packaged Sentrux capability closure --- .github/workflows/ci.yml | 65 ++++++++++++++- .github/workflows/release.yml | 146 ++++++++++++++++++++++++++-------- 2 files changed, 174 insertions(+), 37 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9d608707..0272f8ae 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -287,7 +287,7 @@ jobs: - name: Pipeline smoke shell: pwsh - run: .\legacy/scripts/tests/test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -SkipSentruxGate -Mode normal + run: .\legacy/scripts/tests/test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -Mode normal - name: Model channel and automation regression suites shell: pwsh @@ -672,8 +672,69 @@ jobs: if ($result.status -notin @("installed", "repaired", "already_installed")) { throw "Unexpected bootstrap status '$($result.status)':`n$raw" } + $result | ConvertTo-Json -Depth 20 | Set-Content -Encoding utf8 (Join-Path $env:RUNNER_TEMP "code-intel-smoketest-bootstrap.json") Write-Host "bootstrap.py installed $($result.tag) -> $($result.release_root)" + - name: Packaged Sentrux capability closure smoke + shell: pwsh + run: | + $bootstrap = Get-Content -Raw -LiteralPath (Join-Path $env:RUNNER_TEMP "code-intel-smoketest-bootstrap.json") | ConvertFrom-Json + $payload = [IO.Path]::GetFullPath([string]$bootstrap.release_root) + $binaryName = if ($IsWindows) { "code-intel.exe" } else { "code-intel" } + $binary = Join-Path $payload (Join-Path "bin" $binaryName) + $staging = Join-Path $env:RUNNER_TEMP "code-intel-packaged-sentrux-staging" + $authority = Join-Path $env:RUNNER_TEMP "code-intel-packaged-sentrux-authority" + $finalName = "packaged-sentrux-$env:GITHUB_RUN_ID" + $manifestPath = Join-Path $payload (Join-Path "orchestration" "integrations.json") + New-Item -ItemType Directory -Force -Path $authority | Out-Null + & $binary run execute --repo $payload --out $staging --authority-root $authority --final-name $finalName --manifest $manifestPath --doctor-require-repowise false + if ($LASTEXITCODE -ne 0) { throw "packaged install run execute failed with exit ${LASTEXITCODE}" } + + $repoName = (Get-Item -LiteralPath $payload).Name + $runRoot = Join-Path (Join-Path $authority $repoName) $finalName + $index = Join-Path $authority "index.json" + & $binary artifact index --artifact-root $authority --output $index --operation rebuild + if ($LASTEXITCODE -ne 0) { throw "packaged Sentrux artifact index validation failed" } + $marker = Get-Content -Raw -LiteralPath (Join-Path $runRoot "run-complete.json") | ConvertFrom-Json + if ($marker.schema -ne "code-intel-run-commit.v1") { throw "packaged run completion marker is invalid" } + $manifest = Get-Content -Raw -LiteralPath (Join-Path $runRoot ($marker.manifest.path -replace '/', [IO.Path]::DirectorySeparatorChar)) | ConvertFrom-Json + if ($manifest.schema -ne "code-intel-run-manifest.v1" -or $manifest.outcome -ne "completed") { throw "packaged run manifest is not completed" } + if ($manifest.snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged marker and manifest snapshots differ" } + if ($manifest.nodes.'evidence.sentrux'.status -ne "succeeded" -or $manifest.nodes.'evidence.sentrux'.verdict -ne "pass") { throw "packaged Sentrux evidence node is not a passing verified result" } + + $indexValue = Get-Content -Raw -LiteralPath $index | ConvertFrom-Json + $entry = @($indexValue.entries | Where-Object { $_.repo -eq $repoName -and $_.run -eq $finalName }) + if ($entry.Count -ne 1) { throw "packaged run was not admitted exactly once by artifact index" } + if ($entry[0].outcome -ne "completed" -or $entry[0].snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged artifact index identity does not match completion marker" } + $refs = @($entry[0].artifactRefs) + if (@($refs | Where-Object { $_.type -eq "diagnosis.hospital" }).Count -ne 1) { throw "packaged run is missing its verified hospital report ref" } + $capabilityRefs = @($refs | Where-Object { $_.artifactSchema -eq "code-intel-sentrux-capability-artifact.v1" -and $_.type -eq "provider.sentrux.capability-artifact" }) + if ($capabilityRefs.Count -eq 0) { throw "packaged run has no verified Sentrux capability refs" } + $matrix = Get-Content -Raw -LiteralPath (Join-Path $payload (Join-Path "orchestration" "sentrux-capability-matrix.v1.json")) | ConvertFrom-Json + $matrixById = @{} + foreach ($capability in @($matrix.capabilities)) { $matrixById[$capability.id] = $capability } + $observed = @{} + foreach ($ref in $capabilityRefs) { + if ($ref.path -ne "objects/sha256/$($ref.sha256)") { throw "packaged capability ref is not content-addressed: $($ref.path)" } + $payloadPath = Join-Path $runRoot ($ref.path -replace '/', [IO.Path]::DirectorySeparatorChar) + if (-not (Test-Path -LiteralPath $payloadPath -PathType Leaf)) { throw "packaged capability object is missing: $($ref.path)" } + if ((Get-FileHash -LiteralPath $payloadPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne $ref.sha256) { throw "packaged capability object digest mismatch: $($ref.path)" } + $capabilityPayload = Get-Content -Raw -LiteralPath $payloadPath | ConvertFrom-Json + if ($capabilityPayload.schema -ne "code-intel-sentrux-capability-artifact.v1" -or $capabilityPayload.snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged capability artifact is not snapshot-bound: $($ref.path)" } + if (-not $matrixById.ContainsKey($capabilityPayload.capabilityId)) { throw "packaged capability is absent from the matrix: $($capabilityPayload.capabilityId)" } + if ($matrixById[$capabilityPayload.capabilityId].operation -ne $capabilityPayload.operation) { throw "packaged capability operation disagrees with the matrix: $($capabilityPayload.capabilityId)" } + if ($observed.ContainsKey($capabilityPayload.capabilityId)) { throw "duplicate packaged capability artifact: $($capabilityPayload.capabilityId)" } + $observed[$capabilityPayload.capabilityId] = $capabilityPayload + if ($capabilityPayload.status -ne "succeeded" -and $null -eq $capabilityPayload.failure) { throw "packaged non-success capability has no explicit failure: $($capabilityPayload.capabilityId)" } + } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) + if ($required.Count -eq 0) { throw "capability matrix exposes no required automatic capabilities" } + foreach ($id in $required) { + if (-not $observed.ContainsKey($id)) { throw "packaged run is missing required automatic capability: $id" } + if ($observed[$id].status -ne "succeeded") { throw "packaged required capability did not succeed: $id ($($observed[$id].status))" } + } + Write-Host "Packaged Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; matrix coverage=$($matrix.coverageStatus)" + - name: Assert PATH and CODE_INTEL_HOME persisted (Windows) if: runner.os == 'Windows' shell: pwsh @@ -738,7 +799,7 @@ jobs: - name: Pipeline smoke shell: pwsh - run: ./legacy/scripts/tests/test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -SkipSentruxCheck -SkipSentruxGate -Mode normal + run: ./legacy/scripts/tests/test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -Mode normal - name: Hardcoded path scan shell: pwsh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c188e7da..1f0e6157 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -170,7 +170,7 @@ jobs: - name: Pipeline smoke shell: pwsh - run: .\legacy/scripts/tests/test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -SkipSentruxGate -Mode normal + run: .\legacy/scripts/tests/test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -Mode normal - name: Package shell: pwsh @@ -220,24 +220,62 @@ jobs: } } python (Join-Path $payload "skills\code-intel-pipeline\scripts\bootstrap.py") --help - # The packaged binary must pass its own structural gates against the - # packaged payload: release green, self-scan green, and published - # artifact green must refer to the same snapshot content. + # The packaged binary must publish a committed, snapshot-bound + # Sentrux closure. The manifest and content-addressed artifacts are + # authoritative; command stdout is intentionally not inspected. $packagedBinary = Join-Path $payload "bin\code-intel.exe" - & $packagedBinary sentrux --operation check --repo $payload - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own structural check" } - & $packagedBinary sentrux --operation gate --repo $payload - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own no-degradation gate" } - # scan/health/dsm are not gated (no baseline semantics), but they - # must at least run clean against the real packaged payload -- a - # regression here previously only showed up against synthetic unit - # fixtures, never against a real packaged tree. - & $packagedBinary sentrux --operation scan --repo $payload | Out-Null - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own structural scan" } - & $packagedBinary sentrux --operation health --repo $payload | Out-Null - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own structural health summary" } - & $packagedBinary sentrux --operation dsm --repo $payload | Out-Null - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own dependency-structure analysis" } + $staging = Join-Path $env:RUNNER_TEMP "code-intel-release-packaged-sentrux-staging-$env:GITHUB_RUN_ID" + $authority = Join-Path $env:RUNNER_TEMP "code-intel-release-packaged-sentrux-authority-$env:GITHUB_RUN_ID" + $finalName = "release-packaged-sentrux-$env:GITHUB_RUN_ID" + $manifestPath = Join-Path $payload (Join-Path "orchestration" "integrations.json") + New-Item -ItemType Directory -Force -Path $authority | Out-Null + & $packagedBinary run execute --repo $payload --out $staging --authority-root $authority --final-name $finalName --manifest $manifestPath --doctor-require-repowise false + if ($LASTEXITCODE -ne 0) { throw "packaged release run execute failed with exit ${LASTEXITCODE}" } + + $repoName = (Get-Item -LiteralPath $payload).Name + $runRoot = Join-Path (Join-Path $authority $repoName) $finalName + $index = Join-Path $authority "index.json" + & $packagedBinary artifact index --artifact-root $authority --output $index --operation rebuild + if ($LASTEXITCODE -ne 0) { throw "packaged release artifact index validation failed" } + $marker = Get-Content -Raw -LiteralPath (Join-Path $runRoot "run-complete.json") | ConvertFrom-Json + if ($marker.schema -ne "code-intel-run-commit.v1") { throw "packaged release completion marker is invalid" } + $manifest = Get-Content -Raw -LiteralPath (Join-Path $runRoot ($marker.manifest.path -replace '/', [IO.Path]::DirectorySeparatorChar)) | ConvertFrom-Json + if ($manifest.schema -ne "code-intel-run-manifest.v1" -or $manifest.outcome -ne "completed") { throw "packaged release run manifest is not completed" } + if ($manifest.snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged release marker and manifest snapshots differ" } + if ($manifest.nodes.'evidence.sentrux'.status -ne "succeeded" -or $manifest.nodes.'evidence.sentrux'.verdict -ne "pass") { throw "packaged release Sentrux evidence node is not a passing verified result" } + + $indexValue = Get-Content -Raw -LiteralPath $index | ConvertFrom-Json + $entry = @($indexValue.entries | Where-Object { $_.repo -eq $repoName -and $_.run -eq $finalName }) + if ($entry.Count -ne 1) { throw "packaged release run was not admitted exactly once by artifact index" } + if ($entry[0].outcome -ne "completed" -or $entry[0].snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged release artifact index identity does not match completion marker" } + $refs = @($entry[0].artifactRefs) + if (@($refs | Where-Object { $_.type -eq "diagnosis.hospital" }).Count -ne 1) { throw "packaged release run is missing its verified hospital report ref" } + $capabilityRefs = @($refs | Where-Object { $_.artifactSchema -eq "code-intel-sentrux-capability-artifact.v1" -and $_.type -eq "provider.sentrux.capability-artifact" }) + if ($capabilityRefs.Count -eq 0) { throw "packaged release run has no verified Sentrux capability refs" } + $matrix = Get-Content -Raw -LiteralPath (Join-Path $payload (Join-Path "orchestration" "sentrux-capability-matrix.v1.json")) | ConvertFrom-Json + $matrixById = @{} + foreach ($capability in @($matrix.capabilities)) { $matrixById[$capability.id] = $capability } + $observed = @{} + foreach ($ref in $capabilityRefs) { + if ($ref.path -ne "objects/sha256/$($ref.sha256)") { throw "packaged release capability ref is not content-addressed: $($ref.path)" } + $payloadPath = Join-Path $runRoot ($ref.path -replace '/', [IO.Path]::DirectorySeparatorChar) + if (-not (Test-Path -LiteralPath $payloadPath -PathType Leaf)) { throw "packaged release capability object is missing: $($ref.path)" } + if ((Get-FileHash -LiteralPath $payloadPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne $ref.sha256) { throw "packaged release capability object digest mismatch: $($ref.path)" } + $capabilityPayload = Get-Content -Raw -LiteralPath $payloadPath | ConvertFrom-Json + if ($capabilityPayload.schema -ne "code-intel-sentrux-capability-artifact.v1" -or $capabilityPayload.snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged release capability artifact is not snapshot-bound: $($ref.path)" } + if (-not $matrixById.ContainsKey($capabilityPayload.capabilityId)) { throw "packaged release capability is absent from the matrix: $($capabilityPayload.capabilityId)" } + if ($matrixById[$capabilityPayload.capabilityId].operation -ne $capabilityPayload.operation) { throw "packaged release capability operation disagrees with the matrix: $($capabilityPayload.capabilityId)" } + if ($observed.ContainsKey($capabilityPayload.capabilityId)) { throw "duplicate packaged release capability artifact: $($capabilityPayload.capabilityId)" } + $observed[$capabilityPayload.capabilityId] = $capabilityPayload + if ($capabilityPayload.status -ne "succeeded" -and $null -eq $capabilityPayload.failure) { throw "packaged release non-success capability has no explicit failure: $($capabilityPayload.capabilityId)" } + } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) + if ($required.Count -eq 0) { throw "capability matrix exposes no required automatic capabilities" } + foreach ($id in $required) { + if (-not $observed.ContainsKey($id)) { throw "packaged release run is missing required automatic capability: $id" } + if ($observed[$id].status -ne "succeeded") { throw "packaged release required capability did not succeed: $id ($($observed[$id].status))" } + } + Write-Host "Packaged release Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; matrix coverage=$($matrix.coverageStatus)" # ai-safety-003 (issue #34): if the packaged snapshot carries an # audit report, it must pass the same fail-closed validate @@ -396,23 +434,61 @@ jobs: $packagedBinary = Join-Path $payload "bin/code-intel" bash -c "test -x '$packagedBinary'" if ($LASTEXITCODE -ne 0) { throw "packaged binary lost its executable bit in the zip round trip: $packagedBinary" } - # The packaged binary must pass its own structural gates against the - # packaged payload: release green, self-scan green, and published - # artifact green must refer to the same snapshot content. - & $packagedBinary sentrux --operation check --repo $payload - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own structural check" } - & $packagedBinary sentrux --operation gate --repo $payload - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own no-degradation gate" } - # scan/health/dsm are not gated (no baseline semantics), but they - # must at least run clean against the real packaged payload -- a - # regression here previously only showed up against synthetic unit - # fixtures, never against a real packaged tree. - & $packagedBinary sentrux --operation scan --repo $payload | Out-Null - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own structural scan" } - & $packagedBinary sentrux --operation health --repo $payload | Out-Null - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own structural health summary" } - & $packagedBinary sentrux --operation dsm --repo $payload | Out-Null - if ($LASTEXITCODE -ne 0) { throw "packaged binary failed its own dependency-structure analysis" } + # The packaged binary must publish a committed, snapshot-bound + # Sentrux closure. The manifest and content-addressed artifacts are + # authoritative; command stdout is intentionally not inspected. + $staging = Join-Path $env:RUNNER_TEMP "code-intel-release-packaged-sentrux-staging-$env:GITHUB_RUN_ID" + $authority = Join-Path $env:RUNNER_TEMP "code-intel-release-packaged-sentrux-authority-$env:GITHUB_RUN_ID" + $finalName = "release-packaged-sentrux-$env:GITHUB_RUN_ID" + $manifestPath = Join-Path $payload (Join-Path "orchestration" "integrations.json") + New-Item -ItemType Directory -Force -Path $authority | Out-Null + & $packagedBinary run execute --repo $payload --out $staging --authority-root $authority --final-name $finalName --manifest $manifestPath --doctor-require-repowise false + if ($LASTEXITCODE -ne 0) { throw "packaged release run execute failed with exit ${LASTEXITCODE}" } + + $repoName = (Get-Item -LiteralPath $payload).Name + $runRoot = Join-Path (Join-Path $authority $repoName) $finalName + $index = Join-Path $authority "index.json" + & $packagedBinary artifact index --artifact-root $authority --output $index --operation rebuild + if ($LASTEXITCODE -ne 0) { throw "packaged release artifact index validation failed" } + $marker = Get-Content -Raw -LiteralPath (Join-Path $runRoot "run-complete.json") | ConvertFrom-Json + if ($marker.schema -ne "code-intel-run-commit.v1") { throw "packaged release completion marker is invalid" } + $manifest = Get-Content -Raw -LiteralPath (Join-Path $runRoot ($marker.manifest.path -replace '/', [IO.Path]::DirectorySeparatorChar)) | ConvertFrom-Json + if ($manifest.schema -ne "code-intel-run-manifest.v1" -or $manifest.outcome -ne "completed") { throw "packaged release run manifest is not completed" } + if ($manifest.snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged release marker and manifest snapshots differ" } + if ($manifest.nodes.'evidence.sentrux'.status -ne "succeeded" -or $manifest.nodes.'evidence.sentrux'.verdict -ne "pass") { throw "packaged release Sentrux evidence node is not a passing verified result" } + + $indexValue = Get-Content -Raw -LiteralPath $index | ConvertFrom-Json + $entry = @($indexValue.entries | Where-Object { $_.repo -eq $repoName -and $_.run -eq $finalName }) + if ($entry.Count -ne 1) { throw "packaged release run was not admitted exactly once by artifact index" } + if ($entry[0].outcome -ne "completed" -or $entry[0].snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged release artifact index identity does not match completion marker" } + $refs = @($entry[0].artifactRefs) + if (@($refs | Where-Object { $_.type -eq "diagnosis.hospital" }).Count -ne 1) { throw "packaged release run is missing its verified hospital report ref" } + $capabilityRefs = @($refs | Where-Object { $_.artifactSchema -eq "code-intel-sentrux-capability-artifact.v1" -and $_.type -eq "provider.sentrux.capability-artifact" }) + if ($capabilityRefs.Count -eq 0) { throw "packaged release run has no verified Sentrux capability refs" } + $matrix = Get-Content -Raw -LiteralPath (Join-Path $payload (Join-Path "orchestration" "sentrux-capability-matrix.v1.json")) | ConvertFrom-Json + $matrixById = @{} + foreach ($capability in @($matrix.capabilities)) { $matrixById[$capability.id] = $capability } + $observed = @{} + foreach ($ref in $capabilityRefs) { + if ($ref.path -ne "objects/sha256/$($ref.sha256)") { throw "packaged release capability ref is not content-addressed: $($ref.path)" } + $payloadPath = Join-Path $runRoot ($ref.path -replace '/', [IO.Path]::DirectorySeparatorChar) + if (-not (Test-Path -LiteralPath $payloadPath -PathType Leaf)) { throw "packaged release capability object is missing: $($ref.path)" } + if ((Get-FileHash -LiteralPath $payloadPath -Algorithm SHA256).Hash.ToLowerInvariant() -ne $ref.sha256) { throw "packaged release capability object digest mismatch: $($ref.path)" } + $capabilityPayload = Get-Content -Raw -LiteralPath $payloadPath | ConvertFrom-Json + if ($capabilityPayload.schema -ne "code-intel-sentrux-capability-artifact.v1" -or $capabilityPayload.snapshotIdentity -ne $marker.snapshotIdentity) { throw "packaged release capability artifact is not snapshot-bound: $($ref.path)" } + if (-not $matrixById.ContainsKey($capabilityPayload.capabilityId)) { throw "packaged release capability is absent from the matrix: $($capabilityPayload.capabilityId)" } + if ($matrixById[$capabilityPayload.capabilityId].operation -ne $capabilityPayload.operation) { throw "packaged release capability operation disagrees with the matrix: $($capabilityPayload.capabilityId)" } + if ($observed.ContainsKey($capabilityPayload.capabilityId)) { throw "duplicate packaged release capability artifact: $($capabilityPayload.capabilityId)" } + $observed[$capabilityPayload.capabilityId] = $capabilityPayload + if ($capabilityPayload.status -ne "succeeded" -and $null -eq $capabilityPayload.failure) { throw "packaged release non-success capability has no explicit failure: $($capabilityPayload.capabilityId)" } + } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) + if ($required.Count -eq 0) { throw "capability matrix exposes no required automatic capabilities" } + foreach ($id in $required) { + if (-not $observed.ContainsKey($id)) { throw "packaged release run is missing required automatic capability: $id" } + if ($observed[$id].status -ne "succeeded") { throw "packaged release required capability did not succeed: $id ($($observed[$id].status))" } + } + Write-Host "Packaged release Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; matrix coverage=$($matrix.coverageStatus)" # ai-safety-003 (issue #34): if the packaged snapshot carries an # audit report, it must pass the same fail-closed validate From f026b2ae7d34580c2303c0de252c72d0102e9f52 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:35:42 +0800 Subject: [PATCH 10/27] feat(sentrux): dispatch the complete capability matrix --- .../src/builtin_provider_evidence.rs | 34 +- .../src/sentrux_capability_artifacts.rs | 342 +++++++++++++++--- .../src/sentrux_lite_capabilities.rs | 122 +++++++ tests/test_sentrux_capability_executor.py | 49 +++ 4 files changed, 487 insertions(+), 60 deletions(-) create mode 100644 crates/code-intel-cli/src/sentrux_lite_capabilities.rs create mode 100644 tests/test_sentrux_capability_executor.py diff --git a/crates/code-intel-cli/src/builtin_provider_evidence.rs b/crates/code-intel-cli/src/builtin_provider_evidence.rs index 1b5acba8..ebb7cf12 100644 --- a/crates/code-intel-cli/src/builtin_provider_evidence.rs +++ b/crates/code-intel-cli/src/builtin_provider_evidence.rs @@ -35,6 +35,8 @@ mod sentrux_analysis; mod sentrux_capability_artifacts; #[path = "sentrux_gate.rs"] mod sentrux_gate; +#[path = "sentrux_lite_capabilities.rs"] +mod sentrux_lite_capabilities; use codenexus_scratch::{create_codenexus_scratch_dir, ScratchDir}; use sentrux_gate::Violation; @@ -523,8 +525,13 @@ fn run_sentrux( Some(prefix) => { let resolved = resolve_sentrux(prefix)?; let mut command = external_command(&resolved); + let provider_subcommand = if subcommand == "provider_discovery" { + "pro_status" + } else { + subcommand + }; let output = command - .arg(subcommand) + .arg(provider_subcommand) .arg(".") .current_dir(repo) .output() @@ -537,7 +544,32 @@ fn run_sentrux( let run = match subcommand { "dsm" => return json_command(sentrux_analysis::analyze(repo), subcommand), "scan" => return json_command(sentrux_gate::scan_json(repo), subcommand), + "rescan" => return json_command(sentrux_gate::scan_json(repo), subcommand), "health" => return json_command(sentrux_health_json(repo), subcommand), + "git_stats" => { + return json_command( + sentrux_lite_capabilities::git_stats_json(repo), + subcommand, + ) + } + "evolution" => { + return json_command( + sentrux_lite_capabilities::evolution_json(repo), + subcommand, + ) + } + "test_gaps" => { + return json_command( + sentrux_lite_capabilities::test_gaps_json(repo), + subcommand, + ) + } + "provider_discovery" => { + return json_command( + sentrux_lite_capabilities::provider_discovery_json(), + subcommand, + ) + } "check_rules" => sentrux_gate::run_check(repo), "check" => sentrux_gate::run_check_aligned(repo, true), "gate" => sentrux_gate::run_gate(repo, false), diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs index 22817bd1..cd705ac8 100644 --- a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -7,69 +7,201 @@ use crate::capability::{rfc3339_now, sha256_hex}; use super::{command_evidence, run_sentrux, SentruxCommand}; -const SENTRUX_CAPABILITY_COMMANDS: [(&str, &str); 7] = [ - ("sentrux.gate", "gate"), - ("sentrux.check", "check"), - ("sentrux.scan", "scan"), - ("sentrux.health", "health"), - ("sentrux.dsm", "dsm"), - ("sentrux.check_rules", "check_rules"), - ("sentrux.gate_save", "gate_save"), +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +enum RouteKind { + Command, + ReuseScan, + NotApplicable { + failure_kind: &'static str, + message: &'static str, + }, +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct CapabilityRoute { + capability_id: &'static str, + operation: &'static str, + command: &'static str, + kind: RouteKind, +} + +// This is the executor's canonical dispatch table. The matrix remains the +// inventory of record; keeping the table explicit here makes an omitted route +// observable in tests and in the emitted artifact set instead of becoming a +// silent loop omission. +const SENTRUX_CAPABILITY_ROUTES: [CapabilityRoute; 15] = [ + route("sentrux.gate", "gate", "gate", RouteKind::Command), + route("sentrux.check", "check", "check", RouteKind::Command), + route("sentrux.scan", "scan", "scan", RouteKind::Command), + route("sentrux.health", "health", "health", RouteKind::Command), + route("sentrux.dsm", "dsm", "dsm", RouteKind::Command), + route( + "sentrux.check_rules", + "check_rules", + "check_rules", + RouteKind::Command, + ), + route( + "sentrux.baseline_save", + "gate_save", + "gate_save", + RouteKind::NotApplicable { + failure_kind: "explicit_mutation_required", + message: "baseline_save mutates the repository baseline and requires explicit authority", + }, + ), + route( + "sentrux.rescan", + "rescan", + "rescan", + RouteKind::ReuseScan, + ), + route( + "sentrux.git_stats", + "git_stats", + "git_stats", + RouteKind::Command, + ), + route( + "sentrux.evolution", + "evolution", + "evolution", + RouteKind::Command, + ), + route( + "sentrux.test_gaps", + "test_gaps", + "test_gaps", + RouteKind::Command, + ), + route( + "sentrux.what_if", + "what_if", + "what_if", + RouteKind::NotApplicable { + failure_kind: "dag_scope_not_supported", + message: "what_if requires an explicit change set and is not applicable to this repository DAG snapshot", + }, + ), + route( + "sentrux.session_start", + "session_start", + "session_start", + RouteKind::NotApplicable { + failure_kind: "session_lifecycle_outside_dag", + message: "session_start is an agent lifecycle event and is not applicable to this repository DAG run", + }, + ), + route( + "sentrux.session_end", + "session_end", + "session_end", + RouteKind::NotApplicable { + failure_kind: "session_lifecycle_outside_dag", + message: "session_end is an agent lifecycle event and is not applicable to this repository DAG run", + }, + ), + route( + "sentrux.provider_discovery", + "provider_discovery", + "provider_discovery", + RouteKind::Command, + ), ]; +const fn route( + capability_id: &'static str, + operation: &'static str, + command: &'static str, + kind: RouteKind, +) -> CapabilityRoute { + CapabilityRoute { + capability_id, + operation, + command, + kind, + } +} + pub(super) fn collect_sentrux_capabilities( repo: &Path, tool_path_prefix: Option<&Path>, ) -> Result<(SentruxCommand, SentruxCommand, Vec), AdapterError> { let mut gate = None; let mut check = None; - let mut observations = Vec::with_capacity(SENTRUX_CAPABILITY_COMMANDS.len()); - for &(capability_id, subcommand) in &SENTRUX_CAPABILITY_COMMANDS { - if subcommand == "gate_save" { - observations.push(json!({ - "capabilityId":capability_id, - "operation":subcommand, - "providerMode":sentrux_provider_mode(tool_path_prefix), - "status":"not_run", - "verdict":"unknown", - "command":Value::Null, - "failure":{ - "kind":"explicit_mutation_required", - "message":"gate_save writes the repository baseline and requires explicit authority" + let mut observations = Vec::with_capacity(SENTRUX_CAPABILITY_ROUTES.len()); + for route in SENTRUX_CAPABILITY_ROUTES { + let observation = match route.kind { + RouteKind::NotApplicable { + failure_kind, + message, + } => not_applicable_observation(&route, tool_path_prefix, failure_kind, message), + RouteKind::ReuseScan => { + match run_sentrux( + repo, + tool_path_prefix, + if tool_path_prefix.is_some() { + route.command + } else { + "scan" + }, + ) { + Ok(command) => capability_observation( + &route, + tool_path_prefix, + &command, + Some("authoritative"), + ), + Err(error) => route_error_observation(&route, tool_path_prefix, &error), } - })); - continue; - } - let command = match run_sentrux(repo, tool_path_prefix, subcommand) { - Ok(command) => command, - Err(error) if matches!(subcommand, "gate" | "check") => return Err(error), - Err(error) => { - observations.push(json!({ - "capabilityId":capability_id, - "operation":subcommand, - "providerMode":sentrux_provider_mode(tool_path_prefix), - "status":"failed", - "verdict":"unknown", - "command":Value::Null, - "failure":{ - "kind":adapter_error_kind(&error), - "message":format!("{error:?}") + } + RouteKind::Command => { + if tool_path_prefix.is_none() + && !matches!( + route.command, + "gate" + | "check" + | "scan" + | "health" + | "dsm" + | "check_rules" + | "git_stats" + | "evolution" + | "test_gaps" + | "provider_discovery" + ) + { + unavailable_observation( + &route, + tool_path_prefix, + "the built-in Rust provider has no route for this capability", + ) + } else { + match run_sentrux(repo, tool_path_prefix, route.command) { + Ok(command) => capability_observation( + &route, + tool_path_prefix, + &command, + Some("authoritative"), + ), + Err(error) if matches!(route.command, "gate" | "check") => { + return Err(error) + } + Err(error) => route_error_observation(&route, tool_path_prefix, &error), } - })); - continue; + } } }; - observations.push(capability_observation( - capability_id, - subcommand, - tool_path_prefix, - &command, - )); - match subcommand { - "gate" => gate = Some(command), - "check" => check = Some(command), - _ => {} + if route.capability_id == "sentrux.gate" { + if let Some(command) = observation_command(&observation) { + gate = Some(command); + } + } else if route.capability_id == "sentrux.check" { + if let Some(command) = observation_command(&observation) { + check = Some(command); + } } + observations.push(observation); } Ok(( gate.ok_or_else(|| AdapterError::Internal("Sentrux gate observation is missing".into()))?, @@ -97,7 +229,7 @@ pub(super) fn build_capability_artifacts( })?; let raw_status = observation["status"].as_str().unwrap_or("failed"); let status = match raw_status { - "not_run" => "not_applicable", + "not_run" | "not_applicable" => "not_applicable", "succeeded" | "degraded" | "unavailable" | "skipped" | "failed" => raw_status, other => { return Err(AdapterError::Contract(format!( @@ -114,7 +246,7 @@ pub(super) fn build_capability_artifacts( "snapshotIdentity":snapshot_identity, "provider":provider.clone(), "status":status, - "authority":if status == "not_applicable" { "declared_only" } else { "authoritative" }, + "authority":artifact_authority(observation, status), "inputs":{"snapshotIdentity":snapshot_identity}, "outputs":{ "command":observation["command"], @@ -177,8 +309,11 @@ fn capability_failure(observation: &Value, status: &str) -> Value { } let raw_kind = observation["failure"]["kind"].as_str().unwrap_or("unknown"); let kind = match raw_kind { - "explicit_mutation_required" | "not_applicable" => "not_applicable", - "provider_unavailable" => "provider_unavailable", + "explicit_mutation_required" + | "dag_scope_not_supported" + | "session_lifecycle_outside_dag" + | "not_applicable" => "not_applicable", + "provider_unavailable" | "capability_unavailable" => "provider_unavailable", "contract_error" | "invalid_options" => "config_error", "io_error" => "local_tool_error", _ => "provider_error", @@ -206,10 +341,10 @@ fn sentrux_decision_consumers(capability_id: &str) -> Value { } fn capability_observation( - capability_id: &str, - subcommand: &str, + route: &CapabilityRoute, tool_path_prefix: Option<&Path>, command: &SentruxCommand, + authority: Option<&str>, ) -> Value { let (status, verdict, failure) = if command.success { ("succeeded", "pass", json!({"kind":"none"})) @@ -226,16 +361,105 @@ fn capability_observation( ) }; json!({ - "capabilityId":capability_id, - "operation":subcommand, + "capabilityId":route.capability_id, + "operation":route.operation, "providerMode":sentrux_provider_mode(tool_path_prefix), + "authority":authority.unwrap_or("compatibility"), "status":status, "verdict":verdict, - "command":command_evidence(subcommand, command), + "command":command_evidence(route.operation, command), "failure":failure }) } +fn not_applicable_observation( + route: &CapabilityRoute, + tool_path_prefix: Option<&Path>, + failure_kind: &str, + message: &str, +) -> Value { + json!({ + "capabilityId":route.capability_id, + "operation":route.operation, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "authority":"declared_only", + "status":"not_applicable", + "verdict":"unknown", + "command":Value::Null, + "failure":{"kind":failure_kind,"message":message} + }) +} + +fn unavailable_observation( + route: &CapabilityRoute, + tool_path_prefix: Option<&Path>, + message: &str, +) -> Value { + json!({ + "capabilityId":route.capability_id, + "operation":route.operation, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "authority":"compatibility", + "status":"unavailable", + "verdict":"unknown", + "command":Value::Null, + "failure":{"kind":"capability_unavailable","message":message} + }) +} + +fn route_error_observation( + route: &CapabilityRoute, + tool_path_prefix: Option<&Path>, + error: &AdapterError, +) -> Value { + let unavailable = matches!(error, AdapterError::Unavailable(_)); + json!({ + "capabilityId":route.capability_id, + "operation":route.operation, + "providerMode":sentrux_provider_mode(tool_path_prefix), + "authority":if unavailable { "compatibility" } else { "authoritative" }, + "status":if unavailable { "unavailable" } else { "failed" }, + "verdict":"unknown", + "command":Value::Null, + "failure":{ + "kind":if unavailable { "provider_unavailable" } else { adapter_error_kind(error) }, + "message":format!("{error:?}") + } + }) +} + +fn artifact_authority(observation: &Value, status: &str) -> &'static str { + match observation["authority"].as_str() { + Some("authoritative") => "authoritative", + Some("fallback") => "fallback", + Some("compatibility") => "compatibility", + Some("declared_only") => "declared_only", + _ => match status { + "succeeded" | "failed" => "authoritative", + "not_applicable" => "declared_only", + _ => "compatibility", + }, + } +} + +fn observation_command(observation: &Value) -> Option { + let command = observation["command"].as_object()?; + Some(SentruxCommand { + argv: command["argv"] + .as_array()? + .iter() + .filter_map(Value::as_str) + .map(str::to_owned) + .collect(), + exit_code: command["exitCode"].as_i64().map(|value| value as i32), + success: command["success"].as_bool().unwrap_or(false), + stdout: command["stdout"].as_str().unwrap_or_default().to_owned(), + stderr: command["stderr"].as_str().unwrap_or_default().to_owned(), + violations: Vec::new(), + governed: true, + }) +} + fn sentrux_provider_mode(tool_path_prefix: Option<&Path>) -> &'static str { if tool_path_prefix.is_some() { "external" diff --git a/crates/code-intel-cli/src/sentrux_lite_capabilities.rs b/crates/code-intel-cli/src/sentrux_lite_capabilities.rs new file mode 100644 index 00000000..1d2af528 --- /dev/null +++ b/crates/code-intel-cli/src/sentrux_lite_capabilities.rs @@ -0,0 +1,122 @@ +use std::fs; +use std::path::Path; +use std::process::Command; + +use serde_json::{json, Value}; + +pub(super) fn provider_discovery_json() -> Result { + Ok(json!({ + "provider":"sentrux", + "mode":"builtin_lite", + "available":true, + "operations":["scan","health","dsm","git_stats","evolution","test_gaps","check_rules","check","gate","rescan"], + "aliases":["pro_status","plugin_list","plugin_validate"], + "legacyFallback":"legacy/Invoke-SentruxAgentTool.ps1" + })) +} + +fn git_command(repo: &Path, args: &[&str]) -> Result { + let output = Command::new("git") + .args(args) + .current_dir(repo) + .output() + .map_err(|error| format!("start git {}: {error}", args.join(" ")))?; + if !output.status.success() { + return Err(format!( + "git {} failed: {}", + args.join(" "), + String::from_utf8_lossy(&output.stderr).trim() + )); + } + String::from_utf8(output.stdout).map_err(|error| format!("git output is not UTF-8: {error}")) +} + +pub(super) fn git_stats_json(repo: &Path) -> Result { + let count = git_command(repo, &["rev-list", "--count", "HEAD"])? + .trim() + .parse::() + .map_err(|error| format!("git commit count is invalid: {error}"))?; + let recent = git_command(repo, &["log", "-n", "20", "--format=%H%x09%aI"])? + .lines() + .filter_map(|line| { + let (commit, authored_at) = line.split_once('\t')?; + Some(json!({"commit":commit,"authoredAt":authored_at})) + }) + .collect::>(); + Ok(json!({"commitCount":count,"recentCommits":recent,"status":"ok"})) +} + +pub(super) fn evolution_json(repo: &Path) -> Result { + let recent = git_command(repo, &["log", "-n", "20", "--format=%H%x09%aI%x09%an"])? + .lines() + .filter_map(|line| { + let mut fields = line.splitn(3, '\t'); + Some(json!({ + "commit":fields.next()?, + "authoredAt":fields.next()?, + "author":fields.next()? + })) + }) + .collect::>(); + Ok(json!({ + "status":"ok", + "windowCommits":recent.len(), + "trend":"observed", + "recentCommits":recent + })) +} + +pub(super) fn test_gaps_json(repo: &Path) -> Result { + let mut source_files = 0_u64; + let mut test_files = 0_u64; + let mut stack = vec![repo.to_path_buf()]; + while let Some(directory) = stack.pop() { + for entry in fs::read_dir(&directory).map_err(|error| { + format!( + "read test inventory directory {}: {error}", + directory.display() + ) + })? { + let entry = entry.map_err(|error| format!("read test inventory entry: {error}"))?; + let path = entry.path(); + let name = entry.file_name().to_string_lossy().to_ascii_lowercase(); + if entry + .file_type() + .map_err(|error| format!("inspect test inventory entry: {error}"))? + .is_dir() + { + if !matches!(name.as_str(), ".git" | "target" | "node_modules") { + stack.push(path); + } + continue; + } + let extension = path + .extension() + .and_then(|value| value.to_str()) + .unwrap_or(""); + if !matches!( + extension, + "rs" | "py" | "js" | "ts" | "tsx" | "go" | "java" | "cs" + ) { + continue; + } + let relative = path + .strip_prefix(repo) + .unwrap_or(&path) + .to_string_lossy() + .to_ascii_lowercase(); + if relative.contains("test") || relative.contains("spec") { + test_files += 1; + } else { + source_files += 1; + } + } + } + Ok(json!({ + "status":"heuristic", + "sourceFiles":source_files, + "testFiles":test_files, + "gapStatus":if test_files == 0 { "unknown" } else { "inventory_only" }, + "limitations":["This lite fallback inventories test files; it does not prove symbol-level test coverage."] + })) +} diff --git a/tests/test_sentrux_capability_executor.py b/tests/test_sentrux_capability_executor.py new file mode 100644 index 00000000..c32d77d3 --- /dev/null +++ b/tests/test_sentrux_capability_executor.py @@ -0,0 +1,49 @@ +from __future__ import annotations + +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] +EXECUTOR = ROOT / "crates" / "code-intel-cli" / "src" / "sentrux_capability_artifacts.rs" + + +class SentruxCapabilityExecutorTests(unittest.TestCase): + @classmethod + def setUpClass(cls) -> None: + cls.source = EXECUTOR.read_text(encoding="utf-8") + + def test_dispatch_table_names_every_remaining_canonical_capability(self) -> None: + for capability in ( + "sentrux.provider_discovery", + "sentrux.rescan", + "sentrux.git_stats", + "sentrux.evolution", + "sentrux.test_gaps", + "sentrux.what_if", + "sentrux.session_start", + "sentrux.session_end", + "sentrux.baseline_save", + ): + with self.subTest(capability=capability): + self.assertIn(f'"{capability}"', self.source) + + def test_dag_unsupported_routes_are_explicitly_non_applicable(self) -> None: + self.assertIn("RouteKind::NotApplicable", self.source) + for failure_kind in ( + "explicit_mutation_required", + "dag_scope_not_supported", + "session_lifecycle_outside_dag", + ): + with self.subTest(failure_kind=failure_kind): + self.assertIn(f'failure_kind: "{failure_kind}"', self.source) + self.assertIn('"authority":"declared_only"', self.source) + + def test_unimplemented_builtin_routes_are_not_reported_as_success(self) -> None: + self.assertIn('"status":"unavailable"', self.source) + self.assertIn('"authority":"compatibility"', self.source) + self.assertIn("capability_unavailable", self.source) + + +if __name__ == "__main__": + unittest.main() From 03e0290dd80c370785300f5c6ae2e3a1429c6dc1 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:43:09 +0800 Subject: [PATCH 11/27] feat(impact): surface verified Sentrux test signals --- crates/code-intel-cli/src/change_impact.rs | 150 ++++++++++++++++++ crates/code-intel-cli/tests/dag_run.rs | 29 ++++ .../code-intel-change-impact.v1.schema.json | 32 +++- 3 files changed, 208 insertions(+), 3 deletions(-) diff --git a/crates/code-intel-cli/src/change_impact.rs b/crates/code-intel-cli/src/change_impact.rs index f57e4cfd..fa9c1653 100644 --- a/crates/code-intel-cli/src/change_impact.rs +++ b/crates/code-intel-cli/src/change_impact.rs @@ -298,6 +298,7 @@ fn build_result( }) .collect::>(); let (sentrux_evidence_refs, sentrux_evidence) = sentrux_evidence(evidence, stale); + let sentrux_signals = sentrux_test_selection_signals(evidence, stale); let mut result = json!({ "schema":"code-intel-change-impact.v1", "repo":cli.repo, @@ -321,6 +322,7 @@ fn build_result( "commands":commands, "advisoryOnly":true, "rationale":"Select impacted test files reachable through the verified snapshot's reverse import graph; use same-module test co-location only as a fallback.", + "sentruxSignals":sentrux_signals, }, "limitations":[ "Native import extraction is heuristic and does not prove runtime call paths.", @@ -392,6 +394,117 @@ fn sentrux_evidence(evidence: &CommittedEvidence, stale: bool) -> (Vec, V } } +/// Consume only the JSON payloads already verified by the committed manifest. +/// +/// The capability payload contains command provenance for audit purposes, but +/// this projection intentionally never reads `outputs.command.stdout`. The +/// current lite `test_gaps` and DSM routes do not publish structured test +/// candidates, so their status is useful for deciding whether to expand the +/// graph-selected candidates, not for inventing new paths from provider text. +fn sentrux_test_selection_signals(evidence: &CommittedEvidence, stale: bool) -> Value { + let payloads = evidence + .refs + .iter() + .zip(evidence.verified.iter()) + .filter(|(reference, _)| { + reference["artifactSchema"] == SENTRUX_CAPABILITY_ARTIFACT_SCHEMA + && reference["type"] == SENTRUX_CAPABILITY_ARTIFACT_TYPE + }) + .filter_map(|(_, verified)| serde_json::from_slice::(verified.bytes()).ok()) + .collect::>(); + let test_gap_payload = payloads + .iter() + .find(|payload| payload["capabilityId"] == "sentrux.test_gaps"); + let dsm_payload = payloads + .iter() + .find(|payload| payload["capabilityId"] == "sentrux.dsm"); + let test_gap = sentrux_signal("test_gaps", test_gap_payload); + let dsm = sentrux_signal("dsm", dsm_payload); + let has_signal = test_gap["status"] != "unknown" || dsm["status"] != "unknown"; + let all_available = test_gap["status"] == "available" && dsm["status"] == "available"; + let candidate_test_impact = if !has_signal { + "unknown" + } else if all_available && !stale { + "retains_graph_candidates" + } else { + "withholds_sentrux_expansion" + }; + let status = if !has_signal { + "unknown" + } else if all_available && !stale { + "available" + } else { + "advisory" + }; + let mut limitations = vec![ + "Sentrux signals are advisory and never execute tests or gate this impact result." + .to_string(), + "Only committed-manifest capability payloads verified against the snapshot are consumed." + .to_string(), + ]; + if stale { + limitations.push( + "Sentrux signals are stale-advisory because the committed snapshot differs from the current checkout." + .to_string(), + ); + } + for signal in [&test_gap, &dsm] { + if let Some(items) = signal["limitations"].as_array() { + limitations.extend(items.iter().filter_map(Value::as_str).map(str::to_owned)); + } + } + limitations.sort(); + limitations.dedup(); + json!({ + "status":status, + "testGap":test_gap, + "dsm":dsm, + "candidateTestImpact":candidate_test_impact, + "limitations":limitations, + }) +} + +fn sentrux_signal(name: &str, payload: Option<&Value>) -> Value { + let Some(payload) = payload else { + return json!({ + "status":"unknown", + "capabilityStatus":"missing", + "authority":"unknown", + "candidateImpact":"unknown", + "limitations":[format!("No verified sentrux.{name} capability artifact payload is present in the committed manifest.")], + }); + }; + let capability_status = payload["status"].as_str().unwrap_or("unknown"); + let authority = payload["authority"].as_str().unwrap_or("unknown"); + let available = capability_status == "succeeded" + && matches!(authority, "authoritative" | "fallback") + && payload["freshness"]["status"] == "current"; + let status = if available { "available" } else { "degraded" }; + let candidate_impact = if available { + "retains_graph_candidates" + } else { + "withholds_sentrux_expansion" + }; + let limitation = match name { + "test_gaps" if available => { + "The verified test_gaps payload exposes no structured candidate test paths; graph-selected candidates are retained and no new tests are auto-added." + } + "dsm" if available => { + "The verified DSM payload exposes no structured test-selection mapping; the DSM signal is advisory and does not auto-add tests." + } + _ => payload["failure"]["message"].as_str().unwrap_or( + "The verified capability payload is not successful enough to expand candidate tests.", + ), + }; + json!({ + "status":status, + "capabilityStatus":capability_status, + "authority":authority, + "candidateImpact":candidate_impact, + "limitations":[limitation], + }) +} + fn normalize_relative(path: &str) -> Result { let path = path.replace('\\', "/"); if path.is_empty() @@ -445,4 +558,41 @@ mod tests { .as_str() .is_some_and(|text| text.contains("advisory/unknown")))); } + + #[test] + fn sentrux_test_selection_does_not_parse_provider_stdout() { + let payload = json!({ + "capabilityId":"sentrux.test_gaps", + "status":"succeeded", + "authority":"authoritative", + "freshness":{"status":"current"}, + "outputs":{"command":{"stdout":"{\"candidateTests\":[\"tests/forged.rs\"]}"}} + }); + + let signal = sentrux_signal("test_gaps", Some(&payload)); + + assert_eq!(signal["status"], "available"); + assert_eq!(signal["candidateImpact"], "retains_graph_candidates"); + assert!(signal["limitations"][0] + .as_str() + .unwrap() + .contains("no structured candidate test paths")); + } + + #[test] + fn missing_sentrux_test_selection_signals_are_unknown() { + let evidence = CommittedEvidence { + entry: Value::Null, + refs: Vec::new(), + verified: Vec::new(), + run_root: std::path::PathBuf::new(), + }; + + let signals = sentrux_test_selection_signals(&evidence, false); + + assert_eq!(signals["status"], "unknown"); + assert_eq!(signals["candidateTestImpact"], "unknown"); + assert_eq!(signals["testGap"]["status"], "unknown"); + assert_eq!(signals["dsm"]["status"], "unknown"); + } } diff --git a/crates/code-intel-cli/tests/dag_run.rs b/crates/code-intel-cli/tests/dag_run.rs index 60adeec3..d4cb2170 100644 --- a/crates/code-intel-cli/tests/dag_run.rs +++ b/crates/code-intel-cli/tests/dag_run.rs @@ -351,6 +351,35 @@ fn production_dag_output_commits_and_enters_the_authoritative_index() { reference["artifactSchema"] == "code-intel-sentrux-capability-artifact.v1" && reference["type"] == "provider.sentrux.capability-artifact" })); + assert_eq!( + impact["testSelection"]["sentruxSignals"]["status"], + "advisory" + ); + assert_eq!( + impact["testSelection"]["sentruxSignals"]["testGap"]["status"], + "available" + ); + assert_eq!( + impact["testSelection"]["sentruxSignals"]["testGap"]["capabilityStatus"], + "succeeded" + ); + assert_eq!( + impact["testSelection"]["sentruxSignals"]["dsm"]["status"], + "degraded" + ); + assert_eq!( + impact["testSelection"]["sentruxSignals"]["candidateTestImpact"], + "withholds_sentrux_expansion" + ); + assert!( + impact["testSelection"]["sentruxSignals"]["testGap"]["limitations"] + .as_array() + .unwrap() + .iter() + .any(|item| item + .as_str() + .is_some_and(|text| text.contains("no structured candidate test paths"))) + ); assert_eq!( impact["testSelection"]["files"], json!(["tests/lib_test.rs"]) diff --git a/orchestration/schemas/code-intel-change-impact.v1.schema.json b/orchestration/schemas/code-intel-change-impact.v1.schema.json index 9423d497..ef1bdcd4 100644 --- a/orchestration/schemas/code-intel-change-impact.v1.schema.json +++ b/orchestration/schemas/code-intel-change-impact.v1.schema.json @@ -90,16 +90,42 @@ "testSelection": { "type": "object", "additionalProperties": false, - "required": ["status", "files", "commands", "advisoryOnly", "rationale"], + "required": ["status", "files", "commands", "advisoryOnly", "rationale", "sentruxSignals"], "properties": { "status": { "enum": ["candidates", "none"] }, "files": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, "commands": { "type": "array", "items": { "type": "string", "minLength": 1 }, "uniqueItems": true }, "advisoryOnly": { "const": true }, - "rationale": { "type": "string", "minLength": 1 } + "rationale": { "type": "string", "minLength": 1 }, + "sentruxSignals": { + "type": "object", + "additionalProperties": false, + "required": ["status", "testGap", "dsm", "candidateTestImpact", "limitations"], + "properties": { + "status": { "enum": ["available", "advisory", "unknown"] }, + "testGap": { "$ref": "#/$defs/sentruxSignal" }, + "dsm": { "$ref": "#/$defs/sentruxSignal" }, + "candidateTestImpact": { "enum": ["retains_graph_candidates", "withholds_sentrux_expansion", "unknown"] }, + "limitations": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + } + } } }, "limitations": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } }, - "$defs": { "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" } } + "$defs": { + "digest": { "type": "string", "pattern": "^[a-f0-9]{64}$" }, + "sentruxSignal": { + "type": "object", + "additionalProperties": false, + "required": ["status", "capabilityStatus", "authority", "candidateImpact", "limitations"], + "properties": { + "status": { "enum": ["available", "degraded", "unknown"] }, + "capabilityStatus": { "enum": ["succeeded", "degraded", "unavailable", "skipped", "failed", "not_applicable", "missing", "unknown"] }, + "authority": { "enum": ["authoritative", "fallback", "compatibility", "declared_only", "unknown"] }, + "candidateImpact": { "enum": ["retains_graph_candidates", "withholds_sentrux_expansion", "unknown"] }, + "limitations": { "type": "array", "minItems": 1, "items": { "type": "string", "minLength": 1 }, "uniqueItems": true } + } + } + } } From 219a021efb37351862be976bff15497c3b2ebaed Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 00:51:24 +0800 Subject: [PATCH 12/27] feat(sentrux): make capability dispatch bounded and auditable --- .../src/builtin_provider_evidence.rs | 102 +--------- .../src/sentrux_capability_artifacts.rs | 59 +++++- crates/code-intel-cli/src/sentrux_command.rs | 174 ++++++++++++++++++ ...sentrux-capability-artifact.v1.schema.json | 35 ++++ .../sentrux-capability-matrix.v1.json | 98 +++++----- tests/test_sentrux_capability_matrix.py | 19 ++ 6 files changed, 335 insertions(+), 152 deletions(-) create mode 100644 crates/code-intel-cli/src/sentrux_command.rs diff --git a/crates/code-intel-cli/src/builtin_provider_evidence.rs b/crates/code-intel-cli/src/builtin_provider_evidence.rs index ebb7cf12..727c32c9 100644 --- a/crates/code-intel-cli/src/builtin_provider_evidence.rs +++ b/crates/code-intel-cli/src/builtin_provider_evidence.rs @@ -1,6 +1,6 @@ use std::fs; use std::path::{Path, PathBuf}; -use std::process::{Command, Output}; +use std::process::Command; use std::time::{SystemTime, UNIX_EPOCH}; use serde_json::{json, Value}; @@ -33,15 +33,17 @@ mod sentrux_adapter; mod sentrux_analysis; #[path = "sentrux_capability_artifacts.rs"] mod sentrux_capability_artifacts; +#[path = "sentrux_command.rs"] +mod sentrux_command; #[path = "sentrux_gate.rs"] mod sentrux_gate; #[path = "sentrux_lite_capabilities.rs"] mod sentrux_lite_capabilities; use codenexus_scratch::{create_codenexus_scratch_dir, ScratchDir}; +pub(super) use sentrux_command::{command_evidence, SentruxCommand}; use sentrux_gate::Violation; const MAX_AGE_SECONDS: u64 = 300; -const MAX_COMMAND_EVIDENCE_BYTES: usize = 1024 * 1024; // The codenexus-domain effect vocabulary validated by // `codenexus_adapter::validate_native` -- distinct from the generic // repo_read/local_write/process_spawn effects `publish_admission` reports at @@ -458,64 +460,6 @@ fn sentrux_provider_options<'a>( Ok((repo, tool_path_prefix)) } -struct SentruxCommand { - argv: Vec, - exit_code: Option, - success: bool, - stdout: String, - stderr: String, - violations: Vec, - governed: bool, -} - -impl SentruxCommand { - fn from_native(run: sentrux_gate::EngineRun, subcommand: &str) -> Self { - Self { - argv: vec![ - "code-intel".into(), - "sentrux".into(), - subcommand.into(), - ".".into(), - ], - exit_code: Some(if run.success { 0 } else { 1 }), - success: run.success, - stdout: run.stdout, - stderr: String::new(), - violations: run.violations, - governed: run.governed, - } - } - - fn from_external(output: Output, subcommand: &str) -> Self { - let stdout = String::from_utf8_lossy(&output.stdout).into_owned(); - let violations = if output.status.success() { - Vec::new() - } else { - stdout - .lines() - .filter_map(|line| line.strip_prefix("- ")) - .map(str::trim) - .filter(|message| !message.is_empty()) - .take(32) - .map(|message| Violation { - rule: format!("sentrux_{subcommand}"), - message: message.chars().take(1024).collect(), - targets: Vec::new(), - }) - .collect() - }; - Self { - argv: vec!["sentrux".into(), subcommand.into(), ".".into()], - exit_code: output.status.code(), - success: output.status.success(), - stdout, - stderr: String::from_utf8_lossy(&output.stderr).into_owned(), - violations, - governed: true, - } - } -} - fn run_sentrux( repo: &Path, tool_path_prefix: Option<&Path>, @@ -583,13 +527,6 @@ fn run_sentrux( SentruxCommand::from_native(run, subcommand) } }; - if command.stdout.len() > MAX_COMMAND_EVIDENCE_BYTES - || command.stderr.len() > MAX_COMMAND_EVIDENCE_BYTES - { - return Err(AdapterError::Contract(format!( - "Sentrux {subcommand} output exceeds the bounded evidence limit" - ))); - } Ok(command) } @@ -602,25 +539,7 @@ fn json_command( let stdout = serde_json::to_string_pretty(&value).map_err(|error| { AdapterError::Internal(format!("serialize Sentrux {subcommand}: {error}")) })?; - if stdout.len() > MAX_COMMAND_EVIDENCE_BYTES { - return Err(AdapterError::Contract(format!( - "Sentrux {subcommand} output exceeds the bounded evidence limit" - ))); - } - Ok(SentruxCommand { - argv: vec![ - "code-intel".into(), - "sentrux".into(), - subcommand.into(), - ".".into(), - ], - exit_code: Some(0), - success: true, - stdout, - stderr: String::new(), - violations: Vec::new(), - governed: true, - }) + Ok(SentruxCommand::from_json(stdout.into_bytes(), subcommand)) } fn sentrux_health_json(repo: &Path) -> Result { @@ -673,17 +592,6 @@ fn resolve_sentrux(prefix: &Path) -> Result { }) } -fn command_evidence(subcommand: &str, command: &SentruxCommand) -> Value { - json!({ - "id":subcommand, - "argv":command.argv, - "exitCode":command.exit_code, - "success":command.success, - "stdout":command.stdout, - "stderr":command.stderr - }) -} - fn command_rule(kind: &str, command: &SentruxCommand) -> Value { let verdict = if command.success || !command.governed { "pass" diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs index cd705ac8..b322bb56 100644 --- a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -250,7 +250,8 @@ pub(super) fn build_capability_artifacts( "inputs":{"snapshotIdentity":snapshot_identity}, "outputs":{ "command":observation["command"], - "verdict":observation["verdict"] + "verdict":observation["verdict"], + "outputSummary":observation["outputSummary"] }, "failure":capability_failure(observation, status), "freshness":{ @@ -335,8 +336,28 @@ fn sentrux_decision_consumers(capability_id: &str) -> Value { "sentrux.gate" | "sentrux.check" => { json!(["diagnosis.hospital", "pr_gate", "release_gate"]) } - "sentrux.gate_save" => json!(["sentrux.gate"]), - _ => json!(["evidence.sentrux", "diagnosis.hospital"]), + "sentrux.baseline_save" => json!(["sentrux.gate"]), + "sentrux.dsm" | "sentrux.test_gaps" => json!([ + "evidence.sentrux", + "diagnosis.hospital", + "report", + "change_impact", + "test_selection", + "pr_gate", + "release_gate" + ]), + "sentrux.what_if" => json!(["change_impact", "pr_gate", "release_gate"]), + "sentrux.session_start" => json!(["sentrux.rescan", "sentrux.session_end"]), + "sentrux.session_end" => json!(["pr_gate", "release_gate"]), + "sentrux.provider_discovery" => { + json!(["doctor", "run_planner", "install_smoke", "release_gate"]) + } + _ => json!([ + "evidence.sentrux", + "diagnosis.hospital", + "report", + "release_gate" + ]), } } @@ -346,7 +367,19 @@ fn capability_observation( command: &SentruxCommand, authority: Option<&str>, ) -> Value { - let (status, verdict, failure) = if command.success { + let (status, verdict, failure) = if command.success && !command.output_summary.complete() { + ( + "degraded", + "unknown", + json!({ + "kind":"degraded", + "message":format!( + "Sentrux {} output exceeded the bounded evidence limit; only metadata and preview were retained", + route.operation + ) + }), + ) + } else if command.success { ("succeeded", "pass", json!({"kind":"none"})) } else if !command.governed { ("succeeded", "unknown", json!({"kind":"none"})) @@ -368,6 +401,7 @@ fn capability_observation( "status":status, "verdict":verdict, "command":command_evidence(route.operation, command), + "outputSummary":command.output_summary.to_json(&command.stdout, &command.stderr), "failure":failure }) } @@ -444,6 +478,18 @@ fn artifact_authority(observation: &Value, status: &str) -> &'static str { fn observation_command(observation: &Value) -> Option { let command = observation["command"].as_object()?; + let stdout = command["stdout"].as_str().unwrap_or_default().to_owned(); + let stderr = command["stderr"].as_str().unwrap_or_default().to_owned(); + let output_summary = command["outputSummary"] + .as_object() + .and_then(|summary| { + Some(super::sentrux_command::OutputSummary::from_metadata( + summary, + )) + }) + .unwrap_or_else(|| { + super::sentrux_command::OutputSummary::from_bytes(stdout.as_bytes(), stderr.as_bytes()) + }); Some(SentruxCommand { argv: command["argv"] .as_array()? @@ -453,10 +499,11 @@ fn observation_command(observation: &Value) -> Option { .collect(), exit_code: command["exitCode"].as_i64().map(|value| value as i32), success: command["success"].as_bool().unwrap_or(false), - stdout: command["stdout"].as_str().unwrap_or_default().to_owned(), - stderr: command["stderr"].as_str().unwrap_or_default().to_owned(), + stdout, + stderr, violations: Vec::new(), governed: true, + output_summary, }) } diff --git a/crates/code-intel-cli/src/sentrux_command.rs b/crates/code-intel-cli/src/sentrux_command.rs new file mode 100644 index 00000000..9b0df587 --- /dev/null +++ b/crates/code-intel-cli/src/sentrux_command.rs @@ -0,0 +1,174 @@ +use std::process::Output; + +use serde_json::{json, Value}; + +use super::sentrux_gate::Violation; +use crate::capability::sha256_hex; + +pub(crate) const MAX_COMMAND_EVIDENCE_BYTES: usize = 1024 * 1024; +const MAX_COMMAND_PREVIEW_BYTES: usize = 8 * 1024; + +pub(crate) struct SentruxCommand { + pub(crate) argv: Vec, + pub(crate) exit_code: Option, + pub(crate) success: bool, + pub(crate) stdout: String, + pub(crate) stderr: String, + pub(crate) violations: Vec, + pub(crate) governed: bool, + pub(crate) output_summary: OutputSummary, +} + +#[derive(Clone, Debug)] +pub(crate) struct OutputSummary { + stdout_bytes: usize, + stdout_sha256: String, + stderr_bytes: usize, + stderr_sha256: String, +} + +impl OutputSummary { + pub(crate) fn from_bytes(stdout: &[u8], stderr: &[u8]) -> Self { + Self { + stdout_bytes: stdout.len(), + stdout_sha256: sha256_hex(stdout), + stderr_bytes: stderr.len(), + stderr_sha256: sha256_hex(stderr), + } + } + + pub(crate) fn complete(&self) -> bool { + self.stdout_bytes <= MAX_COMMAND_EVIDENCE_BYTES + && self.stderr_bytes <= MAX_COMMAND_EVIDENCE_BYTES + } + + pub(crate) fn to_json(&self, stdout_preview: &str, stderr_preview: &str) -> Value { + json!({ + "authority":"metadata_only", + "complete":self.complete(), + "bounded":!self.complete(), + "limitBytes":MAX_COMMAND_EVIDENCE_BYTES, + "totalBytes":self.stdout_bytes + self.stderr_bytes, + "stdout":{ + "bytes":self.stdout_bytes, + "sha256":self.stdout_sha256, + "preview":stdout_preview, + "previewBytes":stdout_preview.len() + }, + "stderr":{ + "bytes":self.stderr_bytes, + "sha256":self.stderr_sha256, + "preview":stderr_preview, + "previewBytes":stderr_preview.len() + }, + "note":"preview is non-authoritative; consumers must use the artifact metadata" + }) + } + + pub(crate) fn from_metadata(summary: &serde_json::Map) -> Self { + fn digest(summary: &serde_json::Map, stream: &str) -> String { + summary[stream]["sha256"] + .as_str() + .unwrap_or_default() + .to_owned() + } + fn bytes(summary: &serde_json::Map, stream: &str) -> usize { + summary[stream]["bytes"].as_u64().unwrap_or(0) as usize + } + Self { + stdout_bytes: bytes(summary, "stdout"), + stdout_sha256: digest(summary, "stdout"), + stderr_bytes: bytes(summary, "stderr"), + stderr_sha256: digest(summary, "stderr"), + } + } +} + +fn bounded_text(bytes: &[u8]) -> String { + let end = bytes.len().min(MAX_COMMAND_PREVIEW_BYTES); + String::from_utf8_lossy(&bytes[..end]).into_owned() +} + +impl SentruxCommand { + pub(crate) fn from_native(run: super::sentrux_gate::EngineRun, subcommand: &str) -> Self { + let stdout_bytes = run.stdout.into_bytes(); + let output_summary = OutputSummary::from_bytes(&stdout_bytes, &[]); + Self { + argv: vec![ + "code-intel".into(), + "sentrux".into(), + subcommand.into(), + ".".into(), + ], + exit_code: Some(if run.success { 0 } else { 1 }), + success: run.success, + stdout: bounded_text(&stdout_bytes), + stderr: String::new(), + violations: run.violations, + governed: run.governed, + output_summary, + } + } + + pub(crate) fn from_external(output: Output, subcommand: &str) -> Self { + let output_summary = OutputSummary::from_bytes(&output.stdout, &output.stderr); + let stdout_full = String::from_utf8_lossy(&output.stdout).into_owned(); + let violations = if output.status.success() { + Vec::new() + } else { + stdout_full + .lines() + .filter_map(|line| line.strip_prefix("- ")) + .map(str::trim) + .filter(|message| !message.is_empty()) + .take(32) + .map(|message| Violation { + rule: format!("sentrux_{subcommand}"), + message: message.chars().take(1024).collect(), + targets: Vec::new(), + }) + .collect() + }; + Self { + argv: vec!["sentrux".into(), subcommand.into(), ".".into()], + exit_code: output.status.code(), + success: output.status.success(), + stdout: bounded_text(&output.stdout), + stderr: bounded_text(&output.stderr), + violations, + governed: true, + output_summary, + } + } + + pub(crate) fn from_json(stdout: Vec, subcommand: &str) -> Self { + let output_summary = OutputSummary::from_bytes(&stdout, &[]); + Self { + argv: vec![ + "code-intel".into(), + "sentrux".into(), + subcommand.into(), + ".".into(), + ], + exit_code: Some(0), + success: true, + stdout: bounded_text(&stdout), + stderr: String::new(), + violations: Vec::new(), + governed: true, + output_summary, + } + } +} + +pub(crate) fn command_evidence(subcommand: &str, command: &SentruxCommand) -> Value { + json!({ + "id":subcommand, + "argv":command.argv, + "exitCode":command.exit_code, + "success":command.success, + "stdout":command.stdout, + "stderr":command.stderr, + "outputSummary":command.output_summary.to_json(&command.stdout, &command.stderr) + }) +} diff --git a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json index 836efe0e..64612fda 100644 --- a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json +++ b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json @@ -45,7 +45,16 @@ "inputs": { "type": "object" }, "outputs": { "type": "object", + "additionalProperties": false, "properties": { + "command": { "type": ["object", "null"] }, + "verdict": { "enum": ["pass", "fail", "unknown"] }, + "outputSummary": { + "oneOf": [ + { "$ref": "#/$defs/outputSummary" }, + { "type": "null" } + ] + }, "artifacts": { "type": "array", "items": { "$ref": "#/$defs/artifactRef" }, @@ -189,6 +198,32 @@ "evaluatedAt": { "type": "string", "format": "date-time" }, "consumedSnapshotIdentity": { "$ref": "#/$defs/sha256" } } + }, + "outputSummary": { + "type": "object", + "additionalProperties": false, + "required": ["authority", "complete", "bounded", "limitBytes", "totalBytes", "stdout", "stderr", "note"], + "properties": { + "authority": { "const": "metadata_only" }, + "complete": { "type": "boolean" }, + "bounded": { "type": "boolean" }, + "limitBytes": { "type": "integer", "minimum": 1 }, + "totalBytes": { "type": "integer", "minimum": 0 }, + "stdout": { "$ref": "#/$defs/outputStream" }, + "stderr": { "$ref": "#/$defs/outputStream" }, + "note": { "$ref": "#/$defs/nonEmpty" } + } + }, + "outputStream": { + "type": "object", + "additionalProperties": false, + "required": ["bytes", "sha256", "preview", "previewBytes"], + "properties": { + "bytes": { "type": "integer", "minimum": 0 }, + "sha256": { "$ref": "#/$defs/sha256" }, + "preview": { "type": "string" }, + "previewBytes": { "type": "integer", "minimum": 0 } + } } } } diff --git a/orchestration/sentrux-capability-matrix.v1.json b/orchestration/sentrux-capability-matrix.v1.json index 98984b0a..5688c895 100644 --- a/orchestration/sentrux-capability-matrix.v1.json +++ b/orchestration/sentrux-capability-matrix.v1.json @@ -21,12 +21,12 @@ "aliases": ["sentrux_scan"], "scope": "core_analysis", "trigger": "repository_run", - "route": "rust_native_manual", - "currentState": "native_manual", + "route": "provider.sentrux-adapt/builtin_bounded", + "currentState": "automatic_degraded", "requiredForRelease": true, "inputs": ["repository.snapshot"], - "artifacts": ["sentrux-scan.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "report", "pr_gate", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -36,12 +36,12 @@ "aliases": ["sentrux_health"], "scope": "core_analysis", "trigger": "repository_run", - "route": "rust_native_manual", - "currentState": "native_manual", + "route": "provider.sentrux-adapt/builtin_lite", + "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["sentrux.scan.v1"], - "artifacts": ["sentrux-health.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "report", "pr_gate", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -51,12 +51,12 @@ "aliases": ["sentrux_dsm"], "scope": "core_analysis", "trigger": "repository_run", - "route": "rust_native_manual", - "currentState": "native_manual", + "route": "provider.sentrux-adapt/builtin_bounded", + "currentState": "automatic_degraded", "requiredForRelease": true, "inputs": ["repository.snapshot"], - "artifacts": ["sentrux-dsm.v1", "sentrux-hotspots.v1", "sentrux-file-details.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "report", "change_impact", "test_selection", "pr_gate", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -66,12 +66,12 @@ "aliases": ["sentrux_git_stats"], "scope": "history", "trigger": "repository_run", - "route": "legacy_compatibility", - "currentState": "compatibility_only", + "route": "provider.sentrux-adapt/builtin_lite", + "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "git.history"], - "artifacts": ["sentrux-git-stats.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["doctor", "run_planner", "install_smoke", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -81,12 +81,12 @@ "aliases": [], "scope": "history", "trigger": "repository_run", - "route": "legacy_compatibility", - "currentState": "compatibility_only", + "route": "provider.sentrux-adapt/builtin_lite", + "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "git.history"], - "artifacts": ["sentrux-evolution.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "report", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -96,12 +96,12 @@ "aliases": ["sentrux_test_gaps"], "scope": "test_selection", "trigger": "repository_run", - "route": "legacy_compatibility", - "currentState": "compatibility_only", + "route": "provider.sentrux-adapt/builtin_lite", + "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.dsm.v1", "test.inventory"], - "artifacts": ["sentrux-test-gaps.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "report", "change_impact", "test_selection", "pr_gate", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -111,12 +111,12 @@ "aliases": [], "scope": "change_prediction", "trigger": "change_or_pr", - "route": "legacy_compatibility", - "currentState": "compatibility_only", + "route": "explicit_change_set", + "currentState": "not_applicable_dag", "requiredForRelease": true, "inputs": ["repository.snapshot", "change.set", "sentrux.dsm.v1"], - "artifacts": ["sentrux-what-if.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["change_impact", "pr_gate", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -126,12 +126,12 @@ "aliases": ["rules"], "scope": "rules", "trigger": "repository_run", - "route": "rust_native_manual", - "currentState": "native_manual", + "route": "provider.sentrux-adapt/builtin_native", + "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.rules"], - "artifacts": ["sentrux-check-rules.v1"], - "decisionConsumers": [], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "report", "pr_gate", "release_gate"], "failureSemantics": ["domain_fail", "failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -145,7 +145,7 @@ "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.rules", "sentrux.baseline"], - "artifacts": ["code-intel-sentrux-command-observation.v1", "code-intel-structural-evidence-payload.v1"], + "artifacts": ["code-intel-sentrux-capability-artifact.v1", "code-intel-sentrux-command-observation.v1", "code-intel-structural-evidence-payload.v1"], "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "pr_gate", "release_gate"], "failureSemantics": ["domain_fail", "failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] @@ -171,11 +171,11 @@ "aliases": ["save_baseline", "baseline"], "scope": "rules", "trigger": "bootstrap_or_explicit_approval", - "route": "rust_native_manual", - "currentState": "native_manual", + "route": "explicit_mutation", + "currentState": "explicit_authority_required", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.rules"], - "artifacts": ["sentrux-baseline.v1"], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], "decisionConsumers": ["sentrux.gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] @@ -186,11 +186,11 @@ "aliases": [], "scope": "agent_session", "trigger": "agent_edit_session_start", - "route": "legacy_compatibility", - "currentState": "compatibility_only", + "route": "agent_lifecycle_external", + "currentState": "lifecycle_external", "requiredForRelease": true, "inputs": ["repository.snapshot", "session.scope"], - "artifacts": ["sentrux-session-start.v1"], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], "decisionConsumers": ["sentrux.session_end"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] @@ -201,11 +201,11 @@ "aliases": [], "scope": "agent_session", "trigger": "agent_edit_session_end", - "route": "legacy_compatibility", - "currentState": "compatibility_only", + "route": "agent_lifecycle_external", + "currentState": "lifecycle_external", "requiredForRelease": true, "inputs": ["sentrux.session_start.v1", "repository.snapshot", "working_tree"], - "artifacts": ["sentrux-session-end.v1"], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], "decisionConsumers": ["pr_gate", "release_gate"], "failureSemantics": ["domain_fail", "failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] @@ -216,12 +216,12 @@ "aliases": [], "scope": "agent_session", "trigger": "stale_or_changed_snapshot", - "route": "legacy_compatibility", - "currentState": "compatibility_only", + "route": "provider.sentrux-adapt/builtin_bounded", + "currentState": "automatic_degraded", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.session_start.v1"], - "artifacts": ["sentrux-rescan.v1"], - "decisionConsumers": ["sentrux.session_end"], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], + "decisionConsumers": ["evidence.sentrux", "diagnosis.hospital", "report", "pr_gate", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], "platforms": ["windows", "macos", "linux"] }, @@ -231,11 +231,11 @@ "aliases": ["pro_status", "plugin_list", "plugin_validate"], "scope": "installation", "trigger": "doctor_or_install_smoke", - "route": "provider_probe", - "currentState": "declared_only", + "route": "provider.sentrux-adapt/builtin_lite", + "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["installation.layout"], - "artifacts": ["sentrux-provider-capabilities.v1"], + "artifacts": ["code-intel-sentrux-capability-artifact.v1"], "decisionConsumers": ["doctor", "run_planner", "install_smoke", "release_gate"], "failureSemantics": ["unavailable", "failed", "unknown"], "platforms": ["windows", "macos", "linux"] diff --git a/tests/test_sentrux_capability_matrix.py b/tests/test_sentrux_capability_matrix.py index 975b7aa4..7fea2e93 100644 --- a/tests/test_sentrux_capability_matrix.py +++ b/tests/test_sentrux_capability_matrix.py @@ -10,6 +10,7 @@ MATRIX_PATH = ROOT / "orchestration" / "sentrux-capability-matrix.v1.json" INTEGRATIONS_PATH = ROOT / "orchestration" / "integrations.json" RUST_SENTRUX_PATH = ROOT / "crates" / "code-intel-cli" / "src" / "sentrux.rs" +EXECUTOR_PATH = ROOT / "crates" / "code-intel-cli" / "src" / "sentrux_capability_artifacts.rs" LEGACY_SENTRY_PATH = ROOT / "legacy" / "Invoke-SentruxAgentTool.ps1" @@ -19,6 +20,7 @@ def setUpClass(cls) -> None: cls.matrix = json.loads(MATRIX_PATH.read_text(encoding="utf-8")) cls.integrations = json.loads(INTEGRATIONS_PATH.read_text(encoding="utf-8")) cls.rust_source = RUST_SENTRUX_PATH.read_text(encoding="utf-8") + cls.executor_source = EXECUTOR_PATH.read_text(encoding="utf-8") cls.legacy_source = LEGACY_SENTRY_PATH.read_text(encoding="utf-8") def test_matrix_has_a_single_identity_for_every_capability_and_alias(self) -> None: @@ -80,6 +82,23 @@ def test_partial_coverage_is_explicit_until_all_release_capabilities_are_automat {"declared_only", "compatibility_only"}, ) + def test_automatic_matrix_routes_have_executor_entries_and_consumers(self) -> None: + for capability in self.matrix["capabilities"]: + with self.subTest(capability=capability["id"]): + if capability["route"].startswith("provider.sentrux-adapt"): + self.assertIn(f'"{capability["id"]}"', self.executor_source) + self.assertNotEqual(capability["currentState"], "compatibility_only") + self.assertNotEqual(capability["currentState"], "declared_only") + self.assertTrue(capability["artifacts"]) + self.assertTrue(capability["decisionConsumers"]) + + def test_dag_non_applicable_capabilities_are_explicit(self) -> None: + states = {item["id"]: item["currentState"] for item in self.matrix["capabilities"]} + self.assertEqual(states["sentrux.what_if"], "not_applicable_dag") + self.assertEqual(states["sentrux.baseline_save"], "explicit_authority_required") + self.assertEqual(states["sentrux.session_start"], "lifecycle_external") + self.assertEqual(states["sentrux.session_end"], "lifecycle_external") + if __name__ == "__main__": unittest.main() From 6034777258aaa90373e63bc56317ce733554c9c2 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 01:01:19 +0800 Subject: [PATCH 13/27] fix(sentrux): preserve closed command observation contract --- crates/code-intel-cli/src/sentrux_capability_artifacts.rs | 4 +++- crates/code-intel-cli/src/sentrux_command.rs | 3 +-- crates/code-intel-cli/tests/dag_run.rs | 6 +++--- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs index b322bb56..591a04f4 100644 --- a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -480,7 +480,9 @@ fn observation_command(observation: &Value) -> Option { let command = observation["command"].as_object()?; let stdout = command["stdout"].as_str().unwrap_or_default().to_owned(); let stderr = command["stderr"].as_str().unwrap_or_default().to_owned(); - let output_summary = command["outputSummary"] + let output_summary = command + .get("outputSummary") + .unwrap_or(&Value::Null) .as_object() .and_then(|summary| { Some(super::sentrux_command::OutputSummary::from_metadata( diff --git a/crates/code-intel-cli/src/sentrux_command.rs b/crates/code-intel-cli/src/sentrux_command.rs index 9b0df587..1b5e8c88 100644 --- a/crates/code-intel-cli/src/sentrux_command.rs +++ b/crates/code-intel-cli/src/sentrux_command.rs @@ -168,7 +168,6 @@ pub(crate) fn command_evidence(subcommand: &str, command: &SentruxCommand) -> Va "exitCode":command.exit_code, "success":command.success, "stdout":command.stdout, - "stderr":command.stderr, - "outputSummary":command.output_summary.to_json(&command.stdout, &command.stderr) + "stderr":command.stderr }) } diff --git a/crates/code-intel-cli/tests/dag_run.rs b/crates/code-intel-cli/tests/dag_run.rs index d4cb2170..62dec4d2 100644 --- a/crates/code-intel-cli/tests/dag_run.rs +++ b/crates/code-intel-cli/tests/dag_run.rs @@ -353,7 +353,7 @@ fn production_dag_output_commits_and_enters_the_authoritative_index() { })); assert_eq!( impact["testSelection"]["sentruxSignals"]["status"], - "advisory" + "available" ); assert_eq!( impact["testSelection"]["sentruxSignals"]["testGap"]["status"], @@ -365,11 +365,11 @@ fn production_dag_output_commits_and_enters_the_authoritative_index() { ); assert_eq!( impact["testSelection"]["sentruxSignals"]["dsm"]["status"], - "degraded" + "available" ); assert_eq!( impact["testSelection"]["sentruxSignals"]["candidateTestImpact"], - "withholds_sentrux_expansion" + "retains_graph_candidates" ); assert!( impact["testSelection"]["sentruxSignals"]["testGap"]["limitations"] From 2b31b57c33bba2ac857353476188c5e45ab6394f Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 01:06:48 +0800 Subject: [PATCH 14/27] ci(pr-gate): install ripgrep for authoritative runs --- .github/workflows/pr-gate.yml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml index 273f67df..fe074d15 100644 --- a/.github/workflows/pr-gate.yml +++ b/.github/workflows/pr-gate.yml @@ -246,6 +246,12 @@ jobs: rustup toolchain install rustup show active-toolchain + - name: Install ripgrep + shell: pwsh + run: | + sudo apt-get update + sudo apt-get install -y ripgrep + - name: Build Rust CLI shell: pwsh run: cargo build -p code-intel --release --locked From 7e462b6d635140f3803693ff61c41e7645d9c742 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 01:16:29 +0800 Subject: [PATCH 15/27] test(cli): record sentrux capability help contract --- crates/code-intel-cli/tests/fixtures/cli-head-parity.v2.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/code-intel-cli/tests/fixtures/cli-head-parity.v2.json b/crates/code-intel-cli/tests/fixtures/cli-head-parity.v2.json index 0c0fe555..4caf989d 100644 --- a/crates/code-intel-cli/tests/fixtures/cli-head-parity.v2.json +++ b/crates/code-intel-cli/tests/fixtures/cli-head-parity.v2.json @@ -587,7 +587,7 @@ }, "new": { "exitCode": 0, - "stdoutUtf8": "code-intel [options]\n\nCommands:\n --version|-V [--json]\n help|--help|-h [--all]\n run [] [--mode lite|normal|full] [--json]\n status [] [--json]\n query [] --kind evidence [--artifact-schema ] [--type ] [--contains ] [--limit <1..100>] --json\n report --repo [--artifact-root ] [--json]\n resume --repo [--artifact-root ] [--json]\n classify --report [--json]\n sentrux-normalize --steps [--out ]\n sentrux-debt-register --failures [--repo ] [--out ]\n doctor [--artifact-root ] [--json]\n doctor bootstrap [--repo ] [--repo-path ] [--config ] [--platform auto|windows|macos|linux] [--no-require-repowise] [--require-understand] [--json]\n graph|understand --repo [--language zh] [--full] [--write] [--json]\n provider|providers [--action List|Plan|Validate|Invoke] [--provider repowise|understand] [--operation ] [--repo ] [--language zh] [--write] [--json]\n provider repowise-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider graph-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider sentrux-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider session-adapt --repo --trace [--hotspots ] [--out ] [--working-tree-policy head_only|explicit_overlay]\n provider codenexus-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider file-boundary --request --out \n provider runtime-ci-evidence --artifact-root --request --out \n compatibility retirement-ticket lint --ticket --evaluated-at \n lint hardcoded-paths [] [--json]\n route|routes [--action List|Plan|Validate] [--provider repowise|understand] [--operation ] [--repo ] [--json]\n sentrux [--no-ratchet]\n (--no-ratchet: `check` only, skip the .sentrux/baseline.json ratchet)\n capability exec --request --out [--artifact-root ] [--manifest ]\n model inventory-validate --request [--out ]\n model route --request [--out ]\n snapshot identity --repo --working-tree-policy [--scope ]...\n repin [--repo ] [--write] [--json] [--exclude ]...\n repowise-hooks [--repo ] [--write] (detects/installs the optional repowise post-commit and distill-rewrite hooks; no-op if repowise is not on PATH)\n evidence validate --request --artifact-root \n repository survival-scan --request --artifact-root \n audit --operation validate|render --repo --report [--format markdown|html]\n audit --operation scope --repo --since \n artifact index --artifact-root [--output ] [--operation rebuild|incremental] [--existing ]\n artifact query --artifact-root --repo [--repo-path ] [--artifact-schema ] [--type ] [--contains ] [--limit <1..100>]\n change impact --artifact-root --repo --repo-path --changed [--changed ]... [--staleness current|advisory]\n change risk [--repo ] [--sample ] [--format json|text] (git-only defect-risk score, no prior run, no index)\n change agenda [--repo ] [--min-cochange ] [--format json|text] (git-only review units clustered by co-change, ranked worst first)\n edit impact --repo-path --changed [--changed ]... [--scope ]... (working tree, no prior run, authority: none)\n edit apply --repo-path --file (--span --expect-sha256 --replacement |--replacement-file )... [--out ] [--manifest ] [--envelope] (span-addressed patch; refuses with evidence on digest drift, exit 10)\n decision request-response --request [--response |--cancel ] --now --branch ...\n decision record --resolution --store \n decision replay --query --store \n run execute --repo --out --authority-root --final-name [--profile default|strict|offline] [--manifest ] [--max-concurrency ] [--session-evidence ]\n run dag-coordinate --repo --out [--manifest ] [--max-concurrency ] [--session-evidence ]\n run commit --source-root --authority-root --manifest-ref --final-name \n serve --mcp [--repo-path ] [--repo ] [--artifact-root ] [--manifest ] (stdio MCP query surface over the committed run; read-only, gates nowhere)\n benchmark orientation --out [--repetitions <2..10>]\n benchmark tools --corpus --runs --artifact-root --out \n governance ponytail-gate --request \n orchestrate|orchestration [--action Validate|List|Plan] [--repo ] [--mode lite|normal|full] [--capability ] [--manifest ] [--json]\n language set --language --repo [--json]\n", + "stdoutUtf8": "code-intel [options]\n\nCommands:\n --version|-V [--json]\n help|--help|-h [--all]\n run [] [--mode lite|normal|full] [--json]\n status [] [--json]\n query [] --kind evidence [--artifact-schema ] [--type ] [--contains ] [--limit <1..100>] --json\n report --repo [--artifact-root ] [--json]\n resume --repo [--artifact-root ] [--json]\n classify --report [--json]\n sentrux-normalize --steps [--out ]\n sentrux-debt-register --failures [--repo ] [--out ]\n doctor [--artifact-root ] [--json]\n doctor bootstrap [--repo ] [--repo-path ] [--config ] [--platform auto|windows|macos|linux] [--no-require-repowise] [--require-understand] [--json]\n graph|understand --repo [--language zh] [--full] [--write] [--json]\n provider|providers [--action List|Plan|Validate|Invoke] [--provider repowise|understand] [--operation ] [--repo ] [--language zh] [--write] [--json]\n provider repowise-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider graph-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider sentrux-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider session-adapt --repo --trace [--hotspots ] [--out ] [--working-tree-policy head_only|explicit_overlay]\n provider codenexus-adapt --request --artifact-root --evaluated-at --max-age-seconds \n provider file-boundary --request --out \n provider runtime-ci-evidence --artifact-root --request --out \n compatibility retirement-ticket lint --ticket --evaluated-at \n lint hardcoded-paths [] [--json]\n route|routes [--action List|Plan|Validate] [--provider repowise|understand] [--operation ] [--repo ] [--json]\n sentrux [--no-ratchet]\n sentrux capabilities [] [--json] (read-only capability matrix audit)\n (--no-ratchet: `check` only, skip the .sentrux/baseline.json ratchet)\n capability exec --request --out [--artifact-root ] [--manifest ]\n model inventory-validate --request [--out ]\n model route --request [--out ]\n snapshot identity --repo --working-tree-policy [--scope ]...\n repin [--repo ] [--write] [--json] [--exclude ]...\n repowise-hooks [--repo ] [--write] (detects/installs the optional repowise post-commit and distill-rewrite hooks; no-op if repowise is not on PATH)\n evidence validate --request --artifact-root \n repository survival-scan --request --artifact-root \n audit --operation validate|render --repo --report [--format markdown|html]\n audit --operation scope --repo --since \n artifact index --artifact-root [--output ] [--operation rebuild|incremental] [--existing ]\n artifact query --artifact-root --repo [--repo-path ] [--artifact-schema ] [--type ] [--contains ] [--limit <1..100>]\n change impact --artifact-root --repo --repo-path --changed [--changed ]... [--staleness current|advisory]\n change risk [--repo ] [--sample ] [--format json|text] (git-only defect-risk score, no prior run, no index)\n change agenda [--repo ] [--min-cochange ] [--format json|text] (git-only review units clustered by co-change, ranked worst first)\n edit impact --repo-path --changed [--changed ]... [--scope ]... (working tree, no prior run, authority: none)\n edit apply --repo-path --file (--span --expect-sha256 --replacement |--replacement-file )... [--out ] [--manifest ] [--envelope] (span-addressed patch; refuses with evidence on digest drift, exit 10)\n decision request-response --request [--response |--cancel ] --now --branch ...\n decision record --resolution --store \n decision replay --query --store \n run execute --repo --out --authority-root --final-name [--profile default|strict|offline] [--manifest ] [--max-concurrency ] [--session-evidence ]\n run dag-coordinate --repo --out [--manifest ] [--max-concurrency ] [--session-evidence ]\n run commit --source-root --authority-root --manifest-ref --final-name \n serve --mcp [--repo-path ] [--repo ] [--artifact-root ] [--manifest ] (stdio MCP query surface over the committed run; read-only, gates nowhere)\n benchmark orientation --out [--repetitions <2..10>]\n benchmark tools --corpus --runs --artifact-root --out \n governance ponytail-gate --request \n orchestrate|orchestration [--action Validate|List|Plan] [--repo ] [--mode lite|normal|full] [--capability ] [--manifest ] [--json]\n language set --language --repo [--json]\n", "stderrUtf8": "" } }, From e274cac4a2e6820eee0f2411ec1009013e518668 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 01:38:11 +0800 Subject: [PATCH 16/27] fix(sentrux): preserve capability gate semantics and violations --- .../src/sentrux_capability_artifacts.rs | 24 +++++++-- crates/code-intel-cli/src/sentrux_command.rs | 31 +++++++++++ ...sentrux-capability-artifact.v1.schema.json | 51 ++++++++++++++++++- 3 files changed, 102 insertions(+), 4 deletions(-) diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs index 591a04f4..039beb38 100644 --- a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -400,12 +400,19 @@ fn capability_observation( "authority":authority.unwrap_or("compatibility"), "status":status, "verdict":verdict, - "command":command_evidence(route.operation, command), + "command":capability_command_evidence(route.operation, command), "outputSummary":command.output_summary.to_json(&command.stdout, &command.stderr), "failure":failure }) } +fn capability_command_evidence(operation: &str, command: &SentruxCommand) -> Value { + let mut evidence = command_evidence(operation, command); + evidence["governed"] = json!(command.governed); + evidence["violations"] = command.violations_json(); + evidence +} + fn not_applicable_observation( route: &CapabilityRoute, tool_path_prefix: Option<&Path>, @@ -503,8 +510,19 @@ fn observation_command(observation: &Value) -> Option { success: command["success"].as_bool().unwrap_or(false), stdout, stderr, - violations: Vec::new(), - governed: true, + violations: SentruxCommand::violations_from_json(command.get("violations")), + // Capability artifacts intentionally retain an ungoverned gate as a + // successful, unknown observation. Rehydrate that distinction here so + // the capability evidence cannot turn an absent baseline into a + // structural failure when it is fed back into the authoritative rules. + governed: command + .get("governed") + .and_then(Value::as_bool) + .unwrap_or_else(|| { + !(command["success"] == false + && observation["status"] == "succeeded" + && observation["verdict"] == "unknown") + }), output_summary, }) } diff --git a/crates/code-intel-cli/src/sentrux_command.rs b/crates/code-intel-cli/src/sentrux_command.rs index 1b5e8c88..2fde7a41 100644 --- a/crates/code-intel-cli/src/sentrux_command.rs +++ b/crates/code-intel-cli/src/sentrux_command.rs @@ -90,6 +90,37 @@ fn bounded_text(bytes: &[u8]) -> String { } impl SentruxCommand { + pub(crate) fn violations_json(&self) -> Value { + json!(self + .violations + .iter() + .map(Violation::to_json) + .collect::>()) + } + + pub(crate) fn violations_from_json(value: Option<&Value>) -> Vec { + value + .and_then(Value::as_array) + .map(|items| { + items + .iter() + .filter_map(|item| { + Some(Violation { + rule: item["rule"].as_str()?.to_owned(), + message: item["message"].as_str()?.to_owned(), + targets: item["targets"] + .as_array()? + .iter() + .filter_map(Value::as_str) + .map(str::to_owned) + .collect(), + }) + }) + .collect() + }) + .unwrap_or_default() + } + pub(crate) fn from_native(run: super::sentrux_gate::EngineRun, subcommand: &str) -> Self { let stdout_bytes = run.stdout.into_bytes(); let output_summary = OutputSummary::from_bytes(&stdout_bytes, &[]); diff --git a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json index 64612fda..26293409 100644 --- a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json +++ b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json @@ -47,7 +47,12 @@ "type": "object", "additionalProperties": false, "properties": { - "command": { "type": ["object", "null"] }, + "command": { + "oneOf": [ + { "$ref": "#/$defs/command" }, + { "type": "null" } + ] + }, "verdict": { "enum": ["pass", "fail", "unknown"] }, "outputSummary": { "oneOf": [ @@ -162,6 +167,50 @@ "consumedSnapshotIdentity": { "$ref": "#/$defs/sha256" } } }, + "command": { + "type": "object", + "additionalProperties": false, + "required": [ + "id", + "argv", + "exitCode", + "success", + "stdout", + "stderr", + "governed", + "violations" + ], + "properties": { + "id": { "$ref": "#/$defs/identifier" }, + "argv": { + "type": "array", + "minItems": 1, + "items": { "$ref": "#/$defs/nonEmpty" } + }, + "exitCode": { "type": ["integer", "null"] }, + "success": { "type": "boolean" }, + "stdout": { "type": "string" }, + "stderr": { "type": "string" }, + "governed": { "type": "boolean" }, + "violations": { + "type": "array", + "items": { "$ref": "#/$defs/violation" } + } + } + }, + "violation": { + "type": "object", + "additionalProperties": false, + "required": ["rule", "message", "targets"], + "properties": { + "rule": { "$ref": "#/$defs/nonEmpty" }, + "message": { "$ref": "#/$defs/nonEmpty" }, + "targets": { + "type": "array", + "items": { "$ref": "#/$defs/nonEmpty" } + } + } + }, "failure": { "type": "object", "additionalProperties": false, From b99799f557fa01c6e88a068b660f83135e0cea00 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 01:48:36 +0800 Subject: [PATCH 17/27] chore(internalization): refresh sentrux evidence pins --- orchestration/internalization/codenexus.json | 2 +- orchestration/internalization/graph.json | 2 +- orchestration/internalization/sentrux.json | 12 ++++++------ 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/orchestration/internalization/codenexus.json b/orchestration/internalization/codenexus.json index bab533d6..75ec45ec 100644 --- a/orchestration/internalization/codenexus.json +++ b/orchestration/internalization/codenexus.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.codenexus-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider codenexus-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "codenexus.full", "implementationId": "codenexus.service.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_route_runs_full_lite_and_unavailable_through_a04" } }, { "integrationId": "provider.codenexus-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -CodeNexusAdapterRequest -CodeNexusAdapterArtifactRoot -CodeNexusAdapterEvaluatedAt -CodeNexusAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_registry_facade_and_route_schema_are_declared" } }, - { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "004ad153b019ac9ae9869c095d90dce8b7a49bc5cdf745ce68f16dcd9ad795e0" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, + { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "b5807ae41c1f3befde1ee2c62b0aa628f4fb0f4e916a27c409d3a855a624a723" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, { "integrationId": "runtime.code-nexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } }, { "integrationId": "localization.codenexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } } ], diff --git a/orchestration/internalization/graph.json b/orchestration/internalization/graph.json index 59c38fab..afa8c3b5 100644 --- a/orchestration/internalization/graph.json +++ b/orchestration/internalization/graph.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.graph-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider graph-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, { "integrationId": "provider.graph-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -GraphAdapterRequest -GraphAdapterArtifactRoot -GraphAdapterEvaluatedAt -GraphAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "compatibility_facade" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, - { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "004ad153b019ac9ae9869c095d90dce8b7a49bc5cdf745ce68f16dcd9ad795e0" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "198cb2ca6abaffd40445ce0e03f821356ab191ee084446fd78235dabe93ccc45", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, + { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "b5807ae41c1f3befde1ee2c62b0aa628f4fb0f4e916a27c409d3a855a624a723" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, { "integrationId": "graph.code-intel-understand", "operation": "refresh", "command": "target/debug/code-intel.exe graph --repo --language zh --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.code-intel-understand", "operation": "refreshFull", "command": "target/debug/code-intel.exe graph --repo --language zh --full --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.understand-external", "operation": "refresh", "command": "/understand --language zh", "implementationIdentity": { "providerId": "understand-anything", "implementationId": "unverified-upstream", "activation": "explicit_fallback" }, "source": { "path": "docs/graph-provider-adapter.md", "sha256": "21a6633e164aa5aab122e1d2de36fad47866611abe930b64d03bb67633408095" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "fallback_identity_and_payload_identity_cannot_be_relabelled" } }, diff --git a/orchestration/internalization/sentrux.json b/orchestration/internalization/sentrux.json index 041fa656..f459f42e 100644 --- a/orchestration/internalization/sentrux.json +++ b/orchestration/internalization/sentrux.json @@ -122,11 +122,11 @@ }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", - "sha256": "004ad153b019ac9ae9869c095d90dce8b7a49bc5cdf745ce68f16dcd9ad795e0" + "sha256": "b5807ae41c1f3befde1ee2c62b0aa628f4fb0f4e916a27c409d3a855a624a723" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", - "sha256": "198cb2ca6abaffd40445ce0e03f821356ab191ee084446fd78235dabe93ccc45", + "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, @@ -141,7 +141,7 @@ }, "source": { "path": "crates/code-intel-cli/src/sentrux.rs", - "sha256": "e9eb3fcb06ffbd736dbb75c36af7126fed7a57b57fdab362124f7e19140f8066" + "sha256": "d69f51b84a1f60036acc42e72bc7fe99eef4e9d6333944e2ffc3c90848490724" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", @@ -160,7 +160,7 @@ }, "source": { "path": "crates/code-intel-cli/src/sentrux.rs", - "sha256": "e9eb3fcb06ffbd736dbb75c36af7126fed7a57b57fdab362124f7e19140f8066" + "sha256": "d69f51b84a1f60036acc42e72bc7fe99eef4e9d6333944e2ffc3c90848490724" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", @@ -179,7 +179,7 @@ }, "source": { "path": "crates/code-intel-cli/src/sentrux.rs", - "sha256": "e9eb3fcb06ffbd736dbb75c36af7126fed7a57b57fdab362124f7e19140f8066" + "sha256": "d69f51b84a1f60036acc42e72bc7fe99eef4e9d6333944e2ffc3c90848490724" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", @@ -259,7 +259,7 @@ }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", - "sha256": "198cb2ca6abaffd40445ce0e03f821356ab191ee084446fd78235dabe93ccc45", + "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_run_preserves_doctor_domain_failure_and_completes_unrelated_branch" } } From 40c7765486f95dacd484a89e6e6199a62899ce92 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 01:56:41 +0800 Subject: [PATCH 18/27] fix(sentrux): keep capability evidence content-addressed --- crates/code-intel-cli/src/builtin_provider_evidence.rs | 2 +- crates/code-intel-cli/src/sentrux_capability_artifacts.rs | 8 ++++++-- orchestration/internalization/codenexus.json | 2 +- orchestration/internalization/graph.json | 2 +- orchestration/internalization/sentrux.json | 2 +- .../code-intel-sentrux-capability-artifact.v1.schema.json | 5 ++++- 6 files changed, 14 insertions(+), 7 deletions(-) diff --git a/crates/code-intel-cli/src/builtin_provider_evidence.rs b/crates/code-intel-cli/src/builtin_provider_evidence.rs index 727c32c9..d4f7b478 100644 --- a/crates/code-intel-cli/src/builtin_provider_evidence.rs +++ b/crates/code-intel-cli/src/builtin_provider_evidence.rs @@ -206,7 +206,7 @@ pub(super) fn sentrux_admission( }); let first = sentrux_adapter::translate(&native, observed_at, MAX_AGE_SECONDS) .map_err(AdapterError::Contract)?; - let run_id = format!("sentrux-{}-{}", identity, std::process::id()); + let run_id = format!("sentrux-{identity}"); let (capability_artifacts, capability_refs) = sentrux_capability_artifacts::build_capability_artifacts( &capability_observations, diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs index 039beb38..2471b4ec 100644 --- a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -3,7 +3,7 @@ use std::path::Path; use serde_json::{json, Value}; use crate::adapter_contract::{AdapterArtifact, AdapterError}; -use crate::capability::{rfc3339_now, sha256_hex}; +use crate::capability::sha256_hex; use super::{command_evidence, run_sentrux, SentruxCommand}; @@ -256,7 +256,11 @@ pub(super) fn build_capability_artifacts( "failure":capability_failure(observation, status), "freshness":{ "status":"current", - "evaluatedAt":rfc3339_now(), + // The parent Sentrux observation carries the wall-clock + // freshness authority. Capability artifacts are themselves + // content-addressed, so their snapshot-bound projection + // must not embed a per-run timestamp. + "evaluatedAt":null, "consumedSnapshotIdentity":snapshot_identity }, "decisionConsumers":sentrux_decision_consumers(capability_id) diff --git a/orchestration/internalization/codenexus.json b/orchestration/internalization/codenexus.json index 75ec45ec..d088502f 100644 --- a/orchestration/internalization/codenexus.json +++ b/orchestration/internalization/codenexus.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.codenexus-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider codenexus-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "codenexus.full", "implementationId": "codenexus.service.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_route_runs_full_lite_and_unavailable_through_a04" } }, { "integrationId": "provider.codenexus-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -CodeNexusAdapterRequest -CodeNexusAdapterArtifactRoot -CodeNexusAdapterEvaluatedAt -CodeNexusAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_registry_facade_and_route_schema_are_declared" } }, - { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "b5807ae41c1f3befde1ee2c62b0aa628f4fb0f4e916a27c409d3a855a624a723" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, + { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "f75890489a9fdf5a970a0a1e7f18ab6501672e90bfc19309cbba0637ee56b2c8" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, { "integrationId": "runtime.code-nexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } }, { "integrationId": "localization.codenexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } } ], diff --git a/orchestration/internalization/graph.json b/orchestration/internalization/graph.json index afa8c3b5..c2c43b43 100644 --- a/orchestration/internalization/graph.json +++ b/orchestration/internalization/graph.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.graph-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider graph-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, { "integrationId": "provider.graph-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -GraphAdapterRequest -GraphAdapterArtifactRoot -GraphAdapterEvaluatedAt -GraphAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "compatibility_facade" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, - { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "b5807ae41c1f3befde1ee2c62b0aa628f4fb0f4e916a27c409d3a855a624a723" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, + { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "f75890489a9fdf5a970a0a1e7f18ab6501672e90bfc19309cbba0637ee56b2c8" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, { "integrationId": "graph.code-intel-understand", "operation": "refresh", "command": "target/debug/code-intel.exe graph --repo --language zh --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.code-intel-understand", "operation": "refreshFull", "command": "target/debug/code-intel.exe graph --repo --language zh --full --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.understand-external", "operation": "refresh", "command": "/understand --language zh", "implementationIdentity": { "providerId": "understand-anything", "implementationId": "unverified-upstream", "activation": "explicit_fallback" }, "source": { "path": "docs/graph-provider-adapter.md", "sha256": "21a6633e164aa5aab122e1d2de36fad47866611abe930b64d03bb67633408095" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "fallback_identity_and_payload_identity_cannot_be_relabelled" } }, diff --git a/orchestration/internalization/sentrux.json b/orchestration/internalization/sentrux.json index f459f42e..5b9cf01c 100644 --- a/orchestration/internalization/sentrux.json +++ b/orchestration/internalization/sentrux.json @@ -122,7 +122,7 @@ }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", - "sha256": "b5807ae41c1f3befde1ee2c62b0aa628f4fb0f4e916a27c409d3a855a624a723" + "sha256": "f75890489a9fdf5a970a0a1e7f18ab6501672e90bfc19309cbba0637ee56b2c8" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", diff --git a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json index 26293409..2dcf60e7 100644 --- a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json +++ b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json @@ -244,7 +244,10 @@ "status": { "enum": ["current", "stale", "snapshot_mismatch", "unknown"] }, - "evaluatedAt": { "type": "string", "format": "date-time" }, + "evaluatedAt": { + "type": ["string", "null"], + "format": "date-time" + }, "consumedSnapshotIdentity": { "$ref": "#/$defs/sha256" } } }, From e1d8d6f94dc1dc1a124227c551b6d742c92d9035 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 02:17:55 +0800 Subject: [PATCH 19/27] fix(sentrux): dispatch lite capabilities automatically --- .../src/builtin_provider_evidence.rs | 1 - .../src/sentrux_capability_artifacts.rs | 84 +++++++++++-------- .../src/sentrux_lite_capabilities.rs | 58 ++++++++++++- orchestration/internalization/codenexus.json | 2 +- orchestration/internalization/graph.json | 2 +- orchestration/internalization/sentrux.json | 2 +- 6 files changed, 108 insertions(+), 41 deletions(-) diff --git a/crates/code-intel-cli/src/builtin_provider_evidence.rs b/crates/code-intel-cli/src/builtin_provider_evidence.rs index d4f7b478..0eed4063 100644 --- a/crates/code-intel-cli/src/builtin_provider_evidence.rs +++ b/crates/code-intel-cli/src/builtin_provider_evidence.rs @@ -212,7 +212,6 @@ pub(super) fn sentrux_admission( &capability_observations, identity, &run_id, - tool_path_prefix, )?; let payload = json!({ "schema":"code-intel-evidence-payload.v1", diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs index 2471b4ec..ca7b59a5 100644 --- a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -131,16 +131,22 @@ pub(super) fn collect_sentrux_capabilities( let mut check = None; let mut observations = Vec::with_capacity(SENTRUX_CAPABILITY_ROUTES.len()); for route in SENTRUX_CAPABILITY_ROUTES { + let provider_mode = route_provider_mode(&route, tool_path_prefix); + let route_tool_path_prefix = if provider_mode == "lite_fallback" { + None + } else { + tool_path_prefix + }; let observation = match route.kind { RouteKind::NotApplicable { failure_kind, message, - } => not_applicable_observation(&route, tool_path_prefix, failure_kind, message), + } => not_applicable_observation(&route, provider_mode, failure_kind, message), RouteKind::ReuseScan => { match run_sentrux( repo, - tool_path_prefix, - if tool_path_prefix.is_some() { + route_tool_path_prefix, + if route_tool_path_prefix.is_some() { route.command } else { "scan" @@ -148,15 +154,15 @@ pub(super) fn collect_sentrux_capabilities( ) { Ok(command) => capability_observation( &route, - tool_path_prefix, + provider_mode, &command, Some("authoritative"), ), - Err(error) => route_error_observation(&route, tool_path_prefix, &error), + Err(error) => route_error_observation(&route, provider_mode, &error), } } RouteKind::Command => { - if tool_path_prefix.is_none() + if route_tool_path_prefix.is_none() && !matches!( route.command, "gate" @@ -173,21 +179,21 @@ pub(super) fn collect_sentrux_capabilities( { unavailable_observation( &route, - tool_path_prefix, + provider_mode, "the built-in Rust provider has no route for this capability", ) } else { - match run_sentrux(repo, tool_path_prefix, route.command) { + match run_sentrux(repo, route_tool_path_prefix, route.command) { Ok(command) => capability_observation( &route, - tool_path_prefix, + provider_mode, &command, Some("authoritative"), ), Err(error) if matches!(route.command, "gate" | "check") => { return Err(error) } - Err(error) => route_error_observation(&route, tool_path_prefix, &error), + Err(error) => route_error_observation(&route, provider_mode, &error), } } } @@ -215,9 +221,7 @@ pub(super) fn build_capability_artifacts( observations: &[Value], snapshot_identity: &str, run_id: &str, - tool_path_prefix: Option<&Path>, ) -> Result<(Vec, Vec), AdapterError> { - let provider = sentrux_capability_provider(tool_path_prefix); let mut artifacts = Vec::with_capacity(observations.len()); let mut refs = Vec::with_capacity(observations.len()); for observation in observations { @@ -227,6 +231,8 @@ pub(super) fn build_capability_artifacts( let operation = observation["operation"].as_str().ok_or_else(|| { AdapterError::Contract("Sentrux capability observation has no operation".into()) })?; + let provider = + sentrux_capability_provider(observation["providerMode"].as_str().unwrap_or("builtin")); let raw_status = observation["status"].as_str().unwrap_or("failed"); let status = match raw_status { "not_run" | "not_applicable" => "not_applicable", @@ -290,21 +296,26 @@ pub(super) fn build_capability_artifacts( Ok((artifacts, refs)) } -fn sentrux_capability_provider(tool_path_prefix: Option<&Path>) -> Value { - if tool_path_prefix.is_some() { - json!({ +fn sentrux_capability_provider(provider_mode: &str) -> Value { + match provider_mode { + "external" => json!({ "mode":"external", "id":"sentrux.command-adapter", "version":"1.0.0", "digest":sha256_hex(include_bytes!("builtin_provider_evidence.rs")) - }) - } else { - json!({ + }), + "lite_fallback" => json!({ + "mode":"lite_fallback", + "id":"sentrux.lite-capabilities", + "version":"1.0.0", + "digest":sha256_hex(include_bytes!("sentrux_lite_capabilities.rs")) + }), + _ => json!({ "mode":"builtin", "id":super::sentrux_gate::ENGINE_ID, "version":super::sentrux_gate::ENGINE_VERSION, "digest":sha256_hex(include_bytes!("sentrux_gate.rs")) - }) + }), } } @@ -367,7 +378,7 @@ fn sentrux_decision_consumers(capability_id: &str) -> Value { fn capability_observation( route: &CapabilityRoute, - tool_path_prefix: Option<&Path>, + provider_mode: &str, command: &SentruxCommand, authority: Option<&str>, ) -> Value { @@ -400,7 +411,7 @@ fn capability_observation( json!({ "capabilityId":route.capability_id, "operation":route.operation, - "providerMode":sentrux_provider_mode(tool_path_prefix), + "providerMode":provider_mode, "authority":authority.unwrap_or("compatibility"), "status":status, "verdict":verdict, @@ -419,14 +430,14 @@ fn capability_command_evidence(operation: &str, command: &SentruxCommand) -> Val fn not_applicable_observation( route: &CapabilityRoute, - tool_path_prefix: Option<&Path>, + provider_mode: &str, failure_kind: &str, message: &str, ) -> Value { json!({ "capabilityId":route.capability_id, "operation":route.operation, - "providerMode":sentrux_provider_mode(tool_path_prefix), + "providerMode":provider_mode, "authority":"declared_only", "status":"not_applicable", "verdict":"unknown", @@ -435,15 +446,11 @@ fn not_applicable_observation( }) } -fn unavailable_observation( - route: &CapabilityRoute, - tool_path_prefix: Option<&Path>, - message: &str, -) -> Value { +fn unavailable_observation(route: &CapabilityRoute, provider_mode: &str, message: &str) -> Value { json!({ "capabilityId":route.capability_id, "operation":route.operation, - "providerMode":sentrux_provider_mode(tool_path_prefix), + "providerMode":provider_mode, "authority":"compatibility", "status":"unavailable", "verdict":"unknown", @@ -454,14 +461,14 @@ fn unavailable_observation( fn route_error_observation( route: &CapabilityRoute, - tool_path_prefix: Option<&Path>, + provider_mode: &str, error: &AdapterError, ) -> Value { let unavailable = matches!(error, AdapterError::Unavailable(_)); json!({ "capabilityId":route.capability_id, "operation":route.operation, - "providerMode":sentrux_provider_mode(tool_path_prefix), + "providerMode":provider_mode, "authority":if unavailable { "compatibility" } else { "authoritative" }, "status":if unavailable { "unavailable" } else { "failed" }, "verdict":"unknown", @@ -531,14 +538,23 @@ fn observation_command(observation: &Value) -> Option { }) } -fn sentrux_provider_mode(tool_path_prefix: Option<&Path>) -> &'static str { - if tool_path_prefix.is_some() { +fn route_provider_mode(route: &CapabilityRoute, tool_path_prefix: Option<&Path>) -> &'static str { + if matches!(route.kind, RouteKind::Command) && uses_lite_fallback(route.command) { + "lite_fallback" + } else if tool_path_prefix.is_some() { "external" } else { - "builtin_lite" + "builtin" } } +fn uses_lite_fallback(command: &str) -> bool { + matches!( + command, + "git_stats" | "evolution" | "test_gaps" | "provider_discovery" + ) +} + fn adapter_error_kind(error: &AdapterError) -> &'static str { match error { AdapterError::Unavailable(_) => "provider_unavailable", diff --git a/crates/code-intel-cli/src/sentrux_lite_capabilities.rs b/crates/code-intel-cli/src/sentrux_lite_capabilities.rs index 1d2af528..50c9a8b8 100644 --- a/crates/code-intel-cli/src/sentrux_lite_capabilities.rs +++ b/crates/code-intel-cli/src/sentrux_lite_capabilities.rs @@ -32,11 +32,33 @@ fn git_command(repo: &Path, args: &[&str]) -> Result { } pub(super) fn git_stats_json(repo: &Path) -> Result { - let count = git_command(repo, &["rev-list", "--count", "HEAD"])? + let count_output = match git_command(repo, &["rev-list", "--count", "HEAD"]) { + Ok(output) => output, + Err(reason) => { + return Ok(json!({ + "commitCount":0, + "recentCommits":[], + "status":"unavailable", + "reason":reason + })) + } + }; + let count = count_output .trim() .parse::() .map_err(|error| format!("git commit count is invalid: {error}"))?; - let recent = git_command(repo, &["log", "-n", "20", "--format=%H%x09%aI"])? + let recent_output = match git_command(repo, &["log", "-n", "20", "--format=%H%x09%aI"]) { + Ok(output) => output, + Err(reason) => { + return Ok(json!({ + "commitCount":count, + "recentCommits":[], + "status":"unavailable", + "reason":reason + })) + } + }; + let recent = recent_output .lines() .filter_map(|line| { let (commit, authored_at) = line.split_once('\t')?; @@ -47,7 +69,19 @@ pub(super) fn git_stats_json(repo: &Path) -> Result { } pub(super) fn evolution_json(repo: &Path) -> Result { - let recent = git_command(repo, &["log", "-n", "20", "--format=%H%x09%aI%x09%an"])? + let recent_output = match git_command(repo, &["log", "-n", "20", "--format=%H%x09%aI%x09%an"]) { + Ok(output) => output, + Err(reason) => { + return Ok(json!({ + "status":"unavailable", + "windowCommits":0, + "trend":"unknown", + "recentCommits":[], + "reason":reason + })) + } + }; + let recent = recent_output .lines() .filter_map(|line| { let mut fields = line.splitn(3, '\t'); @@ -66,6 +100,24 @@ pub(super) fn evolution_json(repo: &Path) -> Result { })) } +#[cfg(test)] +mod tests { + #[test] + fn history_capabilities_are_auditable_without_git_history() { + let repo = std::path::Path::new("this-path-does-not-contain-a-git-checkout"); + let stats = + super::git_stats_json(repo).expect("missing git history is a valid lite result"); + let evolution = + super::evolution_json(repo).expect("missing git history is a valid lite result"); + + assert_eq!(stats["status"], "unavailable"); + assert_eq!(stats["commitCount"], 0); + assert_eq!(evolution["status"], "unavailable"); + assert_eq!(evolution["windowCommits"], 0); + assert_eq!(evolution["trend"], "unknown"); + } +} + pub(super) fn test_gaps_json(repo: &Path) -> Result { let mut source_files = 0_u64; let mut test_files = 0_u64; diff --git a/orchestration/internalization/codenexus.json b/orchestration/internalization/codenexus.json index d088502f..35145db4 100644 --- a/orchestration/internalization/codenexus.json +++ b/orchestration/internalization/codenexus.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.codenexus-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider codenexus-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "codenexus.full", "implementationId": "codenexus.service.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_route_runs_full_lite_and_unavailable_through_a04" } }, { "integrationId": "provider.codenexus-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -CodeNexusAdapterRequest -CodeNexusAdapterArtifactRoot -CodeNexusAdapterEvaluatedAt -CodeNexusAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_registry_facade_and_route_schema_are_declared" } }, - { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "f75890489a9fdf5a970a0a1e7f18ab6501672e90bfc19309cbba0637ee56b2c8" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, + { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, { "integrationId": "runtime.code-nexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } }, { "integrationId": "localization.codenexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } } ], diff --git a/orchestration/internalization/graph.json b/orchestration/internalization/graph.json index c2c43b43..2985cb6e 100644 --- a/orchestration/internalization/graph.json +++ b/orchestration/internalization/graph.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.graph-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider graph-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, { "integrationId": "provider.graph-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -GraphAdapterRequest -GraphAdapterArtifactRoot -GraphAdapterEvaluatedAt -GraphAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "compatibility_facade" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, - { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "f75890489a9fdf5a970a0a1e7f18ab6501672e90bfc19309cbba0637ee56b2c8" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, + { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, { "integrationId": "graph.code-intel-understand", "operation": "refresh", "command": "target/debug/code-intel.exe graph --repo --language zh --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.code-intel-understand", "operation": "refreshFull", "command": "target/debug/code-intel.exe graph --repo --language zh --full --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.understand-external", "operation": "refresh", "command": "/understand --language zh", "implementationIdentity": { "providerId": "understand-anything", "implementationId": "unverified-upstream", "activation": "explicit_fallback" }, "source": { "path": "docs/graph-provider-adapter.md", "sha256": "21a6633e164aa5aab122e1d2de36fad47866611abe930b64d03bb67633408095" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "fallback_identity_and_payload_identity_cannot_be_relabelled" } }, diff --git a/orchestration/internalization/sentrux.json b/orchestration/internalization/sentrux.json index 5b9cf01c..743a3451 100644 --- a/orchestration/internalization/sentrux.json +++ b/orchestration/internalization/sentrux.json @@ -122,7 +122,7 @@ }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", - "sha256": "f75890489a9fdf5a970a0a1e7f18ab6501672e90bfc19309cbba0637ee56b2c8" + "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", From 48106810066b6ec1e925026ff051280a8be927c4 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 02:32:22 +0800 Subject: [PATCH 20/27] chore(contract): repin capability toolchains --- orchestration/integrations.json | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/orchestration/integrations.json b/orchestration/integrations.json index 6f63caf9..7ff8a7be 100644 --- a/orchestration/integrations.json +++ b/orchestration/integrations.json @@ -535,7 +535,7 @@ "id": "provider.graph-builtin.compat", "version": "1.0.0", "toolchainDigests": [ - "004ad153b019ac9ae9869c095d90dce8b7a49bc5cdf745ce68f16dcd9ad795e0", + "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947", "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", @@ -587,7 +587,7 @@ "id": "provider.sentrux-builtin.compat", "version": "1.0.0", "toolchainDigests": [ - "004ad153b019ac9ae9869c095d90dce8b7a49bc5cdf745ce68f16dcd9ad795e0", + "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", "29ad124d984f6ab0756bdc7c0b523a84dfada2a655fe0d282ce681165f6bae4f", "f69776e6800f917099794a5a28dbdb1117eaf657568dd09e34bf33e4207ddb4c", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", @@ -662,7 +662,7 @@ "id": "provider.codenexus-builtin.compat", "version": "1.0.0", "toolchainDigests": [ - "004ad153b019ac9ae9869c095d90dce8b7a49bc5cdf745ce68f16dcd9ad795e0", + "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", "98ccc64478b2c61bfd7af741ea1f8ee01a88094065c0f025700e8110b525ef26", @@ -878,7 +878,7 @@ "toolchainDigests": [ "3ba256f4ca0bf62aca08f688f7ecf31800dd10e68371054e2f1605eff197ea81", "295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5", - "255483f15ef2cdfe0b759d84ef64bc20ec5236716ea6668642c6806e834ab437", + "d51f1db572c1ed8a52f2b9575572d76ad4880d25b3d942d7801cc29d8a2b087a", "b118ba78ff3ef4a189525c642184cbd320a268d2885681ab153544db554c5965", "e3072b6b01a4f692c2ac75c7c4995747f9a0fd1ce4f32e1ab1599f348661f570", "40c532ed3aa52144bdc8bb4422e04b23c54196d352ac9cb0f3e93a5a059af120" @@ -939,7 +939,7 @@ "version": "1.0.0", "toolchainDigests": [ "2d2b38c4650795e2bba99e8ec38ca21adcb204042e9289577c535f7087e899d8", - "255483f15ef2cdfe0b759d84ef64bc20ec5236716ea6668642c6806e834ab437", + "d51f1db572c1ed8a52f2b9575572d76ad4880d25b3d942d7801cc29d8a2b087a", "295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5" ] }, @@ -990,7 +990,7 @@ "version": "1.0.0", "toolchainDigests": [ "a6da84444e815dd94c15887190c7d6bd48c7bca5975fb5d493eb77ab39f27d80", - "255483f15ef2cdfe0b759d84ef64bc20ec5236716ea6668642c6806e834ab437", + "d51f1db572c1ed8a52f2b9575572d76ad4880d25b3d942d7801cc29d8a2b087a", "295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5" ] }, @@ -1584,7 +1584,7 @@ "id": "diagnosis.hospital.compat", "version": "1.0.0", "toolchainDigests": [ - "15fe44854b79b0b860ad2b63fbd461f358f2b19ec4ac8921eca08978239a9703" + "196e6364b88635ceea6216545a680a486d57399636d58e9aefbf0b55dbc4704c" ] }, "determinism": "deterministic", From 805a2caa0c9e05740e350dede721cbcafb515474 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 02:41:39 +0800 Subject: [PATCH 21/27] test(contract): sync capability conformance pins --- crates/code-intel-cli/tests/capability_exec.rs | 2 +- orchestration/internalization/ast-grep.json | 6 +++--- orchestration/internalization/codenexus.json | 2 +- orchestration/internalization/rg.json | 8 ++++---- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/crates/code-intel-cli/tests/capability_exec.rs b/crates/code-intel-cli/tests/capability_exec.rs index 6f53e8e4..a84c9f0c 100644 --- a/crates/code-intel-cli/tests/capability_exec.rs +++ b/crates/code-intel-cli/tests/capability_exec.rs @@ -14,7 +14,7 @@ const STRUCTURED_EDIT_DIGEST: &str = const REPO_SNAPSHOT_DIGEST: &str = "4f42b080fd19e501a6315ee204add188d69625bedd15c566fea48bb1f3e78764"; const CODENEXUS_TOOLCHAIN_DIGESTS: [&str; 5] = [ - "004ad153b019ac9ae9869c095d90dce8b7a49bc5cdf745ce68f16dcd9ad795e0", + "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", "98ccc64478b2c61bfd7af741ea1f8ee01a88094065c0f025700e8110b525ef26", diff --git a/orchestration/internalization/ast-grep.json b/orchestration/internalization/ast-grep.json index bdb525ec..94111831 100644 --- a/orchestration/internalization/ast-grep.json +++ b/orchestration/internalization/ast-grep.json @@ -2,15 +2,15 @@ "schema": "code-intel-internalization-record.v1", "id": "internalization.ast-grep-record", "projectId": "code-intel-pipeline", - "subject": { "name": "ast-grep structural search executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/ast-grep/ast-grep; installed-evidence=ast-grep 0.42.3", "revision": "installed-version:0.42.3; local-native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d; local-conformance-sha256:91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d" }, "license": { "id": "MIT-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the MIT license text and package provenance are retained locally", "preserve preview-only authority, snapshot lease binding, scope and escape guards, generated-content exclusions, and failure semantics exactly"] } }, + "subject": { "name": "ast-grep structural search executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/ast-grep/ast-grep; installed-evidence=ast-grep 0.42.3", "revision": "installed-version:0.42.3; local-native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d; local-conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5" }, "license": { "id": "MIT-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the MIT license text and package provenance are retained locally", "preserve preview-only authority, snapshot lease binding, scope and escape guards, generated-content exclusions, and failure semantics exactly"] } }, "adoption": { "rung": "invoke", "ownedBoundary": ["Pipeline owns edit.ast-grep-plan request validation, snapshot lease, path scope and escape guards, artifact contract, and failure mapping", "ast-grep owns pattern parsing, structural matching, and rewrite preview computation; this record does not authorize upgrade, vendoring, repository mutation, or reimplementation"], "necessityEvidence": { "evidenceIds": ["local:registry:edit.ast-grep-plan:optional", "local:i40:installed-ast-grep-0.42.3", "gap:ast-grep:package-provenance"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "compatibilityEvidence": { "evidenceIds": ["local:i40:preview-only-artifact-contract", "gap:ast-grep:replacement-command-drill"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "conformanceEvidence": { "evidenceIds": ["local:i40:scope-and-escape-conformance", "local:i40:operation-trace", "local:i41:ci-platform-matrix"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "operationTrace": [ - { "integrationId": "edit.ast-grep-plan", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec edit.ast-grep-plan --request --out ", "implementationIdentity": { "providerId": "ast-grep", "implementationId": "edit.ast-grep-plan.compat+ast-grep-0.42.3", "activation": "optional preview-only capability envelope" }, "source": { "path": "crates/code-intel-cli/src/structured_edit.rs", "sha256": "fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "structured_edit_plan_is_scope_bound_and_preview_only" } } + { "integrationId": "edit.ast-grep-plan", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec edit.ast-grep-plan --request --out ", "implementationIdentity": { "providerId": "ast-grep", "implementationId": "edit.ast-grep-plan.compat+ast-grep-0.42.3", "activation": "optional preview-only capability envelope" }, "source": { "path": "crates/code-intel-cli/src/structured_edit.rs", "sha256": "fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "testName": "structured_edit_plan_is_scope_bound_and_preview_only" } } ], "economics": { "benefit": { "metric": "registered preview-only structural edit operations with recomputable invocation trace", "value": 1, "unit": "operations" }, "cost": { "metric": "unclosed executable lifecycle gaps", "value": 4, "unit": "gaps" }, "benefitEvidence": { "evidenceIds": ["local:i40:operation-trace", "local:i40:scope-and-escape-conformance"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "costEvidence": { "evidenceIds": ["gap:ast-grep:package-provenance", "gap:ast-grep:local-license-copy", "gap:ast-grep:replacement-command-drill", "gap:ast-grep:latency-p50-p95-measurement"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "assurance": { "maintenanceEvidence": { "evidenceIds": ["local:i40:pinned-installed-version", "gap:ast-grep:upstream-maintenance-review"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "securityEvidence": { "evidenceIds": ["local:i40:no-network-read-only-invocation", "local:i41:ci-pinned-artifact-digests", "gap:ast-grep:package-supply-chain-review"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "update": { "policy": "Do not upgrade ast-grep implicitly; before 2026-10-24 retain package provenance/license, rerun the scope and escape conformance test on the CI platform matrix, and refresh the pinned release artifact digests in ci.yml together with this record", "nextCheckAt": 1792800000, "evidence": { "evidenceIds": ["gap:ast-grep:update-review"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, - "ownedModifications": [{ "path": "crates/code-intel-cli/src/structured_edit.rs", "description": "Pipeline-owned preview-only planning adapter and snapshot-bound invocation boundary", "evidenceIds": ["local:i40:native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d", "local:i40:conformance-sha256:91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d"] }], + "ownedModifications": [{ "path": "crates/code-intel-cli/src/structured_edit.rs", "description": "Pipeline-owned preview-only planning adapter and snapshot-bound invocation boundary", "evidenceIds": ["local:i40:native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d", "local:i40:conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5"] }], "rollback": { "strategy": "disable the optional edit.ast-grep-plan capability without changing any other artifact contract; no consumer holds authority through it", "evidence": { "evidenceIds": ["local:registry:edit.ast-grep-plan:optional", "gap:ast-grep:replacement-command-drill"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "exit": { "strategy": "replace the executable only behind edit.ast-grep-plan after exact artifact and failure parity", "replacementCriteria": ["alternate command passes pattern, rewrite, scope, escape, generated-content, snapshot-lease, and oversized-output 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:ast-grep:replacement-command-drill", "gap:ast-grep:latency-p50-p95-measurement"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "retirement": { "status": "candidate", "triggers": ["replacement passes the complete structural edit planning contract", "installed executable identity or package provenance becomes unverifiable", "security or maintenance policy rejects the pinned package"], "evidence": { "evidenceIds": ["local:i40:operation-trace", "gap:ast-grep:replacement-command-drill"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, diff --git a/orchestration/internalization/codenexus.json b/orchestration/internalization/codenexus.json index 35145db4..0fcf4b46 100644 --- a/orchestration/internalization/codenexus.json +++ b/orchestration/internalization/codenexus.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.codenexus-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider codenexus-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "codenexus.full", "implementationId": "codenexus.service.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_route_runs_full_lite_and_unavailable_through_a04" } }, { "integrationId": "provider.codenexus-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -CodeNexusAdapterRequest -CodeNexusAdapterArtifactRoot -CodeNexusAdapterEvaluatedAt -CodeNexusAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_registry_facade_and_route_schema_are_declared" } }, - { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, + { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, { "integrationId": "runtime.code-nexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } }, { "integrationId": "localization.codenexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } } ], diff --git a/orchestration/internalization/rg.json b/orchestration/internalization/rg.json index 0bf9af9c..416e957d 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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5; local-conformance-sha256:91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d" }, "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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5; local-conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5" }, "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": "legacy/run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "legacy/run-code-intel.ps1", "sha256": "2059c6a80e3bf4ac73dfe8297a1474714d881e83dac5d1e55a80d78184384a3e" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "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": "295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d", "testName": "inventory_rg_exec_emits_one_result_and_stable_real_rg_artifact" } } + { "integrationId": "inventory.rg", "operation": "run", "command": "legacy/run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "legacy/run-code-intel.ps1", "sha256": "2059c6a80e3bf4ac73dfe8297a1474714d881e83dac5d1e55a80d78184384a3e" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "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": "295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5", "local:r09:conformance-sha256:91f9ee984fef0c28da495058e8c016ec3a0616aa01ebd6e1103b4b7df9b60a7d"] }], + "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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5", "local:r09:conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5"] }], "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 b31598afaad0b03fc144c5ad8a7ab5d063b435ef Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 03:39:50 +0800 Subject: [PATCH 22/27] ci: avoid redundant ripgrep apt installs --- .github/workflows/ci.yml | 9 +++++++-- .github/workflows/pr-gate.yml | 9 +++++++-- .github/workflows/release.yml | 9 +++++++-- 3 files changed, 21 insertions(+), 6 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 0272f8ae..1a951010 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -391,8 +391,13 @@ jobs: brew install ripgrep } else { - sudo apt-get update - sudo apt-get install -y ripgrep + if (Get-Command rg -ErrorAction SilentlyContinue) { + rg --version + } + else { + sudo apt-get update + sudo apt-get install -y ripgrep + } } - name: Install ast-grep (pinned) diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml index fe074d15..f43609e2 100644 --- a/.github/workflows/pr-gate.yml +++ b/.github/workflows/pr-gate.yml @@ -249,8 +249,13 @@ jobs: - name: Install ripgrep shell: pwsh run: | - sudo apt-get update - sudo apt-get install -y ripgrep + if (Get-Command rg -ErrorAction SilentlyContinue) { + rg --version + } + else { + sudo apt-get update + sudo apt-get install -y ripgrep + } - name: Build Rust CLI shell: pwsh diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 1f0e6157..66d0f753 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -345,8 +345,13 @@ jobs: brew install ripgrep } else { - sudo apt-get update - sudo apt-get install -y ripgrep + if (Get-Command rg -ErrorAction SilentlyContinue) { + rg --version + } + else { + sudo apt-get update + sudo apt-get install -y ripgrep + } } - name: Build Rust CLI From 57421960edd47f57a01f88b4ba794e61e6cae863 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 04:23:42 +0800 Subject: [PATCH 23/27] feat: dispatch all sentrux capabilities automatically --- .github/workflows/ci.yml | 24 +- .github/workflows/pr-gate.yml | 22 +- .github/workflows/release.yml | 48 +++- .../src/builtin_provider_evidence.rs | 3 + crates/code-intel-cli/src/change_impact.rs | 25 +- .../src/sentrux_capabilities.rs | 48 +++- .../src/sentrux_capability_artifacts.rs | 25 +- .../src/sentrux_lite_capabilities.rs | 213 +++++++++++++++++- .../code-intel-cli/tests/capability_exec.rs | 2 +- crates/code-intel-cli/tests/dag_run.rs | 6 +- orchestration/integrations.json | 6 +- orchestration/internalization/ast-grep.json | 6 +- orchestration/internalization/codenexus.json | 2 +- orchestration/internalization/graph.json | 2 +- orchestration/internalization/rg.json | 8 +- orchestration/internalization/sentrux.json | 6 +- ...sentrux-capability-artifact.v1.schema.json | 3 + ...l-sentrux-capability-matrix.v1.schema.json | 9 +- .../sentrux-capability-matrix.v1.json | 25 +- 19 files changed, 416 insertions(+), 67 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1a951010..28f2920b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -732,13 +732,25 @@ jobs: $observed[$capabilityPayload.capabilityId] = $capabilityPayload if ($capabilityPayload.status -ne "succeeded" -and $null -eq $capabilityPayload.failure) { throw "packaged non-success capability has no explicit failure: $($capabilityPayload.capabilityId)" } } - $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) - if ($required.Count -eq 0) { throw "capability matrix exposes no required automatic capabilities" } - foreach ($id in $required) { - if (-not $observed.ContainsKey($id)) { throw "packaged run is missing required automatic capability: $id" } - if ($observed[$id].status -ne "succeeded") { throw "packaged required capability did not succeed: $id ($($observed[$id].status))" } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease }) + if ($required.Count -eq 0) { throw "capability matrix exposes no required capabilities" } + foreach ($capability in $required) { + $id = [string]$capability.id + if (-not $observed.ContainsKey($id)) { throw "packaged run is missing required Sentrux capability: $id" } + $payload = $observed[$id] + $mode = [string]$capability.executionMode + if ($mode -eq "automatic") { + if ($capability.currentState -eq "authoritative_automatic" -and $payload.status -ne "succeeded") { throw "packaged authoritative automatic capability did not succeed: $id ($($payload.status))" } + if ($capability.currentState -eq "automatic_degraded" -and $payload.status -notin @("succeeded", "degraded")) { throw "packaged degraded automatic capability did not produce an accepted result: $id ($($payload.status))" } + if ($capability.currentState -notin @("authoritative_automatic", "automatic_degraded")) { throw "packaged automatic capability has an unsupported matrix state: $id ($($capability.currentState))" } + } + elseif ($mode -in @("explicit_authority", "lifecycle_external")) { + if ($payload.status -ne "not_applicable") { throw "packaged explicit/lifecycle capability must be explicitly not_applicable: $id ($($payload.status))" } + if ($null -eq $payload.failure -or [string]::IsNullOrWhiteSpace([string]$payload.failure.kind)) { throw "packaged explicit/lifecycle capability has no failure reason: $id" } + } + else { throw "packaged capability has no recognized execution mode: $id ($mode)" } } - Write-Host "Packaged Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; matrix coverage=$($matrix.coverageStatus)" + Write-Host "Packaged Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; required=$($required.Count); matrix coverage=$($matrix.coverageStatus)" - name: Assert PATH and CODE_INTEL_HOME persisted (Windows) if: runner.os == 'Windows' diff --git a/.github/workflows/pr-gate.yml b/.github/workflows/pr-gate.yml index f43609e2..e2180b3e 100644 --- a/.github/workflows/pr-gate.yml +++ b/.github/workflows/pr-gate.yml @@ -312,12 +312,24 @@ jobs: $observed[$payload.capabilityId] = $payload if ($payload.status -ne "succeeded" -and $null -eq $payload.failure) { throw "non-success Sentrux capability has no explicit failure: $($payload.capabilityId)" } } - $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) - foreach ($id in $required) { - if (-not $observed.ContainsKey($id)) { throw "required automatic Sentrux capability is missing from the committed run: $id" } - if ($observed[$id].status -ne "succeeded") { throw "required automatic Sentrux capability did not succeed: $id ($($observed[$id].status))" } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease }) + foreach ($capability in $required) { + $id = [string]$capability.id + if (-not $observed.ContainsKey($id)) { throw "required Sentrux capability is missing from the committed run: $id" } + $payload = $observed[$id] + $mode = [string]$capability.executionMode + if ($mode -eq "automatic") { + if ($capability.currentState -eq "authoritative_automatic" -and $payload.status -ne "succeeded") { throw "authoritative automatic Sentrux capability did not succeed: $id ($($payload.status))" } + if ($capability.currentState -eq "automatic_degraded" -and $payload.status -notin @("succeeded", "degraded")) { throw "degraded automatic Sentrux capability did not produce an accepted result: $id ($($payload.status))" } + if ($capability.currentState -notin @("authoritative_automatic", "automatic_degraded")) { throw "automatic Sentrux capability has an unsupported matrix state: $id ($($capability.currentState))" } + } + elseif ($mode -in @("explicit_authority", "lifecycle_external")) { + if ($payload.status -ne "not_applicable") { throw "explicit/lifecycle Sentrux capability must be explicitly not_applicable in a DAG run: $id ($($payload.status))" } + if ($null -eq $payload.failure -or [string]::IsNullOrWhiteSpace([string]$payload.failure.kind)) { throw "explicit/lifecycle Sentrux capability has no failure reason: $id" } + } + else { throw "Sentrux capability has no recognized execution mode: $id ($mode)" } } - Write-Host "Sentrux capability closure verified: $($capabilityRefs.Count) capability refs; required automatic=$($required.Count); matrix coverage=$($matrix.coverageStatus)" + Write-Host "Sentrux capability closure verified: $($capabilityRefs.Count) capability refs; required=$($required.Count); matrix coverage=$($matrix.coverageStatus)" - name: Upload Sentrux closure evidence if: always() diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 66d0f753..b4a709fd 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -269,13 +269,25 @@ jobs: $observed[$capabilityPayload.capabilityId] = $capabilityPayload if ($capabilityPayload.status -ne "succeeded" -and $null -eq $capabilityPayload.failure) { throw "packaged release non-success capability has no explicit failure: $($capabilityPayload.capabilityId)" } } - $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) - if ($required.Count -eq 0) { throw "capability matrix exposes no required automatic capabilities" } - foreach ($id in $required) { - if (-not $observed.ContainsKey($id)) { throw "packaged release run is missing required automatic capability: $id" } - if ($observed[$id].status -ne "succeeded") { throw "packaged release required capability did not succeed: $id ($($observed[$id].status))" } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease }) + if ($required.Count -eq 0) { throw "capability matrix exposes no required capabilities" } + foreach ($capability in $required) { + $id = [string]$capability.id + if (-not $observed.ContainsKey($id)) { throw "packaged release run is missing required Sentrux capability: $id" } + $payload = $observed[$id] + $mode = [string]$capability.executionMode + if ($mode -eq "automatic") { + if ($capability.currentState -eq "authoritative_automatic" -and $payload.status -ne "succeeded") { throw "packaged release authoritative automatic capability did not succeed: $id ($($payload.status))" } + if ($capability.currentState -eq "automatic_degraded" -and $payload.status -notin @("succeeded", "degraded")) { throw "packaged release degraded automatic capability did not produce an accepted result: $id ($($payload.status))" } + if ($capability.currentState -notin @("authoritative_automatic", "automatic_degraded")) { throw "packaged release automatic capability has an unsupported matrix state: $id ($($capability.currentState))" } + } + elseif ($mode -in @("explicit_authority", "lifecycle_external")) { + if ($payload.status -ne "not_applicable") { throw "packaged release explicit/lifecycle capability must be explicitly not_applicable: $id ($($payload.status))" } + if ($null -eq $payload.failure -or [string]::IsNullOrWhiteSpace([string]$payload.failure.kind)) { throw "packaged release explicit/lifecycle capability has no failure reason: $id" } + } + else { throw "packaged release capability has no recognized execution mode: $id ($mode)" } } - Write-Host "Packaged release Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; matrix coverage=$($matrix.coverageStatus)" + Write-Host "Packaged release Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; required=$($required.Count); matrix coverage=$($matrix.coverageStatus)" # ai-safety-003 (issue #34): if the packaged snapshot carries an # audit report, it must pass the same fail-closed validate @@ -487,13 +499,25 @@ jobs: $observed[$capabilityPayload.capabilityId] = $capabilityPayload if ($capabilityPayload.status -ne "succeeded" -and $null -eq $capabilityPayload.failure) { throw "packaged release non-success capability has no explicit failure: $($capabilityPayload.capabilityId)" } } - $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease -and $_.currentState -eq "authoritative_automatic" } | ForEach-Object { $_.id }) - if ($required.Count -eq 0) { throw "capability matrix exposes no required automatic capabilities" } - foreach ($id in $required) { - if (-not $observed.ContainsKey($id)) { throw "packaged release run is missing required automatic capability: $id" } - if ($observed[$id].status -ne "succeeded") { throw "packaged release required capability did not succeed: $id ($($observed[$id].status))" } + $required = @($matrix.capabilities | Where-Object { $_.requiredForRelease }) + if ($required.Count -eq 0) { throw "capability matrix exposes no required capabilities" } + foreach ($capability in $required) { + $id = [string]$capability.id + if (-not $observed.ContainsKey($id)) { throw "packaged release run is missing required Sentrux capability: $id" } + $payload = $observed[$id] + $mode = [string]$capability.executionMode + if ($mode -eq "automatic") { + if ($capability.currentState -eq "authoritative_automatic" -and $payload.status -ne "succeeded") { throw "packaged release authoritative automatic capability did not succeed: $id ($($payload.status))" } + if ($capability.currentState -eq "automatic_degraded" -and $payload.status -notin @("succeeded", "degraded")) { throw "packaged release degraded automatic capability did not produce an accepted result: $id ($($payload.status))" } + if ($capability.currentState -notin @("authoritative_automatic", "automatic_degraded")) { throw "packaged release automatic capability has an unsupported matrix state: $id ($($capability.currentState))" } + } + elseif ($mode -in @("explicit_authority", "lifecycle_external")) { + if ($payload.status -ne "not_applicable") { throw "packaged release explicit/lifecycle capability must be explicitly not_applicable: $id ($($payload.status))" } + if ($null -eq $payload.failure -or [string]::IsNullOrWhiteSpace([string]$payload.failure.kind)) { throw "packaged release explicit/lifecycle capability has no failure reason: $id" } + } + else { throw "packaged release capability has no recognized execution mode: $id ($mode)" } } - Write-Host "Packaged release Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; matrix coverage=$($matrix.coverageStatus)" + Write-Host "Packaged release Sentrux closure verified from committed refs: $($capabilityRefs.Count) capability refs; required=$($required.Count); matrix coverage=$($matrix.coverageStatus)" # ai-safety-003 (issue #34): if the packaged snapshot carries an # audit report, it must pass the same fail-closed validate diff --git a/crates/code-intel-cli/src/builtin_provider_evidence.rs b/crates/code-intel-cli/src/builtin_provider_evidence.rs index 0eed4063..1cb849f3 100644 --- a/crates/code-intel-cli/src/builtin_provider_evidence.rs +++ b/crates/code-intel-cli/src/builtin_provider_evidence.rs @@ -507,6 +507,9 @@ fn run_sentrux( subcommand, ) } + "what_if" => { + return json_command(sentrux_lite_capabilities::what_if_json(repo), subcommand) + } "provider_discovery" => { return json_command( sentrux_lite_capabilities::provider_discovery_json(), diff --git a/crates/code-intel-cli/src/change_impact.rs b/crates/code-intel-cli/src/change_impact.rs index fa9c1653..e8c6a1a9 100644 --- a/crates/code-intel-cli/src/change_impact.rs +++ b/crates/code-intel-cli/src/change_impact.rs @@ -398,9 +398,9 @@ fn sentrux_evidence(evidence: &CommittedEvidence, stale: bool) -> (Vec, V /// /// The capability payload contains command provenance for audit purposes, but /// this projection intentionally never reads `outputs.command.stdout`. The -/// current lite `test_gaps` and DSM routes do not publish structured test -/// candidates, so their status is useful for deciding whether to expand the -/// graph-selected candidates, not for inventing new paths from provider text. +/// Capability consumers use only structured data admitted into the artifact; +/// provider stdout remains provenance/preview evidence and is never parsed by +/// this projection. fn sentrux_test_selection_signals(evidence: &CommittedEvidence, stale: bool) -> Value { let payloads = evidence .refs @@ -418,12 +418,23 @@ fn sentrux_test_selection_signals(evidence: &CommittedEvidence, stale: bool) -> let dsm_payload = payloads .iter() .find(|payload| payload["capabilityId"] == "sentrux.dsm"); + let what_if_payload = payloads + .iter() + .find(|payload| payload["capabilityId"] == "sentrux.what_if"); let test_gap = sentrux_signal("test_gaps", test_gap_payload); let dsm = sentrux_signal("dsm", dsm_payload); - let has_signal = test_gap["status"] != "unknown" || dsm["status"] != "unknown"; + let what_if = sentrux_signal("what_if", what_if_payload); + let has_signal = test_gap["status"] != "unknown" + || dsm["status"] != "unknown" + || what_if["status"] != "unknown"; let all_available = test_gap["status"] == "available" && dsm["status"] == "available"; + let what_if_risk = what_if["structuredData"]["summary"]["failingScenarioCount"] + .as_u64() + .unwrap_or(0); let candidate_test_impact = if !has_signal { "unknown" + } else if what_if_risk > 0 && !stale { + "retains_graph_candidates_with_what_if_risk" } else if all_available && !stale { "retains_graph_candidates" } else { @@ -448,7 +459,7 @@ fn sentrux_test_selection_signals(evidence: &CommittedEvidence, stale: bool) -> .to_string(), ); } - for signal in [&test_gap, &dsm] { + for signal in [&test_gap, &dsm, &what_if] { if let Some(items) = signal["limitations"].as_array() { limitations.extend(items.iter().filter_map(Value::as_str).map(str::to_owned)); } @@ -459,6 +470,8 @@ fn sentrux_test_selection_signals(evidence: &CommittedEvidence, stale: bool) -> "status":status, "testGap":test_gap, "dsm":dsm, + "whatIf":what_if, + "whatIfFailingScenarioCount":what_if_risk, "candidateTestImpact":candidate_test_impact, "limitations":limitations, }) @@ -471,6 +484,7 @@ fn sentrux_signal(name: &str, payload: Option<&Value>) -> Value { "capabilityStatus":"missing", "authority":"unknown", "candidateImpact":"unknown", + "structuredData":Value::Null, "limitations":[format!("No verified sentrux.{name} capability artifact payload is present in the committed manifest.")], }); }; @@ -501,6 +515,7 @@ fn sentrux_signal(name: &str, payload: Option<&Value>) -> Value { "capabilityStatus":capability_status, "authority":authority, "candidateImpact":candidate_impact, + "structuredData":payload["outputs"]["structuredData"], "limitations":[limitation], }) } diff --git a/crates/code-intel-cli/src/sentrux_capabilities.rs b/crates/code-intel-cli/src/sentrux_capabilities.rs index 4adfb2f4..006e8aad 100644 --- a/crates/code-intel-cli/src/sentrux_capabilities.rs +++ b/crates/code-intel-cli/src/sentrux_capabilities.rs @@ -85,6 +85,24 @@ fn capability_audit(matrix: &Value) -> Result { .iter() .filter_map(Value::as_str) .collect::>(); + let automatic_modes = policy["automaticExecutionModes"] + .as_array() + .ok_or("sentrux capabilities matrix schema/header error: automatic execution modes must be an array")? + .iter() + .filter_map(Value::as_str) + .collect::>(); + let explicit_modes = policy["explicitExecutionModes"] + .as_array() + .ok_or("sentrux capabilities matrix schema/header error: explicit execution modes must be an array")? + .iter() + .filter_map(Value::as_str) + .collect::>(); + let forbidden_states = policy["forbiddenSilentStates"] + .as_array() + .ok_or("sentrux capabilities matrix schema/header error: forbidden silent states must be an array")? + .iter() + .filter_map(Value::as_str) + .collect::>(); let mut state_counts = BTreeMap::::new(); let mut required = 0u64; @@ -99,7 +117,16 @@ fn capability_audit(matrix: &Value) -> Result { let current_state = item["currentState"].as_str().ok_or( "sentrux capabilities matrix schema/header error: currentState must be a string", )?; - let is_covered = required_states.contains(current_state); + let execution_mode = item["executionMode"].as_str().ok_or( + "sentrux capabilities matrix schema/header error: executionMode must be a string", + )?; + let is_covered = if automatic_modes.contains(execution_mode) { + required_states.contains(current_state) + } else if explicit_modes.contains(execution_mode) { + !forbidden_states.contains(current_state) + } else { + false + }; *state_counts.entry(current_state.to_string()).or_default() += 1; if is_covered { covered += 1; @@ -114,6 +141,7 @@ fn capability_audit(matrix: &Value) -> Result { "id": item["id"], "operation": item["operation"], "currentState": item["currentState"], + "executionMode": item["executionMode"], "route": item["route"], "decisionConsumers": item["decisionConsumers"], })); @@ -123,16 +151,23 @@ fn capability_audit(matrix: &Value) -> Result { .iter() .filter(|item| item["requiredForRelease"].as_bool().unwrap_or(false)) .all(|item| { + let execution_mode = item["executionMode"].as_str().unwrap_or(""); let state_allowed = item["currentState"] .as_str() .is_some_and(|state| required_states.contains(state)); + let explicit_state_allowed = item["currentState"] + .as_str() + .is_some_and(|state| !forbidden_states.contains(state)); let has_artifact = item["artifacts"] .as_array() .is_some_and(|values| !values.is_empty()); let has_consumer = item["decisionConsumers"] .as_array() .is_some_and(|values| !values.is_empty()); - state_allowed && has_artifact && has_consumer + ((automatic_modes.contains(execution_mode) && state_allowed) + || (explicit_modes.contains(execution_mode) && explicit_state_allowed)) + && has_artifact + && has_consumer }); let coverage = serde_json::json!({ @@ -186,6 +221,8 @@ fn validate_capability_matrix(matrix: &Value, path: &Path) -> Result<()> { path, "completionPolicy", )?; + string_array(policy, "automaticExecutionModes", path, "completionPolicy")?; + string_array(policy, "explicitExecutionModes", path, "completionPolicy")?; string_array(policy, "forbiddenSilentStates", path, "completionPolicy")?; if policy.get("rule").and_then(Value::as_str).is_none() { return Err(matrix_error(path, "completionPolicy.rule must be a string")); @@ -205,7 +242,7 @@ fn validate_capability_matrix(matrix: &Value, path: &Path) -> Result<()> { let item = capability.as_object().ok_or_else(|| { matrix_error(path, &format!("capabilities[{index}] must be an object")) })?; - for field in ["id", "operation", "currentState", "route"] { + for field in ["id", "operation", "executionMode", "currentState", "route"] { if item.get(field).and_then(Value::as_str).is_none() { return Err(matrix_error( path, @@ -360,6 +397,8 @@ mod tests { "coverageStatus": "partial", "completionPolicy": { "requiredStatesForComplete": ["authoritative_automatic"], + "automaticExecutionModes": ["automatic"], + "explicitExecutionModes": ["explicit_authority", "lifecycle_external"], "forbiddenSilentStates": ["declared_only"], "rule": "rule" }, @@ -381,6 +420,8 @@ mod tests { "coverageStatus": "complete", "completionPolicy": { "requiredStatesForComplete": ["authoritative_automatic"], + "automaticExecutionModes": ["automatic"], + "explicitExecutionModes": ["explicit_authority", "lifecycle_external"], "forbiddenSilentStates": ["declared_only"], "rule": "rule" }, @@ -389,6 +430,7 @@ mod tests { "operation": "example", "aliases": [], "currentState": "authoritative_automatic", + "executionMode": "automatic", "route": "provider.sentrux-adapt", "requiredForRelease": true, "artifacts": ["example.v1"], diff --git a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs index ca7b59a5..c624aad5 100644 --- a/crates/code-intel-cli/src/sentrux_capability_artifacts.rs +++ b/crates/code-intel-cli/src/sentrux_capability_artifacts.rs @@ -74,15 +74,7 @@ const SENTRUX_CAPABILITY_ROUTES: [CapabilityRoute; 15] = [ "test_gaps", RouteKind::Command, ), - route( - "sentrux.what_if", - "what_if", - "what_if", - RouteKind::NotApplicable { - failure_kind: "dag_scope_not_supported", - message: "what_if requires an explicit change set and is not applicable to this repository DAG snapshot", - }, - ), + route("sentrux.what_if", "what_if", "what_if", RouteKind::Command), route( "sentrux.session_start", "session_start", @@ -174,6 +166,7 @@ pub(super) fn collect_sentrux_capabilities( | "git_stats" | "evolution" | "test_gaps" + | "what_if" | "provider_discovery" ) { @@ -257,7 +250,8 @@ pub(super) fn build_capability_artifacts( "outputs":{ "command":observation["command"], "verdict":observation["verdict"], - "outputSummary":observation["outputSummary"] + "outputSummary":observation["outputSummary"], + "structuredData":capability_structured_data(observation) }, "failure":capability_failure(observation, status), "freshness":{ @@ -296,6 +290,14 @@ pub(super) fn build_capability_artifacts( Ok((artifacts, refs)) } +fn capability_structured_data(observation: &Value) -> Value { + observation["command"]["stdout"] + .as_str() + .and_then(|stdout| serde_json::from_str::(stdout).ok()) + .filter(|value| value.is_object() || value.is_array()) + .unwrap_or(Value::Null) +} + fn sentrux_capability_provider(provider_mode: &str) -> Value { match provider_mode { "external" => json!({ @@ -325,6 +327,7 @@ fn capability_failure(observation: &Value, status: &str) -> Value { } let raw_kind = observation["failure"]["kind"].as_str().unwrap_or("unknown"); let kind = match raw_kind { + "degraded" => "degraded", "explicit_mutation_required" | "dag_scope_not_supported" | "session_lifecycle_outside_dag" @@ -551,7 +554,7 @@ fn route_provider_mode(route: &CapabilityRoute, tool_path_prefix: Option<&Path>) fn uses_lite_fallback(command: &str) -> bool { matches!( command, - "git_stats" | "evolution" | "test_gaps" | "provider_discovery" + "git_stats" | "evolution" | "test_gaps" | "what_if" | "provider_discovery" ) } diff --git a/crates/code-intel-cli/src/sentrux_lite_capabilities.rs b/crates/code-intel-cli/src/sentrux_lite_capabilities.rs index 50c9a8b8..66f8a531 100644 --- a/crates/code-intel-cli/src/sentrux_lite_capabilities.rs +++ b/crates/code-intel-cli/src/sentrux_lite_capabilities.rs @@ -9,8 +9,10 @@ pub(super) fn provider_discovery_json() -> Result { "provider":"sentrux", "mode":"builtin_lite", "available":true, - "operations":["scan","health","dsm","git_stats","evolution","test_gaps","check_rules","check","gate","rescan"], + "operations":["scan","health","dsm","git_stats","evolution","test_gaps","what_if","check_rules","check","gate","rescan"], "aliases":["pro_status","plugin_list","plugin_validate"], + "explicitAuthorityOperations":["gate_save"], + "lifecycleOperations":["session_start","session_end"], "legacyFallback":"legacy/Invoke-SentruxAgentTool.ps1" })) } @@ -116,6 +118,38 @@ mod tests { assert_eq!(evolution["windowCommits"], 0); assert_eq!(evolution["trend"], "unknown"); } + + #[test] + fn what_if_is_a_bounded_snapshot_capability() { + let root = std::env::temp_dir().join(format!( + "code-intel-lite-what-if-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .expect("clock") + .as_nanos() + )); + std::fs::create_dir_all(root.join(".sentrux")).expect("rules directory"); + std::fs::create_dir_all(root.join("src")).expect("source directory"); + std::fs::write( + root.join(".sentrux/rules.toml"), + "max_cc = 1\nmax_coupling = 1\n", + ) + .expect("rules"); + std::fs::write( + root.join("src/lib.rs"), + "pub fn sample() { if true { println!(\"x\"); } }\n", + ) + .expect("source"); + + let value = super::what_if_json(&root).expect("what_if should be available"); + assert_eq!(value["status"], "ok"); + assert_eq!(value["summary"]["scenarioCount"], 4); + assert!(value["summary"]["failingScenarioCount"].as_u64().unwrap() > 0); + assert_eq!(value["scenarios"].as_array().unwrap().len(), 4); + assert!(value["limitations"].as_array().unwrap().len() >= 2); + let _ = std::fs::remove_dir_all(root); + } } pub(super) fn test_gaps_json(repo: &Path) -> Result { @@ -172,3 +206,180 @@ pub(super) fn test_gaps_json(repo: &Path) -> Result { "limitations":["This lite fallback inventories test files; it does not prove symbol-level test coverage."] })) } + +pub(super) fn what_if_json(repo: &Path) -> Result { + let dsm = super::sentrux_analysis::analyze(repo)?; + let max_cc = read_rule_number(repo, "max_cc").unwrap_or(25.0); + let max_coupling = read_rule_number(repo, "max_coupling").unwrap_or(76.0); + let blast_limit = max_coupling + 2.0; + + let complexity = dsm["file_details"] + .as_array() + .into_iter() + .flatten() + .flat_map(|file| { + file["functions"] + .as_array() + .into_iter() + .flatten() + .filter_map(|function| { + let value = function["complexity"].as_f64()?; + (value > max_cc).then(|| { + json!({ + "id":function["id"], + "name":function["name"], + "file":file["path"], + "sourceAnchor":function["source_anchor"], + "value":value, + "limit":max_cc, + "overBy":value - max_cc + }) + }) + }) + }) + .collect::>(); + let coupling = dsm["modules"] + .as_array() + .into_iter() + .flatten() + .filter_map(|module| { + let value = module["metrics"]["coupling"].as_f64()?; + (value > max_coupling).then(|| { + json!({ + "id":module["id"], + "name":module["name"], + "metric":"coupling", + "value":value, + "limit":max_coupling, + "risk":module["metrics"]["risk"] + }) + }) + }) + .collect::>(); + let blast_radius = dsm["modules"] + .as_array() + .into_iter() + .flatten() + .filter_map(|module| { + let value = module["metrics"]["blast_radius"].as_f64()?; + (value > blast_limit).then(|| { + json!({ + "id":module["id"], + "name":module["name"], + "metric":"blast_radius", + "value":value, + "limit":blast_limit, + "risk":module["metrics"]["risk"] + }) + }) + }) + .collect::>(); + let test_gaps = dsm["modules"] + .as_array() + .into_iter() + .flatten() + .filter_map(|module| { + let value = module["metrics"]["test_gap"].as_f64()?; + (value > 0.0).then(|| { + json!({ + "id":module["id"], + "name":module["name"], + "metric":"test_gap", + "value":value, + "limit":0, + "risk":module["metrics"]["risk"] + }) + }) + }) + .collect::>(); + + let scenarios = vec![ + what_if_scenario( + "current_max_cc_gate", + "max_cc", + max_cc, + complexity, + "Split or simplify functions above the current Sentrux complexity ceiling.", + ), + what_if_scenario( + "module_coupling_cap", + "max_coupling", + max_coupling, + coupling, + "Inspect dependency edges and preserve provider boundaries before adding coupling.", + ), + what_if_scenario( + "blast_radius_cap", + "max_blast_radius", + blast_limit, + blast_radius, + "Reduce fan-out or split the highest-impact module before expanding its surface.", + ), + what_if_scenario( + "test_gap_gate", + "test_gap", + 0.0, + test_gaps, + "Add or select tests for source-heavy modules before treating the change as fully covered.", + ), + ]; + let failing = scenarios + .iter() + .filter(|scenario| scenario["pass"] == false) + .count(); + let primary_risk = scenarios + .iter() + .find(|scenario| scenario["pass"] == false) + .and_then(|scenario| scenario["id"].as_str()) + .unwrap_or("none"); + Ok(json!({ + "status":"ok", + "scope":"repository_snapshot", + "rules":{ + "max_cc":max_cc, + "max_coupling":max_coupling, + "max_blast_radius":blast_limit, + "source":if repo.join(".sentrux/rules.toml").is_file() { "repository" } else { "defaults" } + }, + "scenarios":scenarios, + "summary":{ + "scenarioCount":4, + "failingScenarioCount":failing, + "primaryRisk":primary_risk + }, + "limitations":[ + "Lite what_if evaluates the current snapshot; it does not mutate or synthesize a hypothetical checkout.", + "Function and dependency extraction are heuristic and remain bounded by the lite DSM parser." + ] + })) +} + +fn read_rule_number(repo: &Path, name: &str) -> Option { + let text = fs::read_to_string(repo.join(".sentrux/rules.toml")).ok()?; + text.lines() + .find_map(|line| { + let (key, value) = line.split_once('=')?; + (key.trim() == name).then(|| value.trim().trim_matches('"').parse().ok()) + }) + .flatten() +} + +fn what_if_scenario( + id: &str, + metric: &str, + limit: f64, + affected: Vec, + action: &str, +) -> Value { + let pass = affected.is_empty(); + json!({ + "id":id, + "metric":metric, + "pass":pass, + "severity":if pass { "ok" } else { "high" }, + "impactCount":affected.len(), + "affected":affected.into_iter().take(20).collect::>(), + "limit":limit, + "action":action + }) +} diff --git a/crates/code-intel-cli/tests/capability_exec.rs b/crates/code-intel-cli/tests/capability_exec.rs index a84c9f0c..036cf132 100644 --- a/crates/code-intel-cli/tests/capability_exec.rs +++ b/crates/code-intel-cli/tests/capability_exec.rs @@ -14,7 +14,7 @@ const STRUCTURED_EDIT_DIGEST: &str = const REPO_SNAPSHOT_DIGEST: &str = "4f42b080fd19e501a6315ee204add188d69625bedd15c566fea48bb1f3e78764"; const CODENEXUS_TOOLCHAIN_DIGESTS: [&str; 5] = [ - "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", + "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150", "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", "98ccc64478b2c61bfd7af741ea1f8ee01a88094065c0f025700e8110b525ef26", diff --git a/crates/code-intel-cli/tests/dag_run.rs b/crates/code-intel-cli/tests/dag_run.rs index 62dec4d2..ff6989f9 100644 --- a/crates/code-intel-cli/tests/dag_run.rs +++ b/crates/code-intel-cli/tests/dag_run.rs @@ -369,8 +369,12 @@ fn production_dag_output_commits_and_enters_the_authoritative_index() { ); assert_eq!( impact["testSelection"]["sentruxSignals"]["candidateTestImpact"], - "retains_graph_candidates" + "retains_graph_candidates_with_what_if_risk" ); + assert!(impact["testSelection"]["sentruxSignals"]["whatIfFailingScenarioCount"] + .as_u64() + .unwrap_or(0) + > 0); assert!( impact["testSelection"]["sentruxSignals"]["testGap"]["limitations"] .as_array() diff --git a/orchestration/integrations.json b/orchestration/integrations.json index 7ff8a7be..2b1119fa 100644 --- a/orchestration/integrations.json +++ b/orchestration/integrations.json @@ -535,7 +535,7 @@ "id": "provider.graph-builtin.compat", "version": "1.0.0", "toolchainDigests": [ - "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", + "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150", "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947", "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", @@ -587,7 +587,7 @@ "id": "provider.sentrux-builtin.compat", "version": "1.0.0", "toolchainDigests": [ - "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", + "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150", "29ad124d984f6ab0756bdc7c0b523a84dfada2a655fe0d282ce681165f6bae4f", "f69776e6800f917099794a5a28dbdb1117eaf657568dd09e34bf33e4207ddb4c", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", @@ -662,7 +662,7 @@ "id": "provider.codenexus-builtin.compat", "version": "1.0.0", "toolchainDigests": [ - "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36", + "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150", "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8", "52644a812174988ede91d98ddfec63c6a91f8478277d7bf74c73f106dd0f776b", "98ccc64478b2c61bfd7af741ea1f8ee01a88094065c0f025700e8110b525ef26", diff --git a/orchestration/internalization/ast-grep.json b/orchestration/internalization/ast-grep.json index 94111831..65f4bf79 100644 --- a/orchestration/internalization/ast-grep.json +++ b/orchestration/internalization/ast-grep.json @@ -2,15 +2,15 @@ "schema": "code-intel-internalization-record.v1", "id": "internalization.ast-grep-record", "projectId": "code-intel-pipeline", - "subject": { "name": "ast-grep structural search executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/ast-grep/ast-grep; installed-evidence=ast-grep 0.42.3", "revision": "installed-version:0.42.3; local-native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d; local-conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5" }, "license": { "id": "MIT-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the MIT license text and package provenance are retained locally", "preserve preview-only authority, snapshot lease binding, scope and escape guards, generated-content exclusions, and failure semantics exactly"] } }, + "subject": { "name": "ast-grep structural search executable", "kind": "adapted_capability", "source": { "uri": "https://github.com/ast-grep/ast-grep; installed-evidence=ast-grep 0.42.3", "revision": "installed-version:0.42.3; local-native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d; local-conformance-sha256:49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171" }, "license": { "id": "MIT-UPSTREAM-CLAIM-LOCAL-COPY-MISSING", "obligations": ["do not redistribute or upgrade from this record until the MIT license text and package provenance are retained locally", "preserve preview-only authority, snapshot lease binding, scope and escape guards, generated-content exclusions, and failure semantics exactly"] } }, "adoption": { "rung": "invoke", "ownedBoundary": ["Pipeline owns edit.ast-grep-plan request validation, snapshot lease, path scope and escape guards, artifact contract, and failure mapping", "ast-grep owns pattern parsing, structural matching, and rewrite preview computation; this record does not authorize upgrade, vendoring, repository mutation, or reimplementation"], "necessityEvidence": { "evidenceIds": ["local:registry:edit.ast-grep-plan:optional", "local:i40:installed-ast-grep-0.42.3", "gap:ast-grep:package-provenance"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "compatibilityEvidence": { "evidenceIds": ["local:i40:preview-only-artifact-contract", "gap:ast-grep:replacement-command-drill"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "conformanceEvidence": { "evidenceIds": ["local:i40:scope-and-escape-conformance", "local:i40:operation-trace", "local:i41:ci-platform-matrix"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "operationTrace": [ - { "integrationId": "edit.ast-grep-plan", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec edit.ast-grep-plan --request --out ", "implementationIdentity": { "providerId": "ast-grep", "implementationId": "edit.ast-grep-plan.compat+ast-grep-0.42.3", "activation": "optional preview-only capability envelope" }, "source": { "path": "crates/code-intel-cli/src/structured_edit.rs", "sha256": "fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "testName": "structured_edit_plan_is_scope_bound_and_preview_only" } } + { "integrationId": "edit.ast-grep-plan", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec edit.ast-grep-plan --request --out ", "implementationIdentity": { "providerId": "ast-grep", "implementationId": "edit.ast-grep-plan.compat+ast-grep-0.42.3", "activation": "optional preview-only capability envelope" }, "source": { "path": "crates/code-intel-cli/src/structured_edit.rs", "sha256": "fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171", "testName": "structured_edit_plan_is_scope_bound_and_preview_only" } } ], "economics": { "benefit": { "metric": "registered preview-only structural edit operations with recomputable invocation trace", "value": 1, "unit": "operations" }, "cost": { "metric": "unclosed executable lifecycle gaps", "value": 4, "unit": "gaps" }, "benefitEvidence": { "evidenceIds": ["local:i40:operation-trace", "local:i40:scope-and-escape-conformance"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "costEvidence": { "evidenceIds": ["gap:ast-grep:package-provenance", "gap:ast-grep:local-license-copy", "gap:ast-grep:replacement-command-drill", "gap:ast-grep:latency-p50-p95-measurement"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "assurance": { "maintenanceEvidence": { "evidenceIds": ["local:i40:pinned-installed-version", "gap:ast-grep:upstream-maintenance-review"], "checkedAt": 1785024000, "expiresAt": 1792800000 }, "securityEvidence": { "evidenceIds": ["local:i40:no-network-read-only-invocation", "local:i41:ci-pinned-artifact-digests", "gap:ast-grep:package-supply-chain-review"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "update": { "policy": "Do not upgrade ast-grep implicitly; before 2026-10-24 retain package provenance/license, rerun the scope and escape conformance test on the CI platform matrix, and refresh the pinned release artifact digests in ci.yml together with this record", "nextCheckAt": 1792800000, "evidence": { "evidenceIds": ["gap:ast-grep:update-review"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, - "ownedModifications": [{ "path": "crates/code-intel-cli/src/structured_edit.rs", "description": "Pipeline-owned preview-only planning adapter and snapshot-bound invocation boundary", "evidenceIds": ["local:i40:native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d", "local:i40:conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5"] }], + "ownedModifications": [{ "path": "crates/code-intel-cli/src/structured_edit.rs", "description": "Pipeline-owned preview-only planning adapter and snapshot-bound invocation boundary", "evidenceIds": ["local:i40:native-source-sha256:fb1bc02fbe9335e1ccbe66ad12ca2927bb3bace4722735e62b1fb2ab053af72d", "local:i40:conformance-sha256:49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171"] }], "rollback": { "strategy": "disable the optional edit.ast-grep-plan capability without changing any other artifact contract; no consumer holds authority through it", "evidence": { "evidenceIds": ["local:registry:edit.ast-grep-plan:optional", "gap:ast-grep:replacement-command-drill"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "exit": { "strategy": "replace the executable only behind edit.ast-grep-plan after exact artifact and failure parity", "replacementCriteria": ["alternate command passes pattern, rewrite, scope, escape, generated-content, snapshot-lease, and oversized-output 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:ast-grep:replacement-command-drill", "gap:ast-grep:latency-p50-p95-measurement"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, "retirement": { "status": "candidate", "triggers": ["replacement passes the complete structural edit planning contract", "installed executable identity or package provenance becomes unverifiable", "security or maintenance policy rejects the pinned package"], "evidence": { "evidenceIds": ["local:i40:operation-trace", "gap:ast-grep:replacement-command-drill"], "checkedAt": 1785024000, "expiresAt": 1792800000 } }, diff --git a/orchestration/internalization/codenexus.json b/orchestration/internalization/codenexus.json index 0fcf4b46..f2352f02 100644 --- a/orchestration/internalization/codenexus.json +++ b/orchestration/internalization/codenexus.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.codenexus-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider codenexus-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "codenexus.full", "implementationId": "codenexus.service.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_route_runs_full_lite_and_unavailable_through_a04" } }, { "integrationId": "provider.codenexus-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -CodeNexusAdapterRequest -CodeNexusAdapterArtifactRoot -CodeNexusAdapterEvaluatedAt -CodeNexusAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "crates/code-intel-cli/src/codenexus_adapter.rs", "sha256": "645675312135932dfce365a8dfc14e214cec78ee733f248606547b3eaa56edc8" }, "conformance": { "path": "crates/code-intel-cli/tests/codenexus_adapter.rs", "sha256": "4b2ea42c33aa9c180df550278d6e74501deb9f3f2c6133f7c2def850e468bdc5", "testName": "production_registry_facade_and_route_schema_are_declared" } }, - { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, + { "integrationId": "provider.codenexus-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.codenexus-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171", "testName": "codenexus_builtin_compat_dispatches_through_provider_codenexus_adapt" } }, { "integrationId": "runtime.code-nexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "explicit_fallback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } }, { "integrationId": "localization.codenexus-lite", "operation": "compat", "command": "pwsh -NoProfile -File \"$env:CODE_INTEL_HOME\\legacy/Invoke-CodeNexusLite.ps1\" -RepoPath ''", "implementationIdentity": { "providerId": "codenexus.lite-compat", "implementationId": "invoke-codenexus-lite.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-CodeNexusLite.ps1", "sha256": "cdd5c6d0fe940d2756c45a51095c275b13ebe64347914b581641693e1288ca56" }, "conformance": { "path": "legacy/scripts/tests/test-codenexus-adapter-contract.ps1", "sha256": "a835382e48223e35d680f29d27e05fc12c9d92306e1bafafc7add1135f26b1a8", "testName": "Invoke-LiteScriptEndToEnd" } } ], diff --git a/orchestration/internalization/graph.json b/orchestration/internalization/graph.json index 2985cb6e..76daff38 100644 --- a/orchestration/internalization/graph.json +++ b/orchestration/internalization/graph.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.graph-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider graph-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, { "integrationId": "provider.graph-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -GraphAdapterRequest -GraphAdapterArtifactRoot -GraphAdapterEvaluatedAt -GraphAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "compatibility_facade" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, - { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, + { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "8cd2aae94c5fdfd2678adcb1caa6fa6641bad2faccdb87c4ebcdb98428b58632", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, { "integrationId": "graph.code-intel-understand", "operation": "refresh", "command": "target/debug/code-intel.exe graph --repo --language zh --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.code-intel-understand", "operation": "refreshFull", "command": "target/debug/code-intel.exe graph --repo --language zh --full --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.understand-external", "operation": "refresh", "command": "/understand --language zh", "implementationIdentity": { "providerId": "understand-anything", "implementationId": "unverified-upstream", "activation": "explicit_fallback" }, "source": { "path": "docs/graph-provider-adapter.md", "sha256": "21a6633e164aa5aab122e1d2de36fad47866611abe930b64d03bb67633408095" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "fallback_identity_and_payload_identity_cannot_be_relabelled" } }, diff --git a/orchestration/internalization/rg.json b/orchestration/internalization/rg.json index 416e957d..46c71664 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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5; local-conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5" }, "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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5; local-conformance-sha256:49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171" }, "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": "legacy/run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "legacy/run-code-intel.ps1", "sha256": "2059c6a80e3bf4ac73dfe8297a1474714d881e83dac5d1e55a80d78184384a3e" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "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": "295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5", "testName": "inventory_rg_exec_emits_one_result_and_stable_real_rg_artifact" } } + { "integrationId": "inventory.rg", "operation": "run", "command": "legacy/run-code-intel.ps1 -RepoPath -Mode ", "implementationIdentity": { "providerId": "ripgrep", "implementationId": "rg-15.1.0-af60c2de9d-via-compat-facade", "activation": "required production facade" }, "source": { "path": "legacy/run-code-intel.ps1", "sha256": "2059c6a80e3bf4ac73dfe8297a1474714d881e83dac5d1e55a80d78184384a3e" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171", "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": "295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5" }, "conformance": { "path": "crates/code-intel-cli/tests/capability_exec.rs", "sha256": "49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171", "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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5", "local:r09:conformance-sha256:44c93f7f38c1f7ad8b53d920093fc34c156af66c90e330427fbb4a0bbc90e7d5"] }], + "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:295eb1ce67760638a81136febf727285f0feb4692a228df65ac75316b4a566c5", "local:r09:conformance-sha256:49753a4a9c491931453b0b8be775f01d3e8920bbccba919d087376f876c32171"] }], "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 } }, diff --git a/orchestration/internalization/sentrux.json b/orchestration/internalization/sentrux.json index 743a3451..e289def8 100644 --- a/orchestration/internalization/sentrux.json +++ b/orchestration/internalization/sentrux.json @@ -122,11 +122,11 @@ }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", - "sha256": "694eb43271ad1751cb50f556f0dd25bca0162ebfef8754d5002e59baaafb0d36" + "sha256": "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", - "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", + "sha256": "8cd2aae94c5fdfd2678adcb1caa6fa6641bad2faccdb87c4ebcdb98428b58632", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, @@ -259,7 +259,7 @@ }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", - "sha256": "ca6a8279c1f27850e3f37b6b286c93572b24c1bdfb2f75e677a02cec4df7a602", + "sha256": "8cd2aae94c5fdfd2678adcb1caa6fa6641bad2faccdb87c4ebcdb98428b58632", "testName": "production_run_preserves_doctor_domain_failure_and_completes_unrelated_branch" } } diff --git a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json index 2dcf60e7..24861e0b 100644 --- a/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json +++ b/orchestration/schemas/code-intel-sentrux-capability-artifact.v1.schema.json @@ -60,6 +60,9 @@ { "type": "null" } ] }, + "structuredData": { + "type": ["object", "array", "null"] + }, "artifacts": { "type": "array", "items": { "$ref": "#/$defs/artifactRef" }, diff --git a/orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json b/orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json index d5f887e3..9a81d6ca 100644 --- a/orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json +++ b/orchestration/schemas/code-intel-sentrux-capability-matrix.v1.schema.json @@ -14,9 +14,11 @@ "completionPolicy": { "type": "object", "additionalProperties": false, - "required": ["requiredStatesForComplete", "forbiddenSilentStates", "rule"], + "required": ["requiredStatesForComplete", "automaticExecutionModes", "explicitExecutionModes", "forbiddenSilentStates", "rule"], "properties": { "requiredStatesForComplete": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "automaticExecutionModes": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, + "explicitExecutionModes": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, "forbiddenSilentStates": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, "rule": { "type": "string", "minLength": 1 } } @@ -27,7 +29,7 @@ "items": { "type": "object", "additionalProperties": false, - "required": ["id", "operation", "aliases", "scope", "trigger", "route", "currentState", "requiredForRelease", "inputs", "artifacts", "decisionConsumers", "failureSemantics", "platforms"], + "required": ["id", "operation", "aliases", "scope", "trigger", "route", "executionMode", "currentState", "requiredForRelease", "inputs", "artifacts", "decisionConsumers", "failureSemantics", "platforms"], "properties": { "id": { "type": "string", "pattern": "^sentrux\\.[a-z0-9_]+$" }, "operation": { "type": "string", "pattern": "^[a-z0-9_]+$" }, @@ -35,7 +37,8 @@ "scope": { "type": "string", "minLength": 1 }, "trigger": { "type": "string", "minLength": 1 }, "route": { "type": "string", "minLength": 1 }, - "currentState": { "enum": ["authoritative_automatic", "native_manual", "compatibility_only", "declared_only"] }, + "executionMode": { "enum": ["automatic", "explicit_authority", "lifecycle_external"] }, + "currentState": { "enum": ["authoritative_automatic", "automatic_degraded", "not_applicable_dag", "explicit_authority_required", "lifecycle_external", "native_manual", "compatibility_only", "declared_only"] }, "requiredForRelease": { "type": "boolean" }, "inputs": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, "artifacts": { "type": "array", "items": { "type": "string", "minLength": 1 }, "minItems": 1, "uniqueItems": true }, diff --git a/orchestration/sentrux-capability-matrix.v1.json b/orchestration/sentrux-capability-matrix.v1.json index 5688c895..66da5334 100644 --- a/orchestration/sentrux-capability-matrix.v1.json +++ b/orchestration/sentrux-capability-matrix.v1.json @@ -11,6 +11,8 @@ ], "completionPolicy": { "requiredStatesForComplete": ["authoritative_automatic"], + "automaticExecutionModes": ["automatic"], + "explicitExecutionModes": ["explicit_authority", "lifecycle_external"], "forbiddenSilentStates": ["declared_only", "compatibility_only"], "rule": "Every applicable capability must have an automatic route, a validated artifact, and at least one downstream decision consumer before a complete release is allowed." }, @@ -22,6 +24,7 @@ "scope": "core_analysis", "trigger": "repository_run", "route": "provider.sentrux-adapt/builtin_bounded", + "executionMode": "automatic", "currentState": "automatic_degraded", "requiredForRelease": true, "inputs": ["repository.snapshot"], @@ -37,6 +40,7 @@ "scope": "core_analysis", "trigger": "repository_run", "route": "provider.sentrux-adapt/builtin_lite", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["sentrux.scan.v1"], @@ -52,6 +56,7 @@ "scope": "core_analysis", "trigger": "repository_run", "route": "provider.sentrux-adapt/builtin_bounded", + "executionMode": "automatic", "currentState": "automatic_degraded", "requiredForRelease": true, "inputs": ["repository.snapshot"], @@ -67,6 +72,7 @@ "scope": "history", "trigger": "repository_run", "route": "provider.sentrux-adapt/builtin_lite", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "git.history"], @@ -82,6 +88,7 @@ "scope": "history", "trigger": "repository_run", "route": "provider.sentrux-adapt/builtin_lite", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "git.history"], @@ -97,6 +104,7 @@ "scope": "test_selection", "trigger": "repository_run", "route": "provider.sentrux-adapt/builtin_lite", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.dsm.v1", "test.inventory"], @@ -110,11 +118,12 @@ "operation": "what_if", "aliases": [], "scope": "change_prediction", - "trigger": "change_or_pr", - "route": "explicit_change_set", - "currentState": "not_applicable_dag", + "trigger": "repository_run", + "route": "provider.sentrux-adapt/builtin_lite", + "executionMode": "automatic", + "currentState": "automatic_degraded", "requiredForRelease": true, - "inputs": ["repository.snapshot", "change.set", "sentrux.dsm.v1"], + "inputs": ["repository.snapshot", "sentrux.dsm.v1"], "artifacts": ["code-intel-sentrux-capability-artifact.v1"], "decisionConsumers": ["change_impact", "pr_gate", "release_gate"], "failureSemantics": ["failed", "unavailable", "unknown"], @@ -127,6 +136,7 @@ "scope": "rules", "trigger": "repository_run", "route": "provider.sentrux-adapt/builtin_native", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.rules"], @@ -142,6 +152,7 @@ "scope": "rules", "trigger": "repository_run", "route": "provider.sentrux-adapt", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.rules", "sentrux.baseline"], @@ -157,6 +168,7 @@ "scope": "rules", "trigger": "repository_run", "route": "provider.sentrux-adapt", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.rules", "sentrux.baseline"], @@ -172,6 +184,7 @@ "scope": "rules", "trigger": "bootstrap_or_explicit_approval", "route": "explicit_mutation", + "executionMode": "explicit_authority", "currentState": "explicit_authority_required", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.rules"], @@ -187,6 +200,7 @@ "scope": "agent_session", "trigger": "agent_edit_session_start", "route": "agent_lifecycle_external", + "executionMode": "lifecycle_external", "currentState": "lifecycle_external", "requiredForRelease": true, "inputs": ["repository.snapshot", "session.scope"], @@ -202,6 +216,7 @@ "scope": "agent_session", "trigger": "agent_edit_session_end", "route": "agent_lifecycle_external", + "executionMode": "lifecycle_external", "currentState": "lifecycle_external", "requiredForRelease": true, "inputs": ["sentrux.session_start.v1", "repository.snapshot", "working_tree"], @@ -217,6 +232,7 @@ "scope": "agent_session", "trigger": "stale_or_changed_snapshot", "route": "provider.sentrux-adapt/builtin_bounded", + "executionMode": "automatic", "currentState": "automatic_degraded", "requiredForRelease": true, "inputs": ["repository.snapshot", "sentrux.session_start.v1"], @@ -232,6 +248,7 @@ "scope": "installation", "trigger": "doctor_or_install_smoke", "route": "provider.sentrux-adapt/builtin_lite", + "executionMode": "automatic", "currentState": "authoritative_automatic", "requiredForRelease": true, "inputs": ["installation.layout"], From a51e094a75761fabceffcf0a69b67047d245a644 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 04:26:57 +0800 Subject: [PATCH 24/27] test: keep dag assertions formatter-stable --- crates/code-intel-cli/tests/dag_run.rs | 10 ++++++---- orchestration/internalization/graph.json | 2 +- orchestration/internalization/sentrux.json | 4 ++-- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/code-intel-cli/tests/dag_run.rs b/crates/code-intel-cli/tests/dag_run.rs index ff6989f9..1f8bdb19 100644 --- a/crates/code-intel-cli/tests/dag_run.rs +++ b/crates/code-intel-cli/tests/dag_run.rs @@ -371,10 +371,12 @@ fn production_dag_output_commits_and_enters_the_authoritative_index() { impact["testSelection"]["sentruxSignals"]["candidateTestImpact"], "retains_graph_candidates_with_what_if_risk" ); - assert!(impact["testSelection"]["sentruxSignals"]["whatIfFailingScenarioCount"] - .as_u64() - .unwrap_or(0) - > 0); + assert!( + impact["testSelection"]["sentruxSignals"]["whatIfFailingScenarioCount"] + .as_u64() + .unwrap_or(0) + > 0 + ); assert!( impact["testSelection"]["sentruxSignals"]["testGap"]["limitations"] .as_array() diff --git a/orchestration/internalization/graph.json b/orchestration/internalization/graph.json index 76daff38..50344c7c 100644 --- a/orchestration/internalization/graph.json +++ b/orchestration/internalization/graph.json @@ -13,7 +13,7 @@ "operationTrace": [ { "integrationId": "provider.graph-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider graph-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, { "integrationId": "provider.graph-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -GraphAdapterRequest -GraphAdapterArtifactRoot -GraphAdapterEvaluatedAt -GraphAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "graph-adapter.v1", "activation": "compatibility_facade" }, "source": { "path": "crates/code-intel-cli/src/graph_adapter.rs", "sha256": "68a4f55b76080575721cde3ee62fd746999c6df8e79303b809baecf5d2514338" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "public_route_usage_registry_facade_and_schemas_are_real" } }, - { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "8cd2aae94c5fdfd2678adcb1caa6fa6641bad2faccdb87c4ebcdb98428b58632", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, + { "integrationId": "provider.graph-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.graph-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "provider.graph-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "1ee8976cc50b9a9f71bce61abe1be32fd406527d702040f7f223db48899b4150" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "ab974d8eb1763e772545dc4236aa30f2c5b67dcb8e0a1464f58f51ef8f882c8b", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, { "integrationId": "graph.code-intel-understand", "operation": "refresh", "command": "target/debug/code-intel.exe graph --repo --language zh --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.code-intel-understand", "operation": "refreshFull", "command": "target/debug/code-intel.exe graph --repo --language zh --full --write --json", "implementationIdentity": { "providerId": "code-intel.graph", "implementationId": "internal-rust-graph", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/graph/mod.rs", "sha256": "245bd6d7eb6f774ea6f9cead43eb67f8b351fc92c930252d180d072f00524947" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "internal_and_external_current_outputs_share_one_port_and_provenance_schema" } }, { "integrationId": "graph.understand-external", "operation": "refresh", "command": "/understand --language zh", "implementationIdentity": { "providerId": "understand-anything", "implementationId": "unverified-upstream", "activation": "explicit_fallback" }, "source": { "path": "docs/graph-provider-adapter.md", "sha256": "21a6633e164aa5aab122e1d2de36fad47866611abe930b64d03bb67633408095" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "024625a11f874ccc93185a82e3285845150ebea301909bd23e3dfa9becd76f32", "testName": "fallback_identity_and_payload_identity_cannot_be_relabelled" } }, diff --git a/orchestration/internalization/sentrux.json b/orchestration/internalization/sentrux.json index e289def8..dbee67f6 100644 --- a/orchestration/internalization/sentrux.json +++ b/orchestration/internalization/sentrux.json @@ -126,7 +126,7 @@ }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", - "sha256": "8cd2aae94c5fdfd2678adcb1caa6fa6641bad2faccdb87c4ebcdb98428b58632", + "sha256": "ab974d8eb1763e772545dc4236aa30f2c5b67dcb8e0a1464f58f51ef8f882c8b", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, @@ -259,7 +259,7 @@ }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", - "sha256": "8cd2aae94c5fdfd2678adcb1caa6fa6641bad2faccdb87c4ebcdb98428b58632", + "sha256": "ab974d8eb1763e772545dc4236aa30f2c5b67dcb8e0a1464f58f51ef8f882c8b", "testName": "production_run_preserves_doctor_domain_failure_and_completes_unrelated_branch" } } From 495be5d892e928c89b345a52b05f0e4e3685ccff Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 04:38:55 +0800 Subject: [PATCH 25/27] ci: bound cross-platform smoke duration --- .github/workflows/ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 28f2920b..eb7534f6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -13,6 +13,7 @@ permissions: jobs: windows-build-test-package: runs-on: windows-latest + timeout-minutes: 30 steps: - name: Checkout @@ -357,6 +358,7 @@ jobs: - ubuntu-latest runs-on: ${{ matrix.os }} + timeout-minutes: 30 steps: - name: Checkout From d33182e2d8ed58cd014781935939d4569294e3e4 Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 11:06:14 +0800 Subject: [PATCH 26/27] test(sentrux): align capability matrix with automatic what-if --- tests/test_sentrux_capability_executor.py | 1 - tests/test_sentrux_capability_matrix.py | 4 ++-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/test_sentrux_capability_executor.py b/tests/test_sentrux_capability_executor.py index c32d77d3..53f3f407 100644 --- a/tests/test_sentrux_capability_executor.py +++ b/tests/test_sentrux_capability_executor.py @@ -32,7 +32,6 @@ def test_dag_unsupported_routes_are_explicitly_non_applicable(self) -> None: self.assertIn("RouteKind::NotApplicable", self.source) for failure_kind in ( "explicit_mutation_required", - "dag_scope_not_supported", "session_lifecycle_outside_dag", ): with self.subTest(failure_kind=failure_kind): diff --git a/tests/test_sentrux_capability_matrix.py b/tests/test_sentrux_capability_matrix.py index 7fea2e93..d35e1d50 100644 --- a/tests/test_sentrux_capability_matrix.py +++ b/tests/test_sentrux_capability_matrix.py @@ -92,9 +92,9 @@ def test_automatic_matrix_routes_have_executor_entries_and_consumers(self) -> No self.assertTrue(capability["artifacts"]) self.assertTrue(capability["decisionConsumers"]) - def test_dag_non_applicable_capabilities_are_explicit(self) -> None: + def test_explicit_and_lifecycle_capabilities_are_not_automatic_dag_routes(self) -> None: states = {item["id"]: item["currentState"] for item in self.matrix["capabilities"]} - self.assertEqual(states["sentrux.what_if"], "not_applicable_dag") + self.assertEqual(states["sentrux.what_if"], "automatic_degraded") self.assertEqual(states["sentrux.baseline_save"], "explicit_authority_required") self.assertEqual(states["sentrux.session_start"], "lifecycle_external") self.assertEqual(states["sentrux.session_end"], "lifecycle_external") From 37f707c5c9c7ae1c148935c6c5ad7e517bdc363f Mon Sep 17 00:00:00 2001 From: Curry Date: Wed, 19 Aug 2026 11:52:34 +0800 Subject: [PATCH 27/27] chore(contract): refresh retirement packet snapshots --- .../compatibility-retirement-deletion-diff.json | 2 +- .../e04-codenexus-direct/compatibility-retirement-manifest.json | 2 +- .../e04-codenexus-direct/compatibility-retirement-ticket.json | 2 +- orchestration/retirements/e04-codenexus-direct/e00-request.json | 2 +- orchestration/retirements/e04-codenexus-direct/e01-request.json | 2 +- orchestration/retirements/e04-codenexus-direct/e01-stderr.txt | 2 +- .../e04-codenexus-direct/evidence/c00-necessity.json | 2 +- .../e04-codenexus-direct/evidence/compatibility-window.json | 2 +- .../e04-codenexus-direct/evidence/contract-parity.json | 2 +- .../e04-codenexus-direct/evidence/dependency-b05.json | 2 +- .../e04-codenexus-direct/evidence/effect-parity.json | 2 +- .../e04-codenexus-direct/evidence/golden-parity.json | 2 +- .../e04-codenexus-direct/evidence/independent-approval.json | 2 +- .../e04-codenexus-direct/evidence/registry-reconciliation.json | 2 +- .../e04-codenexus-direct/evidence/replacement-atom.json | 2 +- .../e04-codenexus-direct/evidence/rollback-execution.json | 2 +- .../e04-codenexus-direct/evidence/usage-observation.json | 2 +- .../gate-out/compatibility-retirement-decision.json | 2 +- .../e07-native-code/compatibility-retirement-deletion-diff.json | 2 +- .../e07-native-code/compatibility-retirement-manifest.json | 2 +- .../e07-native-code/compatibility-retirement-ticket.json | 2 +- orchestration/retirements/e07-native-code/e00-request.json | 2 +- orchestration/retirements/e07-native-code/e01-request.json | 2 +- orchestration/retirements/e07-native-code/e01-stderr.txt | 2 +- .../retirements/e07-native-code/evidence/c00-necessity.json | 2 +- .../e07-native-code/evidence/compatibility-window.json | 2 +- .../retirements/e07-native-code/evidence/contract-parity.json | 2 +- .../e07-native-code/evidence/dependency-inventory.json | 2 +- .../e07-native-code/evidence/dependency-snapshot.json | 2 +- .../retirements/e07-native-code/evidence/effect-parity.json | 2 +- .../retirements/e07-native-code/evidence/golden-parity.json | 2 +- .../e07-native-code/evidence/independent-approval.json | 2 +- .../e07-native-code/evidence/registry-reconciliation.json | 2 +- .../retirements/e07-native-code/evidence/replacement-atom.json | 2 +- .../e07-native-code/evidence/rollback-execution.json | 2 +- .../retirements/e07-native-code/evidence/usage-observation.json | 2 +- .../gate-out/compatibility-retirement-decision.json | 2 +- .../e08-hospital/compatibility-retirement-deletion-diff.json | 2 +- .../e08-hospital/compatibility-retirement-manifest.json | 2 +- .../e08-hospital/compatibility-retirement-ticket.json | 2 +- orchestration/retirements/e08-hospital/e00-request.json | 2 +- orchestration/retirements/e08-hospital/e01-request.json | 2 +- orchestration/retirements/e08-hospital/e01-stderr.txt | 2 +- .../retirements/e08-hospital/evidence/c00-necessity.json | 2 +- .../retirements/e08-hospital/evidence/compatibility-window.json | 2 +- .../retirements/e08-hospital/evidence/contract-parity.json | 2 +- .../retirements/e08-hospital/evidence/dependency-a04.json | 2 +- .../retirements/e08-hospital/evidence/dependency-b07.json | 2 +- .../retirements/e08-hospital/evidence/effect-parity.json | 2 +- .../retirements/e08-hospital/evidence/golden-parity.json | 2 +- .../retirements/e08-hospital/evidence/independent-approval.json | 2 +- .../e08-hospital/evidence/registry-reconciliation.json | 2 +- .../retirements/e08-hospital/evidence/replacement-atom.json | 2 +- .../retirements/e08-hospital/evidence/rollback-execution.json | 2 +- .../retirements/e08-hospital/evidence/usage-observation.json | 2 +- .../gate-out/compatibility-retirement-decision.json | 2 +- 56 files changed, 56 insertions(+), 56 deletions(-) diff --git a/orchestration/retirements/e04-codenexus-direct/compatibility-retirement-deletion-diff.json b/orchestration/retirements/e04-codenexus-direct/compatibility-retirement-deletion-diff.json index a1d09912..737b867b 100644 --- a/orchestration/retirements/e04-codenexus-direct/compatibility-retirement-deletion-diff.json +++ b/orchestration/retirements/e04-codenexus-direct/compatibility-retirement-deletion-diff.json @@ -1 +1 @@ -{"schema":"code-intel-compatibility-retirement-deletion-diff.v1","snapshotIdentity":"4a84b1744f16b7fc73e5bd3025f8851b0c6031cc5ede0a939240d2c85221d301","retirementId":"retire-codenexus-direct-branch","legacyBranchId":"run-code-intel.codenexus-lite.direct","affectedFiles":["run-code-intel.ps1"],"deletionsOnly":true,"summary":"Proposed deletion contains only the one live direct CodeNexus-lite facade branch. It is not executable until a separate route substitution is approved.","patch":{"algorithm":"replayable-delete-only-v1","sha256":"e57b8bfa80d9ea74e6f8f73aa9accbd404ae3978a2f6b9284e3ab03556868c68","files":[{"baseBlobSha256":"2059c6a80e3bf4ac73dfe8297a1474714d881e83dac5d1e55a80d78184384a3e","baseText":"#requires -Version 7.2\n\nparam(\n [string]$Repo = \"\",\n [string]$RepoPath = \"\",\n\n [string]$Config = \"\",\n\n [ValidateSet(\"auto\", \"windows\", \"macos\", \"linux\")]\n [string]$Platform = \"auto\",\n\n [ValidateSet(\"lite\", \"normal\", \"full\")]\n [string]$Mode = \"normal\",\n\n [string]$Language = \"\",\n\n [string]$ArtifactRoot = \"\",\n [string]$SentruxPath = \"\",\n [string]$RepowiseWorkspaceRoot = \"\",\n [string]$RepowiseShadowRoot = \"\",\n [string[]]$RepowiseScopePaths = @(),\n [string[]]$RepowiseRootFiles = @(),\n [int]$RepowiseTimeoutSeconds = 600,\n [string]$RepowiseProvider = \"\",\n [string]$RepowiseModel = \"\",\n [string]$RepowiseReasoning = \"\",\n [string]$ModelRoutingResult = \"\",\n [string]$ModelInventoryResult = \"\",\n [string]$ModelExecutableHandle = \"\",\n [string]$ModelPromptFile = \"\",\n [string]$ModelEndpoint = \"\",\n [ValidateSet(\"\", \"openai\", \"anthropic\", \"ollama\")]\n [string]$ModelProtocol = \"\",\n [string]$ModelCredentialEnvName = \"\",\n [ValidateRange(1, 3600)]\n [int]$ModelTimeoutSeconds = 300,\n [ValidateSet(\"json\", \"jsonl\")]\n [string]$ModelResponseFormat = \"json\",\n [string]$ModelAdapterRequest = \"\",\n [string]$ModelAdapterArtifactRoot = \"\",\n [string]$RuntimeCiEvidenceRequest = \"\",\n [string]$RuntimeCiEvidenceArtifactRoot = \"\",\n [string]$RepowiseAdapterRequest = \"\",\n [string]$RepowiseAdapterArtifactRoot = \"\",\n [long]$RepowiseAdapterEvaluatedAt = 0,\n [long]$RepowiseAdapterMaxAgeSeconds = 0,\n [string]$GraphAdapterRequest = \"\",\n [string]$GraphAdapterArtifactRoot = \"\",\n [long]$GraphAdapterEvaluatedAt = 0,\n [long]$GraphAdapterMaxAgeSeconds = 0,\n [string]$SentruxAdapterRequest = \"\",\n [string]$SentruxAdapterArtifactRoot = \"\",\n [long]$SentruxAdapterEvaluatedAt = 0,\n [long]$SentruxAdapterMaxAgeSeconds = 0,\n [string]$CodeNexusAdapterRequest = \"\",\n [string]$CodeNexusAdapterArtifactRoot = \"\",\n [long]$CodeNexusAdapterEvaluatedAt = 0,\n [long]$CodeNexusAdapterMaxAgeSeconds = 0,\n [string]$SurvivalScanRequest = \"\",\n [string]$SurvivalScanArtifactRoot = \"\",\n [string]$RunCommitSourceRoot = \"\",\n [string]$RunCommitAuthorityRoot = \"\",\n [string]$RunCommitManifestRef = \"\",\n [string]$RunCommitFinalName = \"\",\n [string[]]$InventoryExclude = @(),\n\n [switch]$DagCoordinate,\n\n [switch]$SaveSentruxBaseline,\n [switch]$AutoSaveMissingSentruxBaseline,\n [switch]$SkipRepowise,\n [switch]$RepowiseDocs,\n [switch]$AllowRepowiseShadowMutation,\n [switch]$SkipRepomix,\n [ValidateSet(\"xml\", \"markdown\", \"json\", \"plain\")]\n [string]$RepomixStyle = \"markdown\",\n [switch]$RepomixCompress,\n [switch]$SkipSentrux,\n[switch]$SkipSentruxCheck,\n[switch]$SkipSentruxGate,\n[switch]$RequireUnderstandGraph,\n[switch]$WorkspaceAdd,\n[switch]$SkipOpenSpec,\n[switch]$AutoOpenSpec,\n[ValidateSet(\"auto\", \"enabled\", \"disabled\")]\n[string]$ProactiveSkillSuggestions = \"auto\",\n[ValidateSet(\"auto\", \"ask\", \"enabled\", \"disabled\")]\n[string]$AutomaticPullRequests = \"auto\",\n[string]$BugSkill = \"\"\n)\n\nSet-StrictMode -Version Latest\n$ErrorActionPreference = \"Stop\"\n\n$platformModule = Join-Path (Join-Path $PSScriptRoot \"tools\") \"code-intel-platform.psm1\"\nImport-Module $platformModule -Force\n$followUpAutomationModule = Join-Path (Join-Path $PSScriptRoot \"tools\") \"code-intel-follow-up-automation.psm1\"\nImport-Module $followUpAutomationModule -Force\n$effectivePlatform = Get-CodeIntelPlatform -Platform $Platform\n$codeIntelPaths = Get-CodeIntelPaths -Platform $effectivePlatform -Root (Split-Path -Parent $PSScriptRoot)\n$rustExecutableName = if ($effectivePlatform -eq \"windows\") { \"code-intel.exe\" } else { \"code-intel\" }\n$defaultRustCli = Join-Path (Split-Path -Parent $PSScriptRoot) (Join-Path \"target/debug\" $rustExecutableName)\n\n[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new()\n$OutputEncoding = [System.Text.UTF8Encoding]::new()\n$env:PYTHONIOENCODING = \"utf-8\"\n$env:PYTHONUTF8 = \"1\"\n$env:TERM = \"xterm\"\n$env:NO_COLOR = \"1\"\n$env:RICH_FORCE_TERMINAL = \"0\"\n\nif (-not [string]::IsNullOrWhiteSpace($ModelInventoryResult)) {\n if ([string]::IsNullOrWhiteSpace($ModelRoutingResult) -or\n [string]::IsNullOrWhiteSpace($ModelPromptFile) -or\n [string]::IsNullOrWhiteSpace($ModelAdapterArtifactRoot)) {\n throw \"Model request synthesis requires inventory, routing, prompt, and adapter artifact root\"\n }\n $synthesisScript = Join-Path $PSScriptRoot \"New-ModelAdapterRequest.ps1\"\n $delegateScript = Join-Path $PSScriptRoot \"Invoke-ModelChannelDelegate.ps1\"\n if (-not (Test-Path -LiteralPath $synthesisScript -PathType Leaf) -or -not (Test-Path -LiteralPath $delegateScript -PathType Leaf)) {\n throw \"Model request synthesis or delegate implementation is missing\"\n }\n New-Item -ItemType Directory -Force -Path $ModelAdapterArtifactRoot | Out-Null\n $synthesizedRequest = Join-Path ([IO.Path]::GetFullPath($ModelAdapterArtifactRoot)) \"model-adapter-request.v2.json\"\n $synthesisParameters = @{\n Inventory = $ModelInventoryResult\n Routing = $ModelRoutingResult\n PromptFile = $ModelPromptFile\n OutputPath = $synthesizedRequest\n TimeoutSeconds = $ModelTimeoutSeconds\n ResponseFormat = $ModelResponseFormat\n }\n if (-not [string]::IsNullOrWhiteSpace($ModelExecutableHandle)) { $synthesisParameters.ExecutableHandle = $ModelExecutableHandle }\n if (-not [string]::IsNullOrWhiteSpace($ModelEndpoint)) { $synthesisParameters.Endpoint = $ModelEndpoint }\n if (-not [string]::IsNullOrWhiteSpace($ModelProtocol)) { $synthesisParameters.Protocol = $ModelProtocol }\n if (-not [string]::IsNullOrWhiteSpace($ModelCredentialEnvName)) { $synthesisParameters.CredentialEnvName = $ModelCredentialEnvName }\n & $synthesisScript @synthesisParameters | Out-Null\n & $delegateScript -Request $synthesizedRequest -ArtifactRoot $ModelAdapterArtifactRoot\n exit $LASTEXITCODE\n}\n\nif (-not [string]::IsNullOrWhiteSpace($ModelAdapterRequest)) {\n if ([string]::IsNullOrWhiteSpace($ModelAdapterArtifactRoot)) { throw \"Model adapter facade requires an artifact root\" }\n $delegateScript = Join-Path $PSScriptRoot \"Invoke-ModelChannelDelegate.ps1\"\n if (-not (Test-Path -LiteralPath $delegateScript -PathType Leaf)) { throw \"Model channel delegate is missing: $delegateScript\" }\n & $delegateScript -Request $ModelAdapterRequest -ArtifactRoot $ModelAdapterArtifactRoot\n exit $LASTEXITCODE\n}\n\nif (-not [string]::IsNullOrWhiteSpace($RepowiseAdapterRequest)) {\n if ([string]::IsNullOrWhiteSpace($RepowiseAdapterArtifactRoot) -or\n $RepowiseAdapterEvaluatedAt -lt 0 -or\n $RepowiseAdapterMaxAgeSeconds -le 0) {\n throw \"Repowise adapter facade requires artifact root, non-negative evaluated-at, and positive max-age\"\n }\n $rustCli = $defaultRustCli\n if (-not (Test-Path -LiteralPath $rustCli -PathType Leaf)) {\n throw \"Repowise adapter binary is missing: $rustCli\"\n }\n & $rustCli provider repowise-adapt `\n --request $RepowiseAdapterRequest `\n --artifact-root $RepowiseAdapterArtifactRoot `\n --evaluated-at $RepowiseAdapterEvaluatedAt `\n --max-age-seconds $RepowiseAdapterMaxAgeSeconds\n exit $LASTEXITCODE\n}\n\nif (-not [string]::IsNullOrWhiteSpace($GraphAdapterRequest)) {\n if ([string]::IsNullOrWhiteSpace($GraphAdapterArtifactRoot) -or\n $GraphAdapterEvaluatedAt -lt 0 -or\n $GraphAdapterMaxAgeSeconds -le 0) {\n throw \"Graph adapter facade requires artifact root, non-negative evaluated-at, and positive max-age\"\n }\n $rustCli = $defaultRustCli\n if (-not (Test-Path -LiteralPath $rustCli -PathType Leaf)) {\n throw \"Graph adapter binary is missing: $rustCli\"\n }\n & $rustCli provider graph-adapt `\n --request $GraphAdapterRequest `\n --artifact-root $GraphAdapterArtifactRoot `\n --evaluated-at $GraphAdapterEvaluatedAt `\n --max-age-seconds $GraphAdapterMaxAgeSeconds\n exit $LASTEXITCODE\n}\n\nif (-not [string]::IsNullOrWhiteSpace($SentruxAdapterRequest)) {\n if ([string]::IsNullOrWhiteSpace($SentruxAdapterArtifactRoot) -or\n $SentruxAdapterEvaluatedAt -lt 0 -or\n $SentruxAdapterMaxAgeSeconds -le 0) {\n throw \"Sentrux adapter facade requires artifact root, non-negative evaluated-at, and positive max-age\"\n }\n $rustCli = $defaultRustCli\n if (-not (Test-Path -LiteralPath $rustCli -PathType Leaf)) { throw \"Sentrux adapter binary is missing: $rustCli\" }\n & $rustCli provider sentrux-adapt --request $SentruxAdapterRequest --artifact-root $SentruxAdapterArtifactRoot --evaluated-at $SentruxAdapterEvaluatedAt --max-age-seconds $SentruxAdapterMaxAgeSeconds\n exit $LASTEXITCODE\n}\n\nif (-not [string]::IsNullOrWhiteSpace($CodeNexusAdapterRequest)) {\n if ([string]::IsNullOrWhiteSpace($CodeNexusAdapterArtifactRoot) -or\n $CodeNexusAdapterEvaluatedAt -lt 0 -or\n $CodeNexusAdapterMaxAgeSeconds -le 0) {\n throw \"CodeNexus adapter facade requires artifact root, non-negative evaluated-at, and positive max-age\"\n }\n $rustCli = $defaultRustCli\n if (-not (Test-Path -LiteralPath $rustCli -PathType Leaf)) {\n throw \"CodeNexus adapter binary is missing: $rustCli\"\n }\n & $rustCli provider codenexus-adapt `\n --request $CodeNexusAdapterRequest `\n --artifact-root $CodeNexusAdapterArtifactRoot `\n --evaluated-at $CodeNexusAdapterEvaluatedAt `\n --max-age-seconds $CodeNexusAdapterMaxAgeSeconds\n exit $LASTEXITCODE\n}\n\nif (-not [string]::IsNullOrWhiteSpace($SurvivalScanRequest)) {\n if ([string]::IsNullOrWhiteSpace($SurvivalScanArtifactRoot)) {\n throw \"Repository survival scan facade requires an artifact root\"\n }\n $rustCli = $defaultRustCli\n if (-not (Test-Path -LiteralPath $rustCli -PathType Leaf)) {\n throw \"Repository survival scan binary is missing: $rustCli\"\n }\n & $rustCli repository survival-scan `\n --request $SurvivalScanRequest `\n --artifact-root $SurvivalScanArtifactRoot\n exit $LASTEXITCODE\n}\n\nif (-not [string]::IsNullOrWhiteSpace($RunCommitManifestRef)) {\n if ([string]::IsNullOrWhiteSpace($RunCommitSourceRoot) -or\n [string]::IsNullOrWhiteSpace($RunCommitAuthorityRoot) -or\n [string]::IsNullOrWhiteSpace($RunCommitFinalName)) {\n throw \"Run commit facade requires source root, authority root, manifest Artifact Ref, and final name\"\n }\n $rustCli = $defaultRustCli\n if (-not (Test-Path -LiteralPath $rustCli -PathType Leaf)) {\n throw \"Run commit binary is missing: $rustCli\"\n }\n & $rustCli run commit `\n --source-root $RunCommitSourceRoot `\n --authority-root $RunCommitAuthorityRoot `\n --manifest-ref $RunCommitManifestRef `\n --final-name $RunCommitFinalName\n exit $LASTEXITCODE\n}\n\nfunction Resolve-Repo {\n param([string]$Path)\n\n $item = Get-Item -LiteralPath $Path -ErrorAction Stop\n if (-not $item.PSIsContainer) {\n throw \"Repo path is not a directory: $Path\"\n }\n return $item.FullName\n}\n\nfunction Find-RepoConfigByPath {\n param([object]$ReposConfig, [string]$ResolvedRepoPath)\n\n if ($null -eq $ReposConfig -or [string]::IsNullOrWhiteSpace($ResolvedRepoPath)) { return $null }\n $normalizedRepoPath = [System.IO.Path]::TrimEndingDirectorySeparator($ResolvedRepoPath)\n foreach ($entry in $ReposConfig.PSObject.Properties) {\n $configuredPath = Get-JsonProperty $entry.Value \"path\"\n if ([string]::IsNullOrWhiteSpace([string]$configuredPath)) { continue }\n try {\n $resolvedConfiguredPath = Resolve-Repo ([string]$configuredPath)\n }\n catch {\n continue\n }\n $normalizedConfiguredPath = [System.IO.Path]::TrimEndingDirectorySeparator($resolvedConfiguredPath)\n if ([string]::Equals($normalizedConfiguredPath, $normalizedRepoPath, [System.StringComparison]::OrdinalIgnoreCase)) {\n return $entry.Value\n }\n }\n return $null\n}\n\nfunction Test-CommandAvailable {\n param([string]$Name)\n return [bool](Get-Command $Name -ErrorAction SilentlyContinue)\n}\n\n# Git config keys that name a program Git will execute, pinned empty so a\n# scanned repository's own .git/config cannot supply one. `core.fsmonitor`\n# runs on ordinary read commands like `git status`, before any gate in this\n# pipeline has looked at the repository. Mirrors\n# crates/code-intel-cli/src/hardened_git.rs.\n$script:GitHardening = @(\n \"-c\", \"core.fsmonitor=\",\n \"-c\", \"core.hooksPath=\",\n \"-c\", \"core.sshCommand=\",\n \"-c\", \"diff.external=\",\n \"-c\", \"core.pager=\"\n)\n\nfunction Test-GitRepository {\nparam([string]$Path)\n\nif (-not (Test-CommandAvailable \"git\")) { return $false }\n$output = & git @script:GitHardening -C $Path rev-parse --is-inside-work-tree 2>$null\nreturn ($LASTEXITCODE -eq 0 -and [string]$output -eq \"true\")\n}\n\n# Workflow recommendations are owned by the standalone advisory atom in OpenSpec-Detector.ps1.\n\nfunction Get-JsonProperty {\n param(\n [object]$Object,\n [string]$Name\n )\n\n if ($null -eq $Object) { return $null }\n $prop = $Object.PSObject.Properties[$Name]\n if ($null -eq $prop) { return $null }\n return $prop.Value\n}\n\nfunction Resolve-ConfigString {\n param(\n [string]$Value,\n [object]$RepoConfig,\n [object]$ConfigData,\n [string]$Name,\n [string[]]$EnvNames = @(),\n [string]$Default = \"\"\n )\n\n if (-not [string]::IsNullOrWhiteSpace($Value)) { return $Value }\n\n $repoValue = Get-JsonProperty $RepoConfig $Name\n if (-not [string]::IsNullOrWhiteSpace([string]$repoValue)) { return [string]$repoValue }\n\n $globalValue = Get-JsonProperty $ConfigData $Name\n if (-not [string]::IsNullOrWhiteSpace([string]$globalValue)) { return [string]$globalValue }\n\n foreach ($envName in $EnvNames) {\n $envValue = [Environment]::GetEnvironmentVariable($envName, \"Process\")\n if ([string]::IsNullOrWhiteSpace($envValue)) {\n $envValue = [Environment]::GetEnvironmentVariable($envName, \"User\")\n }\n if (-not [string]::IsNullOrWhiteSpace($envValue)) { return $envValue }\n }\n\n return $Default\n}\n\nfunction Normalize-RepowiseProvider {\n param([string]$Provider)\n if ([string]::IsNullOrWhiteSpace($Provider)) { return \"mock\" }\n $normalized = $Provider.Trim()\n if ($normalized -ieq \"ccw\") { return \"codex_cli\" }\n return $normalized\n}\n\nfunction Get-RepowiseProviderArgs {\n param(\n [string]$Provider,\n [string]$Model,\n [string]$Reasoning\n )\n\n $args = @(\"--provider\", $Provider)\n if (-not [string]::IsNullOrWhiteSpace($Model)) { $args += @(\"--model\", $Model) }\n if (-not [string]::IsNullOrWhiteSpace($Reasoning)) { $args += @(\"--reasoning\", $Reasoning) }\n return $args\n}\n\nfunction Get-DefaultArtifactRoot {\n return (Get-CodeIntelArtifactRoot -Platform $effectivePlatform)\n}\n\nfunction Get-DefaultShadowRoot {\n return (Get-CodeIntelShadowRoot -Platform $effectivePlatform)\n}\n\nfunction Resolve-ChildPath {\n param(\n [string]$Base,\n [string]$Path\n )\n\n if ([string]::IsNullOrWhiteSpace($Path)) { return $Base }\n if ([System.IO.Path]::IsPathRooted($Path)) { return (Resolve-Repo $Path) }\n return Resolve-Repo (Join-Path $Base $Path)\n}\n\nfunction Invoke-LoggedStep {\n param(\n [string]$Name,\n [scriptblock]$Body\n )\n\n $started = Get-Date\n $entry = [ordered]@{\n name = $Name\n startedAt = $started.ToString(\"o\")\n status = \"running\"\n exitCode = $null\n output = \"\"\n error = \"\"\n finishedAt = $null\n durationMs = $null\n }\n\n try {\n $global:LASTEXITCODE = 0\n $previousErrorActionPreference = $ErrorActionPreference\n try {\n $ErrorActionPreference = \"Continue\"\n $output = & $Body 2>&1\n }\n finally {\n $ErrorActionPreference = $previousErrorActionPreference\n }\n $entry.output = ($output | ForEach-Object { $_.ToString() } | Out-String).Trim()\n if ($global:LASTEXITCODE -ne 0) {\n throw \"Command exited with code $global:LASTEXITCODE\"\n }\n $entry.status = \"passed\"\n $entry.exitCode = 0\n }\n catch {\n $entry.status = \"failed\"\n if ($global:LASTEXITCODE -ne 0) {\n $entry.exitCode = $global:LASTEXITCODE\n }\n else {\n $entry.exitCode = 1\n }\n $entry.error = $_.Exception.Message\n if ([string]::IsNullOrWhiteSpace([string]$entry.output)) {\n $entry.output = ($_ | Out-String).Trim()\n }\n }\n finally {\n $finished = Get-Date\n $entry.finishedAt = $finished.ToString(\"o\")\n $entry.durationMs = [int]($finished - $started).TotalMilliseconds\n }\n\n return [pscustomobject]$entry\n}\n\nfunction Convert-OptionalRepowiseTimeout {\n param([object]$Step)\n\n if ($null -eq $Step) { return $Step }\n $blob = (([string]$Step.error) + \"`n\" + ([string]$Step.output)).ToLowerInvariant()\n if ([string]$Step.status -eq \"failed\" -and [string]$Step.name -like \"repowise*\" -and $blob -match \"timed out after\") {\n $Step.status = \"skipped\"\n $Step.exitCode = $null\n $Step.output = \"Optional Repowise step skipped after timeout. $($Step.error)\"\n $Step.error = \"\"\n }\n return $Step\n}\n\nfunction Get-RelativePathSafe {\n param(\n [string]$Base,\n [string]$Path\n )\n\n try {\n return [System.IO.Path]::GetRelativePath($Base, $Path)\n }\n catch {\n try {\n $baseFull = [System.IO.Path]::GetFullPath($Base)\n $pathFull = [System.IO.Path]::GetFullPath($Path)\n if (-not $baseFull.EndsWith([System.IO.Path]::DirectorySeparatorChar)) {\n $baseFull = $baseFull + [System.IO.Path]::DirectorySeparatorChar\n }\n if ((Test-Path -LiteralPath $pathFull -PathType Container) -and -not $pathFull.EndsWith([System.IO.Path]::DirectorySeparatorChar)) {\n $pathFull = $pathFull + [System.IO.Path]::DirectorySeparatorChar\n }\n $relative = ([uri]$baseFull).MakeRelativeUri([uri]$pathFull).ToString()\n $relative = [uri]::UnescapeDataString($relative).Replace(\"/\", [System.IO.Path]::DirectorySeparatorChar)\n if ([string]::IsNullOrWhiteSpace($relative)) { return \".\" }\n return $relative\n }\n catch {\n return $Path\n }\n }\n}\n\nfunction Get-StepFailureCategory {\n param([object]$Step)\n\n $name = [string]$Step.name\n $status = [string]$Step.status\n $blob = (([string]$Step.error) + \"`n\" + ([string]$Step.output)).ToLowerInvariant()\n\n if ($name -eq \"understand graph\" -and ($status -eq \"failed\" -or $status -eq \"manual_required\")) {\n return \"graph_missing\"\n }\n if ($name -like \"sentrux*\" -and ($status -eq \"failed\" -or $status -eq \"manual_required\")) {\n return \"sentrux_fail\"\n }\n if (($name -like \"repowise*\" -or $name -eq \"provider preflight\") -and $blob -match \"rate_limit|quota|usage limit exceeded|error code: 429|too many requests|provider_quota\") {\n return \"provider_quota\"\n }\n if (($name -like \"repowise*\" -or $name -eq \"provider preflight\") -and $blob -match \"provider_unavailable|model_not_found|not_found_error|error code: 404|status code: 404\") {\n return \"provider_unavailable\"\n }\n if (($name -like \"repowise*\" -or $name -eq \"provider preflight\") -and $blob -match \"config_error|authentication_error|invalid api key|not authorized|token not match\") {\n return \"config_error\"\n }\n if ($status -eq \"failed\") {\n return \"local_tool_error\"\n }\n return $null\n}\n\nfunction Get-CodeIntelEffectiveFailedSteps {\n param(\n [object[]]$FailedSteps,\n [int]$BlockingSentruxDebt,\n # Issue #130: a \"sentrux gate\" step that failed/manual_required because it found\n # architecture/quality debt (god_files, coupling, quality, cycles, ...) is a FINDING,\n # not a process failure -- it is the gate doing its job. A \"sentrux check\" step keeps\n # its prior blocking-debt-gated behavior; this exemption is scoped to gate only.\n # $Failures (the code-intel-sentrux-failures.v1 object from New-CodeIntelSentruxFailures)\n # is optional so existing callers that only pass -FailedSteps/-BlockingSentruxDebt keep\n # their old behavior unchanged. When it is supplied, $Failures.gate is the discriminator:\n # non-null means a gate:* record was actually parsed from \"sentrux gate\" stdout (a real\n # finding); null means the gate step failed without producing any recognizable output at\n # all (a genuine crash), which must still count as an effective failure.\n [object]$Failures = $null\n )\n\n $gateHasParsedRecord = ($null -ne $Failures -and $null -ne $Failures.gate)\n\n return @($FailedSteps | Where-Object {\n $category = [string](Get-StepFailureCategory $_)\n if ($category -ne \"sentrux_fail\") { return $true }\n if ([string]$_.name -like \"sentrux gate*\") {\n return -not $gateHasParsedRecord\n }\n return $BlockingSentruxDebt -gt 0\n })\n}\n\nfunction Complete-NodeLintHygieneStep {\n param(\n [System.Collections.Specialized.OrderedDictionary]$Step,\n [datetime]$Started\n )\n\n $finished = Get-Date\n $Step[\"finishedAt\"] = $finished.ToString(\"o\")\n $Step[\"durationMs\"] = [int]($finished - $Started).TotalMilliseconds\n return [pscustomobject]$Step\n}\n\nfunction Get-NodeLintHygieneStep {\n param(\n [string]$RepoPath,\n [bool]$RgAvailable\n )\n\n $started = Get-Date\n $step = [ordered]@{\n name = \"node lint hygiene\"\n startedAt = $started.ToString(\"o\")\n status = \"skipped\"\n exitCode = $null\n output = \"\"\n error = \"\"\n finishedAt = \"\"\n durationMs = 0\n }\n\n try {\n $packageJson = Join-Path $RepoPath \"package.json\"\n if (-not (Test-Path -LiteralPath $packageJson -PathType Leaf)) {\n $step[\"output\"] = \"No package.json found.\"\n return (Complete-NodeLintHygieneStep -Step $step -Started $started)\n }\n\n $package = Get-Content -LiteralPath $packageJson -Raw | ConvertFrom-Json\n $scripts = Get-JsonProperty $package \"scripts\"\n $lintScript = [string](Get-JsonProperty $scripts \"lint\")\n if ([string]::IsNullOrWhiteSpace($lintScript) -or $lintScript -notmatch \"\\beslint\\b\") {\n $step[\"output\"] = \"No root ESLint lint script detected.\"\n return (Complete-NodeLintHygieneStep -Step $step -Started $started)\n }\n\n if (-not $RgAvailable) {\n $step[\"output\"] = \"rg unavailable; skip static ESLint asset-boundary check.\"\n return (Complete-NodeLintHygieneStep -Step $step -Started $started)\n }\n\n $rgArgs = @(\n \"--files\",\n \"--hidden\",\n \"--no-ignore\",\n \"-g\", \"!**/.git/**\",\n \"-g\", \"!**/node_modules/**\",\n \"-g\", \"!**/dist/**\",\n \"-g\", \"!**/build/**\",\n $RepoPath\n )\n $repoFiles = @(& rg @rgArgs 2>$null)\n $global:LASTEXITCODE = 0\n $normalizedFiles = @($repoFiles | ForEach-Object { ([string]$_).Replace(\"\\\", \"/\") })\n\n $assetPatterns = New-Object System.Collections.Generic.List[string]\n if (@($normalizedFiles | Where-Object { $_ -match \"(^|/)apps/[^/]+/public/charting_library/\" } | Select-Object -First 1).Count -gt 0) {\n $assetPatterns.Add(\"apps/*/public/charting_library/**\")\n }\n if (@($normalizedFiles | Where-Object { $_ -match \"(^|/)apps/[^/]+/public/datafeeds/\" } | Select-Object -First 1).Count -gt 0) {\n $assetPatterns.Add(\"apps/*/public/datafeeds/**\")\n }\n if (@($normalizedFiles | Where-Object { $_ -match \"(^|/)packages/[^/]+/vendor/\" } | Select-Object -First 1).Count -gt 0) {\n $assetPatterns.Add(\"packages/*/vendor/**\")\n }\n if (@($normalizedFiles | Where-Object { $_ -match \"(^|/)vendor/\" } | Select-Object -First 1).Count -gt 0) {\n $assetPatterns.Add(\"vendor/**\")\n }\n\n if ($assetPatterns.Count -eq 0) {\n $step[\"status\"] = \"passed\"\n $step[\"exitCode\"] = 0\n $step[\"output\"] = \"Root ESLint lint script detected; no known generated/vendor static asset directories found.\"\n return (Complete-NodeLintHygieneStep -Step $step -Started $started)\n }\n\n $configNames = @(\"eslint.config.js\", \"eslint.config.mjs\", \"eslint.config.cjs\", \".eslintignore\", \".eslintrc\", \".eslintrc.json\", \".eslintrc.js\", \".eslintrc.cjs\")\n $configFiles = @($configNames | ForEach-Object {\n $candidate = Join-Path $RepoPath $_\n if (Test-Path -LiteralPath $candidate -PathType Leaf) { $candidate }\n })\n if ($configFiles.Count -eq 0) {\n $step[\"status\"] = \"manual_required\"\n $step[\"exitCode\"] = 0\n $step[\"output\"] = \"Root lint script uses ESLint and known generated/vendor static asset dirs exist, but no root ESLint config or ignore file was found. Add ignores for: $($assetPatterns -join ', '), then run root lint before push.\"\n return (Complete-NodeLintHygieneStep -Step $step -Started $started)\n }\n\n $configText = (($configFiles | ForEach-Object { Get-Content -LiteralPath $_ -Raw }) -join [Environment]::NewLine).Replace(\"\\\", \"/\")\n $missing = New-Object System.Collections.Generic.List[string]\n foreach ($pattern in $assetPatterns) {\n $covered = $false\n if ($pattern -eq \"apps/*/public/charting_library/**\") {\n $covered = ($configText -match \"charting_library|apps/\\*/public|\\*\\*/public|public/\\*\\*\")\n }\n elseif ($pattern -eq \"apps/*/public/datafeeds/**\") {\n $covered = ($configText -match \"datafeeds|apps/\\*/public|\\*\\*/public|public/\\*\\*\")\n }\n elseif ($pattern -eq \"packages/*/vendor/**\" -or $pattern -eq \"vendor/**\") {\n $covered = ($configText -match \"vendor\")\n }\n\n if (-not $covered) {\n $missing.Add($pattern)\n }\n }\n\n if ($missing.Count -gt 0) {\n $step[\"status\"] = \"manual_required\"\n $step[\"exitCode\"] = 0\n $step[\"output\"] = \"Root lint script uses ESLint and known generated/vendor static asset dirs exist, but ignore coverage appears incomplete for: $($missing -join ', '). Add explicit ESLint ignores or run root lint before push.\"\n }\n else {\n $step[\"status\"] = \"passed\"\n $step[\"exitCode\"] = 0\n $step[\"output\"] = \"Root ESLint lint script has ignore coverage for known generated/vendor static asset dirs: $($assetPatterns -join ', ').\"\n }\n }\n catch {\n $step[\"status\"] = \"manual_required\"\n $step[\"exitCode\"] = 0\n $step[\"output\"] = \"Node lint hygiene check could not complete. Run root lint before push and inspect generated/vendor asset ignores.\"\n $step[\"error\"] = $_.Exception.Message\n }\n finally {\n $finished = Get-Date\n $step[\"finishedAt\"] = $finished.ToString(\"o\")\n $step[\"durationMs\"] = [int]($finished - $started).TotalMilliseconds\n }\n\n return (Complete-NodeLintHygieneStep -Step $step -Started $started)\n}\n\nfunction New-GitHubSolutionResearchNotApplicable {\n return [ordered]@{\n status = \"not_applicable\"\n required = $false\n path = \"\"\n markdown = \"\"\n reason = \"No blocker category requires GitHub solution research.\"\n candidates = 0\n queries = 0\n evidenceLinks = @()\n exitCriteria = @(\"GitHub research is not required for clean, graph-missing, governance-only, or surgery-plan-only scans.\")\n }\n}\n\nfunction Join-StatusNames {\n param(\n [object[]]$Items,\n [string]$Empty = \"none\"\n )\n\n if ($Items.Count -eq 0) { return $Empty }\n return (($Items | ForEach-Object { \"$($_.name)=$($_.status)\" }) -join \"; \")\n}\n\nfunction Read-JsonFileSafe {\n param([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path) -or -not (Test-Path -LiteralPath $Path -PathType Leaf)) {\n return $null\n }\n try {\n return Get-Content -LiteralPath $Path -Raw | ConvertFrom-Json\n }\n catch {\n return $null\n }\n}\n\nfunction Get-CodeEvidenceLanguage {\n param([string]$Extension)\n\n switch ($Extension.ToLowerInvariant()) {\n \".ps1\" { return \"powershell\" }\n \".psm1\" { return \"powershell\" }\n \".py\" { return \"python\" }\n \".js\" { return \"javascript\" }\n \".jsx\" { return \"javascript\" }\n \".mjs\" { return \"javascript\" }\n \".cjs\" { return \"javascript\" }\n \".ts\" { return \"typescript\" }\n \".tsx\" { return \"typescript\" }\n \".rs\" { return \"rust\" }\n \".go\" { return \"go\" }\n \".java\" { return \"java\" }\n \".cs\" { return \"csharp\" }\n default { return \"text\" }\n }\n}\n\nfunction New-CodeEvidenceNativeSymbol {\n param(\n [string]$RelativePath,\n [string]$Language,\n [int]$LineNumber,\n [string]$Kind,\n [string]$Name\n )\n\n return [ordered]@{\n id = \"$RelativePath#$Kind`:$Name\"\n kind = $Kind\n name = $Name\n file = $RelativePath\n startLine = $LineNumber\n endLine = $LineNumber\n language = $Language\n confidence = 0.55\n source = \"native-minimal\"\n }\n}\n\nfunction Get-CodeEvidencePowerShellSymbol {\n param([string]$Line)\n\n if ($Line -match '^\\s*function\\s+([A-Za-z0-9_\\-:]+)') {\n return [ordered]@{ kind = \"function\"; name = $Matches[1] }\n }\n return $null\n}\n\nfunction Get-CodeEvidencePythonSymbol {\n param([string]$Line)\n\n if ($Line -match '^\\s*(def|class)\\s+([A-Za-z_][A-Za-z0-9_]*)') {\n $kind = if ($Matches[1] -eq \"class\") { \"class\" } else { \"function\" }\n return [ordered]@{ kind = $kind; name = $Matches[2] }\n }\n return $null\n}\n\nfunction Get-CodeEvidenceJavaScriptSymbol {\n param([string]$Line)\n\n if ($Line -match '^\\s*(export\\s+)?(async\\s+)?function\\s+([A-Za-z_$][A-Za-z0-9_$]*)') {\n return [ordered]@{ kind = \"function\"; name = $Matches[3] }\n }\n if ($Line -match '^\\s*(export\\s+)?(const|let|var)\\s+([A-Za-z_$][A-Za-z0-9_$]*)\\s*=\\s*(async\\s*)?(\\([^)]*\\)|[A-Za-z_$][A-Za-z0-9_$]*)\\s*=>') {\n return [ordered]@{ kind = \"function\"; name = $Matches[3] }\n }\n if ($Line -match '^\\s*(export\\s+)?(class|interface)\\s+([A-Za-z_$][A-Za-z0-9_$]*)') {\n return [ordered]@{ kind = $Matches[2]; name = $Matches[3] }\n }\n return $null\n}\n\nfunction Get-CodeEvidenceRustSymbol {\n param([string]$Line)\n\n if ($Line -match '^\\s*(pub\\s+)?(async\\s+)?fn\\s+([A-Za-z_][A-Za-z0-9_]*)') {\n return [ordered]@{ kind = \"function\"; name = $Matches[3] }\n }\n return $null\n}\n\nfunction Get-CodeEvidenceGoSymbol {\n param([string]$Line)\n\n if ($Line -match '^\\s*func\\s+(\\([^)]+\\)\\s*)?([A-Za-z_][A-Za-z0-9_]*)') {\n return [ordered]@{ kind = \"function\"; name = $Matches[2] }\n }\n return $null\n}\n\nfunction Get-CodeEvidenceJavaSymbol {\n param([string]$Line)\n\n if ($Line -match '^\\s*(public|private|protected)?\\s*(class|interface|enum)\\s+([A-Za-z_][A-Za-z0-9_]*)') {\n return [ordered]@{ kind = $Matches[2]; name = $Matches[3] }\n }\n return $null\n}\n\nfunction Get-CodeEvidenceSymbolCandidate {\n param(\n [string]$Language,\n [string]$Line\n )\n\n switch ($Language) {\n \"powershell\" { return Get-CodeEvidencePowerShellSymbol $Line }\n \"python\" { return Get-CodeEvidencePythonSymbol $Line }\n \"javascript\" { return Get-CodeEvidenceJavaScriptSymbol $Line }\n \"typescript\" { return Get-CodeEvidenceJavaScriptSymbol $Line }\n \"rust\" { return Get-CodeEvidenceRustSymbol $Line }\n \"go\" { return Get-CodeEvidenceGoSymbol $Line }\n \"java\" { return Get-CodeEvidenceJavaSymbol $Line }\n default { return $null }\n }\n}\n\nfunction Get-CodeEvidenceSymbols {\n param(\n [string]$RelativePath,\n [string]$Language,\n [string[]]$Lines\n )\n\n $symbols = New-Object System.Collections.Generic.List[object]\n for ($i = 0; $i -lt $Lines.Count; $i++) {\n $candidate = Get-CodeEvidenceSymbolCandidate -Language $Language -Line ([string]$Lines[$i])\n if ($null -eq $candidate -or [string]::IsNullOrWhiteSpace([string]$candidate[\"name\"])) {\n continue\n }\n\n $symbols.Add((New-CodeEvidenceNativeSymbol `\n -RelativePath $RelativePath `\n -Language $Language `\n -LineNumber ($i + 1) `\n -Kind ([string]$candidate[\"kind\"]) `\n -Name ([string]$candidate[\"name\"])))\n }\n return $symbols.ToArray()\n}\n\nfunction Get-CodeEvidenceImports {\nparam(\n[string]$RelativePath,\n[string]$Language,\n[string[]]$Lines\n )\n\n $imports = New-Object System.Collections.Generic.List[object]\n for ($i = 0; $i -lt $Lines.Count; $i++) {\n $line = [string]$Lines[$i]\n $target = \"\"\n if ($Language -in @(\"javascript\", \"typescript\") -and $line -match 'from\\s+[\"'']([^\"'']+)[\"'']') {\n $target = $Matches[1]\n } elseif ($Language -in @(\"javascript\", \"typescript\") -and $line -match 'require\\([\"'']([^\"'']+)[\"'']\\)') {\n $target = $Matches[1]\n } elseif ($Language -eq \"python\" -and $line -match '^\\s*(from|import)\\s+([A-Za-z0-9_\\.]+)') {\n $target = $Matches[2]\n } elseif ($Language -eq \"rust\" -and $line -match '^\\s*use\\s+([^;]+);') {\n $target = $Matches[1].Trim()\n } elseif ($Language -eq \"go\" -and $line -match '^\\s*import\\s+[\"'']([^\"'']+)[\"'']') {\n $target = $Matches[1]\n } elseif ($line -match '^\\s*#include\\s+[<\"]([^>\"]+)[>\"]') {\n $target = $Matches[1]\n }\n\n if (-not [string]::IsNullOrWhiteSpace($target)) {\n $imports.Add([ordered]@{\n file = $RelativePath\n line = $i + 1\n target = $target\n language = $Language\n confidence = 0.6\n source = \"native-minimal\"\n })\n }\n }\nreturn $imports.ToArray()\n}\n\nfunction New-AgentCodeSliceRanking {\nparam(\n[object[]]$Files,\n[object[]]$Symbols,\n[object[]]$Imports\n)\n\n$symbolsByFile = @{}\nforeach ($symbol in @($Symbols)) {\n$file = [string]$symbol.file\nif ([string]::IsNullOrWhiteSpace($file)) { continue }\nif (-not $symbolsByFile.ContainsKey($file)) {\n$symbolsByFile[$file] = New-Object System.Collections.Generic.List[object]\n}\n$symbolsByFile[$file].Add($symbol)\n}\n\n$importsByFile = @{}\nforeach ($import in @($Imports)) {\n$file = [string]$import.file\nif ([string]::IsNullOrWhiteSpace($file)) { continue }\nif (-not $importsByFile.ContainsKey($file)) {\n$importsByFile[$file] = New-Object System.Collections.Generic.List[object]\n}\n$importsByFile[$file].Add($import)\n}\n\n$rankedFiles = New-Object System.Collections.Generic.List[object]\nforeach ($file in @($Files)) {\n$path = [string]$file.path\nif ([string]::IsNullOrWhiteSpace($path)) { continue }\n\n$reasons = New-Object System.Collections.Generic.List[string]\n$score = 0\n$isTestFile = ($path -match '(test|spec)\\.' -or $path -match '(^|/)(tests?|spec)/')\n$isSupportFile = ($path -match '(^|/)(examples?|fixtures?|demos?|benchmarks?)/')\nif (-not $isTestFile -and -not $isSupportFile -and $path -match '(^|/)(index|main|app|server|cli)\\.') {\n$reasons.Add(\"entrypoint\")\n$score += 40\n}\nif ($isTestFile) {\n$reasons.Add(\"test\")\n}\nif ($symbolsByFile.ContainsKey($path) -and $symbolsByFile[$path].Count -gt 0) {\n$reasons.Add(\"symbols\")\n$score += [Math]::Min(20, 5 * $symbolsByFile[$path].Count)\n}\nif ($importsByFile.ContainsKey($path) -and $importsByFile[$path].Count -gt 0) {\n$reasons.Add(\"imports\")\n$score += [Math]::Min(15, 5 * $importsByFile[$path].Count)\n}\nif ($score -eq 0) {\n$reasons.Add(\"inventory\")\n$score = 1\n}\n\n$rankedFiles.Add([ordered]@{\npath = $path\nlanguage = [string]$file.language\nscore = $score\nreasons = @($reasons.ToArray())\nsymbols = if ($symbolsByFile.ContainsKey($path)) { @($symbolsByFile[$path] | ForEach-Object { $_.name }) } else { @() }\nimports = if ($importsByFile.ContainsKey($path)) { @($importsByFile[$path] | ForEach-Object { $_.target }) } else { @() }\n})\n}\n\n$ordered = @($rankedFiles.ToArray() | Sort-Object -Property @{ Expression = \"score\"; Descending = $true }, @{ Expression = \"path\"; Descending = $false })\nreturn [ordered]@{\nschema = \"agent-code-slice-ranking.v1\"\nstrategy = \"native-evidence-default\"\nfiles = $ordered\n}\n}\n\nfunction Write-CodeEvidenceAgentSlices {\nparam(\n[string]$AgentDir,\n[string]$SliceDir,\n[object[]]$Files,\n[object[]]$Symbols,\n[object[]]$Imports,\n[object]$CocoOutcome\n)\n\n$ranking = New-AgentCodeSliceRanking -Files $Files -Symbols $Symbols -Imports $Imports\n$rankingPath = Join-Path $AgentDir \"ranking.json\"\n$ranking | ConvertTo-Json -Depth 10 | Set-Content -LiteralPath $rankingPath -Encoding UTF8\n\n$agentIndexPath = Join-Path $AgentDir \"index.md\"\n@(\n\"# Agent Code Map\",\n\"\",\n\"## Status\",\n\"- Code Evidence Layer: ok\",\n\"- Native minimal layer: enabled\",\n\"- Ranking: [ranking.json](ranking.json)\",\n\"- Native retrieval slice: [native-retrieval](slices/native-retrieval.md)\",\n\"- cocoindex-code adapter: $($CocoOutcome.status) ($($CocoOutcome.reasonCode))\",\n\"\",\n\"## Full Dumps\",\n\"- [files](../full/files.json)\",\n\"- [symbols](../full/symbols.json)\",\n\"- [chunks](../full/chunks.json)\",\n\"- [symbol chunks](../full/symbol-chunks.json)\",\n\"- [imports](../full/imports.json)\",\n\"\",\n\"## Slices\",\n\"- [native retrieval](slices/native-retrieval.md)\",\n\"- [entrypoints](slices/entrypoints.md)\",\n\"- [tests](slices/tests.md)\",\n\"- [risk hotspots](slices/risk-hotspots.md)\"\n) | Set-Content -LiteralPath $agentIndexPath -Encoding UTF8\n\n$topRanked = @($ranking.files | Select-Object -First 20)\n@(\n\"# Native Retrieval Slice\",\n\"\",\n\"- Strategy: native-evidence-default\",\n\"- Source: Code Evidence files/symbols/imports only\",\n\"\",\n\"## Ranked Files\"\n) + @($topRanked | ForEach-Object {\n\"- $($_.path) score=$($_.score) reasons=$(@($_.reasons) -join ',')\"\n}) | Set-Content -LiteralPath (Join-Path $SliceDir \"native-retrieval.md\") -Encoding UTF8\n\n$entrypoints = @($Files | Where-Object { $_.path -match '(^|/)(index|main|app|server|cli)\\.' -and -not ($_.path -match '(test|spec)\\.' -or $_.path -match '(^|/)(tests?|spec)/') -and -not ($_.path -match '(^|/)(examples?|fixtures?|demos?|benchmarks?)/') } | Select-Object -First 20)\n@(\"# Entrypoints\", \"\") + @($entrypoints | ForEach-Object { \"- $($_.path) ($($_.language))\" }) | Set-Content -LiteralPath (Join-Path $SliceDir \"entrypoints.md\") -Encoding UTF8\n\n$tests = @($Files | Where-Object { $_.path -match '(test|spec)\\.' -or $_.path -match '(^|/)(tests?|spec)/' } | Select-Object -First 30)\n@(\"# Tests\", \"\") + @($tests | ForEach-Object { \"- $($_.path) ($($_.language))\" }) | Set-Content -LiteralPath (Join-Path $SliceDir \"tests.md\") -Encoding UTF8\n\n@(\n\"# Risk Hotspots\",\n\"\",\n\"- Native minimal layer does not calculate complexity.\",\n\"- Treat file-sized chunks as fallback evidence until structural chunking is enabled.\",\n\"- cocoindex-code adapter outcome: $($CocoOutcome.status) ($($CocoOutcome.reasonCode)).\"\n) | Set-Content -LiteralPath (Join-Path $SliceDir \"risk-hotspots.md\") -Encoding UTF8\n\nreturn [ordered]@{\nagentIndex = $agentIndexPath\nranking = $rankingPath\nnativeRetrieval = Join-Path $SliceDir \"native-retrieval.md\"\n}\n}\n\nfunction New-CodeEvidenceLayer {\nparam(\n[string]$RepoPath,\n[string]$RunDir,\n[object[]]$Files,\n[object]$CodeEvidenceConfig = $null\n)\n\n$root = Join-Path $RunDir \"code-evidence\"\n$fullDir = Join-Path $root \"merged\\full\"\n$agentDir = Join-Path $root \"merged\\agent\"\n$sliceDir = Join-Path $agentDir \"slices\"\n$adapterDir = Join-Path $root \"adapters\\cocoindex-code\"\nforeach ($dir in @($fullDir, $agentDir, $sliceDir, $adapterDir)) {\nNew-Item -ItemType Directory -Force -Path $dir | Out-Null\n}\n\n$fileRows = New-Object System.Collections.Generic.List[object]\n$symbols = New-Object System.Collections.Generic.List[object]\n$chunks = New-Object System.Collections.Generic.List[object]\n$symbolChunks = New-Object System.Collections.Generic.List[object]\n$imports = New-Object System.Collections.Generic.List[object]\n\nforeach ($file in @($Files)) {\n$fileText = [string]$file\nif ([string]::IsNullOrWhiteSpace($fileText)) { continue }\n$fullPath = if ([System.IO.Path]::IsPathRooted($fileText)) { $fileText } else { Join-Path $RepoPath $fileText }\nif (-not (Test-Path -LiteralPath $fullPath -PathType Leaf)) { continue }\n\n$relativePath = (Get-RelativePathSafe $RepoPath $fullPath).Replace(\"\\\", \"/\")\n$extension = [System.IO.Path]::GetExtension($fullPath)\n$language = Get-CodeEvidenceLanguage -Extension $extension\n$content = Get-Content -LiteralPath $fullPath -Raw -ErrorAction SilentlyContinue\n if ($null -eq $content) { $content = \"\" }\n $lines = if ([string]::IsNullOrEmpty($content)) { @() } else { @($content -split \"`r?`n\") }\n $lines = @($lines)\n $contentBytes = [System.Text.Encoding]::UTF8.GetBytes($content)\n $hashBytes = [System.Security.Cryptography.SHA256]::HashData($contentBytes)\n$hash = [System.BitConverter]::ToString($hashBytes).Replace(\"-\", \"\").ToLowerInvariant()\n\n$fileRows.Add([ordered]@{\npath = $relativePath\nlanguage = $language\nbytes = $contentBytes.Length\nlines = $lines.Count\ntextHash = $hash\nsource = \"native-minimal\"\n})\n\n$fileSymbols = @(Get-CodeEvidenceSymbols -RelativePath $relativePath -Language $language -Lines $lines)\nforeach ($symbol in $fileSymbols) { $symbols.Add($symbol) }\n\n$chunkId = \"$relativePath#file\"\n$chunks.Add([ordered]@{\nid = $chunkId\nfile = $relativePath\nstartLine = 1\nendLine = [Math]::Max(1, $lines.Count)\nkind = \"file\"\ncontainsSymbols = @($fileSymbols | ForEach-Object { $_.id })\ntextHash = $hash\nsource = \"native-minimal\"\n})\n\nforeach ($symbol in $fileSymbols) {\n$symbolChunks.Add([ordered]@{\nsymbolId = $symbol.id\nchunkId = $chunkId\nrelation = \"contained_by\"\nconfidence = 0.55\n})\n}\n\nforeach ($import in @(Get-CodeEvidenceImports -RelativePath $relativePath -Language $language -Lines $lines)) {\n$imports.Add($import)\n}\n}\n\n$fileRowsArray = @($fileRows.ToArray())\n$symbolsArray = @($symbols.ToArray())\n$chunksArray = @($chunks.ToArray())\n$symbolChunksArray = @($symbolChunks.ToArray())\n$importsArray = @($imports.ToArray())\n\n([ordered]@{ schema = \"code-evidence-files.v1\"; files = $fileRowsArray }) | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $fullDir \"files.json\") -Encoding UTF8\n([ordered]@{ schema = \"code-evidence-symbols.v1\"; symbols = $symbolsArray }) | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $fullDir \"symbols.json\") -Encoding UTF8\n([ordered]@{ schema = \"code-evidence-chunks.v1\"; chunks = $chunksArray }) | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $fullDir \"chunks.json\") -Encoding UTF8\n([ordered]@{ schema = \"code-evidence-symbol-chunks.v1\"; mappings = $symbolChunksArray }) | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $fullDir \"symbol-chunks.json\") -Encoding UTF8\n([ordered]@{ schema = \"code-evidence-imports.v1\"; imports = $importsArray }) | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath (Join-Path $fullDir \"imports.json\") -Encoding UTF8\n\n# R07 reviewed retirement: this compatibility artifact is a static tombstone.\n# There is intentionally no configuration lookup, executable discovery, or provider invocation.\n$cocoOutcome = [ordered]@{\nschema = \"code-evidence-adapter-outcome.v1\"\nadapter = \"cocoindex-code\"\nenabled = $false\nrequired = $false\nstatus = \"skipped\"\nfatal = $false\nreasonCode = \"reviewed_deletion\"\nreason = \"cocoindex-code is a reviewed retirement tombstone; legacy configuration cannot restore discovery or invocation.\"\ncommand = \"\"\n}\n\n$cocoOutcomePath = Join-Path $adapterDir \"outcome.json\"\n$cocoOutcome | ConvertTo-Json -Depth 5 | Set-Content -LiteralPath $cocoOutcomePath -Encoding UTF8\n\n$scorecard = [ordered]@{\nschema = \"code-evidence-scorecard.v1\"\nstatus = \"ok\"\nnativeMinimal = $true\nadapters = @($cocoOutcome)\nmetrics = [ordered]@{\nfiles = $fileRowsArray.Count\nsymbols = $symbolsArray.Count\nchunks = $chunksArray.Count\nimports = $importsArray.Count\nsymbolContainmentRate = if ($symbolsArray.Count -gt 0) { 1.0 } else { $null }\nfallbackChunkRate = 1.0\n}\n}\n$scorecardPath = Join-Path $root \"merged\\scorecard.json\"\n$scorecard | ConvertTo-Json -Depth 8 | Set-Content -LiteralPath $scorecardPath -Encoding UTF8\n$scorecardMarkdownPath = Join-Path $root \"merged\\scorecard.md\"\n@(\n\"# Code Evidence Scorecard\",\n\"\",\n\"- Status: ok\",\n\"- Native minimal: true\",\n\"- Files: $($fileRowsArray.Count)\",\n\"- Symbols: $($symbolsArray.Count)\",\n\"- Chunks: $($chunksArray.Count)\",\n\"- Imports: $($importsArray.Count)\",\n\"- cocoindex-code: $($cocoOutcome.status) ($($cocoOutcome.reasonCode))\"\n) | Set-Content -LiteralPath $scorecardMarkdownPath -Encoding UTF8\n\n$agentSlices = Write-CodeEvidenceAgentSlices `\n-AgentDir $agentDir `\n-SliceDir $sliceDir `\n-Files $fileRowsArray `\n-Symbols $symbolsArray `\n-Imports $importsArray `\n-CocoOutcome $cocoOutcome\n\nreturn [ordered]@{\nschema = \"code-evidence-summary.v1\"\nstatus = \"ok\"\nfatal = $false\nroot = $root\nagentIndex = $agentSlices.agentIndex\nscorecard = $scorecardPath\nscorecardMarkdown = $scorecardMarkdownPath\nfiles = $fileRowsArray.Count\nsymbols = $symbolsArray.Count\nchunks = $chunksArray.Count\nimports = $importsArray.Count\nadapters = @($cocoOutcome)\n}\n}\n\nfunction ConvertTo-NullableDouble {\n param([object]$Value)\n\n if ($null -eq $Value) { return $null }\n try {\n return [double]$Value\n }\n catch {\n return $null\n }\n}\n\nfunction Get-SentruxMetricPair {\n param(\n [string]$Output,\n [string]$Label\n )\n\n if ([string]::IsNullOrWhiteSpace($Output)) { return $null }\n $pattern = [regex]::Escape($Label) + \":\\s+([0-9.]+)\\s+[^\\r\\n0-9.]+\\s+([0-9.]+)\"\n $match = [regex]::Match($Output, $pattern)\n if (-not $match.Success) { return $null }\n\n return [ordered]@{\n before = ConvertTo-NullableDouble $match.Groups[1].Value\n after = ConvertTo-NullableDouble $match.Groups[2].Value\n }\n}\n\nfunction New-SentruxMetricDelta {\n param(\n [string]$Name,\n [object]$Before,\n [object]$After,\n [ValidateSet(\"higher_is_better\", \"lower_is_better\")]\n [string]$Polarity = \"lower_is_better\"\n )\n\n $beforeValue = ConvertTo-NullableDouble $Before\n $afterValue = ConvertTo-NullableDouble $After\n $delta = $null\n $direction = \"unknown\"\n $regressed = $false\n\n if ($null -ne $beforeValue -and $null -ne $afterValue) {\n $delta = $afterValue - $beforeValue\n if ([math]::Abs($delta) -lt 0.000001) {\n $direction = \"stable\"\n }\n elseif ($delta -gt 0) {\n $direction = \"up\"\n }\n else {\n $direction = \"down\"\n }\n\n if ($Polarity -eq \"higher_is_better\") {\n $regressed = $delta -lt 0\n }\n else {\n $regressed = $delta -gt 0\n }\n }\n\n return [ordered]@{\n name = $Name\n before = $beforeValue\n after = $afterValue\n delta = $delta\n direction = $direction\n polarity = $Polarity\n regressed = $regressed\n }\n}\n\nfunction Test-SentruxGateNoDegradation {\n param([string]$GateOutput)\n\n return (-not [string]::IsNullOrWhiteSpace($GateOutput) -and $GateOutput -match \"No degradation detected\")\n}\n\nfunction Resolve-SentruxMetricRegressions {\n param(\n [object[]]$Metrics,\n [bool]$NoDegradation\n )\n\n foreach ($metric in @($Metrics)) {\n if ($null -eq $metric) {\n continue\n }\n\n $rawRegressed = [bool]$metric.regressed\n $gateAccepted = $NoDegradation -and $rawRegressed\n $metric | Add-Member -NotePropertyName rawRegressed -NotePropertyValue $rawRegressed -Force\n $metric | Add-Member -NotePropertyName gateAccepted -NotePropertyValue $gateAccepted -Force\n if ($gateAccepted) {\n $metric.regressed = $false\n }\n $metric\n }\n}\n\nfunction New-SentruxInsight {\n param(\n [string]$RepoName,\n [string]$TargetPath,\n [string]$BaselinePath,\n [object[]]$Steps\n )\n\n $gateStep = @($Steps | Where-Object { $_.name -like \"sentrux gate*\" } | Select-Object -Last 1)\n $checkStep = @($Steps | Where-Object { $_.name -eq \"sentrux check\" } | Select-Object -First 1)\n $rulesPath = if ([string]::IsNullOrWhiteSpace($TargetPath)) { \"\" } else { Join-Path (Join-Path $TargetPath \".sentrux\") \"rules.toml\" }\n $baseline = Read-JsonFileSafe $BaselinePath\n $gateOutput = if ($gateStep.Count -gt 0) { [string]$gateStep[0].output } else { \"\" }\n $noDegradation = Test-SentruxGateNoDegradation $gateOutput\n\n $qualityPair = Get-SentruxMetricPair $gateOutput \"Quality\"\n $couplingPair = Get-SentruxMetricPair $gateOutput \"Coupling\"\n $cyclesPair = Get-SentruxMetricPair $gateOutput \"Cycles\"\n $godFilesPair = Get-SentruxMetricPair $gateOutput \"God files\"\n $distance = $null\n $distanceMatch = [regex]::Match($gateOutput, \"Distance from Main Sequence:\\s+([0-9.]+)\")\n if ($distanceMatch.Success) {\n $distance = ConvertTo-NullableDouble $distanceMatch.Groups[1].Value\n }\n\n $scan = [ordered]@{}\n $resolveMatch = [regex]::Match($gateOutput, \"\\[resolve\\]\\s+([0-9]+)\\s+resolved,\\s+([0-9]+)\\s+unresolved\")\n if ($resolveMatch.Success) {\n $scan[\"resolvedImports\"] = [int]$resolveMatch.Groups[1].Value\n $scan[\"unresolvedImports\"] = [int]$resolveMatch.Groups[2].Value\n }\n $graphMatch = [regex]::Match($gateOutput, \"\\[build_graphs\\]\\s+([0-9]+)\\s+files.*\\|\\s+([0-9]+)\\s+import,\\s+([0-9]+)\\s+call,\\s+([0-9]+)\\s+inherit edges\")\n if ($graphMatch.Success) {\n $scan[\"files\"] = [int]$graphMatch.Groups[1].Value\n $scan[\"importEdges\"] = [int]$graphMatch.Groups[2].Value\n $scan[\"callEdges\"] = [int]$graphMatch.Groups[3].Value\n $scan[\"inheritEdges\"] = [int]$graphMatch.Groups[4].Value\n }\n\n $metrics = @()\n if ($null -ne $qualityPair) {\n $metrics += [pscustomobject](New-SentruxMetricDelta \"quality\" $qualityPair[\"before\"] $qualityPair[\"after\"] \"higher_is_better\")\n }\n if ($null -ne $couplingPair) {\n $metrics += [pscustomobject](New-SentruxMetricDelta \"coupling\" $couplingPair[\"before\"] $couplingPair[\"after\"] \"lower_is_better\")\n }\n if ($null -ne $cyclesPair) {\n $metrics += [pscustomobject](New-SentruxMetricDelta \"cycles\" $cyclesPair[\"before\"] $cyclesPair[\"after\"] \"lower_is_better\")\n }\n if ($null -ne $godFilesPair) {\n $metrics += [pscustomobject](New-SentruxMetricDelta \"god_files\" $godFilesPair[\"before\"] $godFilesPair[\"after\"] \"lower_is_better\")\n }\n $metrics = @(Resolve-SentruxMetricRegressions -Metrics $metrics -NoDegradation $noDegradation)\n\n $regressions = @($metrics | Where-Object { $_.regressed })\n $nextActions = @()\n $codeNexusHints = @()\n\n if ([string]::IsNullOrWhiteSpace($TargetPath)) {\n $nextActions += \"Sentrux target was not resolved; inspect pipeline configuration.\"\n }\n elseif (-not (Test-Path -LiteralPath $BaselinePath -PathType Leaf)) {\n $nextActions += \"Create an intentional Sentrux baseline for this scope before using it as a gate.\"\n }\n elseif ($gateStep.Count -gt 0 -and $gateStep[0].status -eq \"failed\") {\n $nextActions += \"Inspect the Sentrux gate output before saving any new baseline.\"\n }\n elseif ($regressions.Count -gt 0) {\n $nextActions += \"Investigate regressed structural metrics before accepting this change.\"\n }\n else {\n $nextActions += \"No structural regression detected for this scope.\"\n }\n\n if (-not [string]::IsNullOrWhiteSpace($rulesPath) -and -not (Test-Path -LiteralPath $rulesPath -PathType Leaf)) {\n $nextActions += \"Add .sentrux/rules.toml when this scope needs explicit architecture boundary rules.\"\n }\n\n if (@($regressions | Where-Object { $_.name -in @(\"coupling\", \"cycles\") }).Count -gt 0) {\n $codeNexusHints += \"Use CodeNexus impact/context on symbols in newly coupled modules.\"\n $codeNexusHints += \"Suggested query: gitnexus query `\"cross module import dependency cycle`\" --repo $RepoName\"\n }\n elseif (@($regressions | Where-Object { $_.name -eq \"quality\" }).Count -gt 0) {\n $codeNexusHints += \"Use CodeNexus query to locate the flow behind the quality drop.\"\n $codeNexusHints += \"Suggested query: gitnexus query `\"complex hotspot structural regression`\" --repo $RepoName\"\n }\n else {\n $codeNexusHints += \"If a future gate regresses, start with CodeNexus context/impact on the changed files.\"\n }\n\n return [ordered]@{\n targetPath = $TargetPath\n baselinePath = $BaselinePath\n baselineExists = (-not [string]::IsNullOrWhiteSpace($BaselinePath) -and (Test-Path -LiteralPath $BaselinePath -PathType Leaf))\n rulesPath = $rulesPath\n rulesExists = (-not [string]::IsNullOrWhiteSpace($rulesPath) -and (Test-Path -LiteralPath $rulesPath -PathType Leaf))\n checkStatus = if ($checkStep.Count -gt 0) { $checkStep[0].status } else { \"not_run\" }\n gateStatus = if ($gateStep.Count -gt 0) { $gateStep[0].status } else { \"not_run\" }\n noDegradation = $noDegradation\n metrics = $metrics\n baseline = [ordered]@{\n qualitySignal = ConvertTo-NullableDouble (Get-JsonProperty $baseline \"quality_signal\")\n couplingScore = ConvertTo-NullableDouble (Get-JsonProperty $baseline \"coupling_score\")\n cycleCount = ConvertTo-NullableDouble (Get-JsonProperty $baseline \"cycle_count\")\n complexFnCount = ConvertTo-NullableDouble (Get-JsonProperty $baseline \"complex_fn_count\")\n crossModuleEdges = ConvertTo-NullableDouble (Get-JsonProperty $baseline \"cross_module_edges\")\n totalImportEdges = ConvertTo-NullableDouble (Get-JsonProperty $baseline \"total_import_edges\")\n }\n distanceFromMainSequence = $distance\n scan = $scan\n regressions = $regressions\n nextActions = $nextActions\n codeNexusHints = $codeNexusHints\n }\n}\n\nfunction Get-StepMatch {\n param(\n [object[]]$Steps,\n [string]$Pattern,\n [switch]$Last\n )\n\n $matches = @($Steps | Where-Object { [string]$_.name -like $Pattern })\n if ($matches.Count -eq 0) { return $null }\n if ($Last) { return $matches[-1] }\n return $matches[0]\n}\n\nfunction Get-StepScore {\n param([object]$Step)\n\n if ($null -eq $Step) { return 0 }\n switch ([string]$Step.status) {\n \"passed\" { return 100 }\n default { return 0 }\n }\n}\n\nfunction Get-FailureCount {\n param(\n [object]$FailureCounts,\n [string]$Name\n )\n\n if ($FailureCounts -is [System.Collections.IDictionary] -and $FailureCounts.Contains($Name)) {\n return [int]$FailureCounts[$Name]\n }\n if ($null -ne $FailureCounts -and $null -ne $FailureCounts.PSObject.Properties[$Name]) {\n return [int]$FailureCounts.$Name\n }\n\n return 0\n}\n\nfunction Get-FirstLine {\n param([string]$Text)\n\n if ([string]::IsNullOrWhiteSpace($Text)) { return \"\" }\n return (($Text -split \"\\r?\\n\") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 1)\n}\n\nfunction New-QualityDimension {\n param(\n [string]$Name,\n [int]$Score,\n [string]$Status,\n [string]$Evidence\n )\n\n return [ordered]@{\n name = $Name\n score = [math]::Max(0, [math]::Min(100, $Score))\n status = $Status\n evidence = $Evidence\n }\n}\n\nfunction New-Modality {\n param(\n [string]$Name,\n [string]$Role,\n [object]$Step,\n [int]$Confidence,\n [string]$Artifact,\n [string]$Finding,\n [string]$Limit\n )\n\n $status = if ($Finding -eq \"not generated\" -and [string]::IsNullOrWhiteSpace($Artifact)) {\n \"missing\"\n }\n elseif ($null -ne $Step) {\n [string]$Step.status\n }\n elseif (-not [string]::IsNullOrWhiteSpace($Artifact)) {\n \"generated\"\n }\n else {\n \"not_run\"\n }\n return [ordered]@{\n name = $Name\n role = $Role\n status = $status\n confidence = [math]::Max(0, [math]::Min(100, $Confidence))\n artifact = $Artifact\n finding = $Finding\n limit = $Limit\n durationMs = if ($null -eq $Step -or $null -eq $Step.durationMs) { $null } else { [int]$Step.durationMs }\n }\n}\n\nfunction New-HospitalProtocol {\n param(\n [string]$Name,\n [string]$Status,\n [string]$Command,\n [string]$ExitCriteria\n )\n\n return [ordered]@{\n name = $Name\n status = $Status\n command = $Command\n exit_criteria = $ExitCriteria\n }\n}\n\nfunction New-StateTransition {\n param(\n [string]$From,\n [string]$To,\n [string]$Guard,\n [bool]$Pass\n )\n\n return [ordered]@{\n from = $From\n to = $To\n guard = $Guard\n pass = $Pass\n }\n}\n\nfunction New-HospitalStateMachine {\n param(\n [object]$FailureCounts,\n [bool]$RulesExists,\n [string]$GateStatus,\n [string]$CheckStatus,\n [int]$FailingWhatIfCount,\n [string]$Disposition,\n [string]$NextProtocol,\n [bool]$StructuralEvidenceComplete = $true,\n [string]$SurgeryTarget = \"\",\n [string]$CurrentTopHotspot = \"\"\n )\n\n # Keep this guard self-contained because the state-machine seam is also\n # extracted independently by the regression harness.\n $providerQuotaCount = 0\n $providerUnavailableCount = 0\n $configErrorCount = 0\n if ($FailureCounts -is [System.Collections.IDictionary] -and $FailureCounts.Contains(\"providerQuota\")) {\n $providerQuotaCount = [int]$FailureCounts[\"providerQuota\"]\n }\n elseif ($null -ne $FailureCounts -and $null -ne $FailureCounts.PSObject.Properties[\"providerQuota\"]) {\n $providerQuotaCount = [int]$FailureCounts.providerQuota\n }\n if ($FailureCounts -is [System.Collections.IDictionary] -and $FailureCounts.Contains(\"providerUnavailable\")) {\n $providerUnavailableCount = [int]$FailureCounts[\"providerUnavailable\"]\n }\n elseif ($null -ne $FailureCounts -and $null -ne $FailureCounts.PSObject.Properties[\"providerUnavailable\"]) {\n $providerUnavailableCount = [int]$FailureCounts.providerUnavailable\n }\n if ($FailureCounts -is [System.Collections.IDictionary] -and $FailureCounts.Contains(\"configError\")) {\n $configErrorCount = [int]$FailureCounts[\"configError\"]\n }\n elseif ($null -ne $FailureCounts -and $null -ne $FailureCounts.PSObject.Properties[\"configError\"]) {\n $configErrorCount = [int]$FailureCounts.configError\n }\n\n $toolsOk = ([int]$FailureCounts.localToolError -eq 0)\n $providerAvailable = ($providerQuotaCount -eq 0 -and $providerUnavailableCount -eq 0 -and $configErrorCount -eq 0)\n $graphOk = ([int]$FailureCounts.graphMissing -eq 0)\n $sentruxOk = ([int]$FailureCounts.sentruxFail -eq 0 -and $RulesExists -and $GateStatus -eq \"passed\" -and $CheckStatus -eq \"passed\")\n $surgeryDebtCleared = ($StructuralEvidenceComplete -and $FailingWhatIfCount -eq 0)\n\n # surgery_plan -> post_op: the surgery target has actually been treated\n # (it no longer shows up as the current top hotspot) and sentrux confirms\n # the governed scope is clean, so it is safe to move on to post-op review.\n $surgeryTargetResolved = (-not [string]::IsNullOrWhiteSpace($SurgeryTarget) -and\n -not [string]::IsNullOrWhiteSpace($CurrentTopHotspot) -and\n ($SurgeryTarget -ne $CurrentTopHotspot))\n $surgeryToPostOpOk = ($sentruxOk -and $StructuralEvidenceComplete -and $surgeryTargetResolved)\n $postOpOk = ($toolsOk -and $providerAvailable -and $graphOk -and $sentruxOk -and $surgeryDebtCleared -and $surgeryTargetResolved)\n\n $currentState = switch ($NextProtocol) {\n \"triage\" { \"triage\" }\n \"diagnose\" { \"diagnose\" }\n \"govern\" { \"govern\" }\n \"surgery_plan\" { \"surgery_plan\" }\n \"post_op\" { if ($Disposition -eq \"discharge_ready\") { \"discharge_ready\" } else { \"post_op\" } }\n default { \"triage\" }\n }\n\n return [ordered]@{\n schema = \"code-intel-hospital-state-machine.v1\"\n current_state = $currentState\n disposition = $Disposition\n next_protocol = $NextProtocol\n states = @(\"triage\", \"diagnose\", \"govern\", \"surgery_plan\", \"post_op\", \"discharge_ready\")\n transitions = @(\n (New-StateTransition \"triage\" \"diagnose\" \"local toolchain is available\" $toolsOk)\n (New-StateTransition \"diagnose\" \"govern\" \"architecture graph exists or graph absence is accepted\" $graphOk)\n (New-StateTransition \"govern\" \"surgery_plan\" \"rules and gate pass, but what-if still has planned debt\" ($sentruxOk -and -not $surgeryDebtCleared))\n (New-StateTransition \"govern\" \"post_op\" \"rules and gate pass, no planned surgery debt remains\" ($sentruxOk -and $surgeryDebtCleared))\n (New-StateTransition \"surgery_plan\" \"post_op\" \"sentrux gate/check pass and the surgery target no longer appears as the current top hotspot\" $surgeryToPostOpOk)\n (New-StateTransition \"post_op\" \"discharge_ready\" \"post-op verification passes with no regressions\" $postOpOk)\n )\n guards = [ordered]@{\n tools_ok = $toolsOk\n provider_available = $providerAvailable\n graph_ok = $graphOk\n rules_exists = $RulesExists\n sentrux_check = $CheckStatus\n sentrux_gate = $GateStatus\n sentrux_ok = $sentruxOk\n failing_what_if = $FailingWhatIfCount\n structural_evidence_complete = $StructuralEvidenceComplete\n surgery_debt_cleared = $surgeryDebtCleared\n surgery_target = $SurgeryTarget\n current_top_hotspot = $CurrentTopHotspot\n surgery_target_resolved = $surgeryTargetResolved\n surgery_to_post_op_ok = $surgeryToPostOpOk\n post_op_ok = $postOpOk\n }\n }\n}\n\nfunction Read-JsonPathIfExists {\n param([string]$Path)\n\n if ([string]::IsNullOrWhiteSpace($Path)) { return $null }\n if (-not (Test-Path -LiteralPath $Path -PathType Leaf)) { return $null }\n\n return Read-JsonFileSafe $Path\n}\n\nfunction Get-SourceAnchorText {\n param([object]$SourceAnchor)\n\n if ($null -eq $SourceAnchor) { return \"\" }\n if ($SourceAnchor -is [string]) { return [string]$SourceAnchor }\n if ($null -ne $SourceAnchor.label) { return [string]$SourceAnchor.label }\n if ($null -ne $SourceAnchor.path) { return [string]$SourceAnchor.path }\n\n return [string]$SourceAnchor\n}\n\nfunction New-CodeIntelSurgeryPlan {\n param(\n [object]$Hospital,\n [string]$RepoPath,\n [string]$SentruxTargetPath,\n [string]$HotspotsPath,\n [string]$WhatIfPath,\n [string]$CodeNexusPath\n )\n\n $hotspots = Read-JsonPathIfExists $HotspotsPath\n $whatIf = Read-JsonPathIfExists $WhatIfPath\n $codeNexus = Read-JsonPathIfExists $CodeNexusPath\n\n $primaryFunction = $null\n if ($null -ne $hotspots -and $null -ne $hotspots.functions -and @($hotspots.functions).Count -gt 0) {\n $primaryFunction = $hotspots.functions[0]\n }\n $primaryFile = $null\n if ($null -ne $hotspots -and $null -ne $hotspots.files -and @($hotspots.files).Count -gt 0) {\n $primaryFile = $hotspots.files[0]\n }\n $primaryScenario = $null\n $failingScenarios = @()\n if ($null -ne $whatIf -and $null -ne $whatIf.scenarios) {\n $failingScenarios = @($whatIf.scenarios | Where-Object { -not $_.pass })\n if ($failingScenarios.Count -gt 0) { $primaryScenario = $failingScenarios[0] }\n }\n $contextFile = $null\n if ($null -ne $codeNexus -and $null -ne $codeNexus.files -and @($codeNexus.files).Count -gt 0) {\n $contextFile = $codeNexus.files[0]\n }\n\n $targetFile = if ($null -ne $primaryFunction) { [string]$primaryFunction.file } elseif ($null -ne $primaryFile) { [string]$primaryFile.path } else { \"\" }\n $targetName = if ($null -ne $primaryFunction) { [string]$primaryFunction.name } elseif ($null -ne $primaryFile) { [string]$primaryFile.path } else { \"\" }\n $targetAnchor = if ($null -ne $primaryFunction) { Get-SourceAnchorText $primaryFunction.sourceAnchor } elseif ($null -ne $primaryFile) { Get-SourceAnchorText $primaryFile.sourceAnchor } else { \"\" }\n $targetComplexity = if ($null -ne $primaryFunction) { [int]$primaryFunction.complexity } elseif ($null -ne $primaryFile) { [int]$primaryFile.maxComplexity } else { $null }\n $scenarioName = if ($null -ne $primaryScenario) { [string]$primaryScenario.name } else { \"\" }\n $scenarioAction = if ($null -ne $primaryScenario) { [string]$primaryScenario.action } else { \"\" }\n $status = if ([string]$Hospital.triage.next_protocol -eq \"surgery_plan\" -or\n ([string]$Hospital.triage.disposition -eq \"admit\" -and -not [string]::IsNullOrWhiteSpace($targetFile))) {\n \"planned\"\n }\n else {\n \"not_required\"\n }\n\n return [ordered]@{\n schema = \"code-intel-surgery-plan.v1\"\n status = $status\n repo = $RepoPath\n scope = $SentruxTargetPath\n admission = [ordered]@{\n disposition = $Hospital.triage.disposition\n diagnosis = $Hospital.triage.primary_diagnosis\n reason = $Hospital.triage.admission_reason\n }\n primary_target = [ordered]@{\n file = $targetFile\n name = $targetName\n source_anchor = $targetAnchor\n complexity = $targetComplexity\n scenario = $scenarioName\n scenario_action = $scenarioAction\n codenexus_file = if ($null -ne $contextFile) { [string]$contextFile.path } else { \"\" }\n }\n operating_plan = @(\n \"Open the primary target and its CodeNexus context before editing.\",\n \"Reduce the selected hotspot by extraction, boundary clarification, or testable decomposition.\",\n \"Do not raise Sentrux thresholds to make the surgery pass.\",\n \"Add or update the smallest test that proves the behavior stayed intact.\"\n )\n verification = @(\n \"Invoke-SentruxAgentTool.ps1 check_rules `\"$SentruxTargetPath`\"\",\n \"Invoke-SentruxAgentTool.ps1 session_end `\"$SentruxTargetPath`\"\",\n \"scripts/tests/test-code-intel-pipeline.ps1 -RepoPath `\"$RepoPath`\" -SentruxPath `\"$((Get-RelativePathSafe $RepoPath $SentruxTargetPath) -replace '\\\\', '/')`\" -SkipRepowise -Mode normal\"\n )\n discharge_criteria = $Hospital.triage.discharge_criteria\n evidence = [ordered]@{\n hotspots = $HotspotsPath\n what_if = $WhatIfPath\n codenexus = $CodeNexusPath\n failing_scenarios = @($failingScenarios | Select-Object -First 5)\n }\n }\n}\n\nfunction Convert-SurgeryPlanToMarkdown {\n param([object]$Plan)\n\n $lines = @(\n \"# Code Intel Surgery Plan\",\n \"\",\n \"- Status: $($Plan.status)\",\n \"- Repo: $($Plan.repo)\",\n \"- Scope: $($Plan.scope)\",\n \"- Diagnosis: $($Plan.admission.diagnosis)\",\n \"- Admission reason: $($Plan.admission.reason)\",\n \"\",\n \"## Primary Target\",\n \"- File: $($Plan.primary_target.file)\",\n \"- Symbol: $($Plan.primary_target.name)\",\n \"- Anchor: $($Plan.primary_target.source_anchor)\",\n \"- Complexity: $($Plan.primary_target.complexity)\",\n \"- Scenario: $($Plan.primary_target.scenario)\",\n \"- Action: $($Plan.primary_target.scenario_action)\",\n \"- CodeNexus file: $($Plan.primary_target.codenexus_file)\",\n \"\",\n \"## Operating Plan\"\n )\n foreach ($item in @($Plan.operating_plan)) {\n $lines += \"- $item\"\n }\n $lines += \"\"\n $lines += \"## Verification\"\n foreach ($item in @($Plan.verification)) {\n $lines += \"- ``$item``\"\n }\n $lines += \"\"\n $lines += \"## Discharge Criteria\"\n foreach ($item in @($Plan.discharge_criteria)) {\n $lines += \"- $item\"\n }\n return $lines\n}\n\nfunction Get-HospitalDiagnosis {\n param(\n [object]$FailureCounts,\n [bool]$RulesExists,\n [int]$FailingWhatIfCount\n )\n\n $providerQuotaCount = Get-FailureCount $FailureCounts \"providerQuota\"\n $providerUnavailableCount = Get-FailureCount $FailureCounts \"providerUnavailable\"\n $configErrorCount = Get-FailureCount $FailureCounts \"configError\"\n\n if ($FailureCounts.localToolError -gt 0) {\n return [ordered]@{ severity = \"red\"; primaryDiagnosis = \"local tool failure\" }\n }\n if ($providerQuotaCount -gt 0) {\n return [ordered]@{ severity = \"amber\"; primaryDiagnosis = \"provider quota exhausted\" }\n }\n if ($providerUnavailableCount -gt 0) {\n return [ordered]@{ severity = \"amber\"; primaryDiagnosis = \"provider unavailable\" }\n }\n if ($configErrorCount -gt 0) {\n return [ordered]@{ severity = \"amber\"; primaryDiagnosis = \"provider configuration error\" }\n }\n if ($FailureCounts.sentruxFail -gt 0) {\n return [ordered]@{ severity = \"red\"; primaryDiagnosis = \"architecture gate failure\" }\n }\n if ($FailureCounts.graphMissing -gt 0) {\n return [ordered]@{ severity = \"amber\"; primaryDiagnosis = \"architecture graph missing\" }\n }\n if (-not $RulesExists) {\n return [ordered]@{ severity = \"amber\"; primaryDiagnosis = \"ungoverned structural scope\" }\n }\n if ($FailingWhatIfCount -gt 0) {\n return [ordered]@{ severity = \"amber\"; primaryDiagnosis = \"known modernization debt\" }\n }\n\n return [ordered]@{ severity = \"green\"; primaryDiagnosis = \"clean snapshot\" }\n}\n\nfunction Get-HospitalNextProtocol {\n param(\n [object]$FailureCounts,\n [bool]$RulesExists,\n [int]$FailingWhatIfCount,\n [object]$GitHubResearch\n )\n\n $providerQuotaCount = Get-FailureCount $FailureCounts \"providerQuota\"\n $providerUnavailableCount = Get-FailureCount $FailureCounts \"providerUnavailable\"\n $configErrorCount = Get-FailureCount $FailureCounts \"configError\"\n\n if ($FailureCounts.localToolError -gt 0) { return \"triage\" }\n if ($providerQuotaCount -gt 0) { return \"triage\" }\n if ($providerUnavailableCount -gt 0) { return \"triage\" }\n if ($configErrorCount -gt 0) { return \"triage\" }\n if ($null -ne $GitHubResearch -and [bool]$GitHubResearch.required) { return \"github_solution_research\" }\n if ($FailureCounts.graphMissing -gt 0) { return \"diagnose\" }\n if (-not $RulesExists) { return \"govern\" }\n if ($FailingWhatIfCount -gt 0) { return \"surgery_plan\" }\n\n return \"post_op\"\n}\n\nfunction Get-HospitalAdmissionReason {\n param([string]$PrimaryDiagnosis)\n\n switch ($PrimaryDiagnosis) {\n \"clean snapshot\" { return \"No active inpatient issue; ready for discharge after post-op verification.\" }\n \"architecture graph missing\" { return \"Admit for diagnostic imaging: Understand graph is missing or stale.\" }\n \"ungoverned structural scope\" { return \"Admit for governance: rules are missing for the selected scope.\" }\n \"known modernization debt\" { return \"Admit for planned surgery: what-if scenarios show debt that should be scheduled, not ignored.\" }\n \"architecture gate failure\" { return \"Admit for structural treatment: Sentrux gate or rules failed.\" }\n \"provider quota exhausted\" { return \"Admit for triage: provider quota prevented complete evidence collection.\" }\n \"provider unavailable\" { return \"Admit for triage: the configured upstream provider route or model was unavailable.\" }\n \"provider configuration error\" { return \"Admit for triage: provider credentials, endpoint, or model configuration must be corrected.\" }\n \"structural evidence incomplete\" { return \"Admit for diagnosis: required structural summaries are incomplete.\" }\n \"local tool failure\" { return \"Admit for triage: local toolchain failed before diagnosis can be trusted.\" }\n default { return \"Admit until the next protocol clears the diagnosis.\" }\n }\n}\n\nfunction Get-HospitalTreatmentPlan {\n param(\n [object]$FailureCounts,\n [bool]$RulesExists,\n [int]$FailingWhatIfCount,\n [string]$UnderstandCommand,\n [string]$TopContextFile\n )\n\n $providerQuotaCount = Get-FailureCount $FailureCounts \"providerQuota\"\n $providerUnavailableCount = Get-FailureCount $FailureCounts \"providerUnavailable\"\n $configErrorCount = Get-FailureCount $FailureCounts \"configError\"\n\n $treatment = @()\n if ($FailureCounts.localToolError -gt 0) { $treatment += \"Fix local tool errors before interpreting architecture signals.\" }\n if ($providerQuotaCount -gt 0) { $treatment += \"Restore provider quota or use a complete local evidence path before interpreting the result.\" }\n if ($providerUnavailableCount -gt 0) { $treatment += \"Verify the provider model catalog and route availability; keep local index-only evidence available.\" }\n if ($configErrorCount -gt 0) { $treatment += \"Correct provider endpoint, model, or credential configuration before retrying provider-backed docs.\" }\n if ($FailureCounts.graphMissing -gt 0) { $treatment += \"Refresh Understand graph with: $UnderstandCommand\" }\n if (-not $RulesExists) { $treatment += \"Add .sentrux/rules.toml for the chosen scope.\" }\n if ($FailingWhatIfCount -gt 0) { $treatment += \"Use what-if failures as the tightening roadmap; start with the first failing scenario.\" }\n if (-not [string]::IsNullOrWhiteSpace($TopContextFile)) { $treatment += \"Start CodeNexus review at $TopContextFile.\" }\n if ($treatment.Count -eq 0) { $treatment += \"Keep this artifact as the current clean snapshot and compare the next session against it.\" }\n\n return $treatment\n}\n\nfunction New-HospitalDecisionBlock {\n param(\n [object]$FailureCounts,\n [bool]$RulesExists,\n [string]$GateStatus,\n [string]$CheckStatus,\n [int]$FailingWhatIfCount,\n [string]$UnderstandCommand,\n [string]$TopContextFile,\n [bool]$StructuralEvidenceComplete = $false,\n [string]$SurgeryTarget = \"\",\n [string]$CurrentTopHotspot = \"\",\n [object]$GitHubResearch\n )\n\n $diagnosis = Get-HospitalDiagnosis $FailureCounts $RulesExists $FailingWhatIfCount\n $nextProtocol = Get-HospitalNextProtocol $FailureCounts $RulesExists $FailingWhatIfCount $GitHubResearch\n $sentruxVerified = ($RulesExists -and $GateStatus -eq \"passed\" -and $CheckStatus -eq \"passed\")\n if ($diagnosis.severity -eq \"green\" -and -not $sentruxVerified) {\n $hasExplicitFailure = ($GateStatus -eq \"failed\" -or $CheckStatus -eq \"failed\")\n $diagnosis = [ordered]@{\n severity = if ($hasExplicitFailure) { \"red\" } else { \"amber\" }\n primaryDiagnosis = if ($hasExplicitFailure) { \"architecture gate failure\" } else { \"architecture verification incomplete\" }\n }\n $nextProtocol = \"govern\"\n }\n elseif ($diagnosis.severity -eq \"green\" -and -not $StructuralEvidenceComplete) {\n $diagnosis = [ordered]@{ severity = \"amber\"; primaryDiagnosis = \"structural evidence incomplete\" }\n $nextProtocol = \"diagnose\"\n }\n $postOpResolved = (-not [string]::IsNullOrWhiteSpace($SurgeryTarget) -and\n -not [string]::IsNullOrWhiteSpace($CurrentTopHotspot) -and\n $SurgeryTarget -ne $CurrentTopHotspot)\n $disposition = if ($diagnosis.severity -ne \"green\") {\n \"admit\"\n }\n elseif ($sentruxVerified -and $StructuralEvidenceComplete -and $postOpResolved) {\n \"discharge_ready\"\n }\n else {\n \"observe\"\n }\n $admissionReason = Get-HospitalAdmissionReason $diagnosis.primaryDiagnosis\n $dischargeCriteria = @(\n \"failure category counters are zero\",\n \"Sentrux check and gate pass for the governed scope\",\n \"hospital triage status is green or explicitly accepted for observation\",\n \"session_end reports no quality regression after Agent edits\"\n )\n if ($null -ne $GitHubResearch -and [bool]$GitHubResearch.required) {\n $dischargeCriteria += \"GitHub evidence linked or GitHub evidence insufficiency recorded in github-solution-research artifacts\"\n }\n $treatment = Get-HospitalTreatmentPlan $FailureCounts $RulesExists $FailingWhatIfCount $UnderstandCommand $TopContextFile\n if (-not $sentruxVerified) {\n $treatment = @($treatment) + \"Obtain passing Sentrux check and gate evidence before discharge.\"\n }\n\n $stateMachine = New-HospitalStateMachine `\n -FailureCounts $FailureCounts `\n -RulesExists $RulesExists `\n -GateStatus $GateStatus `\n -CheckStatus $CheckStatus `\n -FailingWhatIfCount $FailingWhatIfCount `\n -Disposition $disposition `\n -NextProtocol $nextProtocol `\n -StructuralEvidenceComplete $StructuralEvidenceComplete `\n -SurgeryTarget $SurgeryTarget `\n -CurrentTopHotspot $CurrentTopHotspot\n\n return [ordered]@{\n severity = $diagnosis.severity\n primaryDiagnosis = $diagnosis.primaryDiagnosis\n nextProtocol = $nextProtocol\n disposition = $disposition\n admissionReason = $admissionReason\n dischargeCriteria = $dischargeCriteria\n treatment = $treatment\n stateMachine = $stateMachine\n }\n}\n\nfunction New-HospitalFindings {\n param(\n [int]$InventoryFiles,\n [object]$SentruxFileDetailsSummary,\n [string]$TopFunction,\n [string]$TopModule,\n [object]$ResolvedRatio,\n [int]$ResolvedImports,\n [int]$UnresolvedImports,\n [int]$ExcludedFiles\n )\n\n $findings = @()\n if ($InventoryFiles -gt 0) { $findings += \"X-ray inventory found $InventoryFiles files.\" }\n if ($null -ne $SentruxFileDetailsSummary) { $findings += \"CT structural scan found $($SentruxFileDetailsSummary.files) files and $($SentruxFileDetailsSummary.functions) functions.\" }\n if (-not [string]::IsNullOrWhiteSpace($TopFunction)) { $findings += \"Top surgical hotspot: $TopFunction.\" }\n if (-not [string]::IsNullOrWhiteSpace($TopModule)) { $findings += \"Top module hotspot: $TopModule.\" }\n if ($ResolvedRatio -ne $null) { $findings += \"Import resolution ratio is $ResolvedRatio% ($ResolvedImports resolved, $UnresolvedImports unresolved).\" }\n if ($ExcludedFiles -gt 0) { $findings += \"$ExcludedFiles files were quarantined from governed source metrics.\" }\n return $findings\n}\n\nfunction New-HospitalModalities {\n param(\n [object]$InventoryStep,\n [object]$UnderstandStep,\n [object]$RepowiseStep,\n [object]$SentruxCheckStep,\n [object]$SentruxGateStep,\n [int]$GraphScore,\n [int]$MemoryScore,\n [int]$MriScore,\n [string]$MriStatus,\n [int]$CtScore,\n [string]$CtStatus,\n [int]$PetScore,\n [string]$PetStatus,\n [int]$GovernanceScore,\n [string]$RunDir,\n [string]$RepoPath,\n [int]$InventoryFiles,\n [object]$SentruxDsmSummary,\n [object]$SentruxFileDetailsSummary,\n [object]$CodeNexusContextSummary,\n [object]$SentruxWhatIfSummary,\n [object]$RuntimeCiSummary,\n [string]$GovernanceArtifact,\n [string]$GovernanceFinding\n )\n\n $xrayFinding = if ($InventoryFiles -gt 0) { \"$InventoryFiles files inventoried\" } else { \"no inventory\" }\n $ctArtifact = if ($CtStatus -eq \"available\") { [string]$SentruxDsmSummary.path } else { \"\" }\n $ctFinding = if ($CtStatus -eq \"available\") { \"$($SentruxDsmSummary.modules) modules, $($SentruxFileDetailsSummary.functions) functions\" } else { \"not generated\" }\n $mriArtifact = if ($MriStatus -eq \"available\") { [string]$CodeNexusContextSummary.path } else { \"\" }\n $mriFinding = if ($MriStatus -eq \"available\") { \"$($CodeNexusContextSummary.files) files, $($CodeNexusContextSummary.references) references\" } else { \"not generated\" }\n $petArtifact = if ($null -ne $RuntimeCiSummary) { [string]$RuntimeCiSummary.path } elseif ($PetStatus -eq \"available\") { [string]$SentruxWhatIfSummary.path } else { \"\" }\n $petFinding = if ($null -ne $RuntimeCiSummary) { \"runtime/CI health=$($RuntimeCiSummary.health); freshness=$($RuntimeCiSummary.freshness); completeness=$($RuntimeCiSummary.completeness)\" } elseif ($PetStatus -eq \"available\") { \"$($SentruxWhatIfSummary.failing) failing what-if scenarios\" } else { \"not generated\" }\n $petLimitation = if ($null -ne $RuntimeCiSummary) { \"Provider-neutral runtime/CI evidence is cited; provider logs remain outside this report.\" } else { \"No live runtime trace is captured yet.\" }\n $chartFinding = if ($null -ne $RepowiseStep) { [string]$RepowiseStep.status } else { \"not run\" }\n\n return @(\n (New-Modality \"xray\" \"fast file inventory and repo surface\" $InventoryStep (Get-StepScore $InventoryStep) (Join-Path $RunDir \"files.txt\") $xrayFinding \"Sees files, not semantic impact.\")\n (New-Modality \"anatomy\" \"Understand Anything architecture graph\" $UnderstandStep $GraphScore (Join-Path (Join-Path $RepoPath \".understand-anything\") \"knowledge-graph.json\") (Get-FirstLine ([string]$UnderstandStep.output)) \"Requires a prebuilt graph from the Understand tool.\")\n (New-Modality \"ct\" \"Sentrux DSM, hotspots, and structural slices\" $SentruxGateStep $CtScore $ctArtifact $ctFinding \"Static structure is not runtime truth.\")\n (New-Modality \"mri\" \"CodeNexus context and impact localization\" $null $MriScore $mriArtifact $mriFinding \"Lite mode is local evidence, not a full semantic backend.\")\n (New-Modality \"pet\" \"runtime/CI evidence with test gaps, evolution, and what-if fallback\" $null $PetScore $petArtifact $petFinding $petLimitation)\n (New-Modality \"chart\" \"Repowise long-term project memory\" $RepowiseStep $MemoryScore \"\" $chartFinding \"Provider quota and index freshness can limit semantic memory.\")\n (New-Modality \"governance\" \"rules, gate, and session safety rails\" $SentruxCheckStep $GovernanceScore $GovernanceArtifact $GovernanceFinding \"Rules only protect boundaries that have been encoded.\")\n )\n}\n\nfunction New-HospitalQualityDimensions {\n param(\n [int]$SourceCoverageScore,\n [string]$SourceScopeStatus,\n [int]$InventoryFiles,\n [int]$ScanFiles,\n [int]$GraphScore,\n [object]$UnderstandStep,\n [int]$ResolutionScore,\n [string]$ImportResolutionStatus,\n [int]$ResolvedImports,\n [int]$UnresolvedImports,\n [int]$PollutionScore,\n [string]$PollutionStatus,\n [int]$ExcludedFiles,\n [int]$GovernanceScore,\n [string]$GovernanceStatus,\n [string]$GovernanceEvidence,\n [int]$MriScore,\n [string]$LocalizationStatus,\n [string]$TopContextFile,\n [int]$MemoryScore,\n [string]$MemoryStatus,\n [string]$MemoryEvidence\n )\n\n return @(\n (New-QualityDimension \"source_coverage\" $SourceCoverageScore $SourceScopeStatus \"inventory=$InventoryFiles; sentrux_scan=$ScanFiles\")\n (New-QualityDimension \"graph_freshness\" $GraphScore ([string]$UnderstandStep.status) (Get-FirstLine ([string]$UnderstandStep.output)))\n (New-QualityDimension \"import_resolution\" $ResolutionScore $ImportResolutionStatus \"resolved=$ResolvedImports; unresolved=$UnresolvedImports\")\n (New-QualityDimension \"pollution_control\" $PollutionScore $PollutionStatus \"excluded=$ExcludedFiles\")\n (New-QualityDimension \"governance\" $GovernanceScore $GovernanceStatus $GovernanceEvidence)\n (New-QualityDimension \"localization\" $MriScore $LocalizationStatus \"top_file=$TopContextFile\")\n (New-QualityDimension \"memory\" $MemoryScore $MemoryStatus $MemoryEvidence)\n )\n}\n\nfunction Read-HospitalArtifactFile {\n param([object]$Summary)\n\n if ($null -eq $Summary) { return $null }\n\n $path = [string]$Summary.path\n if ([string]::IsNullOrWhiteSpace($path)) { return $null }\n if (-not (Test-Path -LiteralPath $path -PathType Leaf)) { return $null }\n\n return Read-JsonFileSafe $path\n}\n\nfunction Read-HospitalArtifacts {\n param(\n [object]$SentruxDsmSummary,\n [object]$SentruxFileDetailsSummary,\n [object]$SentruxHotspotsSummary,\n [object]$SentruxEvolutionSummary,\n [object]$SentruxWhatIfSummary,\n [object]$CodeNexusContextSummary\n )\n\n return [ordered]@{\n dsm = Read-HospitalArtifactFile $SentruxDsmSummary\n file_details = Read-HospitalArtifactFile $SentruxFileDetailsSummary\n hotspots = Read-HospitalArtifactFile $SentruxHotspotsSummary\n evolution = Read-HospitalArtifactFile $SentruxEvolutionSummary\n what_if = Read-HospitalArtifactFile $SentruxWhatIfSummary\n codenexus = Read-HospitalArtifactFile $CodeNexusContextSummary\n }\n}\n\nfunction New-HospitalMeasurements {\n param(\n [object]$InventoryStep,\n [object]$SentruxInsight,\n [object]$DsmObject\n )\n\n $inventoryFiles = 0\n $inventoryMatch = [regex]::Match([string]$InventoryStep.output, \"files=([0-9]+)\")\n if ($inventoryMatch.Success) { $inventoryFiles = [int]$inventoryMatch.Groups[1].Value }\n\n $scan = if ($null -ne $SentruxInsight -and $null -ne $SentruxInsight[\"scan\"]) { $SentruxInsight[\"scan\"] } else { @{} }\n $scanFiles = if ($scan.Contains(\"files\")) { [int]$scan[\"files\"] } else { 0 }\n $unresolvedImports = if ($scan.Contains(\"unresolvedImports\")) { [int]$scan[\"unresolvedImports\"] } else { 0 }\n $resolvedImports = if ($scan.Contains(\"resolvedImports\")) { [int]$scan[\"resolvedImports\"] } else { 0 }\n $totalImports = $resolvedImports + $unresolvedImports\n $resolvedRatio = if ($totalImports -gt 0) { [math]::Round(($resolvedImports * 100.0) / $totalImports, 1) } else { $null }\n $dsmScope = $null\n if ($DsmObject -is [System.Collections.IDictionary] -and $DsmObject.Contains(\"scope\")) {\n $dsmScope = $DsmObject[\"scope\"]\n }\n elseif ($null -ne $DsmObject -and $null -ne $DsmObject.PSObject.Properties[\"scope\"]) {\n $dsmScope = $DsmObject.scope\n }\n\n $excludedFilesValue = $null\n $hasPollutionEvidence = $false\n if ($dsmScope -is [System.Collections.IDictionary] -and $dsmScope.Contains(\"excluded_files\")) {\n $excludedFilesValue = $dsmScope[\"excluded_files\"]\n $hasPollutionEvidence = ($null -ne $excludedFilesValue)\n }\n elseif ($null -ne $dsmScope -and $null -ne $dsmScope.PSObject.Properties[\"excluded_files\"]) {\n $excludedFilesValue = $dsmScope.excluded_files\n $hasPollutionEvidence = ($null -ne $excludedFilesValue)\n }\n\n $excludedFiles = if ($hasPollutionEvidence) { [int]$excludedFilesValue } else { 0 }\n $sourceScopeStatus = if ($inventoryFiles -gt 0 -and $scanFiles -gt 0) { \"measured\" } else { \"unknown\" }\n $pollutionStatus = if (-not $hasPollutionEvidence) { \"unknown\" } elseif ($excludedFiles -gt 0) { \"quarantined\" } else { \"clean\" }\n\n return [ordered]@{\n inventory_files = $inventoryFiles\n scan_files = $scanFiles\n unresolved_imports = $unresolvedImports\n resolved_imports = $resolvedImports\n resolved_ratio = $resolvedRatio\n excluded_files = $excludedFiles\n source_scope_status = $sourceScopeStatus\n pollution_status = $pollutionStatus\n }\n}\n\nfunction Get-ImportResolutionScore {\n param([object]$ResolvedRatio)\n\n if ($null -eq $ResolvedRatio) { return 0 }\n if ($ResolvedRatio -ge 75) { return 100 }\n if ($ResolvedRatio -ge 50) { return 75 }\n if ($ResolvedRatio -ge 25) { return 50 }\n\n return 30\n}\n\nfunction Get-SourceCoverageScore {\n param(\n [int]$ScanFiles,\n [int]$InventoryFiles\n )\n\n if ($ScanFiles -le 0 -or $InventoryFiles -le 0) { return 0 }\n\n return [int][math]::Round([math]::Min(100.0, ($ScanFiles * 100.0) / $InventoryFiles))\n}\n\nfunction New-HospitalScoreBlock {\n param(\n [object]$SentruxInsight,\n [object]$Measurements,\n [object]$UnderstandStep,\n [object]$RepowiseStep,\n [object]$SentruxCheckStep,\n [object]$SentruxGateStep,\n [object]$SentruxDsmObject,\n [object]$SentruxFileDetailsObject,\n [object]$SentruxEvolutionObject,\n [object]$SentruxWhatIfObject,\n [object]$CodeNexusContextObject,\n [object]$RuntimeCiSummary\n )\n\n $rulesExists = [bool]$SentruxInsight[\"rulesExists\"]\n $rulesScore = if ($rulesExists) { 100 } else { 45 }\n $gateScore = Get-StepScore $SentruxGateStep\n $checkScore = Get-StepScore $SentruxCheckStep\n $graphScore = Get-StepScore $UnderstandStep\n $memoryScore = Get-StepScore $RepowiseStep\n $mriStatus = if ($null -ne $CodeNexusContextObject) { \"available\" } else { \"missing\" }\n $ctStatus = if ($null -ne $SentruxDsmObject -and $null -ne $SentruxFileDetailsObject) { \"available\" } else { \"missing\" }\n $petStatus = if ($null -ne $RuntimeCiSummary) { if ([string]$RuntimeCiSummary.health -eq \"unknown\") { \"unknown\" } else { \"available\" } } elseif ($null -ne $SentruxWhatIfObject -and $null -ne $SentruxEvolutionObject) { \"available\" } else { \"missing\" }\n $mriScore = if ($mriStatus -eq \"available\") { 100 } else { 0 }\n $ctScore = if ($ctStatus -eq \"available\") { 100 } else { 0 }\n $petScore = if ($null -ne $RuntimeCiSummary) { switch ([string]$RuntimeCiSummary.health) { \"green\" { 100 } \"red\" { 0 } default { 30 } } } elseif ($petStatus -eq \"available\") { 70 } else { 0 }\n $resolutionScore = Get-ImportResolutionScore $Measurements.resolved_ratio\n $pollutionStatus = [string]$Measurements.pollution_status\n $pollutionScore = if ($pollutionStatus -eq \"unknown\") { 0 } elseif ($Measurements.excluded_files -gt 0) { 100 } else { 80 }\n $governanceScore = [int][math]::Round(($rulesScore + $gateScore + $checkScore) / 3.0)\n $diagnosticScore = [int][math]::Round(($ctScore + $mriScore + $graphScore + $memoryScore) / 4.0)\n $overallScore = [int][math]::Round(($diagnosticScore + $governanceScore + $resolutionScore + $pollutionScore) / 4.0)\n $governanceArtifact = if ($rulesExists) { [string]$SentruxInsight[\"rulesPath\"] } else { \"\" }\n $resolvedRatio = $Measurements.resolved_ratio\n\n return [ordered]@{\n rules_exists = $rulesExists\n gate_status = [string]$SentruxInsight[\"gateStatus\"]\n check_status = [string]$SentruxInsight[\"checkStatus\"]\n graph_score = $graphScore\n memory_score = $memoryScore\n mri_score = $mriScore\n mri_status = $mriStatus\n ct_score = $ctScore\n ct_status = $ctStatus\n pet_score = $petScore\n pet_status = $petStatus\n resolution_score = $resolutionScore\n pollution_score = $pollutionScore\n governance_score = $governanceScore\n diagnostic_score = $diagnosticScore\n overall_score = $overallScore\n source_coverage_score = Get-SourceCoverageScore $Measurements.scan_files $Measurements.inventory_files\n import_resolution_status = if ($null -eq $resolvedRatio) { \"unknown\" } else { \"$resolvedRatio%\" }\n pollution_status = $pollutionStatus\n governance_status = if ($rulesExists) { \"rules_present\" } else { \"rules_missing\" }\n governance_artifact = $governanceArtifact\n governance_finding = \"rules=$($SentruxInsight['rulesExists']); gate=$($SentruxInsight['gateStatus']); check=$($SentruxInsight['checkStatus'])\"\n governance_evidence = \"gate=$($SentruxInsight['gateStatus']); check=$($SentruxInsight['checkStatus'])\"\n localization_status = $mriStatus\n memory_status = if ($null -ne $RepowiseStep) { [string]$RepowiseStep.status } else { \"not_run\" }\n memory_evidence = if ($null -ne $RepowiseStep) { Get-FirstLine ([string]$RepowiseStep.output) } else { \"\" }\n }\n}\n\nfunction New-HospitalEvidenceBlock {\n param(\n [object]$HotspotsObject,\n [object]$WhatIfObject,\n [object]$CodeNexusContextSummary\n )\n\n $failingWhatIf = @()\n if ($null -ne $WhatIfObject -and $null -ne $WhatIfObject.scenarios) {\n $failingWhatIf = @($WhatIfObject.scenarios | Where-Object { -not $_.pass })\n }\n\n $topFunction = \"\"\n if ($null -ne $HotspotsObject -and $null -ne $HotspotsObject.functions -and @($HotspotsObject.functions).Count -gt 0) {\n $topFunction = \"{0} in {1} (cc={2})\" -f $HotspotsObject.functions[0].name, $HotspotsObject.functions[0].file, $HotspotsObject.functions[0].complexity\n }\n\n $topModule = \"\"\n if ($null -ne $HotspotsObject -and $null -ne $HotspotsObject.modules -and @($HotspotsObject.modules).Count -gt 0) {\n $topModule = \"{0} (risk={1})\" -f $HotspotsObject.modules[0].name, $HotspotsObject.modules[0].risk\n }\n\n return [ordered]@{\n failing_what_if = $failingWhatIf\n top_function = $topFunction\n top_module = $topModule\n top_context_file = if ($null -ne $CodeNexusContextSummary) { [string]$CodeNexusContextSummary.topFile } else { \"\" }\n }\n}\n\nfunction New-HospitalProtocolBlock {\n param(\n [bool]$RulesExists,\n [int]$FailingWhatIfCount\n )\n\n $governProtocolStatus = if ($RulesExists) { \"active\" } else { \"needs_rules\" }\n $surgeryProtocolStatus = if ($FailingWhatIfCount -gt 0) { \"available\" } else { \"low_risk\" }\n\n return @(\n (New-HospitalProtocol \"triage\" \"available\" \"run-code-intel.ps1 -RepoPath -Mode lite\" \"Classify provider/tool/graph/Sentrux failure bucket and choose next protocol.\")\n (New-HospitalProtocol \"diagnose\" \"available\" \"run-code-intel.ps1 -RepoPath -Mode normal\" \"Produce summary.md, hospital.md, sentrux artifacts, and codenexus context.\")\n (New-HospitalProtocol \"govern\" $governProtocolStatus \"sentrux check ; sentrux gate \" \"Rules pass and gate reports no degradation.\")\n (New-HospitalProtocol \"surgery_plan\" $surgeryProtocolStatus \"read sentrux-what-if.json and codenexus-context.json\" \"Choose one hotspot, one boundary, and one verification command before editing.\")\n (New-HospitalProtocol \"post_op\" \"available\" \"Invoke-SentruxAgentTool.ps1 session_end \" \"Signal does not drop, rules pass, and touched hotspot is lower risk.\")\n )\n}\n\nfunction Get-PreviousSurgeryTarget {\n param([string]$RunDir)\n\n if ([string]::IsNullOrWhiteSpace($RunDir)) { return \"\" }\n $repoArtifactRoot = Split-Path -Parent $RunDir\n if ([string]::IsNullOrWhiteSpace($repoArtifactRoot) -or -not (Test-Path -LiteralPath $repoArtifactRoot -PathType Container)) { return \"\" }\n\n $currentName = Split-Path -Leaf $RunDir\n $previousRun = Get-ChildItem -LiteralPath $repoArtifactRoot -Directory -ErrorAction SilentlyContinue |\n Where-Object { $_.Name -ne $currentName } |\n Sort-Object Name -Descending |\n Select-Object -First 1\n if ($null -eq $previousRun) { return \"\" }\n\n $previousPlanPath = Join-Path $previousRun.FullName \"surgery-plan.json\"\n if (-not (Test-Path -LiteralPath $previousPlanPath -PathType Leaf)) { return \"\" }\n\n $previousPlan = Read-JsonFileSafe $previousPlanPath\n if ($null -eq $previousPlan -or $null -eq $previousPlan.primary_target) { return \"\" }\n if ([string]::IsNullOrWhiteSpace([string]$previousPlan.primary_target.name)) { return \"\" }\n\n return \"$($previousPlan.primary_target.name) in $($previousPlan.primary_target.file)\"\n}\n\nfunction New-CodeIntelHospitalReport {\n param(\n [string]$RepoPath,\n [string]$Mode,\n [string]$RunDir,\n [string]$ReportPath,\n [string]$SummaryPath,\n [string]$UnderstandingPath,\n [object[]]$Steps,\n [object]$FailureCounts,\n [object]$SentruxInsight,\n [object]$SentruxDsmSummary,\n [object]$SentruxFileDetailsSummary,\n [object]$SentruxHotspotsSummary,\n[object]$SentruxEvolutionSummary,\n[object]$SentruxWhatIfSummary,\n[object]$CodeNexusContextSummary,\n[object]$RuntimeCiSummary,\n[string]$UnderstandCommand,\n[object]$ToolState,\n[object]$GitHubResearch\n)\n\n $gitStep = Get-StepMatch $Steps \"git status\"\n $inventoryStep = Get-StepMatch $Steps \"rg file inventory\"\n $understandStep = Get-StepMatch $Steps \"understand graph\"\n $repowiseStep = Get-StepMatch $Steps \"repowise*\" -Last\n $sentruxCheckStep = Get-StepMatch $Steps \"sentrux check\"\n $sentruxGateStep = Get-StepMatch $Steps \"sentrux gate*\" -Last\n\n $artifacts = Read-HospitalArtifacts $SentruxDsmSummary $SentruxFileDetailsSummary $SentruxHotspotsSummary $SentruxEvolutionSummary $SentruxWhatIfSummary $CodeNexusContextSummary\n $structuralEvidenceComplete = ($null -ne $artifacts.dsm -and\n $null -ne $artifacts.file_details -and\n $null -ne $artifacts.hotspots -and\n $null -ne $artifacts.evolution -and\n $null -ne $artifacts.what_if)\n $measurements = New-HospitalMeasurements $inventoryStep $SentruxInsight $artifacts.dsm\n $scores = New-HospitalScoreBlock `\n -SentruxInsight $SentruxInsight `\n -Measurements $measurements `\n -UnderstandStep $understandStep `\n -RepowiseStep $repowiseStep `\n -SentruxCheckStep $sentruxCheckStep `\n -SentruxGateStep $sentruxGateStep `\n -SentruxDsmObject $artifacts.dsm `\n -SentruxFileDetailsObject $artifacts.file_details `\n -SentruxEvolutionObject $artifacts.evolution `\n -SentruxWhatIfObject $artifacts.what_if `\n -CodeNexusContextObject $artifacts.codenexus `\n -RuntimeCiSummary $RuntimeCiSummary\n $evidence = New-HospitalEvidenceBlock $artifacts.hotspots $artifacts.what_if $CodeNexusContextSummary\n\n $currentTopHotspot = \"\"\n if ($null -ne $artifacts.hotspots -and $null -ne $artifacts.hotspots.functions -and @($artifacts.hotspots.functions).Count -gt 0) {\n $topFn = $artifacts.hotspots.functions[0]\n $currentTopHotspot = \"$($topFn.name) in $($topFn.file)\"\n }\n $surgeryTarget = Get-PreviousSurgeryTarget $RunDir\n\n $decision = New-HospitalDecisionBlock `\n -FailureCounts $FailureCounts `\n -RulesExists $scores.rules_exists `\n -GateStatus $scores.gate_status `\n -CheckStatus $scores.check_status `\n -FailingWhatIfCount @($evidence.failing_what_if).Count `\n -UnderstandCommand $UnderstandCommand `\n -TopContextFile $evidence.top_context_file `\n -StructuralEvidenceComplete $structuralEvidenceComplete `\n -SurgeryTarget $surgeryTarget `\n -CurrentTopHotspot $currentTopHotspot `\n -GitHubResearch $GitHubResearch\n\n $findings = New-HospitalFindings `\n -InventoryFiles $measurements.inventory_files `\n -SentruxFileDetailsSummary $SentruxFileDetailsSummary `\n -TopFunction $evidence.top_function `\n -TopModule $evidence.top_module `\n -ResolvedRatio $measurements.resolved_ratio `\n -ResolvedImports $measurements.resolved_imports `\n -UnresolvedImports $measurements.unresolved_imports `\n -ExcludedFiles $measurements.excluded_files\n\n $modalities = New-HospitalModalities `\n -InventoryStep $inventoryStep `\n -UnderstandStep $understandStep `\n -RepowiseStep $repowiseStep `\n -SentruxCheckStep $sentruxCheckStep `\n -SentruxGateStep $sentruxGateStep `\n -GraphScore $scores.graph_score `\n -MemoryScore $scores.memory_score `\n -MriScore $scores.mri_score `\n -MriStatus $scores.mri_status `\n -CtScore $scores.ct_score `\n -CtStatus $scores.ct_status `\n -PetScore $scores.pet_score `\n -PetStatus $scores.pet_status `\n -GovernanceScore $scores.governance_score `\n -RunDir $RunDir `\n -RepoPath $RepoPath `\n -InventoryFiles $measurements.inventory_files `\n -SentruxDsmSummary $SentruxDsmSummary `\n -SentruxFileDetailsSummary $SentruxFileDetailsSummary `\n -CodeNexusContextSummary $CodeNexusContextSummary `\n -SentruxWhatIfSummary $SentruxWhatIfSummary `\n -RuntimeCiSummary $RuntimeCiSummary `\n -GovernanceArtifact $scores.governance_artifact `\n -GovernanceFinding $scores.governance_finding\n\n $quality = New-HospitalQualityDimensions `\n -SourceCoverageScore $scores.source_coverage_score `\n -SourceScopeStatus $measurements.source_scope_status `\n -InventoryFiles $measurements.inventory_files `\n -ScanFiles $measurements.scan_files `\n -GraphScore $scores.graph_score `\n -UnderstandStep $understandStep `\n -ResolutionScore $scores.resolution_score `\n -ImportResolutionStatus $scores.import_resolution_status `\n -ResolvedImports $measurements.resolved_imports `\n -UnresolvedImports $measurements.unresolved_imports `\n -PollutionScore $scores.pollution_score `\n -PollutionStatus $scores.pollution_status `\n -ExcludedFiles $measurements.excluded_files `\n -GovernanceScore $scores.governance_score `\n -GovernanceStatus $scores.governance_status `\n -GovernanceEvidence $scores.governance_evidence `\n -MriScore $scores.mri_score `\n -LocalizationStatus $scores.localization_status `\n -TopContextFile $evidence.top_context_file `\n -MemoryScore $scores.memory_score `\n -MemoryStatus $scores.memory_status `\n -MemoryEvidence $scores.memory_evidence\n\n $protocols = New-HospitalProtocolBlock $scores.rules_exists @($evidence.failing_what_if).Count\n\n return [ordered]@{\n schema = \"code-intel-hospital.v1\"\n generatedAt = (Get-Date).ToString(\"o\")\n repo = $RepoPath\n mode = $Mode\n artifacts = [ordered]@{\n runDir = $RunDir\n report = $ReportPath\n summary = $SummaryPath\n understanding = $UnderstandingPath\n runtime_ci = if ($null -ne $RuntimeCiSummary) { [string]$RuntimeCiSummary.path } else { \"\" }\n github_solution_research = if ($null -ne $GitHubResearch) { [string]$GitHubResearch.path } else { \"\" }\n github_solution_research_markdown = if ($null -ne $GitHubResearch) { [string]$GitHubResearch.markdown } else { \"\" }\n }\n triage = [ordered]@{\n status = $decision.severity\n disposition = $decision.disposition\n primary_diagnosis = $decision.primaryDiagnosis\n overall_score = $scores.overall_score\n next_protocol = $decision.nextProtocol\n research_status = if ($null -ne $GitHubResearch) { [string]$GitHubResearch.status } else { \"not_applicable\" }\n research_required = if ($null -ne $GitHubResearch) { [bool]$GitHubResearch.required } else { $false }\n exit_criteria = if ($null -ne $GitHubResearch) { @($GitHubResearch.exitCriteria) } else { @() }\n admission_reason = $decision.admissionReason\n discharge_criteria = $decision.dischargeCriteria\n }\n state_machine = $decision.stateMachine\n modalities = $modalities\n policies = [ordered]@{\n admission = [ordered]@{\n admit_when = @(\n \"local toolchain fails\",\n \"architecture graph is missing\",\n \"Sentrux rules are missing\",\n \"Sentrux check or gate fails\",\n \"what-if reports planned modernization debt\"\n )\n current_reason = $decision.admissionReason\n }\n discharge = [ordered]@{\n criteria = $decision.dischargeCriteria\n current_state = $decision.stateMachine.current_state\n }\n }\n report_quality = [ordered]@{\n overall_score = $scores.overall_score\n diagnostic_score = $scores.diagnostic_score\n governance_score = $scores.governance_score\n dimensions = $quality\n }\n diagnosis = [ordered]@{\n findings = $findings\n impression = $decision.primaryDiagnosis\n risk = $decision.severity\n evidence = [ordered]@{\n top_function = $evidence.top_function\n top_module = $evidence.top_module\n top_context_file = $evidence.top_context_file\n failing_what_if = @($evidence.failing_what_if | Select-Object -First 5)\n }\n }\n treatment = [ordered]@{\n plan = $decision.treatment\n follow_up = @(\n \"Rerun normal mode after code changes.\",\n \"Compare hospital-report.json overall_score and Sentrux quality signal.\",\n \"Use session_start/session_end around Agent edits.\"\n )\n }\n protocols = $protocols\n tools = $ToolState\n }\n}\n\nfunction Convert-HospitalReportToMarkdown {\n param([object]$Hospital)\n\n $lines = @(\n \"# Code Intel Hospital Report\",\n \"\",\n \"- Repo: $($Hospital.repo)\",\n \"- Mode: $($Hospital.mode)\",\n \"- Status: $($Hospital.triage.status)\",\n \"- Disposition: $($Hospital.triage.disposition)\",\n \"- Primary diagnosis: $($Hospital.triage.primary_diagnosis)\",\n \"- Admission reason: $($Hospital.triage.admission_reason)\",\n\"- Overall score: $($Hospital.triage.overall_score)\",\n\"- Next protocol: $($Hospital.triage.next_protocol)\",\n\"- Research status: $($Hospital.triage.research_status)\",\n\"- Research required: $($Hospital.triage.research_required)\",\n\"- Current state: $($Hospital.state_machine.current_state)\",\n\"\",\n\"## Imaging Modalities\"\n)\nif ($null -ne $Hospital.triage.exit_criteria -and @($Hospital.triage.exit_criteria).Count -gt 0) {\n $lines += \"\"\n $lines += \"## Exit Criteria\"\n foreach ($criterion in @($Hospital.triage.exit_criteria)) {\n $lines += \"- $criterion\"\n }\n}\nforeach ($item in @($Hospital.modalities)) {\n $lines += \"- $($item.name): $($item.status), confidence=$($item.confidence), finding=$($item.finding)\"\n }\n $lines += \"\"\n $lines += \"## Report Quality\"\n foreach ($dimension in @($Hospital.report_quality.dimensions)) {\n $lines += \"- $($dimension.name): $($dimension.score) ($($dimension.status)) - $($dimension.evidence)\"\n }\n $lines += \"\"\n $lines += \"## Diagnosis\"\n foreach ($finding in @($Hospital.diagnosis.findings)) {\n $lines += \"- $finding\"\n }\n $lines += \"\"\n $lines += \"## Treatment\"\n foreach ($item in @($Hospital.treatment.plan)) {\n $lines += \"- $item\"\n }\n if ($null -ne $Hospital.surgery_plan) {\n $lines += \"\"\n $lines += \"## Surgery Plan\"\n $lines += \"- Status: $($Hospital.surgery_plan.status)\"\n $lines += \"- Report: $($Hospital.surgery_plan.path)\"\n $lines += \"- Markdown: $($Hospital.surgery_plan.markdown)\"\n $lines += \"- Primary target: $($Hospital.surgery_plan.primary_target)\"\n }\n $lines += \"\"\n $lines += \"## Discharge Criteria\"\n foreach ($item in @($Hospital.triage.discharge_criteria)) {\n $lines += \"- $item\"\n }\n $lines += \"\"\n $lines += \"## State Machine\"\n foreach ($transition in @($Hospital.state_machine.transitions)) {\n $lines += \"- $($transition.from) -> $($transition.to): pass=$($transition.pass), guard=$($transition.guard)\"\n }\n $lines += \"\"\n $lines += \"## Protocols\"\n foreach ($protocol in @($Hospital.protocols)) {\n $lines += \"- $($protocol.name): $($protocol.status) - $($protocol.exit_criteria)\"\n }\n return $lines\n}\n\nfunction Get-CodeIntelSentruxStep {\n param(\n [object[]]$Steps,\n [string]$NamePattern,\n [switch]$Last\n )\n\n $matches = @($Steps | Where-Object { [string]$_.name -like $NamePattern })\n if ($matches.Count -eq 0) { return $null }\n if ($Last) { return $matches[-1] }\n return $matches[0]\n}\n\nfunction Get-CodeIntelBoundedExcerpt {\n param(\n [string]$Text,\n [int]$MaxLength = 500\n )\n\n if ([string]::IsNullOrWhiteSpace($Text)) { return \"\" }\n $singleLine = (($Text -split \"`r?`n\") | Where-Object { -not [string]::IsNullOrWhiteSpace($_) } | Select-Object -First 8) -join \" | \"\n if ($singleLine.Length -le $MaxLength) { return $singleLine }\n return $singleLine.Substring(0, $MaxLength)\n}\n\nfunction New-CodeIntelSentruxTarget {\n param(\n [ValidateSet(\"resolved\", \"unresolved\", \"aggregate\", \"not_applicable\")]\n [string]$Status,\n [string]$File = \"\",\n [string]$Symbol = \"\"\n )\n\n $target = [ordered]@{ status = $Status }\n if (-not [string]::IsNullOrWhiteSpace($File)) { $target[\"file\"] = $File }\n if (-not [string]::IsNullOrWhiteSpace($Symbol)) { $target[\"symbol\"] = $Symbol }\n return $target\n}\n\nfunction New-CodeIntelSentruxRecord {\n param(\n [string]$Id,\n [string]$Kind,\n [string]$Source,\n [string]$SourceStep,\n [string]$RawOutputPath,\n [string]$Stdout,\n [object]$Target,\n [string]$Metric = \"\",\n [Nullable[int]]$Value = $null,\n [Nullable[int]]$Threshold = $null,\n [Nullable[int]]$Before = $null,\n [Nullable[int]]$After = $null\n )\n\n $record = [ordered]@{\n id = $Id\n kind = $Kind\n source = $Source\n source_step = $SourceStep\n provenance = \"stdout\"\n raw_output_path = $RawOutputPath\n stdout_excerpt = Get-CodeIntelBoundedExcerpt $Stdout\n parsed_at = (Get-Date).ToString(\"o\")\n target = $Target\n }\n if (-not [string]::IsNullOrWhiteSpace($Metric)) { $record[\"metric\"] = $Metric }\n if ($null -ne $Value) { $record[\"value\"] = [int]$Value }\n if ($null -ne $Threshold) { $record[\"threshold\"] = [int]$Threshold }\n if ($null -ne $Before) { $record[\"before\"] = [int]$Before }\n if ($null -ne $After) { $record[\"after\"] = [int]$After }\n return $record\n}\n\nfunction Get-CodeIntelObjectValue {\n param(\n [object]$Object,\n [string]$Name\n )\n\n if ($null -eq $Object) { return $null }\n if ($Object -is [System.Collections.IDictionary] -and $Object.Contains($Name)) {\n return $Object[$Name]\n }\n return Get-JsonProperty $Object $Name\n}\n\nfunction New-CodeIntelSentruxConflict {\n param(\n [object]$Authoritative,\n [object]$Conflicting,\n [string]$ConflictingSource,\n [string]$RawPointer\n )\n\n if ($null -eq $Authoritative -or $null -eq $Conflicting) { return $null }\n $authoritativeValue = ConvertTo-NullableDouble (Get-CodeIntelObjectValue $Authoritative \"value\")\n $conflictingValue = ConvertTo-NullableDouble (Get-CodeIntelObjectValue $Conflicting \"complexity\")\n if ($null -eq $authoritativeValue -or $null -eq $conflictingValue) { return $null }\n if ([int]$authoritativeValue -eq [int]$conflictingValue) { return $null }\n\n $conflictingId = \"{0}:max_cc:{1}:{2}\" -f $ConflictingSource, [string](Get-CodeIntelObjectValue $Conflicting \"file\"), [string](Get-CodeIntelObjectValue $Conflicting \"name\")\n return [ordered]@{\n kind = \"metric_conflict\"\n authoritative_record_id = [string](Get-CodeIntelObjectValue $Authoritative \"id\")\n conflicting_record_id = $conflictingId\n metric = \"cyclomatic_complexity\"\n authoritative_value = [int]$authoritativeValue\n conflicting_value = [int]$conflictingValue\n authoritative_source = [string](Get-CodeIntelObjectValue $Authoritative \"source\")\n conflicting_source = $ConflictingSource\n raw_output_path = $RawPointer\n stdout_excerpt = Get-CodeIntelBoundedExcerpt (\"{0} {1} (cc={2})\" -f [string](Get-CodeIntelObjectValue $Conflicting \"name\"), [string](Get-CodeIntelObjectValue $Conflicting \"file\"), [string](Get-CodeIntelObjectValue $Conflicting \"complexity\"))\n parsed_at = (Get-Date).ToString(\"o\")\n resolution = \"authoritative_stdout_wins\"\n }\n}\n\nfunction New-CodeIntelSentruxFailures {\n param(\n [object[]]$Steps,\n [string]$OutputPath = \"\",\n [string]$HotspotsPath = \"\",\n [string]$FileDetailsPath = \"\"\n )\n\n $checkStep = Get-CodeIntelSentruxStep -Steps $Steps -NamePattern \"sentrux check\"\n $gateStep = Get-CodeIntelSentruxStep -Steps $Steps -NamePattern \"sentrux gate*\" -Last\n $records = [System.Collections.Generic.List[object]]::new()\n $parserNotes = [System.Collections.Generic.List[string]]::new()\n $parserErrors = [System.Collections.Generic.List[string]]::new()\n\n if ($null -ne $checkStep) {\n $checkStatus = [string]$checkStep.status\n $checkText = (([string]$checkStep.output) + \"`n\" + ([string]$checkStep.error)).Trim()\n if ($checkStatus -eq \"failed\" -or $checkStatus -eq \"manual_required\") {\n $namedMatches = @([regex]::Matches($checkText, \"(?im)(?[^\\s:()]+(?:\\.ps1|\\.psm1|\\.ts|\\.tsx|\\.js|\\.jsx|\\.py|\\.rs|\\.go|\\.cs|\\.java|\\.kt|\\.v)):(?[A-Za-z_][A-Za-z0-9_.:-]*)\\s*\\(cc=(?\\d+)\\)\"))\n if ($namedMatches.Count -gt 0) {\n foreach ($match in $namedMatches) {\n $file = [string]$match.Groups[\"file\"].Value\n $symbol = [string]$match.Groups[\"symbol\"].Value\n $value = [int]$match.Groups[\"cc\"].Value\n $records.Add((New-CodeIntelSentruxRecord `\n -Id (\"check:max_cc:{0}:{1}\" -f $file, $symbol) `\n -Kind \"max_cc\" `\n -Source \"sentrux check\" `\n -SourceStep \"sentrux check\" `\n -RawOutputPath \"report.json#/steps/sentrux check/output\" `\n -Stdout $checkText `\n -Metric \"cyclomatic_complexity\" `\n -Value $value `\n -Threshold 70 `\n -Target (New-CodeIntelSentruxTarget -Status \"resolved\" -File $file -Symbol $symbol)))\n }\n }\n elseif ($checkText -match \"(?i)max[_ -]?cc|cyclomatic|complex\") {\n $value = $null\n $valueMatch = [regex]::Match($checkText, \"(?i)(?:max[_ -]?cc|cc|cyclomatic[^0-9]*)(?:\\D+)(?\\d+)\")\n if ($valueMatch.Success) { $value = [int]$valueMatch.Groups[\"cc\"].Value }\n $records.Add((New-CodeIntelSentruxRecord `\n -Id \"check:max_cc:unresolved\" `\n -Kind \"max_cc\" `\n -Source \"sentrux check\" `\n -SourceStep \"sentrux check\" `\n -RawOutputPath \"report.json#/steps/sentrux check/output\" `\n -Stdout $checkText `\n -Metric \"cyclomatic_complexity\" `\n -Value $value `\n -Threshold 70 `\n -Target (New-CodeIntelSentruxTarget -Status \"unresolved\")))\n }\n else {\n $parserErrors.Add(\"sentrux check failed but stdout did not match known max_cc formats.\")\n }\n }\n }\n\n if ($null -ne $gateStep) {\n $gateStatus = [string]$gateStep.status\n $gateText = (([string]$gateStep.output) + \"`n\" + ([string]$gateStep.error)).Trim()\n if ($gateStatus -eq \"failed\" -or $gateStatus -eq \"manual_required\") {\n $gateMatches = @([regex]::Matches($gateText, \"(?im)(?