feat: AUT-2166 add reusable isolated review components - #564
svcnemo-autobot wants to merge 11 commits into
Conversation
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
chtruong814
left a comment
There was a problem hiding this comment.
@svcnemo-autobot Requesting changes. The isolation architecture is directionally correct, but this does not yet satisfy the security and compatibility contract for Megatron-Bridge.
-
The analyzer cannot obtain the review context it needs.
review.diffis stored but no MCP operation exposes its text;diff_hunksreturns only line coordinates, andreadreturns base64. Reads and searches are also restricted to changed-file snapshots, so the reviewer cannot load trustedBASE_SHAinstructions/skills or inspect unchanged definitions, callers, tests, and performance configurations. The validator accepts model-reported coverage counts without proving them from the retrieval audit, so an incomplete review can still be marked complete. Please expose bounded textual diff/source retrieval, permit bounded trusted-base repository reads/searches, capture governing base instructions and skills, and derive completeness from audited retrieval. -
The existing Megatron-Bridge prompt is passed through unchanged even though its tools no longer exist. It requires
Read, trusted skills,gh pr comment, and inline-comment tools, none of which are available in the analysis job. The old publication instructions should not be retained, but they need to be replaced by a trusted, tool-compatible wrapper that explains the immutable context, treats PR data as untrusted, requires trusted-base instructions and complete changed-file accounting, defines incomplete behavior, and asks only for structured output. Repository-specific review policy can remain caller-owned. -
Automatic Megatron-Bridge review remains on the original combined privileged path. This PR provides only the manual
issue_commentcomposition, while companion PR NVIDIA-NeMo/Megatron-Bridge#5854 changes only the manual workflow pin. The current automatic same-repository review still gives the model OIDC/GitHub publication capability. Please provide and adopt an isolated automatic-review entrypoint, or a generic orchestrator that supports both modes, before treating the original concern as resolved. -
Publication can leave partial findings. Inline findings are posted one at a time. Revision drift or an API error after some posts leaves partial comments and may omit the final status. The composed top-level body is not bounded to the GitHub comment limit either. Please preflight all output and use one COMMENT review request containing the body and inline comments so publication is atomic to the extent GitHub supports it.
-
Existing lifecycle behavior regresses. The eyes acknowledgment was removed; context/analysis failures do not produce a visible incomplete status because
publishrequires successful dependencies; concurrency covers only publication, allowing an older analysis to publish after a newer run; and the prior 45-minute timeout has no replacement. Please restore acknowledgment and deterministic failure status, apply concurrency to the complete orchestration, and add explicit time/turn budgets. -
Trigger matching is still not exact.
startsWith(...)accepts arbitrary suffixes and there is no optional expected-head SHA parsing. Please use an exact normalized first-line command grammar and validate any supplied 40-character SHA against the captured head. -
Copyright/license headers are incomplete.
review_components.pyhas the full Apache header, but both new Python test files use only a two-line abbreviated license, and the three new reusable YAML workflows have no header. Please use the full repository-standard NVIDIA copyright and Apache 2.0 block. The JSON schema can remain exempt because JSON does not support comments.
The focused tests pass when Git signing is explicitly disabled, but the fixture inherits global commit.gpgsign and otherwise fails before exercising 12 tests; please disable signing in the fixture. The current tests also do not demonstrate an end-to-end analyzer, publisher, authorized-fork/OIDC flow, stale revision, partial-publication prevention, or automatic-review migration.
The good pieces should remain: id-token: write is correctly confined to the model-free publisher, analysis has no GitHub/OIDC permissions, publication has no model or proposed-code checkout, artifacts are revision/digest bound, and the publisher fails rather than falling back from claude[bot] identity.
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
chtruong814
left a comment
There was a problem hiding this comment.
@svcnemo-autobot The isolation direction remains good, but the reference composition is not ready to merge yet. I left focused comments on the artifact round trip, governing symlink handling, trustworthy search coverage, and making the implementation easier for humans to audit. Each comment includes a concrete suggested fix and the missing regression coverage.
| when inline comments are also posted. Never approve the pull request. | ||
| name: ${{ steps.name.outputs.artifact_name }} | ||
| path: | | ||
| components/.github/actions/isolated-claude-review/review_components.py |
There was a problem hiding this comment.
This artifact cannot be consumed as currently packaged. Both files are under .github, which actions/upload-artifact excludes by default unless include-hidden-files: true is set. In addition, uploading these two paths uses their least common ancestor as the artifact root, so the downloaded files would be at trusted-tools/review_components.py and trusted-tools/review-output-v1.schema.json, while the publisher invokes a nested trusted-tools/components/.github/... path.
Suggested fix: copy the script and schema into a non-hidden staging directory, upload that directory, download it to trusted-tools, and invoke trusted-tools/review_components.py. Please add an artifact round-trip test that uploads, downloads, and executes the tool from the resulting layout.
| mode = tree_entry["mode"] | ||
| size = tree_entry["size"] | ||
| if mode == "120000": | ||
| result["reason"] = "symlink" |
There was a problem hiding this comment.
This makes the intended consumer repositories permanently incomplete when CLAUDE.md is a symlink to AGENTS.md. Both names are classified as governing paths, but the symlink is marked unavailable and retrieval_coverage() later requires every governing path to be available and fully read.
Suggested fix: resolve trusted-base symlinks strictly through captured Git objects, allowing only safe relative targets with traversal rejection, cycle detection, and a small depth bound. A simpler alternative is to treat a safe CLAUDE.md -> AGENTS.md alias as satisfied after the captured AGENTS.md content is read. Please add the exact symlink arrangement to the component tests.
| for path, info in sorted(base_repository.items()): | ||
| if prefix and path != prefix.rstrip("/") and not path.startswith(prefix): | ||
| continue | ||
| if not isinstance(info, dict) or not info.get("available"): |
There was a problem hiding this comment.
Unavailable trusted-base files are silently skipped here. Once the context budget is exhausted, a search with no matches is indistinguishable from a complete repository search with no matches, so the reviewer can draw an absence-based conclusion from partial evidence.
Suggested fix: include search-scope metadata such as files_total, files_searched, files_unavailable, and scope_complete. The analyzer prompt and validation contract should prevent absence-based conclusions when scope_complete is false. Please cover both a complete search and a budget-exhausted search in tests.
There was a problem hiding this comment.
The workflow decomposition is clear, but this 1,293-line module is difficult for a human to audit because it combines Git parsing, snapshot construction, retrieval budgets, MCP protocol handling, output validation, OIDC exchange, GitHub publication, and CLI dispatch. That concentration makes the security boundaries and invariants harder to verify locally.
Suggested change: retain one small executable entrypoint, but split the implementation into focused modules such as context.py, retrieval.py, validation.py, publisher.py, and cli.py. Use typed records (dataclass or TypedDict) for manifests, tree entries, coverage, and validated output instead of passing broad dict[str, Any] structures across every boundary.
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
chtruong814
left a comment
There was a problem hiding this comment.
@svcnemo-autobot The latest commit satisfactorily fixes the trusted-tool artifact layout and the CLAUDE.md -> AGENTS.md governing-symlink case. The focused suite also passes locally (22 component tests, 8 workflow tests, and compilation). I left two remaining recommendations: keep incomplete-search metadata bounded under the retriever byte budget, and replace the refactor's wildcard imports with explicit dependencies so the modules are genuinely easy to audit and Ruff-clean.
| {"path": path, "reason": info.get("reason", "unavailable")} | ||
| for path, info in scope | ||
| if not isinstance(info, dict) or not info.get("available") | ||
| ] |
There was a problem hiding this comment.
The new scope metadata fixes the ambiguity, but files_unavailable is still unbounded: it can contain every unavailable entry from a tree of up to 50,000 paths. Serializing that list can exceed the retriever's 16 MiB output budget, causing a broad search to fail before it can report that its scope was incomplete.
Please return a bounded contract instead—for example files_unavailable_count, counts grouped by reason, and a capped files_unavailable_sample—along with files_total, files_searched, and scope_complete. Add a regression test with enough unavailable, long-path entries to prove the response remains within its byte budget.
|
|
||
| from __future__ import annotations | ||
|
|
||
| from .contracts import * # noqa: F403 |
There was a problem hiding this comment.
Splitting the monolith is a good structural improvement, but the wildcard import keeps each module's dependencies implicit. contracts.py currently re-exports standard-library modules, operational helpers, constants, and types, so names such as Path, Any, git, and ReviewError appear without local imports. This makes the security boundaries harder to audit and currently causes ruff check .github/actions/isolated-claude-review to fail with 355 errors, predominantly F405.
Please replace the wildcard imports throughout reviewlib with explicit standard-library and project imports. Keep contracts.py focused on shared constants, errors, and accurate wire types; move general helpers to a clearly named utility module if necessary. Then run Ruff over this component and correct the TypedDict definitions so they represent the records actually exchanged between modules.
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
|
@svcnemo-autobot A few focused follow-ups from checking the template against the Megatron-LM light/strict caller:
"https://user:password@inference.example.invalid", # pragma: allowlist secretThis exact change passes the repository detector command while preserving the test. Please avoid excluding the file, weakening the detector, or adding a broad baseline entry.
python3 -m unittest discover -s .github/actions/isolated-claude-review -p "test_*.py"Please also add a small Megatron-LM-shaped contract fixture covering two manual callers with distinct trigger phrases and review profiles. It can remain a no-network test that verifies the reusable input contract, mutually independent concurrency keys, and policy-only prompts; it does not need inference credentials or GitHub publication. If actionlint is already available in CI, running it over the reusable workflows and fixture would provide an additional inexpensive syntax check. |
|
@svcnemo-autobot One clarification on the CI recommendation: please wire the isolated-review test job into this repository existing A focused job is sufficient: isolated-review-tests:
name: Isolated review tests
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@<full-commit-sha>
- name: Run isolated review tests
run: python3 -m unittest discover -s .github/actions/isolated-claude-review -p "test_*.py"Please keep it as a required PR check if repository policy supports that. This gives every proposed template change immediate, credential-free component and workflow-contract coverage. |
|
ℹ️ The workflow consumed the already-terminal exact-head CI handoff: item |
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
|
@svcnemo-autobot I'm not sure if the megatron-lm mock workflow fixture test is useful. In fact, all of the tests do not seem useful because they mostly test the Github Action config? Can we add tests that test the python modules themselves? Remove those megatron-lm workflow related tests and focus on testing the python code. Also, breakout the test from the preflight test to its own independent one. Also, ensure all added Python code include tests have human readable docstrings following Napoleon style. |
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
|
@svcnemo-autobot Please consider replacing the repository-owned direct inference loop with the pinned Claude Code Base Action, used only inside the isolated analysis job. This should reduce custom API/tool-loop code while preserving the current context and publication boundaries. Recommended shape:
This should allow removal of most or all of |
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
…ectly - Derive every output budget from explicit contracts so incomplete-search metadata stays inside the retriever byte budget and the aggregate structured output stays below the 60 KiB runner guard. - Replace wildcard imports with explicit dependencies in the components. - Invoke the full-SHA-pinned Claude Code Base Action directly instead of staging and patching a local checkout, with explicit allow/deny tool lists replacing the injected settings blob. Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
Re-lands the isolated Claude review composition previously reviewed on PR NVIDIA-NeMo#564 onto current main: pinned Claude Code Base Action analysis with no shell, filesystem, web, or GitHub mutation tools, immutable context construction, bounded audited retrieval, schema-validated output, and a model-free publisher. Python module tests cover the isolation contracts. Signed-off-by: svcnemo-autobot <svcnemo-autobot@nvidia.com>
Background
Megatron-Bridge review requires auditable isolation between untrusted PR content, model analysis, and GitHub publication without maintaining a custom inference loop.
What changed
Details
Analysis runs in an empty trusted directory with
permissions: {}, no PR-head checkout, no project settings or plugins, and only boundedreview_contextMCP tools. The pinned Base Action checkout remains unmodified. Shell, filesystem, web, and mutation tools are denied. Only the validated structured result and retrieval audit are uploaded.Tested
git diff --checkand repository secrets detection deferred to CI because the typed workstation materialization lacks.gitmetadata