Skip to content

fix: make MCP Slurm wait poll remote job state#1900

Merged
ChenhanYu merged 2 commits into
mainfrom
chenhany/fix-mcp-slurm-wait
Jul 8, 2026
Merged

fix: make MCP Slurm wait poll remote job state#1900
ChenhanYu merged 2 commits into
mainfrom
chenhany/fix-mcp-slurm-wait

Conversation

@ChenhanYu

@ChenhanYu ChenhanYu commented Jul 4, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • run managed Model-Optimizer checkouts via uv run --with-editable so the modelopt-launcher console script is available
  • persist Slurm submission metadata beside the local experiment and make job_status / wait_for_experiment prefer remote sacct / squeue state over local _DONE
  • add the missing common/smoke/hostname.sh used by examples/smoke/hostname.yaml

Why

Detached Slurm submission creates the local _DONE marker when the submit process exits, not when the remote Slurm job completes. This made MCP wait_for_experiment return done while Slurm jobs were still running. Reproduced on both MFA and non-MFA clusters.

Validation

  • uv run --project tools/mcp ruff check tools/mcp/modelopt_mcp/bridge.py tools/mcp/tests/test_bridge.py
  • uv run --project tools/mcp pytest tools/mcp/tests/test_bridge.py
  • uv run --project tools/launcher pytest tools/launcher/tests/test_core_extended.py tools/launcher/tests/test_slurm_config.py
  • uv run --with-editable tools/launcher modelopt-launcher --yaml tools/launcher/examples/smoke/hostname.yaml --dryrun --yes

Manual smoke evidence

  • ptyche smoke completed: experiment cicd_1783037181, Slurm job 2320415, exit 0:0
  • computelab reproduced the wait bug pre-fix: MCP reported done immediately while Slurm job 2945347 was still running; job later completed with exit 0:0

Summary by CodeRabbit

  • New Features

    • Added an automated hostname smoke test to validate launcher environment readiness.
    • Enhanced Slurm-backed job status reporting by persisting scheduler submission metadata and exposing Slurm state in responses.
  • Bug Fixes

    • Job status now prioritizes authoritative remote scheduler state (with graceful fallback to local completion/failure markers when unavailable).
    • Improved terminal-state handling so experiment polling won’t stop early when a local done marker exists but the scheduler is still running.
  • Tests

    • Updated and expanded Slurm status and waiting behavior tests, including validation of persisted scheduler metadata.

Signed-off-by: Chenhan Yu <chenhany@nvidia.com>
@ChenhanYu
ChenhanYu requested a review from a team as a code owner July 4, 2026 03:05
@coderabbitai

coderabbitai Bot commented Jul 4, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: f3ba4602-5cc7-4c3f-9460-a1a455f8eabd

📥 Commits

Reviewing files that changed from the base of the PR and between 9f0c4ec and 1465d79.

📒 Files selected for processing (2)
  • tools/mcp/modelopt_mcp/bridge.py
  • tools/mcp/tests/test_bridge.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tools/mcp/modelopt_mcp/bridge.py

📝 Walkthrough

Walkthrough

Adds Slurm submit metadata persistence and SSH-based status resolution in bridge.py with updated tests, switches the managed-source launcher to --with-editable, and adds a new hostname smoke-test script.

Changes

Slurm Sidecar Status Tracking

Layer / File(s) Summary
Slurm state constants and metadata helpers
tools/mcp/modelopt_mcp/bridge.py
Adds json import, Slurm terminal/failure/running state constants, a sidecar filename constant, and helper functions to write/load metadata, build SSH argv, parse sacct -P output, query status via SSH with squeue fallback, and map Slurm state to MCP status.
Persist metadata on submission
tools/mcp/modelopt_mcp/bridge.py
Slurm submission flow now writes experiment id, Slurm job id, and SSH connection details to the experiment directory sidecar file.
job_status_impl uses sidecar for authoritative status
tools/mcp/modelopt_mcp/bridge.py
job_status_impl queries Slurm via the sidecar for authoritative status instead of relying solely on local _DONE/status_*.out markers.
Tests for sidecar persistence and polling
tools/mcp/tests/test_bridge.py
Adds json import, assertions on persisted sidecar fields, new tests where Slurm RUNNING/COMPLETED states override or confirm status, and a wait_for_experiment_impl test verifying continued polling until Slurm reaches terminal state.

Managed-source Launcher Argv Change

Layer / File(s) Summary
uv invocation switched to --with-editable
tools/mcp/modelopt_mcp/bridge.py, tools/mcp/tests/test_bridge.py
_launcher_argv now builds the uv command with --with-editable instead of --reinstall-package/--project, with matching test expectations and docstring update.

Hostname Smoke Test Script

Layer / File(s) Summary
New hostname smoke-test script
tools/launcher/common/smoke/hostname.sh
New Bash script with SPDX header, strict error handling, and start/done markers around a hostname command.

Estimated code review effort: 4 (Complex) | ~40 minutes

Suggested reviewers: kevalmorabia97

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main MCP Slurm behavior change, even though it omits secondary updates.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.
Security Anti-Patterns ✅ Passed Changed Python files only adjust Slurm status logic/tests; no prohibited torch/numpy/transformers eval/exec, no new # nosec, and no dependency changes.
✨ 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 chenhany/fix-mcp-slurm-wait

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.

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 1

🧹 Nitpick comments (1)
tools/mcp/modelopt_mcp/bridge.py (1)

1705-1780: 🔒 Security & Privacy | 🔵 Trivial | 💤 Low value

Consider validating/quoting slurm_job_id before it reaches the remote shell.

job_id is interpolated into the sacct/squeue command strings, which ssh executes through the remote login shell. Today slurm_job_id is parsed from trusted launcher output (numeric), so this is not currently exploitable, but a non-numeric value would be interpreted by the remote shell. A cheap defense-in-depth is to assert the id matches an expected pattern (e.g. ^\d+(_\d+)?(\.\w+)?$) before building the command.

🤖 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 `@tools/mcp/modelopt_mcp/bridge.py` around lines 1705 - 1780, The
_query_slurm_status helper builds remote sacct/squeue command strings using
slurm_job_id, so add a defensive validation step before constructing those
commands. In _query_slurm_status, ensure job_id matches the expected Slurm
job-id format (for example numeric with optional array/step suffixes) and return
a failed diagnostic if it does not, then only pass the validated id into
_ssh_argv_for_slurm and the sacct/squeue command strings.
🤖 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 `@tools/mcp/modelopt_mcp/bridge.py`:
- Around line 1826-1841: In the Slurm status handling branch of the experiment
status lookup, do not always fall back to running when _query_slurm_status
returns a non-ok result. Update the logic around _load_slurm_status_metadata and
_query_slurm_status to treat slurm_status_not_found as a special case and
consult the local completion indicators (_DONE and status_*.out) before
defaulting to running. Keep the returned slurm_status payload intact, but ensure
the overall status can become completed when the local markers show the
experiment has finished so wait_for_experiment_impl stops polling correctly.

---

Nitpick comments:
In `@tools/mcp/modelopt_mcp/bridge.py`:
- Around line 1705-1780: The _query_slurm_status helper builds remote
sacct/squeue command strings using slurm_job_id, so add a defensive validation
step before constructing those commands. In _query_slurm_status, ensure job_id
matches the expected Slurm job-id format (for example numeric with optional
array/step suffixes) and return a failed diagnostic if it does not, then only
pass the validated id into _ssh_argv_for_slurm and the sacct/squeue command
strings.
🪄 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: 64116f92-4ae7-4046-b0ef-672474565ecb

📥 Commits

Reviewing files that changed from the base of the PR and between 75b5803 and 9f0c4ec.

📒 Files selected for processing (3)
  • tools/launcher/common/smoke/hostname.sh
  • tools/mcp/modelopt_mcp/bridge.py
  • tools/mcp/tests/test_bridge.py

Comment thread tools/mcp/modelopt_mcp/bridge.py
@codecov

codecov Bot commented Jul 4, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 61.23%. Comparing base (75b5803) to head (1465d79).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #1900      +/-   ##
==========================================
+ Coverage   61.21%   61.23%   +0.01%     
==========================================
  Files         515      515              
  Lines       57245    57245              
==========================================
+ Hits        35043    35054      +11     
+ Misses      22202    22191      -11     
Flag Coverage Δ
regression 14.82% <ø> (+0.06%) ⬆️
unit 54.91% <ø> (-0.02%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@ChenhanYu

Copy link
Copy Markdown
Collaborator Author

/claude review

@claude claude Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Claude Review Summary

Reviewed all 3 changed files (tools/mcp/modelopt_mcp/bridge.py, tools/mcp/tests/test_bridge.py, tools/launcher/common/smoke/hostname.sh). This is a focused, well-tested change to the MCP Slurm sidecar status flow. The metadata round-trip, SSH argv construction, sacct/squeue parsing, and state mapping are all sound, and test coverage for the RUNNING/COMPLETED/wait paths is good.

Findings by severity: CRITICAL: 1, IMPORTANT: 0, SUGGESTION: 0

Most impactful finding

[CRITICAL Algorithm] job_status_impl (lines 1826-1841): when the Slurm query returns ok: False, overall is forced to running, discarding the local _DONE marker. This is correct for transient failures (slurm_status_query_failed — SSH down / MFA expired), but it is a regression for slurm_status_not_found: once a completed job's record is purged from sacct (retention is often only hours/days) and has left the queue, the experiment is reported running indefinitely, and wait_for_experiment_impl spins to its full timeout for a job that already finished. Before this PR the _DONE marker returned done immediately. Suggested fix (in the inline comment): special-case slurm_status_not_found to fall back to local completion markers, and add a test for that path.

Assessment

Low-to-moderate risk. The change is scoped to the MCP launcher tooling (no core modelopt/ optimization paths), and the happy paths are well covered. The one CRITICAL is a fallback-path correctness gap that will surface for status queries on aged jobs and for any cluster with short sacct retention; worth addressing before merge. (Note: CodeRabbit's pre-merge gate also flags the new nosec comments on the SSH subprocess calls — that's tracked separately and out of scope for this review.)

Comment on lines +1826 to +1841
slurm_meta = _load_slurm_status_metadata(exp_dir)
if slurm_meta is not None:
slurm_status = _query_slurm_status(slurm_meta)
if slurm_status.get("ok"):
overall = slurm_status.get("status", "unknown")
else:
overall = "running"
return {
"ok": True,
"experiment_id": experiment_id,
"experiment_dir": str(exp_dir),
"status": overall,
"task_statuses": task_statuses,
"has_done_marker": done_marker.exists(),
"slurm_status": slurm_status,
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

[CRITICAL Algorithm] When the sidecar is present but the Slurm query returns ok: False, overall is unconditionally set to "running", discarding the local _DONE/status_*.out signal. This is fine for the transient slurm_status_query_failed case (SSH down, MFA expired), but it is a real bug for slurm_status_not_found.

Why it matters: sacct retention (MinJobAge/purge) is frequently only hours to a few days. Once a completed job's record is purged from the accounting DB and it has left the queue, _query_slurm_status returns slurm_status_not_found. job_status_impl then reports running forever, and wait_for_experiment_impl (which only terminates on done/failed) spins to its full timeout_sec for a job that finished long ago. This is a regression: before this PR the _DONE marker would have returned done immediately.

Suggested fix: treat slurm_status_not_found specially — fall back to the local completion markers instead of forcing running:

slurm_meta = _load_slurm_status_metadata(exp_dir)
if slurm_meta is not None:
    slurm_status = _query_slurm_status(slurm_meta)
    if slurm_status.get("ok"):
        overall = slurm_status.get("status", "unknown")
    elif slurm_status.get("reason") == "slurm_status_not_found":
        # Job aged out of sacct and is not in the queue: trust local markers.
        if done_marker.exists():
            overall = "failed" if any_failed else "done"
        else:
            overall = "running"
    else:
        # Transient query failure (SSH/MFA): keep polling.
        overall = "running"
    return {
        "ok": True,
        ...
        "slurm_status": slurm_status,
    }

Recommend adding a test for the slurm_status_not_found + _DONE path, which is currently uncovered.

Signed-off-by: Chenhan Yu <chenhany@nvidia.com>
@ChenhanYu

Copy link
Copy Markdown
Collaborator Author

/ok to test 1465d79

@kevalmorabia97 kevalmorabia97 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

LGTM. Please address claude feedback before merging

@ChenhanYu
ChenhanYu merged commit 0185726 into main Jul 8, 2026
45 checks passed
@ChenhanYu
ChenhanYu deleted the chenhany/fix-mcp-slurm-wait branch July 8, 2026 16:19
@github-actions

github-actions Bot commented Jul 8, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-08 16:19 UTC

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants