diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d3781b8..667b7e7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -230,10 +230,6 @@ jobs: shell: pwsh run: .\legacy/scripts/tests/test-hospital-trust-contract.ps1 - - name: DAG facade path and parity tests - shell: pwsh - run: .\legacy/scripts/tests/test-dag-facade.ps1 - - name: Scoped Repowise trust-boundary tests shell: pwsh run: | @@ -300,12 +296,6 @@ jobs: if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } } - - name: Run commit contract tests - shell: pwsh - run: | - & pwsh -NoProfile -File .\legacy/scripts/tests/test-run-commit-contract.ps1 - if ($LASTEXITCODE -ne 0) { exit $LASTEXITCODE } - - name: Package shell: pwsh run: | diff --git a/crates/code-intel-cli/src/artifacts.rs b/crates/code-intel-cli/src/artifacts.rs index 53466c3..7f4a167 100644 --- a/crates/code-intel-cli/src/artifacts.rs +++ b/crates/code-intel-cli/src/artifacts.rs @@ -3,9 +3,13 @@ use std::env; use std::error::Error; use std::fs; use std::path::{Path, PathBuf}; +use std::time::{SystemTime, UNIX_EPOCH}; type Result = std::result::Result>; +/// The environment variable that names the artifact root when no flag does. +pub(crate) const ARTIFACT_ROOT_ENV: &str = "CODE_INTEL_ARTIFACT_ROOT"; + #[derive(Debug)] struct ResumeSummary { repo: PathBuf, @@ -283,7 +287,7 @@ pub(crate) fn resolve_artifact_root(explicit: Option<&Path>) -> Result if let Some(path) = explicit { return Ok(path.to_path_buf()); } - if let Ok(value) = env::var("CODE_INTEL_ARTIFACT_ROOT") { + if let Ok(value) = env::var(ARTIFACT_ROOT_ENV) { if !value.trim().is_empty() { return Ok(PathBuf::from(value)); } @@ -300,6 +304,72 @@ pub(crate) fn resolve_artifact_root(explicit: Option<&Path>) -> Result .join("artifacts")) } +/// Composes a DAG staging directory the way `resume` and `artifact index` read +/// runs back: `//`. The PowerShell launcher owned +/// this composition until it was retired, and the failure it existed to prevent +/// is repeating the repository name inside the path, which strands a run where +/// no reader looks. Only the repository directory is created here; the run +/// directory is left absent because `dag_run::execute_dag` claims it +/// exclusively and must keep failing when it already exists. +pub(crate) fn compose_dag_staging_dir( + artifact_root: Option<&Path>, + repo: &Path, +) -> Result { + let repo = absolute_existing_dir(repo)?; + let artifact_root = resolve_artifact_root(artifact_root)?; + let repo_name = repo + .file_name() + .and_then(|name| name.to_str()) + .ok_or("repo path has no final directory name")?; + let repo_artifacts = artifact_root.join(repo_name); + fs::create_dir_all(&repo_artifacts)?; + let now = SystemTime::now() + .duration_since(UNIX_EPOCH) + .map_err(|error| format!("system clock is before the unix epoch: {error}"))?; + let stem = utc_run_stamp(now.as_secs()); + let mut nonce = u64::from(now.subsec_nanos()); + for _ in 0..64 { + let candidate = repo_artifacts.join(format!("{stem}.dag-staging-{nonce:012x}")); + if !candidate.exists() { + return Ok(candidate); + } + nonce = nonce.wrapping_add(1); + } + Err(format!( + "no free DAG staging directory under {}", + repo_artifacts.display() + ) + .into()) +} + +/// `yyyyMMdd-HHmmss` in UTC, by Howard Hinnant's `civil_from_days`. Nothing +/// parses this stem back, but `latest_run_dir` sorts run directories by name, +/// so a run written today must still sort after one the launcher wrote. +fn utc_run_stamp(seconds: u64) -> String { + let days = (seconds / 86_400) as i64; + let second_of_day = seconds % 86_400; + let shifted = days + 719_468; + let era = shifted.div_euclid(146_097); + let day_of_era = shifted.rem_euclid(146_097); + let year_of_era = + (day_of_era - day_of_era / 1_460 + day_of_era / 36_524 - day_of_era / 146_096) / 365; + let day_of_year = day_of_era - (365 * year_of_era + year_of_era / 4 - year_of_era / 100); + let month_position = (5 * day_of_year + 2) / 153; + let day = day_of_year - (153 * month_position + 2) / 5 + 1; + let month = if month_position < 10 { + month_position + 3 + } else { + month_position - 9 + }; + let year = year_of_era + era * 400 + i64::from(month <= 2); + format!( + "{year:04}{month:02}{day:02}-{:02}{:02}{:02}", + second_of_day / 3_600, + (second_of_day % 3_600) / 60, + second_of_day % 60 + ) +} + fn absolute_existing_dir(path: &Path) -> Result { if !path.is_dir() { return Err(format!("repo path is not a directory: {}", path.display()).into()); diff --git a/crates/code-intel-cli/src/run_cli.rs b/crates/code-intel-cli/src/run_cli.rs index 495cd40..6035d7b 100644 --- a/crates/code-intel-cli/src/run_cli.rs +++ b/crates/code-intel-cli/src/run_cli.rs @@ -1,5 +1,7 @@ +use std::env; use std::path::PathBuf; +use crate::artifacts::{self, ARTIFACT_ROOT_ENV}; use crate::dag_run::{self, DagExecutionRequest}; use crate::execution_kernel; use crate::execution_policy::{ExecutionPolicy, RunMode, RunProfile, SkipFlags, WorkingTreePolicy}; @@ -42,7 +44,8 @@ enum RunCommand { struct Cli { command: RunCommand, repo: PathBuf, - out: PathBuf, + out: Option, + artifact_root: Option, authority_root: Option, final_name: Option, manifest: Option, @@ -62,6 +65,7 @@ impl Cli { }; let mut repo = None; let mut out = None; + let mut artifact_root = None; let mut authority_root = None; let mut final_name = None; let mut manifest = None; @@ -87,6 +91,7 @@ impl Cli { flag, "--repo" | "--out" + | "--artifact-root" | "--authority-root" | "--final-name" | "--manifest" @@ -118,6 +123,9 @@ impl Cli { "--out" if out.replace(PathBuf::from(value)).is_some() => { return Err("duplicate --out".into()) } + "--artifact-root" if artifact_root.replace(PathBuf::from(value)).is_some() => { + return Err("duplicate --artifact-root".into()) + } "--authority-root" if authority_root.replace(PathBuf::from(value)).is_some() => { return Err("duplicate --authority-root".into()) } @@ -233,8 +241,30 @@ impl Cli { .into(), ); } + // The two routes disagree about who owns the path, so accepting + // both would silently honour one and drop the other. + if out.is_some() && artifact_root.is_some() { + return Err( + "--out and --artifact-root are mutually exclusive; --out names the staging directory, --artifact-root composes one" + .into(), + ); + } + if out.is_none() + && artifact_root.is_none() + && env::var_os(ARTIFACT_ROOT_ENV).is_none() + { + return Err(format!( + "run dag-coordinate requires --out, --artifact-root, or {ARTIFACT_ROOT_ENV}" + )); + } } RunCommand::Execute => { + if artifact_root.is_some() { + return Err( + "--artifact-root is available only for run dag-coordinate; run execute publishes through --authority-root" + .into(), + ); + } if diagnosis_inputs.is_some() || seed_artifact_root.is_some() { return Err( "run execute does not accept diagnosis-only inputs; use run dag-coordinate for the non-authoritative compatibility primitive" @@ -267,10 +297,14 @@ impl Cli { doctor_require_understand, doctor_tool_path_prefix, ); + if command == RunCommand::Execute && out.is_none() { + return Err("--out is required".into()); + } Ok(Self { command, repo, - out: out.ok_or("--out is required")?, + out, + artifact_root, authority_root, final_name, manifest, @@ -284,7 +318,7 @@ impl Cli { } fn usage() -> String { - "usage: run --repo --out [--authority-root --final-name ] [--profile ] [--mode ] [--skip-repowise ] [--skip-sentrux ] [--require-understand-graph ] [--manifest ] [--max-concurrency ] [--working-tree-policy ] [--scope ]... [--session-evidence ] [--diagnosis-inputs --seed-artifact-root ] [--doctor-tool-path-prefix ] [--doctor-require-repowise ] [--doctor-require-understand ]".into() + "usage: run --repo <--out | --artifact-root (dag-coordinate only; also read from CODE_INTEL_ARTIFACT_ROOT)> [--authority-root --final-name ] [--profile ] [--mode ] [--skip-repowise ] [--skip-sentrux ] [--require-understand-graph ] [--manifest ] [--max-concurrency ] [--working-tree-policy ] [--scope ]... [--session-evidence ] [--diagnosis-inputs --seed-artifact-root ] [--doctor-tool-path-prefix ] [--doctor-require-repowise ] [--doctor-require-understand ]".into() } fn parse_bool_flag(flag: &str, value: &str) -> Result { @@ -298,9 +332,16 @@ fn parse_bool_flag(flag: &str, value: &str) -> Result { fn execute_cli(cli: Cli) -> Result { match cli.command { RunCommand::DagCoordinate => { + let out = match cli.out { + Some(out) => out, + None => artifacts::compose_dag_staging_dir(cli.artifact_root.as_deref(), &cli.repo) + .map_err(|error| { + RunError::io(format!("compose DAG staging directory: {error}")) + })?, + }; let result = dag_run::execute_dag(DagExecutionRequest { repo: cli.repo, - out: cli.out, + out, manifest: cli.manifest, max_concurrency: cli.max_concurrency, policy: cli.policy, @@ -316,7 +357,7 @@ fn execute_cli(cli: Cli) -> Result { RunCommand::Execute => { let result = execution_kernel::execute(execution_kernel::RunRequest { repo: cli.repo, - staging_root: cli.out, + staging_root: cli.out.expect("validated execute staging root"), authority_root: cli .authority_root .expect("validated execute authority root"), diff --git a/crates/code-intel-cli/tests/dag_run.rs b/crates/code-intel-cli/tests/dag_run.rs index 939b91e..5466fcb 100644 --- a/crates/code-intel-cli/tests/dag_run.rs +++ b/crates/code-intel-cli/tests/dag_run.rs @@ -1058,3 +1058,105 @@ fn baselined_repository_that_regresses_still_fails_the_architecture_gate() { let _ = fs::remove_dir_all(root); } + +/// `test-dag-facade.ps1` guarded the artifact-path composition the PowerShell +/// launcher owned: a run lands directly under `/`, +/// the repository name never repeats inside the path, and the explicit route +/// and the environment default produce the same evidence. `run dag-coordinate` +/// owns that composition now, so the guard lives here. The run outcome is +/// deliberately not asserted — the host toolchain decides it, and +/// `production_run_route_executes_snapshot_then_inventory` covers the outcome +/// with the doctor configured. +#[test] +fn artifact_root_routes_runs_where_readers_look_and_matches_the_environment_default() { + let root = temp_dir(); + // The launcher fixture carried a space, an ampersand and non-ASCII, because + // each of those has broken a path round trip in this pipeline before. + let repo_name = "repo & 文"; + let repo = root.join(repo_name); + fs::create_dir_all(&repo).unwrap(); + fs::write(repo.join("README & 文.md"), "fixture").unwrap(); + + let run = |base: &Path, explicit: bool| -> PathBuf { + let mut command = Command::new(env!("CARGO_BIN_EXE_code-intel")); + command.args(["run", "dag-coordinate", "--repo"]).arg(&repo); + if explicit { + command.arg("--artifact-root").arg(base); + } else { + command.env("CODE_INTEL_ARTIFACT_ROOT", base); + } + // A nonzero exit means a node failed on this host, not that the path + // composition failed, so the run is judged from disk below. + let output = command.output().unwrap(); + let repo_artifacts = base.join(repo_name); + let runs: Vec = fs::read_dir(&repo_artifacts) + .unwrap_or_else(|error| { + panic!( + "no artifacts under {}: {error}; stderr={}", + repo_artifacts.display(), + String::from_utf8_lossy(&output.stderr) + ) + }) + .map(|entry| entry.unwrap().path()) + .collect(); + assert_eq!( + runs.len(), + 1, + "a run must be a direct child of the repository artifact root: {runs:?}" + ); + assert!( + !repo_artifacts.join(repo_name).exists(), + "the repository name must not repeat inside the artifact path" + ); + assert!( + runs[0] + .file_name() + .and_then(|name| name.to_str()) + .is_some_and(|name| name.contains(".dag-staging-")), + "run directory={:?}", + runs[0] + ); + runs[0].clone() + }; + + let explicit = run(&root.join("explicit artifacts"), true); + let default = run(&root.join("default artifacts"), false); + + let manifest: Value = + serde_json::from_slice(&fs::read(explicit.join("run-manifest.json")).unwrap()).unwrap(); + assert_eq!(manifest["schema"], "code-intel-run-manifest.v1"); + assert_eq!( + fs::read(explicit.join("inventory.rg/files.txt")).unwrap(), + fs::read(default.join("inventory.rg/files.txt")).unwrap(), + "the explicit and default artifact roots disagreed about the inventory" + ); + + let _ = fs::remove_dir_all(root); +} + +/// The two routes disagree about who owns the staging path, so accepting both +/// would silently honour one and drop the other. +#[test] +fn out_and_artifact_root_cannot_both_name_the_staging_directory() { + let root = temp_dir(); + let repo = root.join("repo"); + fs::create_dir_all(&repo).unwrap(); + + let output = Command::new(env!("CARGO_BIN_EXE_code-intel")) + .args(["run", "dag-coordinate", "--repo"]) + .arg(&repo) + .arg("--out") + .arg(root.join("run")) + .arg("--artifact-root") + .arg(root.join("artifacts")) + .output() + .unwrap(); + assert_eq!(output.status.code(), Some(64)); + assert!( + String::from_utf8_lossy(&output.stderr).contains("mutually exclusive"), + "stderr={}", + String::from_utf8_lossy(&output.stderr) + ); + + let _ = fs::remove_dir_all(root); +} diff --git a/crates/code-intel-cli/tests/internalization_record.rs b/crates/code-intel-cli/tests/internalization_record.rs index c6dfe2c..a05135b 100644 --- a/crates/code-intel-cli/tests/internalization_record.rs +++ b/crates/code-intel-cli/tests/internalization_record.rs @@ -1,5 +1,7 @@ #[path = "../src/authority.rs"] mod authority; +#[path = "../src/content_contract.rs"] +mod content_contract; #[path = "../src/internalization_record.rs"] mod internalization_record; @@ -657,23 +659,17 @@ fn assert_signed_out_of_scope_record_projects(record: &Value, expected_id: &str) assert_checked_schema(record, "code-intel-internalization-record.v1.schema.json"); } +/// Digests are recomputed in process rather than through `Get-FileHash`. Every +/// operation trace pins two files, so shelling out once per pin spawned dozens +/// of concurrent `pwsh` processes; the macOS runner killed them with +/// `Stack overflow.`, which surfaced here as 26 of 40 records failing at once +/// and read exactly like real digest drift. SHA256 over the same bytes is the +/// same digest either way, so nothing about what this asserts changes. fn recompute_sha(relative: &str) -> String { let path = root().join(relative); - let mut command = Command::new("pwsh"); - command.args([ - "-NoProfile", - "-CommandWithArgs", - "(Get-FileHash -LiteralPath $args[0] -Algorithm SHA256).Hash.ToLowerInvariant()", - path.to_str().unwrap(), - ]); - let output = run_command_with_timeout(&mut command); - assert!( - output.status.success(), - "{}", - String::from_utf8_lossy(&output.stderr) - ); - let digest = String::from_utf8(output.stdout).unwrap(); - let digest = digest.trim().to_string(); + let bytes = fs::read(&path) + .unwrap_or_else(|error| panic!("conformance source is unreadable: {relative}: {error}")); + let digest = content_contract::sha256_hex(&bytes); assert_eq!(digest.len(), 64); digest } diff --git a/docs/ps1-exit/contract-inventory.md b/docs/ps1-exit/contract-inventory.md index a6c4666..5f56c90 100644 --- a/docs/ps1-exit/contract-inventory.md +++ b/docs/ps1-exit/contract-inventory.md @@ -77,9 +77,9 @@ classification). | `-CodeNexusAdapterRequest`/`ArtifactRoot`/`EvaluatedAt`/`MaxAgeSeconds` | `:55-58` | `test-codenexus-adapter-contract.ps1` (request + `ArtifactRoot`; not independently confirmed for `EvaluatedAt`/`MaxAgeSeconds`) | shim | Early-exit facade (§1.8, block 6): `:199-215`. | | `-SurvivalScanRequest` | `:59` | **none found** | shim | Early-exit facade (§1.8, block 7): `& $rustCli repository survival-scan ...` (`:217-229`). Untested gap — `legacy/scripts/tests/test-survival-scan-contract.ps1` exists and covers `-SurvivalScanArtifactRoot`, but not `-SurvivalScanRequest` (likely tests the Rust `repository survival-scan` subcommand directly rather than this PS1 facade — see §4.2). | | `-SurvivalScanArtifactRoot` | `:60` | `test-survival-scan-contract.ps1` | shim | Same facade as above; only this one companion param is exercised. | -| `-RunCommitSourceRoot`/`AuthorityRoot`/`ManifestRef`/`FinalName` | `:61-64` | `test-run-commit-contract.ps1` | shim | Early-exit facade (§1.8, block 8): `& $rustCli run commit ...` (`:231-247`). | +| `-RunCommitSourceRoot`/`AuthorityRoot`/`ManifestRef`/`FinalName` | `:61-64` | `tests/run_commit.rs::production_run_commit_cli_restages_a09_refs_through_a06_and_publishes`, `::cli_publication_preserves_the_callers_manifest_bytes_and_digest`; marker shape at `tests/decision_record.rs:318`; admission at `tests/dag_run.rs::production_dag_output_commits_and_enters_the_authoritative_index` and the mutated-marker rejections in `tests/artifact_index.rs:159-182` | shim | Early-exit facade (§1.8, block 8): `& $rustCli run commit ...` (`:231-247`). `test-run-commit-contract.ps1` was deleted once those Rust tests were confirmed to cover every assertion it made; the one assertion not carried over — that the marker lacks the legacy `generatedAt`/`reportSha256` fields — guarded the PowerShell producer, which no longer writes the marker. | | `-InventoryExclude` (string[]) | `:65` | **none found** | port | Merged with `$defaultInventoryExclude` (`:3239`); feeds `rg` file inventory. | -| `-DagCoordinate` (switch) | `:67`, facade at `:3258-3270` | `test-dag-facade.ps1` | shim | `& $rustCli` (DAG coordinate path), `throw`s on non-zero exit rather than propagating — see §1.5. | +| `-DagCoordinate` (switch) | `:67`, facade at `:3258-3270` | `tests/dag_run.rs::artifact_root_routes_runs_where_readers_look_and_matches_the_environment_default`, `::out_and_artifact_root_cannot_both_name_the_staging_directory`, `::ungoverned_repository_completes_instead_of_failing_the_architecture_gate`, `::production_run_preserves_doctor_domain_failure_and_completes_unrelated_branches` | shim | `& $rustCli` (DAG coordinate path), `throw`s on non-zero exit rather than propagating — see §1.5. The artifact-path composition this facade owned (`//.dag-staging-`) is now `run dag-coordinate --artifact-root`, which also reads `CODE_INTEL_ARTIFACT_ROOT`. `test-dag-facade.ps1` is off CI but still on disk: `New-PublicationRetirementPacket.ps1:15` executes it as E05's golden-parity evidence, so it can only be deleted when E05 closes. | | `-SaveSentruxBaseline`, `-AutoSaveMissingSentruxBaseline` (switches) | `:69-70`, effect at `:3836-3852` | **none found** | port | Baseline-save branch of the sentrux gate; copies existing baseline to `.prev.json` before overwrite. | | `-SkipRepomix` (switch), `-RepomixStyle` (`xml`\|`markdown`\|`json`\|`plain`), `-RepomixCompress` (switch) | `:74-77` | `-SkipRepomix`/`-RepomixCompress`: `test-transactional-publication.ps1`. `-RepomixStyle`: **none found** | shim? | Shells out to the external `repomix` tool (`:3582-3597` area); classified `shim` because the PS1 code appears to just build args and invoke it, but I did not fully trace whether output gets reinterpreted — `?`. | | `-SkipSentrux` (switch, whole-stage) | `:78`, gate at `:3785` | `test-code-evidence-layer.ps1`, `test-runtime-ci-hospital-pet.ps1`, `test-transactional-publication.ps1` | port | Mutually exclusive with the `Mode -eq "lite"` skip (`:3773`) — two different code paths produce the same "sentrux skipped" outcome; see §1.3. | diff --git a/legacy/scripts/tests/test-run-commit-contract.ps1 b/legacy/scripts/tests/test-run-commit-contract.ps1 deleted file mode 100644 index c5b1a90..0000000 --- a/legacy/scripts/tests/test-run-commit-contract.ps1 +++ /dev/null @@ -1,89 +0,0 @@ -#requires -Version 7.2 - -param([string]$Root = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot "../../.."))) - -Set-StrictMode -Version Latest -$ErrorActionPreference = "Stop" - -$temp = Join-Path ([IO.Path]::GetTempPath()) ("code-intel-a07-facade-" + [guid]::NewGuid().ToString("N")) -try { - $source = Join-Path $temp "source" - $artifactRoot = Join-Path $temp "artifacts" - $authority = Join-Path $artifactRoot "repo" - New-Item -ItemType Directory -Path $source, $authority | Out-Null - - $snapshot = "a" * 64 - $inventoryBytes = [Text.UTF8Encoding]::new($false).GetBytes("portable evidence`n") - $inventoryDigest = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($inventoryBytes)).ToLowerInvariant() - $inventoryRelative = "objects/sha256/$inventoryDigest" - $inventoryPath = Join-Path $source ($inventoryRelative -replace '/', [IO.Path]::DirectorySeparatorChar) - New-Item -ItemType Directory -Path (Split-Path -Parent $inventoryPath) -Force | Out-Null - [IO.File]::WriteAllBytes($inventoryPath, $inventoryBytes) - - $inventoryRef = [ordered]@{ - schema = "code-intel-artifact-ref.v1" - artifactSchema = "code-intel-file-inventory.v1" - type = "inventory.files" - path = $inventoryRelative - sha256 = $inventoryDigest - consumedSnapshotIdentity = $snapshot - } - $manifest = [ordered]@{ - schema = "code-intel-run-manifest.v1" - runIdentity = "dag-v1:aabb" - snapshotIdentity = $snapshot - outcome = "completed" - nodes = [ordered]@{ - inventory = [ordered]@{ - status = "succeeded" - verdict = "pass" - artifacts = @($inventoryRef) - } - } - } - $manifestBytes = [Text.UTF8Encoding]::new($false).GetBytes(($manifest | ConvertTo-Json -Depth 12 -Compress)) - $manifestDigest = [Convert]::ToHexString([Security.Cryptography.SHA256]::HashData($manifestBytes)).ToLowerInvariant() - $manifestRelative = "objects/sha256/$manifestDigest" - $manifestPath = Join-Path $source ($manifestRelative -replace '/', [IO.Path]::DirectorySeparatorChar) - [IO.File]::WriteAllBytes($manifestPath, $manifestBytes) - $manifestRef = [ordered]@{ - schema = "code-intel-artifact-ref.v1" - artifactSchema = "code-intel-run-manifest.v1" - type = "run.manifest" - path = $manifestRelative - sha256 = $manifestDigest - consumedSnapshotIdentity = $snapshot - } - $manifestRefPath = Join-Path $temp "manifest-ref.json" - [IO.File]::WriteAllText($manifestRefPath, ($manifestRef | ConvertTo-Json -Compress), [Text.UTF8Encoding]::new($false)) - - & (Join-Path $Root "legacy/run-code-intel.ps1") ` - -RunCommitSourceRoot $source ` - -RunCommitAuthorityRoot $authority ` - -RunCommitManifestRef $manifestRefPath ` - -RunCommitFinalName "published" | Out-Null - if ($LASTEXITCODE -ne 0) { throw "run commit facade exited $LASTEXITCODE" } - - $markerPath = Join-Path $authority "published/run-complete.json" - $marker = Get-Content -LiteralPath $markerPath -Raw | ConvertFrom-Json - if ($marker.schema -ne "code-intel-run-commit.v1" -or - $marker.runIdentity -ne "dag-v1:aabb" -or - $marker.snapshotIdentity -ne $snapshot -or - $marker.manifest.sha256 -ne $manifestDigest) { - throw "facade published an incoherent A07 marker" - } - if ($null -ne $marker.PSObject.Properties["generatedAt"] -or - $null -ne $marker.PSObject.Properties["reportSha256"]) { - throw "facade published the legacy marker shape" - } - - $index = Join-Path $artifactRoot "index.md" - & (Join-Path $Root "legacy/update-code-intel-index.ps1") -ArtifactRoot $artifactRoot -OutputPath $index | Out-Null - if (-not (Get-Content -LiteralPath $index -Raw).Contains("published")) { - throw "A08 index did not admit a valid new A07 marker" - } - Write-Output "run.commit facade contract: pass; A08 committed-only admission: pass" -} -finally { - Remove-Item -LiteralPath $temp -Recurse -Force -ErrorAction SilentlyContinue -} diff --git a/orchestration/internalization/graph.json b/orchestration/internalization/graph.json index 8d2f43e..8e00464 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": "339536b6756cecd60b706faafb3ee9f53f8c1436ac6d2becb0448d27dc0b0322" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "6593d9770665b24e4fa115c2ea5d710e36a3ed679eaaaf605ced616b2b479662", "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": "339536b6756cecd60b706faafb3ee9f53f8c1436ac6d2becb0448d27dc0b0322" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "6593d9770665b24e4fa115c2ea5d710e36a3ed679eaaaf605ced616b2b479662", "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": "92dcffeeb4226eac01acf1f995cf32ee3c93b4abcd958c9aff461ccdc86bfe9a" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "22a1fad196eecd4a1a59b61c95712c8d100250b370dd86f0978bc53c475e1bb1", "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": "92dcffeeb4226eac01acf1f995cf32ee3c93b4abcd958c9aff461ccdc86bfe9a" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "792ffb39dbd46d81e366416ae2e19fcc5eb5f6db688ab593bc22a79fafc8d0f3", "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.rs", "sha256": "6671234c5b00847c7130f535cca5b049030d67f34f6e5341faf45f45b9d01c95" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "6593d9770665b24e4fa115c2ea5d710e36a3ed679eaaaf605ced616b2b479662", "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.rs", "sha256": "6671234c5b00847c7130f535cca5b049030d67f34f6e5341faf45f45b9d01c95" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "6593d9770665b24e4fa115c2ea5d710e36a3ed679eaaaf605ced616b2b479662", "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": "9cc96bb7bbd50b6d526dc908be730c90c96f46ed0728cbc51c6abebd7ac94d49" }, "conformance": { "path": "crates/code-intel-cli/tests/graph_adapter.rs", "sha256": "6593d9770665b24e4fa115c2ea5d710e36a3ed679eaaaf605ced616b2b479662", "testName": "fallback_identity_and_payload_identity_cannot_be_relabelled" } }, diff --git a/orchestration/internalization/sentrux.json b/orchestration/internalization/sentrux.json index e5fb17d..f757b88 100644 --- a/orchestration/internalization/sentrux.json +++ b/orchestration/internalization/sentrux.json @@ -14,7 +14,7 @@ { "integrationId": "structure.sentrux", "operation": "rustDsm", "command": "target/debug/code-intel.exe sentrux dsm ", "implementationIdentity": { "providerId": "code-intel.sentrux", "implementationId": "sentrux-rust-analysis.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/sentrux_analysis.rs", "sha256": "bae17f2206cb15b63e63f5899f227349d5bbb7f27110de6f2f3993c37cfdf148" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_analysis.rs", "sha256": "9501b59fbe40f7aa76cf82d1bb5f75ed0cc45bd5f2fa9dae1acb735d0b66f266", "testName": "dsm_snapshot_preserves_contract_and_excludes_non_governed_source" } }, { "integrationId": "provider.sentrux-adapt", "operation": "adapt", "command": "target/debug/code-intel.exe provider sentrux-adapt --request --artifact-root --evaluated-at --max-age-seconds ", "implementationIdentity": { "providerId": "sentrux.provider", "implementationId": "sentrux-adapter.v1", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/sentrux_adapter.rs", "sha256": "08bf222bf5822a5ece89554be86d0687eb1437b18298a940638997459d95f5ee" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", "sha256": "4d73dc2a622541f36bc45de9c5676d8be61291392ebdfca875b88cc2d77fecab", "testName": "public_route_complete_is_eligible_but_never_emits_facts" } }, { "integrationId": "provider.sentrux-adapt", "operation": "facade", "command": "legacy/run-code-intel.ps1 -SentruxAdapterRequest -SentruxAdapterArtifactRoot -SentruxAdapterEvaluatedAt -SentruxAdapterMaxAgeSeconds ", "implementationIdentity": { "providerId": "sentrux.provider", "implementationId": "sentrux-adapter.v1", "activation": "compatibility_facade" }, "source": { "path": "crates/code-intel-cli/src/sentrux_adapter.rs", "sha256": "08bf222bf5822a5ece89554be86d0687eb1437b18298a940638997459d95f5ee" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", "sha256": "4d73dc2a622541f36bc45de9c5676d8be61291392ebdfca875b88cc2d77fecab", "testName": "public_route_complete_is_eligible_but_never_emits_facts" } }, - { "integrationId": "provider.sentrux-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.sentrux-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "sentrux.provider", "implementationId": "provider.sentrux-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "92dcffeeb4226eac01acf1f995cf32ee3c93b4abcd958c9aff461ccdc86bfe9a" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "22a1fad196eecd4a1a59b61c95712c8d100250b370dd86f0978bc53c475e1bb1", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, + { "integrationId": "provider.sentrux-adapt", "operation": "capabilityExec", "command": "target/debug/code-intel.exe capability exec provider.sentrux-adapt --request --out --artifact-root ", "implementationIdentity": { "providerId": "sentrux.provider", "implementationId": "provider.sentrux-builtin.compat", "activation": "required production capability envelope" }, "source": { "path": "crates/code-intel-cli/src/builtin_provider_evidence.rs", "sha256": "92dcffeeb4226eac01acf1f995cf32ee3c93b4abcd958c9aff461ccdc86bfe9a" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "792ffb39dbd46d81e366416ae2e19fcc5eb5f6db688ab593bc22a79fafc8d0f3", "testName": "production_dag_output_commits_and_enters_the_authoritative_index" } }, { "integrationId": "structure.sentrux", "operation": "rustScan", "command": "target/debug/code-intel.exe sentrux scan ", "implementationIdentity": { "providerId": "code-intel.sentrux", "implementationId": "internal-rust", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/sentrux.rs", "sha256": "340534e82dc8a13437ca87b730d1532ffce7957ace4dd5cf45013892b842e959" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", "sha256": "4d73dc2a622541f36bc45de9c5676d8be61291392ebdfca875b88cc2d77fecab", "testName": "complete_normalizes_every_authoritative_kind_and_passes_a04" } }, { "integrationId": "structure.sentrux", "operation": "rustHealth", "command": "target/debug/code-intel.exe sentrux health ", "implementationIdentity": { "providerId": "code-intel.sentrux", "implementationId": "internal-rust", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/sentrux.rs", "sha256": "340534e82dc8a13437ca87b730d1532ffce7957ace4dd5cf45013892b842e959" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", "sha256": "4d73dc2a622541f36bc45de9c5676d8be61291392ebdfca875b88cc2d77fecab", "testName": "complete_normalizes_every_authoritative_kind_and_passes_a04" } }, { "integrationId": "structure.sentrux", "operation": "rustSessionStart", "command": "target/debug/code-intel.exe sentrux session_start ", "implementationIdentity": { "providerId": "code-intel.sentrux", "implementationId": "internal-rust", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/sentrux.rs", "sha256": "340534e82dc8a13437ca87b730d1532ffce7957ace4dd5cf45013892b842e959" }, "conformance": { "path": "crates/code-intel-cli/tests/sentrux_adapter.rs", "sha256": "4d73dc2a622541f36bc45de9c5676d8be61291392ebdfca875b88cc2d77fecab", "testName": "complete_normalizes_every_authoritative_kind_and_passes_a04" } }, @@ -25,12 +25,12 @@ { "integrationId": "structure.sentrux", "operation": "shimScan", "command": "legacy/Invoke-SentruxAgentTool.ps1 scan ", "implementationIdentity": { "providerId": "sentrux.compat", "implementationId": "legacy/Invoke-SentruxAgentTool.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-SentruxAgentTool.ps1", "sha256": "77a6015be567c686eaea5de8c1036d3e1dd8ee157602230e580527df1a601ee8" }, "conformance": { "path": "legacy/scripts/tests/test-sentrux-failure-normalization.ps1", "sha256": "0ea4c890c5f75c5c9eec94acf74e38550ff87179da7c4258dac0432625620cb4", "testName": "Sentrux failure normalization tests passed" } }, { "integrationId": "structure.sentrux", "operation": "shimSessionStart", "command": "legacy/Invoke-SentruxAgentTool.ps1 session_start ", "implementationIdentity": { "providerId": "sentrux.compat", "implementationId": "legacy/Invoke-SentruxAgentTool.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-SentruxAgentTool.ps1", "sha256": "77a6015be567c686eaea5de8c1036d3e1dd8ee157602230e580527df1a601ee8" }, "conformance": { "path": "legacy/scripts/tests/test-sentrux-failure-normalization.ps1", "sha256": "0ea4c890c5f75c5c9eec94acf74e38550ff87179da7c4258dac0432625620cb4", "testName": "Sentrux failure normalization tests passed" } }, { "integrationId": "structure.sentrux", "operation": "shimSessionEnd", "command": "legacy/Invoke-SentruxAgentTool.ps1 session_end ", "implementationIdentity": { "providerId": "sentrux.compat", "implementationId": "legacy/Invoke-SentruxAgentTool.ps1", "activation": "legacy_rollback" }, "source": { "path": "legacy/Invoke-SentruxAgentTool.ps1", "sha256": "77a6015be567c686eaea5de8c1036d3e1dd8ee157602230e580527df1a601ee8" }, "conformance": { "path": "legacy/scripts/tests/test-sentrux-failure-normalization.ps1", "sha256": "0ea4c890c5f75c5c9eec94acf74e38550ff87179da7c4258dac0432625620cb4", "testName": "Sentrux failure normalization tests passed" } }, - { "integrationId": "provider.sentrux-adapt", "operation": "native-gate", "command": "target/debug/code-intel.exe sentrux --operation check|gate --repo ", "implementationIdentity": { "providerId": "sentrux.provider", "implementationId": "sentrux-native.v2", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/sentrux_gate.rs", "sha256": "0689c2b7bb3d93e44e0758faf9683a53b07a49cd054a5aa3c4a1ef318bf290fc" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "22a1fad196eecd4a1a59b61c95712c8d100250b370dd86f0978bc53c475e1bb1", "testName": "production_run_preserves_doctor_domain_failure_and_completes_unrelated_branch" } } + { "integrationId": "provider.sentrux-adapt", "operation": "native-gate", "command": "target/debug/code-intel.exe sentrux --operation check|gate --repo ", "implementationIdentity": { "providerId": "sentrux.provider", "implementationId": "sentrux-native.v2", "activation": "primary" }, "source": { "path": "crates/code-intel-cli/src/sentrux_gate.rs", "sha256": "0689c2b7bb3d93e44e0758faf9683a53b07a49cd054a5aa3c4a1ef318bf290fc" }, "conformance": { "path": "crates/code-intel-cli/tests/dag_run.rs", "sha256": "792ffb39dbd46d81e366416ae2e19fcc5eb5f6db688ab593bc22a79fafc8d0f3", "testName": "production_run_preserves_doctor_domain_failure_and_completes_unrelated_branch" } } ], "economics": { "benefit": { "metric": "B03 production adapter operations traced to conformance", "value": 2, "unit": "operations" }, "cost": { "metric": "registered B03 command surfaces plus retained shim lifecycle", "value": 3, "unit": "surfaces" }, "benefitEvidence": { "evidenceIds": ["local:b03:production-operation-trace", "gap:sentrux:representative-rule-value-measurement"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "costEvidence": { "evidenceIds": ["local:b03:shim-retained", "gap:sentrux:latency-maintenance-cost-measurement"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "assurance": { "maintenanceEvidence": { "evidenceIds": ["local:b03:shim-rollback-identity", "gap:sentrux:upstream-maintenance-status"], "checkedAt": 1783900800, "expiresAt": 1791676800 }, "securityEvidence": { "evidenceIds": ["local:b03:effect-reconciliation", "gap:sentrux:external-binary-security-review", "gap:sentrux:plugin-supply-chain-review"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "update": { "policy": "Before 2026-10-11, verify upstream revision/license, Windows and plugin conformance, binary/plugin security, maintenance status, and representative rule utility/cost; retain the shim and research-only status until closure", "nextCheckAt": 1791676800, "evidence": { "evidenceIds": ["gap:sentrux:update-check"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, - "ownedModifications": [ { "path": "crates/code-intel-cli/src/sentrux_adapter.rs", "description": "Pipeline-owned B03 structural-evidence adapter and normalization boundary", "evidenceIds": ["local:b03:adapter-source-sha256:08bf222bf5822a5ece89554be86d0687eb1437b18298a940638997459d95f5ee", "local:b03:sentrux-adapter-conformance:4d73dc2a622541f36bc45de9c5676d8be61291392ebdfca875b88cc2d77fecab"] }, { "path": "crates/code-intel-cli/src/sentrux_analysis.rs", "description": "native Rust governed-source, file/function metric, module graph, and DSM risk kernel", "evidenceIds": ["local:sentrux:rust-dsm-source-sha256:bae17f2206cb15b63e63f5899f227349d5bbb7f27110de6f2f3993c37cfdf148", "local:sentrux:rust-dsm-conformance-sha256:9501b59fbe40f7aa76cf82d1bb5f75ed0cc45bd5f2fa9dae1acb735d0b66f266"] }, { "path": "legacy/Invoke-SentruxAgentTool.ps1", "description": "retained compatibility implementation and rollback path, not provider authority", "evidenceIds": ["local:b03:shim-retained", "gap:sentrux:shim-retirement-proof"] }, { "path": "crates/code-intel-cli/src/sentrux_gate.rs", "description": "Pipeline-owned built-in structural gate engine (metrics, rules check, no-degradation gate, cycle detection)", "evidenceIds": ["local:b03:native-gate-source-sha256:0689c2b7bb3d93e44e0758faf9683a53b07a49cd054a5aa3c4a1ef318bf290fc", "local:b03:native-gate-conformance:22a1fad196eecd4a1a59b61c95712c8d100250b370dd86f0978bc53c475e1bb1"] } ], + "ownedModifications": [ { "path": "crates/code-intel-cli/src/sentrux_adapter.rs", "description": "Pipeline-owned B03 structural-evidence adapter and normalization boundary", "evidenceIds": ["local:b03:adapter-source-sha256:08bf222bf5822a5ece89554be86d0687eb1437b18298a940638997459d95f5ee", "local:b03:sentrux-adapter-conformance:4d73dc2a622541f36bc45de9c5676d8be61291392ebdfca875b88cc2d77fecab"] }, { "path": "crates/code-intel-cli/src/sentrux_analysis.rs", "description": "native Rust governed-source, file/function metric, module graph, and DSM risk kernel", "evidenceIds": ["local:sentrux:rust-dsm-source-sha256:bae17f2206cb15b63e63f5899f227349d5bbb7f27110de6f2f3993c37cfdf148", "local:sentrux:rust-dsm-conformance-sha256:9501b59fbe40f7aa76cf82d1bb5f75ed0cc45bd5f2fa9dae1acb735d0b66f266"] }, { "path": "legacy/Invoke-SentruxAgentTool.ps1", "description": "retained compatibility implementation and rollback path, not provider authority", "evidenceIds": ["local:b03:shim-retained", "gap:sentrux:shim-retirement-proof"] }, { "path": "crates/code-intel-cli/src/sentrux_gate.rs", "description": "Pipeline-owned built-in structural gate engine (metrics, rules check, no-degradation gate, cycle detection)", "evidenceIds": ["local:b03:native-gate-source-sha256:0689c2b7bb3d93e44e0758faf9683a53b07a49cd054a5aa3c4a1ef318bf290fc", "local:b03:native-gate-conformance:792ffb39dbd46d81e366416ae2e19fcc5eb5f6db688ab593bc22a79fafc8d0f3"] } ], "rollback": { "strategy": "keep legacy/Invoke-SentruxAgentTool.ps1 as the declared implementation/rollback behind provider.sentrux-adapt; never bypass A04", "evidence": { "evidenceIds": ["local:b03:shim-rollback-identity", "local:b03:fail-closed-unknown-rules"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "exit": { "strategy": "replace the accelerator or shim independently while preserving B03 normalized evidence and failure semantics", "replacementCriteria": ["upstream Windows and plugin conformance is current", "replacement passes authoritative rule and effect reconciliation fixtures", "rollback remains exercised until the replacement has an exit drill"], "evidence": { "evidenceIds": ["local:b03:adapter-replaceability", "gap:sentrux:upstream-windows-conformance", "gap:sentrux:upstream-plugin-conformance"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, "retirement": { "status": "candidate", "triggers": ["shim has no production callers after upstream Windows/plugin conformance passes", "external binary provenance or security remains unverifiable", "replacement passes B03 and rollback/exit drills"], "evidence": { "evidenceIds": ["local:b03:shim-retained", "gap:sentrux:shim-retirement-proof", "gap:sentrux:upstream-windows-conformance", "gap:sentrux:upstream-plugin-conformance"], "checkedAt": 1783900800, "expiresAt": 1791676800 } }, diff --git a/orchestration/retirements/e07-native-code/compatibility-retirement-deletion-diff.json b/orchestration/retirements/e07-native-code/compatibility-retirement-deletion-diff.json index f454989..e735491 100644 --- a/orchestration/retirements/e07-native-code/compatibility-retirement-deletion-diff.json +++ b/orchestration/retirements/e07-native-code/compatibility-retirement-deletion-diff.json @@ -1 +1 @@ -{"schema":"code-intel-compatibility-retirement-deletion-diff.v1","snapshotIdentity":"f6adcff6ef105b861abcfaf432c766781b1ef8d6981eb01f42f2730641a3b49e","retirementId":"retire-native-code-branch","legacyBranchId":"run-code-intel.native-code.embedded","affectedFiles":["run-code-intel.ps1"],"deletionsOnly":true,"summary":"Proposed deletion removes only the embedded Native Code Evidence function family and its direct call. It remains non-executable while normal/full are not routed through A09.","patch":{"algorithm":"replayable-delete-only-v1","sha256":"b7b64512fc17b73db3822bde412fd1ab4f2ae0ac9f4c3a35beec58e4a5f2e35c","files":[{"baseBlobSha256":"a17f6761c104b0b3965b31bb3e2f8083bfaf28218fa3a9fc23ef6ea3b917ddd0","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]$SkipGitHubResearch,\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 )\n\n return @($FailedSteps | Where-Object {\n $category = [string](Get-StepFailureCategory $_)\n $category -ne \"sentrux_fail\" -or $BlockingSentruxDebt -gt 0\n })\n}\n\nfunction Test-GitHubSolutionResearchRequired {\nparam([object]$FailureCounts)\n\n if ($null -eq $FailureCounts) { return $false }\n if ($FailureCounts.localToolError -gt 0) { return $true }\n if ($FailureCounts.sentruxFail -gt 0) { return $true }\n if ($FailureCounts.providerQuota -gt 0) { return $true }\n\n return $false\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\nif ($path -match '(^|/)(index|main|app|server|cli)\\.') {\n$reasons.Add(\"entrypoint\")\n$score += 40\n}\nif ($path -match '(test|spec)\\.' -or $path -match '(^|/)(tests?|spec)/') {\n$reasons.Add(\"test\")\n$score += 35\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)\\.' } | 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)(?