Skip to content

feat(auditor): reattach job-submit through local config and pause/resume#758

Open
parkanzky wants to merge 4 commits into
mainfrom
AALGO-224/auditor-pause-resume-pparkanzky
Open

feat(auditor): reattach job-submit through local config and pause/resume#758
parkanzky wants to merge 4 commits into
mainfrom
AALGO-224/auditor-pause-resume-pparkanzky

Conversation

@parkanzky

@parkanzky parkanzky commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Local work around to get job submission working correctly - there is a hack in the platform job runner that makes the determination via gpu / cpu combinations which does not match auditor's deployment model. For right now adding local config to override that, but platform will hopefully support it out of the box very soon.

Also, copy back in the pause/resume code from previous release.

Summary by CodeRabbit

  • New Features
    • Added Auditor v2 “jobs/audit” endpoints for submitting, listing (with pagination/filtering/sorting), fetching, canceling, checking status, viewing logs, and downloading job results/artifacts.
    • Auditor workspace config/target APIs now return expanded output payloads with additional read-only metadata.
    • Auditor jobs now execute per probe with persisted progress, retry controls, and pause/resume handling.
    • Introduced a new Docker-backed executor profile for Auditor jobs.
  • Tests
    • Expanded automated coverage for routing and per-probe execution behaviors (retries, pause/resume, partial/failed outcomes).
  • Chores
    • Refreshed third-party requirements/export and license metadata.

@parkanzky
parkanzky requested review from a team as code owners July 17, 2026 15:58
@github-actions github-actions Bot added the feat label Jul 17, 2026

@ironcommit ironcommit left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. We can back out of this once, jobs is further along.

@github-actions

github-actions Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor
Suite Lines Covered Line Rate Branch Rate
Unit Tests 25872/33534 77.1% 61.4%
Integration Tests 14952/32183 46.5% 18.8%

@coderabbitai

coderabbitai Bot commented Jul 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The auditor plugin adds workspace-scoped job APIs and Docker-backed task launching, then executes audits independently per probe with persistent state, retries, resume handling, signal-aware aggregation, and combined artifacts. Tests and dependency metadata are updated accordingly.

Changes

Auditor jobs

Layer / File(s) Summary
Job API contracts and route wiring
plugins/nemo-auditor/openapi/openapi.yaml, plugins/nemo-auditor/src/nemo_auditor/service.py, plugins/nemo-auditor/tests/test_service.py
Adds workspace-scoped auditor job operations, input/output schemas, platform job responses, pagination, filtering, and route registration.
Per-probe execution and persistence
plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
Runs probes independently with persistent directories, retries, resume handling, SIGTERM aggregation, progress updates, and combined report artifacts.
Auditor task launch configuration
plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py, packages/nmp_platform/config/local.yaml, services/core/jobs/tests/test_config.py
Adds the auditor task entrypoint and Docker-backed executor profile, with executor profile test updates.
Execution and routing validation
plugins/nemo-auditor/tests/test_audit_job.py
Tests probe config generation, aggregation, retries, resume behavior, signal handling, persistent options, schema fields, and spec propagation.

Dependency metadata refresh

Layer / File(s) Summary
Pinned dependencies and license advisories
third_party/requirements-main.txt, third_party/osv-licenses.json
Updates package pins, hashes, dependency provenance comments, and OSV metadata including MCP advisories.

Sequence Diagram(s)

sequenceDiagram
  participant Client
  participant AuditorPluginService
  participant AuditJob
  participant PersistentJobStorage
  participant Garak
  Client->>AuditorPluginService: Submit audit job
  AuditorPluginService->>AuditJob: Dispatch workspace-scoped job
  AuditJob->>PersistentJobStorage: Create per-probe state
  AuditJob->>Garak: Execute probes with retries
  Garak-->>PersistentJobStorage: Store probe reports
  AuditJob-->>Client: Return status and aggregated results
Loading

Suggested reviewers: mckornfield, matthewgrossman, soluwalana

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 22.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main changes: restoring auditor job submission via local config and adding pause/resume behavior.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch AALGO-224/auditor-pause-resume-pparkanzky

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py (1)

395-439: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use PlatformJobSpec as the return type. compile() already constructs that spec; -> object hides the contract. Import PlatformJobSpec at module scope and annotate the method directly.

🤖 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 `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py` around lines 395 - 439,
Update the Audit job classmethod compile to return PlatformJobSpec instead of
object. Move the PlatformJobSpec import from inside compile to module scope,
then annotate the method directly while preserving its existing PlatformJobSpec
construction.

Source: Coding guidelines

🤖 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 `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py`:
- Around line 491-501: Update the initialization flow around todo_dir and
_divide_and_write_confs so todo_dir is not treated as initialized until all
probe configuration files are successfully written. Build the split
configuration in a temporary directory, then atomically rename or publish it as
todo_dir after completion, preserving safe resume behavior when interrupted.
- Around line 261-310: Harden _aggregate_reports against path traversal from the
user-controlled report_dir_name and report_prefix: reject absolute values and
any path separators or parent-traversal components, then resolve the constructed
report paths and verify they remain contained within persistent before globbing,
creating, writing, or registering artifacts. Preserve the existing aggregation
behavior for valid names.
- Around line 545-585: The retry loop in the probe execution flow must isolate
generated outputs for each attempt. Update the setup around retry_n and
probe_env so every retry uses a fresh per-attempt XDG_DATA_HOME, or reliably
clears report_marker and partial JSONL outputs before running; ensure only
artifacts produced by the current successful attempt are moved into
complete_dir.
- Around line 471-489: Move SIGTERM registration out of AuditJob.run() in
plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py:471-489; expose the
partial-result save behavior as callback/state passed into the job, and register
the handler only from the main-thread caller in
plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py:31-39 so the
asyncio.to_thread path remains valid. Update
plugins/nemo-auditor/tests/test_audit_job.py:662-689 to trigger the real SIGTERM
dispatch path rather than invoking the former closure directly.

In `@plugins/nemo-auditor/src/nemo_auditor/service.py`:
- Around line 60-65: Expose pause and resume operations for auditor jobs across
all affected sites: update the AuditJob routes in
plugins/nemo-auditor/src/nemo_auditor/service.py at lines 60-65, add the
corresponding /pause and /resume paths and schemas to
plugins/nemo-auditor/openapi/openapi.yaml at lines 453-592, and extend the route
coverage in plugins/nemo-auditor/tests/test_service.py at lines 14-25 to verify
both endpoints.

---

Nitpick comments:
In `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py`:
- Around line 395-439: Update the Audit job classmethod compile to return
PlatformJobSpec instead of object. Move the PlatformJobSpec import from inside
compile to module scope, then annotate the method directly while preserving its
existing PlatformJobSpec construction.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 39b5e776-e874-4bf0-828d-77823299193d

📥 Commits

Reviewing files that changed from the base of the PR and between d18f684 and 138c20f.

📒 Files selected for processing (8)
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/openapi/openapi.yaml
  • plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • plugins/nemo-auditor/tests/test_audit_job.py
  • plugins/nemo-auditor/tests/test_service.py
  • services/core/jobs/tests/test_config.py

Comment thread plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
Comment thread plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py Outdated
Comment thread plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py Outdated
Comment thread plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
Comment thread plugins/nemo-auditor/src/nemo_auditor/service.py

@mmogallapalli mmogallapalli left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM

@parkanzky
parkanzky force-pushed the AALGO-224/auditor-pause-resume-pparkanzky branch from 419a066 to 4e0fd93 Compare July 17, 2026 19:41

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 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 `@plugins/nemo-auditor/openapi/openapi.yaml`:
- Around line 273-281: Constrain the pagination parameters in the OpenAPI
definitions for page_size and limit, including the additional occurrences, with
a finite maximum and valid positive lower bound. Apply the same bounds to the
corresponding job and log pagination schemas, and ensure response array limits
are capped consistently so requests cannot produce arbitrarily large result
sets.

In `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py`:
- Around line 6-8: Correct the audit command documentation to state the actual
default garak interpreter path used by the configuration at line 47,
/app/.garak_venv/bin/python, while retaining the NEMO_AUDITOR_GARAK_PYTHON
override description.
- Around line 574-600: Update the terminal probe handling around the completed
and retries-exhausted branches to unlink probe_yaml before moving probe_dir into
complete_dir or failed_dir, and remove the later unconditional unlink. Ensure
the marker is deleted before either terminal move or the fail-job raise, while
preserving recovery through running/config.yaml if a move fails.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 03930fbe-4e8f-45c4-b6b9-d98cc1acc313

📥 Commits

Reviewing files that changed from the base of the PR and between 138c20f and 4e0fd93.

📒 Files selected for processing (8)
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/openapi/openapi.yaml
  • plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • plugins/nemo-auditor/tests/test_audit_job.py
  • plugins/nemo-auditor/tests/test_service.py
  • services/core/jobs/tests/test_config.py
🚧 Files skipped from review as they are similar to previous changes (6)
  • services/core/jobs/tests/test_config.py
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • plugins/nemo-auditor/tests/test_service.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • plugins/nemo-auditor/tests/test_audit_job.py

Comment thread plugins/nemo-auditor/openapi/openapi.yaml
Comment thread plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
Comment thread plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
@parkanzky
parkanzky force-pushed the AALGO-224/auditor-pause-resume-pparkanzky branch 4 times, most recently from cd95ccd to e575461 Compare July 17, 2026 21:43
@parkanzky
parkanzky enabled auto-merge July 17, 2026 21:46

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 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 `@plugins/nemo-auditor/openapi/openapi.yaml`:
- Around line 453-484: Add POST OpenAPI operations for
/apis/auditor/v2/workspaces/{workspace}/jobs/audit/{name}/pause and /resume
alongside
cancel_job_apis_auditor_v2_workspaces__workspace__jobs_audit__name__cancel_post.
Include the same required workspace and name path parameters, return AuditJob on
success, and document validation errors consistently with the cancellation
operation.

In `@third_party/osv-licenses.json`:
- Around line 1603-1691: Update the mcp dependency pin from 1.26.0 to version
1.28.1 or later, then regenerate the dependency lockfile and third-party license
artifacts so they reflect the upgraded package and resolved transitive versions.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: a6502532-f08e-4ace-83cf-2e79deea1684

📥 Commits

Reviewing files that changed from the base of the PR and between 4e0fd93 and a30708e.

📒 Files selected for processing (10)
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/openapi/openapi.yaml
  • plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • plugins/nemo-auditor/tests/test_audit_job.py
  • plugins/nemo-auditor/tests/test_service.py
  • services/core/jobs/tests/test_config.py
  • third_party/osv-licenses.json
  • third_party/requirements-main.txt
🚧 Files skipped from review as they are similar to previous changes (5)
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/tests/test_service.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • plugins/nemo-auditor/tests/test_audit_job.py

Comment thread plugins/nemo-auditor/openapi/openapi.yaml
Comment thread third_party/osv-licenses.json
@parkanzky
parkanzky disabled auto-merge July 17, 2026 21:47
@parkanzky
parkanzky enabled auto-merge July 17, 2026 21:52

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py (1)

395-415: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use the concrete compile return type.

compile() always returns PlatformJobSpec; -> object removes useful type checking. Move that import to module scope and annotate the return unless a verified import cycle prevents it.

As per coding guidelines, prefer concrete type hints and import those types normally when possible.

🤖 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 `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py` around lines 395 - 415,
Update the classmethod compile to return PlatformJobSpec instead of object, and
move the PlatformJobSpec import from the method body to module scope using a
normal import. Preserve the existing behavior and retain the local import only
if a verified import cycle makes a module-level import impossible.

Source: Coding guidelines

🤖 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 `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py`:
- Around line 304-309: Update the hitlog aggregation flow around agg_hitlog so
it first collects or checks matching hitlog paths and only creates and writes
agg_hitlog when at least one hitlog exists. Preserve the existing concatenation
behavior for non-empty matches, and ensure _collect_report_artifacts() cannot
register an empty artifact.

---

Nitpick comments:
In `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py`:
- Around line 395-415: Update the classmethod compile to return PlatformJobSpec
instead of object, and move the PlatformJobSpec import from the method body to
module scope using a normal import. Preserve the existing behavior and retain
the local import only if a verified import cycle makes a module-level import
impossible.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 9a4d2a33-42b6-471b-b06e-711e978b4d0a

📥 Commits

Reviewing files that changed from the base of the PR and between a30708e and e575461.

📒 Files selected for processing (10)
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/openapi/openapi.yaml
  • plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • plugins/nemo-auditor/tests/test_audit_job.py
  • plugins/nemo-auditor/tests/test_service.py
  • services/core/jobs/tests/test_config.py
  • third_party/osv-licenses.json
  • third_party/requirements-main.txt
🚧 Files skipped from review as they are similar to previous changes (6)
  • plugins/nemo-auditor/tests/test_service.py
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • services/core/jobs/tests/test_config.py
  • third_party/requirements-main.txt
  • plugins/nemo-auditor/tests/test_audit_job.py

Comment on lines +304 to +309
hitlog_pattern = str(persistent / "complete" / "*" / "garak" / report_dir_name / f"{report_prefix}.hitlog.jsonl")
agg_hitlog = agg_dir / f"{report_prefix}.hitlog.jsonl"
with agg_hitlog.open("wb") as out_fd:
for hitlog_path in glob.glob(hitlog_pattern):
with open(hitlog_path, "rb") as in_fd:
shutil.copyfileobj(in_fd, out_fd)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Do not publish an empty hitlog artifact.

Opening agg_hitlog unconditionally creates an empty file when no probe produced hitlogs; _collect_report_artifacts() then registers it as a result.

Proposed fix
     hitlog_pattern = str(persistent / "complete" / "*" / "garak" / report_dir_name / f"{report_prefix}.hitlog.jsonl")
     agg_hitlog = agg_dir / f"{report_prefix}.hitlog.jsonl"
-    with agg_hitlog.open("wb") as out_fd:
-        for hitlog_path in glob.glob(hitlog_pattern):
+    hitlogs = glob.glob(hitlog_pattern)
+    if hitlogs:
+        with agg_hitlog.open("wb") as out_fd:
+            for hitlog_path in hitlogs:
+                with open(hitlog_path, "rb") as in_fd:
+                    shutil.copyfileobj(in_fd, out_fd)
+    elif agg_hitlog.exists():
+        agg_hitlog.unlink()
-            with open(hitlog_path, "rb") as in_fd:
-                shutil.copyfileobj(in_fd, out_fd)
📝 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.

Suggested change
hitlog_pattern = str(persistent / "complete" / "*" / "garak" / report_dir_name / f"{report_prefix}.hitlog.jsonl")
agg_hitlog = agg_dir / f"{report_prefix}.hitlog.jsonl"
with agg_hitlog.open("wb") as out_fd:
for hitlog_path in glob.glob(hitlog_pattern):
with open(hitlog_path, "rb") as in_fd:
shutil.copyfileobj(in_fd, out_fd)
hitlog_pattern = str(persistent / "complete" / "*" / "garak" / report_dir_name / f"{report_prefix}.hitlog.jsonl")
agg_hitlog = agg_dir / f"{report_prefix}.hitlog.jsonl"
hitlogs = glob.glob(hitlog_pattern)
if hitlogs:
with agg_hitlog.open("wb") as out_fd:
for hitlog_path in hitlogs:
with open(hitlog_path, "rb") as in_fd:
shutil.copyfileobj(in_fd, out_fd)
elif agg_hitlog.exists():
agg_hitlog.unlink()
🧰 Tools
🪛 ast-grep (0.44.1)

[warning] 307-307: File path is request-/variable-derived; validate and normalize to prevent path traversal.
Context: open(hitlog_path, "rb")
Note: [CWE-22] Improper Limitation of a Pathname to a Restricted Directory ('Path Traversal').

(open-filename-from-request)

🤖 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 `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py` around lines 304 - 309,
Update the hitlog aggregation flow around agg_hitlog so it first collects or
checks matching hitlog paths and only creates and writes agg_hitlog when at
least one hitlog exists. Preserve the existing concatenation behavior for
non-empty matches, and ensure _collect_report_artifacts() cannot register an
empty artifact.

Signed-off-by: Paul A. Parkanzky <parkanzky@users.noreply.github.com>
Signed-off-by: Paul A. Parkanzky <parkanzky@users.noreply.github.com>
Signed-off-by: Paul A. Parkanzky <parkanzky@users.noreply.github.com>
Signed-off-by: Paul A. Parkanzky <parkanzky@users.noreply.github.com>
@parkanzky
parkanzky force-pushed the AALGO-224/auditor-pause-resume-pparkanzky branch from b25e9aa to b168ae0 Compare July 18, 2026 00:26

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 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 `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py`:
- Around line 277-302: Update the aggregate report flow around agg_jsonl and
agg_html to write both command outputs to temporary files in the same directory,
keeping the existing final paths untouched while either subprocess runs. Only
after aggregate_reports and report_digest both succeed should the temporary
files replace the final JSONL and HTML paths atomically; ensure temporary files
are cleaned up on failure.
🪄 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: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: bef996e8-4e94-4b2f-8418-d21f7ed8ba2c

📥 Commits

Reviewing files that changed from the base of the PR and between e575461 and b168ae0.

📒 Files selected for processing (10)
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/openapi/openapi.yaml
  • plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • plugins/nemo-auditor/tests/test_audit_job.py
  • plugins/nemo-auditor/tests/test_service.py
  • services/core/jobs/tests/test_config.py
  • third_party/osv-licenses.json
  • third_party/requirements-main.txt
🚧 Files skipped from review as they are similar to previous changes (7)
  • plugins/nemo-auditor/tests/test_service.py
  • plugins/nemo-auditor/src/nemo_auditor/service.py
  • packages/nmp_platform/config/local.yaml
  • plugins/nemo-auditor/src/nemo_auditor/tasks/audit.py
  • services/core/jobs/tests/test_config.py
  • third_party/requirements-main.txt
  • plugins/nemo-auditor/tests/test_audit_job.py

Comment on lines +277 to +302
agg_dir = persistent / "garak" / report_dir_name
agg_dir.mkdir(parents=True, exist_ok=True)
agg_jsonl = agg_dir / f"{report_prefix}.report.jsonl"

result = subprocess.run(
[garak_python, "-m", "garak.analyze.aggregate_reports", "-o", str(agg_jsonl)] + jsonls,
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
raise GarakFailure(
f"garak aggregate_reports failed (rc={result.returncode}): {result.stderr[-_LOG_TAIL_BYTES:]}"
)

agg_html = agg_dir / f"{report_prefix}.report.html"
with agg_html.open("w") as html_fd:
result = subprocess.run(
[garak_python, "-m", "garak.analyze.report_digest", "-r", str(agg_jsonl)],
stdout=html_fd,
stderr=subprocess.PIPE,
text=True,
check=False,
)
if result.returncode != 0:
raise GarakFailure(f"garak report_digest failed (rc={result.returncode}): {result.stderr[-_LOG_TAIL_BYTES:]}")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Publish aggregate reports atomically.

Both commands overwrite final paths before success is known. A failed resume can corrupt a previously valid partial aggregate. Write temporary JSONL/HTML files and replace the finals only after both commands succeed.

Proposed fix
     agg_jsonl = agg_dir / f"{report_prefix}.report.jsonl"
+    agg_jsonl_tmp = agg_dir / f"{report_prefix}.report.jsonl.tmp"

     result = subprocess.run(
-        [garak_python, "-m", "garak.analyze.aggregate_reports", "-o", str(agg_jsonl)] + jsonls,
+        [garak_python, "-m", "garak.analyze.aggregate_reports", "-o", str(agg_jsonl_tmp)] + jsonls,
...
     if result.returncode != 0:
+        if agg_jsonl_tmp.exists():
+            agg_jsonl_tmp.unlink()
         raise GarakFailure(...)

     agg_html = agg_dir / f"{report_prefix}.report.html"
-    with agg_html.open("w") as html_fd:
+    agg_html_tmp = agg_dir / f"{report_prefix}.report.html.tmp"
+    with agg_html_tmp.open("w") as html_fd:
         result = subprocess.run(
-            [garak_python, "-m", "garak.analyze.report_digest", "-r", str(agg_jsonl)],
+            [garak_python, "-m", "garak.analyze.report_digest", "-r", str(agg_jsonl_tmp)],
...
     if result.returncode != 0:
+        agg_jsonl_tmp.unlink()
+        agg_html_tmp.unlink()
         raise GarakFailure(...)

+    os.replace(agg_jsonl_tmp, agg_jsonl)
+    os.replace(agg_html_tmp, agg_html)
📝 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.

Suggested change
agg_dir = persistent / "garak" / report_dir_name
agg_dir.mkdir(parents=True, exist_ok=True)
agg_jsonl = agg_dir / f"{report_prefix}.report.jsonl"
result = subprocess.run(
[garak_python, "-m", "garak.analyze.aggregate_reports", "-o", str(agg_jsonl)] + jsonls,
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
raise GarakFailure(
f"garak aggregate_reports failed (rc={result.returncode}): {result.stderr[-_LOG_TAIL_BYTES:]}"
)
agg_html = agg_dir / f"{report_prefix}.report.html"
with agg_html.open("w") as html_fd:
result = subprocess.run(
[garak_python, "-m", "garak.analyze.report_digest", "-r", str(agg_jsonl)],
stdout=html_fd,
stderr=subprocess.PIPE,
text=True,
check=False,
)
if result.returncode != 0:
raise GarakFailure(f"garak report_digest failed (rc={result.returncode}): {result.stderr[-_LOG_TAIL_BYTES:]}")
agg_dir = persistent / "garak" / report_dir_name
agg_dir.mkdir(parents=True, exist_ok=True)
agg_jsonl = agg_dir / f"{report_prefix}.report.jsonl"
agg_jsonl_tmp = agg_dir / f"{report_prefix}.report.jsonl.tmp"
result = subprocess.run(
[garak_python, "-m", "garak.analyze.aggregate_reports", "-o", str(agg_jsonl_tmp)] + jsonls,
capture_output=True,
text=True,
check=False,
)
if result.returncode != 0:
if agg_jsonl_tmp.exists():
agg_jsonl_tmp.unlink()
raise GarakFailure(
f"garak aggregate_reports failed (rc={result.returncode}): {result.stderr[-_LOG_TAIL_BYTES:]}"
)
agg_html = agg_dir / f"{report_prefix}.report.html"
agg_html_tmp = agg_dir / f"{report_prefix}.report.html.tmp"
with agg_html_tmp.open("w") as html_fd:
result = subprocess.run(
[garak_python, "-m", "garak.analyze.report_digest", "-r", str(agg_jsonl_tmp)],
stdout=html_fd,
stderr=subprocess.PIPE,
text=True,
check=False,
)
if result.returncode != 0:
if agg_jsonl_tmp.exists():
agg_jsonl_tmp.unlink()
if agg_html_tmp.exists():
agg_html_tmp.unlink()
raise GarakFailure(f"garak report_digest failed (rc={result.returncode}): {result.stderr[-_LOG_TAIL_BYTES:]}")
os.replace(agg_jsonl_tmp, agg_jsonl)
os.replace(agg_html_tmp, agg_html)
🧰 Tools
🪛 ast-grep (0.44.1)

[error] 280-285: Use of unsanitized data to create processes
Context: subprocess.run(
[garak_python, "-m", "garak.analyze.aggregate_reports", "-o", str(agg_jsonl)] + jsonls,
capture_output=True,
text=True,
check=False,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(os-system-unsanitized-data)


[error] 280-285: Command coming from incoming request
Context: subprocess.run(
[garak_python, "-m", "garak.analyze.aggregate_reports", "-o", str(agg_jsonl)] + jsonls,
capture_output=True,
text=True,
check=False,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)


[error] 293-299: Command coming from incoming request
Context: subprocess.run(
[garak_python, "-m", "garak.analyze.report_digest", "-r", str(agg_jsonl)],
stdout=html_fd,
stderr=subprocess.PIPE,
text=True,
check=False,
)
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(subprocess-from-request)

🤖 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 `@plugins/nemo-auditor/src/nemo_auditor/jobs/audit.py` around lines 277 - 302,
Update the aggregate report flow around agg_jsonl and agg_html to write both
command outputs to temporary files in the same directory, keeping the existing
final paths untouched while either subprocess runs. Only after aggregate_reports
and report_digest both succeed should the temporary files replace the final
JSONL and HTML paths atomically; ensure temporary files are cleaned up on
failure.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants