Integrate provider orchestration surfaces - #7
Conversation
- Remove forced RequireRepowise=true default; restore switch default so CI (which never passes -RequireRepowise) does not require repowise - Drop crates/code-nexus-lite required-file entries; that crate was already demoted to .gitignore+README.md only, so the install step always reported missingRequired and failed CI
check-code-intel-tools.ps1 defaults RequireRepowise to true whenever the switch isn't explicitly bound. install-code-intel-pipeline.ps1 only added it to \ when true, so the false case fell through to that default and failed CI where repowise isn't installed. Always pass the switch explicitly. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
check-code-intel-tools.ps1 now defaults RequireRepowise to true unless explicitly bound, so the Doctor step failed on CI runners that don't have repowise installed. Explicitly opt out, matching the existing -SkipRepowise treatment used by the Pipeline smoke step. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
check-code-intel-tools.ps1 now defaults RequireRepowise to true unless explicitly bound. test-code-intel-pipeline.ps1 already threaded -SkipRepowise into the pipeline runner but never told the doctor call, so 'Pipeline smoke' failed the doctor gate on CI even with -SkipRepowise. Co-authored-by: factory-droid[bot] <138933559+factory-droid[bot]@users.noreply.github.com>
|
Warning Review limit reached
Next review available in: 38 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (4)
📝 WalkthroughWalkthroughThis PR adds a Rust-based ChangesRust orchestration pipeline
Unrelated tooling fixes
Estimated code review effort: 4 (Complex) | ~75 minutes Sequence Diagram(s)sequenceDiagram
participant Wrapper as invoke-code-intel.ps1
participant CLI as code-intel.exe
participant Orchestration as orchestration.rs
participant Manifest as integrations.json
participant Providers as providers.rs
Wrapper->>CLI: orchestrate --action Validate
CLI->>Orchestration: run(options)
Orchestration->>Manifest: load and validate stages/integrations
Manifest-->>Orchestration: parsed JSON
Orchestration-->>CLI: ok/errors result
CLI-->>Wrapper: exit code + JSON
Wrapper->>CLI: provider invoke understand graph
CLI->>Providers: invoke(options)
Providers->>Providers: invoke_understand -> graph::generate
Providers-->>CLI: artifact path + result JSON
CLI-->>Wrapper: printed output
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request introduces a robust integration orchestration layer to the pipeline, transitioning it from a loose toolchain into a self-contained intelligence pipeline. It adds a central registry (orchestration/integrations.json), a PowerShell orchestrator, and Rust modules for orchestration, graph generation, provider routing, and Sentrux integration. The code review feedback focuses on improving Windows path handling (specifically stripping the UNC prefix correctly to support network shares and clean standard paths), using more idiomatic dynamic property access in PowerShell, filtering out empty symbol names during graph generation, and optimizing the orchestration module to avoid redundant disk I/O by passing the integration count directly in the JSON.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| fn cli_path(path: &Path) -> PathBuf { | ||
| let text = path.to_string_lossy(); | ||
| if let Some(stripped) = text.strip_prefix(r"\\?\") { | ||
| return PathBuf::from(stripped); | ||
| } | ||
| path.to_path_buf() | ||
| } |
There was a problem hiding this comment.
On Windows, network share paths canonicalized by Rust start with \\?\UNC\. Simply stripping \\?\ leaves UNC\server\share, which is an invalid path. Correctly handling \\?\UNC\ by replacing it with \\ ensures robustness on network shares.
fn cli_path(path: &Path) -> PathBuf {
let text = path.to_string_lossy();
if let Some(stripped) = text.strip_prefix(r#"\\?\\UNC\\"#) {
return PathBuf::from(format!(r"\\{}", stripped));
}
if let Some(stripped) = text.strip_prefix(r#"\\?\\"#) {
return PathBuf::from(stripped);
}
path.to_path_buf()
}| fn cli_path(path: &Path) -> String { | ||
| let text = path.to_string_lossy(); | ||
| if let Some(stripped) = text.strip_prefix(r"\\?\") { | ||
| return stripped.to_string(); | ||
| } | ||
| text.to_string() | ||
| } |
There was a problem hiding this comment.
On Windows, network share paths canonicalized by Rust start with \\?\UNC\. Simply stripping \\?\ leaves UNC\server\share, which is an invalid path. Correctly handling \\?\UNC\ by replacing it with \\ ensures robustness on network shares.
| fn cli_path(path: &Path) -> String { | |
| let text = path.to_string_lossy(); | |
| if let Some(stripped) = text.strip_prefix(r"\\?\") { | |
| return stripped.to_string(); | |
| } | |
| text.to_string() | |
| } | |
| fn cli_path(path: &Path) -> String { | |
| let text = path.to_string_lossy(); | |
| if let Some(stripped) = text.strip_prefix(r#"\\?\\UNC\\"#) { | |
| return format!(r"\\{}", stripped); | |
| } | |
| if let Some(stripped) = text.strip_prefix(r#"\\?\\"#) { | |
| return stripped.to_string(); | |
| } | |
| text.to_string() | |
| } |
| function Get-JsonProperty { | ||
| param( | ||
| [object]$Object, | ||
| [string]$Name | ||
| ) | ||
|
|
||
| if ($null -eq $Object) { return $null } | ||
| $prop = $Object.PSObject.Properties[$Name] | ||
| if ($null -eq $prop) { return $null } | ||
| return $prop.Value | ||
| } |
There was a problem hiding this comment.
In PowerShell, accessing properties dynamically using $Object.$Name is more idiomatic, robust, and compatible with both PSCustomObject and Hashtable than manually traversing PSObject.Properties.
function Get-JsonProperty {
param(
[object]$Object,
[string]$Name
)
if ($null -eq $Object) { return $null }
return $Object.$Name
}
| if let Some((kind, name)) = symbol { | ||
| symbols.push(json!({ | ||
| "file": file.rel, | ||
| "kind": kind, | ||
| "name": name, | ||
| "line": line_index + 1 | ||
| })); | ||
| } |
There was a problem hiding this comment.
If take_ident returns an empty string (e.g., due to unexpected or malformed syntax), an empty symbol name will be added to the symbols list. It is safer to filter out empty names before pushing them.
if let Some((kind, name)) = symbol {
if !name.is_empty() {
symbols.push(json!({
"file": file.rel,
"kind": kind,
"name": name,
"line": line_index + 1
}));
}
}| fn normalize_path(path: impl AsRef<Path>) -> String { | ||
| path.as_ref().to_string_lossy().replace('\\', "/") | ||
| } |
There was a problem hiding this comment.
On Windows, canonicalize() prepends the UNC prefix \\?\ to absolute paths. Replacing backslashes without stripping this prefix results in paths starting with //?/, which can break downstream consumers. Stripping the prefix ensures clean, standard paths.
fn normalize_path(path: impl AsRef<Path>) -> String {
let s = path.as_ref().to_string_lossy();
let s = s.strip_prefix(r#"\\?\\"#).unwrap_or(&s);
s.replace('\\', "/")
}| let out = serde_json::json!({ | ||
| "ok": ok, | ||
| "action": action, | ||
| "manifest": manifest_path, | ||
| "policy": manifest.get("policy").cloned().unwrap_or(Value::Null), | ||
| "errors": errors, | ||
| "stages": sorted_stages, | ||
| "integrations": if action == "Validate" { Vec::<Value>::new() } else { plan.clone() }, | ||
| "plan": if action == "Plan" { plan.clone() } else { Vec::<Value>::new() } | ||
| }); |
There was a problem hiding this comment.
To avoid re-reading and re-parsing the manifest file from disk in print_text, we can include the total integrations count directly in the output JSON.
| let out = serde_json::json!({ | |
| "ok": ok, | |
| "action": action, | |
| "manifest": manifest_path, | |
| "policy": manifest.get("policy").cloned().unwrap_or(Value::Null), | |
| "errors": errors, | |
| "stages": sorted_stages, | |
| "integrations": if action == "Validate" { Vec::<Value>::new() } else { plan.clone() }, | |
| "plan": if action == "Plan" { plan.clone() } else { Vec::<Value>::new() } | |
| }); | |
| let out = serde_json::json!({ | |
| "ok": ok, | |
| "action": action, | |
| "manifest": manifest_path, | |
| "policy": manifest.get("policy").cloned().unwrap_or(Value::Null), | |
| "errors": errors, | |
| "stages": sorted_stages, | |
| "integrations": if action == "Validate" { Vec::<Value>::new() } else { plan.clone() }, | |
| "plan": if action == "Plan" { plan.clone() } else { Vec::<Value>::new() }, | |
| "total_integrations": integrations.len() | |
| }); |
| .and_then(Value::as_array) | ||
| .map(Vec::len) | ||
| .unwrap_or(0); | ||
| let integrations = read_manifest_integration_count(result).unwrap_or(0); |
There was a problem hiding this comment.
Instead of re-reading the manifest file from disk, retrieve the pre-calculated total_integrations count directly from the result JSON.
| let integrations = read_manifest_integration_count(result).unwrap_or(0); | |
| let integrations = result.get("total_integrations").and_then(Value::as_u64).unwrap_or(0) as usize; |
| fn read_manifest_integration_count(result: &Value) -> Option<usize> { | ||
| let manifest = result.get("manifest")?.as_str()?; | ||
| let data = read_json(Path::new(manifest)).ok()?; | ||
| data.get("integrations") | ||
| .and_then(Value::as_array) | ||
| .map(Vec::len) | ||
| } |
There was a problem hiding this comment.
Actionable comments posted: 8
🧹 Nitpick comments (3)
Install-SentruxVlangOverlay.ps1 (2)
144-146: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winInclude captured detail in the thrown error.
$validationDetailis captured but discarded on the generic failure path, leaving operators without the actual CLI output to diagnose why validation failed.♻️ Proposed fix
else { - throw "sentrux plugin validate failed for $targetRoot" + throw "sentrux plugin validate failed for $targetRoot: $validationDetail" }🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Install-SentruxVlangOverlay.ps1` around lines 144 - 146, The generic validation failure in the plugin install flow drops the captured CLI output, so update the failure path in the validation logic to include $validationDetail in the thrown error message. Locate the throw inside the sentrux plugin validation block and change it so the exception surfaces the actual validation details along with the targetRoot context, rather than only the generic “validate failed” text.
140-146: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueFragile string-coupling to sentrux-lite-core.ps1's error text.
The
"unknown command 'plugin'"match depends on the exact wording emitted bytools/sentrux-shim/sentrux-lite-core.ps1's default switch case. If that message text changes later, this detection silently breaks (falls through tothrow), turning previously-graceful skips into hard install failures.Consider extracting a shared marker/constant, or matching on a more stable signal (e.g., exit code convention) instead of free-text.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@Install-SentruxVlangOverlay.ps1` around lines 140 - 146, The plugin validation fallback in Install-SentruxVlangOverlay.ps1 is brittle because it hard-codes the exact “unknown command 'plugin'” text from sentrux-lite-core.ps1. Update the validation flow around the $validationDetail check to use a shared stable marker/constant or a non-text signal such as an agreed exit-code convention, so the skip path in the validation block remains reliable even if the sentrux-lite error wording changes.crates/code-intel-cli/src/sentrux.rs (1)
63-69: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winDuplicate
cli_pathhelper.Identical to
providers.rs::cli_path(crates/code-intel-cli/src/providers.rs:324-330). Consider extracting to a shared utility module to avoid the two implementations drifting.♻️ Suggested consolidation
-fn cli_path(path: &Path) -> String { - let text = path.to_string_lossy(); - if let Some(stripped) = text.strip_prefix(r"\\?\") { - return stripped.to_string(); - } - text.to_string() -} +use crate::util::cli_path; // shared helper, also used by providers.rs🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@crates/code-intel-cli/src/sentrux.rs` around lines 63 - 69, The cli_path helper in sentrux.rs duplicates the same logic already present in providers.rs, so consolidate it into a shared utility instead of keeping two copies. Move the path-normalization helper into a common module, update both sentrux.rs and providers.rs to call the shared function, and remove the duplicate local implementation to prevent drift.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@check-code-intel-tools.ps1`:
- Around line 14-17: The default handling for RequireRepowise in
check-code-intel-tools.ps1 now flips the [switch] to true when callers omit it,
which reintroduces the Repowise requirement. Change the default logic so unbound
RequireRepowise stays false, or update every caller that should not require
Repowise, including Invoke-OneRepo in invoke-code-intel.ps1 and the
cross-platform-smoke Doctor step in .github/workflows/ci.yml, to pass
RequireRepowise explicitly. Keep the behavior consistent with the PR’s goal so
invoke-code-intel and CI don’t fail when Repowise is unavailable.
In `@crates/code-intel-cli/src/graph.rs`:
- Around line 479-484: The truncate helper can panic because it slices strings
by byte index in truncate, which breaks on multibyte UTF-8 characters. Update
truncate to use a char-boundary-safe approach such as chars().take(max) or
char_indices, and keep the behavior in build_graph unchanged except for
preventing panics on long non-ASCII lines.
In `@crates/code-intel-cli/src/main.rs`:
- Around line 847-909: The helper block in main contains duplicate
`artifacts::resume` utilities that are now only used by test-only paths, so
remove the redundant functions instead of keeping dead code in normal builds.
Delete the unused helpers like `bool_at`, `existing_path`, `string_path`, and
`string_first`, and also review the nearby `build_resume_summary`, `next_read`,
and `print_resume_*` test-only code to see whether it should be removed as well
since the equivalent logic already exists in `artifacts.rs`.
In `@crates/code-intel-cli/src/providers.rs`:
- Around line 290-322: The stdin write in invoke_repowise can return early and
skip reaping the spawned repowise process, causing a false failure and possible
zombie child. Update the stdin handling so a failed write to child.stdin does
not immediately propagate past child.wait_with_output(); instead, treat the
write error as non-fatal for process cleanup, always wait on the child, and then
report the write issue in the returned JSON if needed. Use the invoke_repowise
and child.stdin.take() flow to keep the process reaped even when stdin is
closed.
In `@crates/code-intel-cli/src/sentrux.rs`:
- Around line 10-61: The run function currently waits indefinitely on the
sentrux subprocess because Command::output blocks until exit; add a timeout
around the execution in run() for the sentrux binary invocation. Refactor the
process launch and capture logic so you can poll or wait with a deadline,
terminate the child if it exceeds the limit, and then surface a clear timeout
error alongside the existing stdout/stderr handling and non-zero exit path.
In `@docs/code-intel-architecture.md`:
- Around line 14-16: The crate description for code-nexus-lite contains a stray
editing artifact (“iii”) in the Rust targets list. Update the documentation text
in the Rust targets section so the code-nexus-lite entry reads naturally and
removes the extra token, keeping the description consistent with the other crate
summaries.
- Around line 138-144: The Integration registry command examples use a hardcoded
developer-machine path, which should be replaced with the same portable path
style used elsewhere in the docs. Update the example commands in the Integration
registry block to reference $env:CODE_INTEL_HOME/target/debug/code-intel.exe
instead of the D:\projects\_tools\code-intel-pipeline path, keeping the rest of
the orchestrate arguments unchanged.
In `@Invoke-CodeIntelOrchestrator.ps1`:
- Around line 18-21: The root resolution in Invoke-CodeIntelOrchestrator.ps1
currently always uses the script directory, which causes custom -Manifest paths
to validate against the wrong base. Update the root calculation near the initial
$root assignment and where entrypoint paths are resolved so it mirrors
orchestration.rs::root_for_manifest: derive the root from the manifest’s
directory, or its grandparent when the parent folder is named orchestration.
Make sure the same root is used consistently for manifest loading and entrypoint
validation when -Manifest is provided.
---
Nitpick comments:
In `@crates/code-intel-cli/src/sentrux.rs`:
- Around line 63-69: The cli_path helper in sentrux.rs duplicates the same logic
already present in providers.rs, so consolidate it into a shared utility instead
of keeping two copies. Move the path-normalization helper into a common module,
update both sentrux.rs and providers.rs to call the shared function, and remove
the duplicate local implementation to prevent drift.
In `@Install-SentruxVlangOverlay.ps1`:
- Around line 144-146: The generic validation failure in the plugin install flow
drops the captured CLI output, so update the failure path in the validation
logic to include $validationDetail in the thrown error message. Locate the throw
inside the sentrux plugin validation block and change it so the exception
surfaces the actual validation details along with the targetRoot context, rather
than only the generic “validate failed” text.
- Around line 140-146: The plugin validation fallback in
Install-SentruxVlangOverlay.ps1 is brittle because it hard-codes the exact
“unknown command 'plugin'” text from sentrux-lite-core.ps1. Update the
validation flow around the $validationDetail check to use a shared stable
marker/constant or a non-text signal such as an agreed exit-code convention, so
the skip path in the validation block remains reliable even if the sentrux-lite
error wording changes.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 6071627b-7981-4ccf-8c11-2f94111f9566
📒 Files selected for processing (19)
.github/workflows/ci.ymlInstall-SentruxVlangOverlay.ps1Invoke-CodeIntelOrchestrator.ps1README.mdcheck-code-intel-tools.ps1crates/code-intel-cli/src/artifacts.rscrates/code-intel-cli/src/artifacts_tests.rscrates/code-intel-cli/src/graph.rscrates/code-intel-cli/src/main.rscrates/code-intel-cli/src/orchestration.rscrates/code-intel-cli/src/providers.rscrates/code-intel-cli/src/routes.rscrates/code-intel-cli/src/sentrux.rsdocs/code-intel-architecture.mddocs/integration-orchestration.mdinvoke-code-intel.ps1orchestration/integrations.jsontest-integration-orchestration.ps1tools/sentrux-shim/sentrux-lite-core.ps1
| fn truncate(value: &str, max: usize) -> String { | ||
| if value.len() <= max { | ||
| return value.to_string(); | ||
| } | ||
| format!("{}...", &value[..max]) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the helper and its callers.
rg -n "fn truncate|truncate\\(" crates/code-intel-cli/src/graph.rs crates/code-intel-cli/src -S
# Show the relevant section around the helper.
sed -n '430,520p' crates/code-intel-cli/src/graph.rsRepository: 2233admin/code-intel-pipeline
Length of output: 3107
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '200,240p' crates/code-intel-cli/src/graph.rsRepository: 2233admin/code-intel-pipeline
Length of output: 1386
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
root = Path('crates/code-intel-cli/src')
hits = []
for path in root.rglob('*'):
if not path.is_file():
continue
try:
text = path.read_text(encoding='utf-8')
except Exception:
continue
if any(ord(ch) > 127 for ch in text):
hits.append(str(path))
if len(hits) >= 20:
break
print("\n".join(hits))
PYRepository: 2233admin/code-intel-pipeline
Length of output: 201
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
s = "a" * 219 + "é" + "b"
b = s.encode("utf-8")
print(len(b), b[:220])
try:
print(b[:220].decode("utf-8"))
except UnicodeDecodeError as e:
print("decode-error", e)
PYRepository: 2233admin/code-intel-pipeline
Length of output: 488
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
root = Path('crates/code-intel-cli/src')
for path in root.rglob('*'):
if not path.is_file():
continue
try:
data = path.read_text(encoding='utf-8').splitlines()
except Exception:
continue
for i, line in enumerate(data, 1):
if len(line.encode('utf-8')) > 220 and any(ord(ch) > 127 for ch in line):
print(f"{path}:{i}:{len(line.encode('utf-8'))}:{line[:80]}")
raise SystemExit
print("NO_MATCH")
PYRepository: 2233admin/code-intel-pipeline
Length of output: 176
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import subprocess
files = subprocess.check_output(['git', 'ls-files'], text=True).splitlines()
for rel in files:
path = Path(rel)
try:
lines = path.read_text(encoding='utf-8').splitlines()
except Exception:
continue
for i, line in enumerate(lines, 1):
b = line.encode('utf-8')
if len(b) > 220 and any(ch > 127 for ch in line.encode('utf-8')):
print(f"{rel}:{i}:{len(b)}")
raise SystemExit
print("NO_MATCH")
PYRepository: 2233admin/code-intel-pipeline
Length of output: 187
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
line = Path('CHANGELOG.md').read_text(encoding='utf-8').splitlines()[18]
print(line)
print("chars=", len(line))
print("bytes=", len(line.encode('utf-8')))
print("non_ascii=", any(ord(ch) > 127 for ch in line))
PYRepository: 2233admin/code-intel-pipeline
Length of output: 474
🏁 Script executed:
#!/bin/bash
set -euo pipefail
rg -n "CHANGELOG|walk|glob|read_dir|SourceFile|extract_edges|build_graph|tracked" crates/code-intel-cli/src/graph.rs crates/code-intel-cli/src -SRepository: 2233admin/code-intel-pipeline
Length of output: 2335
🏁 Script executed:
#!/bin/bash
set -euo pipefail
sed -n '1,200p' crates/code-intel-cli/src/graph.rsRepository: 2233admin/code-intel-pipeline
Length of output: 5654
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
line = Path('CHANGELOG.md').read_text(encoding='utf-8').splitlines()[18]
b = line.encode('utf-8')
print("bytes=", len(b))
print("slice220_ok=", True)
try:
s = b[:220].decode('utf-8')
print("decoded_prefix=", s)
except UnicodeDecodeError as e:
print("slice220_ok=", False)
print("error=", e)
PYRepository: 2233admin/code-intel-pipeline
Length of output: 431
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import subprocess
for rel in subprocess.check_output(['git', 'ls-files'], text=True).splitlines():
path = Path(rel)
try:
lines = path.read_text(encoding='utf-8').splitlines()
except Exception:
continue
for i, line in enumerate(lines, 1):
b = line.encode('utf-8')
if len(b) > 220:
try:
b[:220].decode('utf-8')
except UnicodeDecodeError:
print(f"{rel}:{i}:{len(b)}")
raise SystemExit
print("NO_PANIC_CASE")
PYRepository: 2233admin/code-intel-pipeline
Length of output: 185
🏁 Script executed:
#!/bin/bash
set -euo pipefail
python3 - <<'PY'
from pathlib import Path
import subprocess
def check(path):
try:
lines = Path(path).read_text(encoding='utf-8').splitlines()
except Exception:
return None
for i, line in enumerate(lines, 1):
b = line.encode('utf-8')
if len(b) > 220:
try:
b[:220].decode('utf-8')
except UnicodeDecodeError as e:
return f"{path}:{i}:{len(b)}:PANIC:{e}"
return None
# Check README first.
print("README", check('README.md') or "OK")
# Scan tracked files for the first actual panic case.
for rel in subprocess.check_output(['git', 'ls-files'], text=True).splitlines():
res = check(rel)
if res:
print(res)
break
else:
print("NO_PANIC_CASE")
PYRepository: 2233admin/code-intel-pipeline
Length of output: 376
truncate can panic on UTF-8 lines.
&value[..max] slices by byte index, so truncate(line, 220) will panic when byte 220 lands in the middle of a multibyte character. build_graph scans tracked files, so a single long non-ASCII line can abort graph generation. Use a char-boundary-safe truncation (chars().take(max) or char_indices).
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/code-intel-cli/src/graph.rs` around lines 479 - 484, The truncate
helper can panic because it slices strings by byte index in truncate, which
breaks on multibyte UTF-8 characters. Update truncate to use a
char-boundary-safe approach such as chars().take(max) or char_indices, and keep
the behavior in build_graph unchanged except for preventing panics on long
non-ASCII lines.
| fn read_json(path: &Path) -> Result<Value> { | ||
| let text = fs::read_to_string(path)?; | ||
| Ok(serde_json::from_str(text.trim_start_matches('\u{feff}'))?) | ||
| } | ||
|
|
||
| fn string_at(value: &Value, path: &[&str]) -> Option<String> { | ||
| let mut current = value; | ||
| for segment in path { | ||
| current = current.get(*segment)?; | ||
| } | ||
| Ok(()) | ||
| current.as_str().map(ToString::to_string) | ||
| } | ||
|
|
||
| fn int_at(value: &Value, path: &[&str]) -> i64 { | ||
| let mut current = value; | ||
| for segment in path { | ||
| current = match current.get(*segment) { | ||
| Some(value) => value, | ||
| None => return 0, | ||
| }; | ||
| } | ||
| current.as_i64().unwrap_or(0) | ||
| } | ||
|
|
||
| fn bool_at(value: &Value, path: &[&str]) -> bool { | ||
| let mut current = value; | ||
| for segment in path { | ||
| current = match current.get(*segment) { | ||
| Some(value) => value, | ||
| None => return false, | ||
| }; | ||
| } | ||
| current.as_bool().unwrap_or(false) | ||
| } | ||
|
|
||
| fn existing_path(dir: &Path, file_name: &str) -> Option<PathBuf> { | ||
| let path = dir.join(file_name); | ||
| path.exists().then_some(path) | ||
| } | ||
|
|
||
| fn string_path(value: &Value, path: &[&str]) -> Option<PathBuf> { | ||
| string_at(value, path).and_then(|s| { | ||
| if s.trim().is_empty() { | ||
| None | ||
| } else { | ||
| Some(PathBuf::from(s)) | ||
| } | ||
| }) | ||
| } | ||
|
|
||
| fn string_first(values: &[&Value], paths: &[&[&str]]) -> String { | ||
| for value in values { | ||
| for path in paths { | ||
| if let Some(text) = string_at(value, path) { | ||
| if !text.trim().is_empty() { | ||
| return text; | ||
| } | ||
| } | ||
| } | ||
| } | ||
| String::new() | ||
| } | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win
Dead helper functions left behind by the artifacts::resume extraction.
bool_at, existing_path, string_path, and string_first are only reachable from the #[cfg(test)]-gated build_resume_summary/next_read (Lines 910, 968), but aren't themselves gated, so Clippy correctly flags them as unused in normal builds. The same logic already lives in crates/code-intel-cli/src/artifacts.rs (string_at/int_at/bool_at/existing_path/string_path/string_first), so this looks like duplicate debris rather than a shared utility. Prefer deleting this now-redundant block (and the test-only build_resume_summary/next_read/print_resume_* it supports, if artifacts_tests.rs already covers this behavior) rather than just adding #[cfg(test)] to silence the warning.
🧰 Tools
🪛 Clippy (1.96.0)
[warning] 871-871: function bool_at is never used
(warning)
[warning] 882-882: function existing_path is never used
(warning)
[warning] 887-887: function string_path is never used
(warning)
[warning] 897-897: function string_first is never used
(warning)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/code-intel-cli/src/main.rs` around lines 847 - 909, The helper block
in main contains duplicate `artifacts::resume` utilities that are now only used
by test-only paths, so remove the redundant functions instead of keeping dead
code in normal builds. Delete the unused helpers like `bool_at`,
`existing_path`, `string_path`, and `string_first`, and also review the nearby
`build_resume_summary`, `next_read`, and `print_resume_*` test-only code to see
whether it should be removed as well since the equivalent logic already exists
in `artifacts.rs`.
Source: Linters/SAST tools
| fn invoke_repowise(repo: &Path, subcommand: &str, args: &[&str]) -> Result<Value> { | ||
| let repo_cli = cli_path(repo); | ||
| let mut child = Command::new("repowise") | ||
| .arg(subcommand) | ||
| .args(args) | ||
| .arg(&repo_cli) | ||
| .current_dir(&repo_cli) | ||
| .stdin(Stdio::piped()) | ||
| .stdout(Stdio::piped()) | ||
| .stderr(Stdio::piped()) | ||
| .spawn()?; | ||
|
|
||
| if let Some(mut stdin) = child.stdin.take() { | ||
| stdin.write_all(b"n\n")?; | ||
| } | ||
|
|
||
| let output = child.wait_with_output()?; | ||
|
|
||
| let stdout = String::from_utf8_lossy(&output.stdout).to_string(); | ||
| let stderr = String::from_utf8_lossy(&output.stderr).to_string(); | ||
| let ok = output.status.success(); | ||
|
|
||
| Ok(json!({ | ||
| "ok": ok, | ||
| "schema": "code-intel-provider-api.v1", | ||
| "provider": "repowise", | ||
| "operation": if subcommand == "update" || subcommand == "init" { "index" } else { subcommand }, | ||
| "exitCode": output.status.code().unwrap_or(-1), | ||
| "artifact": repo_cli.join(".repowise").join("wiki.db"), | ||
| "stdoutTail": tail(&stdout, 80), | ||
| "stderrTail": tail(&stderr, 80) | ||
| })) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Stdin write failure aborts before reaping the child, causing a false failure + potential zombie process.
If stdin.write_all(b"n\n") fails (e.g. the child already closed stdin), the ? returns early and child.wait_with_output() is never called — the process is never reaped, and invoke_repowise reports failure even if repowise itself would have succeeded.
🔧 Suggested fix
if let Some(mut stdin) = child.stdin.take() {
- stdin.write_all(b"n\n")?;
+ let _ = stdin.write_all(b"n\n");
}
let output = child.wait_with_output()?;📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| fn invoke_repowise(repo: &Path, subcommand: &str, args: &[&str]) -> Result<Value> { | |
| let repo_cli = cli_path(repo); | |
| let mut child = Command::new("repowise") | |
| .arg(subcommand) | |
| .args(args) | |
| .arg(&repo_cli) | |
| .current_dir(&repo_cli) | |
| .stdin(Stdio::piped()) | |
| .stdout(Stdio::piped()) | |
| .stderr(Stdio::piped()) | |
| .spawn()?; | |
| if let Some(mut stdin) = child.stdin.take() { | |
| stdin.write_all(b"n\n")?; | |
| } | |
| let output = child.wait_with_output()?; | |
| let stdout = String::from_utf8_lossy(&output.stdout).to_string(); | |
| let stderr = String::from_utf8_lossy(&output.stderr).to_string(); | |
| let ok = output.status.success(); | |
| Ok(json!({ | |
| "ok": ok, | |
| "schema": "code-intel-provider-api.v1", | |
| "provider": "repowise", | |
| "operation": if subcommand == "update" || subcommand == "init" { "index" } else { subcommand }, | |
| "exitCode": output.status.code().unwrap_or(-1), | |
| "artifact": repo_cli.join(".repowise").join("wiki.db"), | |
| "stdoutTail": tail(&stdout, 80), | |
| "stderrTail": tail(&stderr, 80) | |
| })) | |
| } | |
| fn invoke_repowise(repo: &Path, subcommand: &str, args: &[&str]) -> Result<Value> { | |
| let repo_cli = cli_path(repo); | |
| let mut child = Command::new("repowise") | |
| .arg(subcommand) | |
| .args(args) | |
| .arg(&repo_cli) | |
| .current_dir(&repo_cli) | |
| .stdin(Stdio::piped()) | |
| .stdout(Stdio::piped()) | |
| .stderr(Stdio::piped()) | |
| .spawn()?; | |
| if let Some(mut stdin) = child.stdin.take() { | |
| let _ = stdin.write_all(b"n\n"); | |
| } | |
| let output = child.wait_with_output()?; | |
| let stdout = String::from_utf8_lossy(&output.stdout).to_string(); | |
| let stderr = String::from_utf8_lossy(&output.stderr).to_string(); | |
| let ok = output.status.success(); | |
| Ok(json!({ | |
| "ok": ok, | |
| "schema": "code-intel-provider-api.v1", | |
| "provider": "repowise", | |
| "operation": if subcommand == "update" || subcommand == "init" { "index" } else { subcommand }, | |
| "exitCode": output.status.code().unwrap_or(-1), | |
| "artifact": repo_cli.join(".repowise").join("wiki.db"), | |
| "stdoutTail": tail(&stdout, 80), | |
| "stderrTail": tail(&stderr, 80) | |
| })) | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/code-intel-cli/src/providers.rs` around lines 290 - 322, The stdin
write in invoke_repowise can return early and skip reaping the spawned repowise
process, causing a false failure and possible zombie child. Update the stdin
handling so a failed write to child.stdin does not immediately propagate past
child.wait_with_output(); instead, treat the write error as non-fatal for
process cleanup, always wait on the child, and then report the write issue in
the returned JSON if needed. Use the invoke_repowise and child.stdin.take() flow
to keep the process reaped even when stdin is closed.
| pub fn run(options: &Options<'_>) -> Result<()> { | ||
| let operation = options.operation.ok_or("sentrux requires an operation")?; | ||
| let repo = options.repo.ok_or("sentrux requires a repo/path")?; | ||
| let repo = repo.canonicalize()?; | ||
| let repo_cli = cli_path(&repo); | ||
|
|
||
| let mut args = Vec::new(); | ||
| match operation { | ||
| "scan" | "health" | "check" | "gate" => args.push(operation.to_string()), | ||
| "check_rules" => args.push("check".to_string()), | ||
| "gate_save" | "save_baseline" => { | ||
| args.push("gate".to_string()); | ||
| args.push("--save".to_string()); | ||
| } | ||
| other => { | ||
| return Err(format!("sentrux operation not yet implemented in Rust: {other}").into()) | ||
| } | ||
| } | ||
| args.push(repo_cli.clone()); | ||
|
|
||
| let binary = if cfg!(windows) { | ||
| "sentrux.cmd" | ||
| } else { | ||
| "sentrux" | ||
| }; | ||
|
|
||
| let output = Command::new(binary) | ||
| .args(&args) | ||
| .current_dir(&repo_cli) | ||
| .stdout(Stdio::piped()) | ||
| .stderr(Stdio::piped()) | ||
| .output()?; | ||
|
|
||
| let stdout = String::from_utf8_lossy(&output.stdout); | ||
| let stderr = String::from_utf8_lossy(&output.stderr); | ||
| if !stdout.is_empty() { | ||
| print!("{stdout}"); | ||
| } | ||
| if !stderr.is_empty() { | ||
| eprint!("{stderr}"); | ||
| } | ||
|
|
||
| if !output.status.success() { | ||
| return Err(format!( | ||
| "sentrux {operation} failed with exit code {}", | ||
| output.status.code().unwrap_or(-1) | ||
| ) | ||
| .into()); | ||
| } | ||
|
|
||
| Ok(()) | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
🧩 Analysis chain
🌐 Web query:
Rust std::process::Command wait with timeout best practice 2026
💡 Result:
The standard Rust library (std::process::Command) does not provide a native way to wait for a process with a timeout [1][2]. As of 2026, the best practice for implementing this functionality is to use the community-maintained wait-timeout crate [3][4][5]. The wait-timeout crate provides an extension trait, ChildExt, for std::process::Child that allows you to wait on a process for a specified Duration [3][6]. Best Practice Implementation: 1. Add the dependency to your Cargo.toml: [dependencies] wait-timeout = "0.2" 2. Use the wait_timeout method to poll for the process exit, and call kill if the timeout expires [3]: use std::process::Command; use std::time::Duration; use wait_timeout::ChildExt; fn main { let mut child = Command::new("your_command").spawn.expect("failed to spawn"); let timeout = Duration::from_secs(5); match child.wait_timeout(timeout).expect("failed to wait") { Some(status) => println!("Process exited with status: {}", status), None => { // Timeout reached, kill the process child.kill.expect("failed to kill process"); // Optionally wait to reap the zombie process child.wait.expect("failed to wait for killed process"); println!("Process timed out and was killed"); } } } Important Considerations: - Zombie Processes: Always ensure you call wait or a related method after killing a child process to reap it and prevent zombie processes [1]. - Cross-Platform: The wait-timeout crate handles the platform-specific complexities of implementing timeouts on Unix and Windows, which is significantly more involved than just a simple wait call [3][7]. - Alternatives: If you are working in an asynchronous context (e.g., Tokio), you should use tokio::process::Command, which provides first-class support for timeouts via tokio::time::timeout instead of relying on external crates for synchronization [3].
Citations:
- 1: https://doc.rust-lang.org/std/process/struct.Child.html
- 2: https://doc.rust-lang.org/stable/std/process/struct.Command.html
- 3: https://docs.rs/wait-timeout/latest/wait_timeout/index.html
- 4: https://crates.io/crates/wait-timeout
- 5: https://docs.rs/crate/wait-timeout/latest
- 6: https://docs.rs/wait-timeout/latest/wait_timeout/trait.ChildExt.html
- 7: https://alexcrichton.github.io/wait-timeout/
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Files mentioning sentrux:\n'
git ls-files | rg 'sentrux|orchestration/integrations\.json|wait-timeout|timeout' || true
printf '\nSearch for timeout handling around subprocesses:\n'
rg -n --hidden --glob '!target' --glob '!node_modules' \
'wait_timeout|wait-timeout|kill\(|timeout|Command::new\(|output\(\)' \
crates code-intel-cli . 2>/dev/null | head -n 200Repository: 2233admin/code-intel-pipeline
Length of output: 2091
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Map sentrux wrapper file:\n'
ast-grep outline crates/code-intel-cli/src/sentrux.rs --view expanded || true
printf '\nShow the relevant file with line numbers:\n'
cat -n crates/code-intel-cli/src/sentrux.rs | sed -n '1,140p'Repository: 2233admin/code-intel-pipeline
Length of output: 2819
🏁 Script executed:
#!/bin/bash
set -euo pipefail
printf 'Check whether wait-timeout is already declared:\n'
rg -n --hidden --glob '!target' --glob '!node_modules' 'wait-timeout' Cargo.toml crates Cargo.lock || true
printf '\nCheck for any other child-process timeout wrapper patterns:\n'
rg -n --hidden --glob '!target' --glob '!node_modules' 'wait_with_output|wait_timeout|tokio::time::timeout|ChildExt' .Repository: 2233admin/code-intel-pipeline
Length of output: 365
Add a timeout around the sentrux subprocess. Command::output() blocks until sentrux exits, so a hung binary can stall this CLI indefinitely.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@crates/code-intel-cli/src/sentrux.rs` around lines 10 - 61, The run function
currently waits indefinitely on the sentrux subprocess because Command::output
blocks until exit; add a timeout around the execution in run() for the sentrux
binary invocation. Refactor the process launch and capture logic so you can poll
or wait with a deadline, terminate the child if it exceeds the limit, and then
surface a clear timeout error alongside the existing stdout/stderr handling and
non-zero exit path.
| 2. Rust targets | ||
| - `crates/code-intel-cli`: compiled `code-intel` CLI for integration orchestration, artifact resume, classify, and artifact doctor contracts. | ||
| - `crates/code-nexus-lite`: compiled `code-nexus-lite` iii worker for CodeNexus scan/lite/doctor behavior. |
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Typo: stray "iii" in the crate description.
"compiled code-nexus-lite iii worker" reads as a leftover editing artifact.
✏️ Suggested fix
- - `crates/code-nexus-lite`: compiled `code-nexus-lite` iii worker for CodeNexus scan/lite/doctor behavior.
+ - `crates/code-nexus-lite`: compiled `code-nexus-lite` worker for CodeNexus scan/lite/doctor behavior.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| 2. Rust targets | |
| - `crates/code-intel-cli`: compiled `code-intel` CLI for integration orchestration, artifact resume, classify, and artifact doctor contracts. | |
| - `crates/code-nexus-lite`: compiled `code-nexus-lite` iii worker for CodeNexus scan/lite/doctor behavior. | |
| 2. Rust targets | |
| - `crates/code-intel-cli`: compiled `code-intel` CLI for integration orchestration, artifact resume, classify, and artifact doctor contracts. | |
| - `crates/code-nexus-lite`: compiled `code-nexus-lite` worker for CodeNexus scan/lite/doctor behavior. |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/code-intel-architecture.md` around lines 14 - 16, The crate description
for code-nexus-lite contains a stray editing artifact (“iii”) in the Rust
targets list. Update the documentation text in the Rust targets section so the
code-nexus-lite entry reads naturally and removes the extra token, keeping the
description consistent with the other crate summaries.
| Integration registry: | ||
|
|
||
| ```powershell | ||
| D:\projects\_tools\code-intel-pipeline\target\debug\code-intel.exe orchestrate --action Validate --json | ||
| D:\projects\_tools\code-intel-pipeline\target\debug\code-intel.exe orchestrate --action Plan --repo C:\path\to\repo --mode normal --json | ||
| ``` | ||
|
|
There was a problem hiding this comment.
📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win
Hardcoded developer-machine path in the doc example.
Every other command block in this file (and the equivalent README example) uses $env:CODE_INTEL_HOME-relative or repo-relative paths. This block hardcodes D:\projects\_tools\code-intel-pipeline\..., which won't apply to other machines.
✏️ Suggested fix
```powershell
-D:\projects\_tools\code-intel-pipeline\target\debug\code-intel.exe orchestrate --action Validate --json
-D:\projects\_tools\code-intel-pipeline\target\debug\code-intel.exe orchestrate --action Plan --repo C:\path\to\repo --mode normal --json
+"$env:CODE_INTEL_HOME/target/debug/code-intel.exe" orchestrate --action Validate --json
+"$env:CODE_INTEL_HOME/target/debug/code-intel.exe" orchestrate --action Plan --repo C:\path\to\repo --mode normal --json</details>
<!-- suggestion_start -->
<details>
<summary>📝 Committable suggestion</summary>
> ‼️ **IMPORTANT**
> Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
```suggestion
Integration registry:
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@docs/code-intel-architecture.md` around lines 138 - 144, The Integration
registry command examples use a hardcoded developer-machine path, which should
be replaced with the same portable path style used elsewhere in the docs. Update
the example commands in the Integration registry block to reference
$env:CODE_INTEL_HOME/target/debug/code-intel.exe instead of the
D:\projects\_tools\code-intel-pipeline path, keeping the rest of the orchestrate
arguments unchanged.
| $root = Split-Path -Parent $PSCommandPath | ||
| if ([string]::IsNullOrWhiteSpace($Manifest)) { | ||
| $Manifest = Join-Path $root "orchestration\integrations.json" | ||
| } |
There was a problem hiding this comment.
🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win
Root resolution diverges from the Rust orchestrator for custom -Manifest paths.
$root is always the script's own directory (Line 18), and is reused for resolving entrypoint paths even when -Manifest points elsewhere (Line 105). The Rust engine (orchestration.rs::root_for_manifest) instead derives the root from the manifest's own location (parent, or grandparent when the parent directory is named orchestration). With a custom -Manifest, this script will validate entrypoints against the wrong root, producing results that disagree with code-intel.exe orchestrate for the same manifest.
🔧 Proposed fix to mirror Rust's root_for_manifest
$root = Split-Path -Parent $PSCommandPath
if ([string]::IsNullOrWhiteSpace($Manifest)) {
$Manifest = Join-Path $root "orchestration\integrations.json"
+} else {
+ $manifestParent = Split-Path -Parent $Manifest
+ if ((Split-Path -Leaf $manifestParent) -eq "orchestration") {
+ $root = Split-Path -Parent $manifestParent
+ } else {
+ $root = $manifestParent
+ }
}Also applies to: 104-109
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@Invoke-CodeIntelOrchestrator.ps1` around lines 18 - 21, The root resolution
in Invoke-CodeIntelOrchestrator.ps1 currently always uses the script directory,
which causes custom -Manifest paths to validate against the wrong base. Update
the root calculation near the initial $root assignment and where entrypoint
paths are resolved so it mirrors orchestration.rs::root_for_manifest: derive the
root from the manifest’s directory, or its grandparent when the parent folder is
named orchestration. Make sure the same root is used consistently for manifest
loading and entrypoint validation when -Manifest is provided.
Integrates PR #5 on top of current main while preserving Repowise 0.25 provider/model/reasoning behavior and ccw -> codex_cli aliasing. Adds Rust orchestration/provider/route/graph surfaces, manifest registration, docs, and smoke coverage.\n\nValidation run locally:\n- cargo fmt -p code-intel -- --check\n- cargo check -p code-intel\n- cargo test -p code-intel\n- PowerShell parser checks for runner/orchestration/provider scripts\n- Python ast.parse for Run-ScopedRepowiseDocs.py\n- .\test-integration-orchestration.ps1 -RepoPath .\n- .\test-code-intel-provider.ps1 -Provider mock\n- .\target\debug\code-intel.exe provider --action Validate --json\n- .\target\debug\code-intel.exe orchestrate --action Validate --json\n- .\test-code-intel-pipeline.ps1 -RepoPath . -SkipRepowise -AllowGraphMissing -SkipSentruxCheck -SkipSentruxGate -SkipGitHubResearch -Mode normal\n\nNotes:\n- Anthropic provider preflight was not runnable locally because ANTHROPIC_API_KEY is not set.\n- Full Sentrux check still reports existing max_cc debt in run-code-intel.ps1:Get-CodeEvidenceSymbols (cc=415), so local smoke skipped Sentrux check/gate after capturing that failure.