Skip to content

Quote the model args miles inlines into the launch command - #1911

Merged
fzyzcjy merged 18 commits into
mainfrom
tom/refactor-miles/op8-18
Aug 9, 2026
Merged

Quote the model args miles inlines into the launch command#1911
fzyzcjy merged 18 commits into
mainfrom
tom/refactor-miles/op8-18

Conversation

@fzyzcjy

@fzyzcjy fzyzcjy commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

ci-megatron-pr: tom/refactor-miles-repo-megatron/op8-13

Part of #1837

@gemini-code-assist

Copy link
Copy Markdown
Contributor

Caution

The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased.

@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-11 branch from 4583664 to cf656e3 Compare July 29, 2026 01:02
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from ad36ea8 to 0cf03d3 Compare July 29, 2026 01:02
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-11 branch from cf656e3 to 740e031 Compare July 29, 2026 01:45
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch 2 times, most recently from 72db26b to c99f071 Compare July 29, 2026 02:06
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-11 branch from 740e031 to cbc0762 Compare July 29, 2026 02:06
@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. Triaged the stage-a-cpu (0) failure on this PR after rebasing the M8 chain onto latest main, traced it to a python-version-dependent Path.exists() behaviour on the CPU runner, and pushed a fix into op8-4. Please push back if any conclusion is off.

Failure

stage-a-cpu (0) / run-cpu failed on 72db26b93:

ERROR tests/fast/launch_scripts/test_py_launch_scripts.py::TestEveryLauncherEntrypoint::test_commands_match_snapshot[scripts/amd/run_deepseek_v4.py::prepare_single]
PermissionError: [Errno 13] Permission denied: '/root/models/DeepSeek-V4-Flash-FP8-bf16/model.safetensors.index.json'
!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!!

Classification: ours, pre-existing, previously masked

Not a flake and not infra — but also not introduced by the rebase.

  • The launcher snapshot test drives every scripts/run_*.py entrypoint. Several of those entrypoints probe whether an artifact already exists before emitting the command that would build it, e.g. command_utils.fp8_cast_bf16 checks Path(path_dst) / "model.safetensors.index.json". With the default --model-dir, that path is under /root.
  • The CPU lane runs on ubuntu-latest as a non-root user with python-version: '3.11'. On python 3.11 Path.exists() re-raises EACCES (its _ignore_error allow-list covers only ENOENT/ENOTDIR/EBADF/ELOOP); python 3.12 changed it to swallow OSError and return False. /root is mode 0700, so every such probe raises.
  • The GPU lanes never hit it: they run in a container as root and bind-mount /data/miles_ci/models onto /root/models.
  • It was masked because stage-a-cpu was SKIPPED on the previous head 0cf03d38f, so this suite had not actually executed on the CPU runner before.

Fix

Pushed into op8-4 (a61bf669b); the chain head is now c99f071bd.

The harness already freezes env vars, the run id, the PID and generated pseudo-files so a recording cannot depend on the machine. It now freezes the filesystem the same way: while an entrypoint records, only the pytest sandbox is visible and every other path reports absence. That removes the whole class in one place (~20 probe sites across 16 launchers) instead of per-script overrides, and it also fixes a latent non-determinism — a machine that happens to carry /root/models/<ckpt> would otherwise record a shorter command list than the committed snapshots.

Two regression tests cover it, including the "unreadable parent must report absence instead of raising" case.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from c99f071 to d8234df Compare July 29, 2026 02:26
@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. The CPU lanes are green after the harness fix; this comment records that, and triages the one GPU failure (I believe it is not caused by this PR — evidence below). Please push back if any conclusion is off.

stage-a-cpu is green on d8234df3e

All four shards plus stage-b-cpu pass:

lane result
stage-a-cpu (0) / run-cpu SUCCESS
stage-a-cpu (1) / run-cpu SUCCESS
stage-a-cpu (2) / run-cpu SUCCESS
stage-a-cpu (3) / run-cpu SUCCESS
stage-b-cpu / run-cpu SUCCESS

One correction to my previous comment: the first version of that fix hid the checkout as well, which broke load_model_args (AssertionError: no model args script at .../scripts/models/deepseek-v4-flash.py, job). The landed version keeps the checkout visible and hides only paths outside it and the sandbox.

stage-c-8-gpu-h100 (0) failure — not from this PR

Job, runner novita-host4, image radixark/miles:dev. tests/e2e/megatron/test_qwen3_5_35b_a3b_lora_ci.py dies inside a real training step:

RuntimeError: /TransformerEngine/transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu:934
  cuDNN Error: detail::set_attribute(reshape_operation.get_raw_desc(), CUDNN_ATTR_OPERATION_RESHAPE_MODE,
  CUDNN_TYPE_RESHAPE_MODE, 1, &cudnn_reshape_mode) failed ... code: CUDNN_STATUS_BAD_PARAM

Two independent reasons this is not the PR's change:

  1. The effective argv is unchanged. This PR's entire effect on that launch command is one pair of quotes, which the snapshot pins:

    -  --moe-layer-freq [1,1,...,1]
    +  --moe-layer-freq '[1,1,...,1]'

    Unquoted, [1,1,...,1] is a bracket glob matching one character from {1, ,}; the launch cwd (/__w/miles/miles) contains no file named 1 or ,, so bash passed it through literally. Megatron receives the identical string either way — the quotes only remove the latent glob hazard. Nothing in that diff can reach a cuDNN fused-attention descriptor.

  2. Every sibling PR passes the same lane on the same image. The whole M8 chain was rebased onto the same main this morning, and shard 0 of this suite is SUCCESS on #1901, #1902, #1903, #1904, #1905, #1906, #1907, #1908, #1909 and #1910, as well as on unrelated PRs #1912, #1913, #1916, #1919 and #1921. Only this PR's shard 0 is red, and it differs from Replace the model config shell scripts with python #1910 by nothing but those quotes.

Classification: flake / host-specific cuDNN failure, most likely on novita-host4. Next step: let the remaining nine GPU jobs of this round finish (so live siblings are not cancelled), then rerun the failed job and confirm it turns green. I have not touched the code for it.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. Correction to my previous comment: one of the two arguments I gave for the h100 failure was unsound, and I am retracting it. The conclusion is unchanged but now rests on different evidence. Please push back if any of this is off.

Retraction

I claimed the sibling PRs "pass the same lane on the same image". That evidence is vacuous and I withdraw it.

Only this PR carries the run-ci-image label; the other 16 PRs of the chain carry no labels. stage-c-8-gpu-h100 has zero tests registered with labels=[], so without a label run_suite.py reports

No tests found for hw=CUDA, suite=stage-c-8-gpu-h100, cadence=regular
No tests to run. Exiting with success.

— which I reproduced myself by running that exact command. So those SUCCESS marks mean "ran nothing", not "passed". The failing test itself is registered labels=["model-scripts"], so it only runs under a matching label or the --match-all-labels that run-ci-image forces.

Consequence I should have stated: this lane has never had real coverage anywhere on the M8 chain, so the failure cannot be pinned on the tip commit by elimination — any op in the chain would have been equally untested here.

What still stands

  1. The args reaching train.py are byte-identical to the pre-M8 .sh era. That is precisely what op8-17 froze and what op8-11 had to reproduce unchanged; git diff over tests/snapshots/model_args/ from op8-17 to this PR's head is empty, and tests/snapshots/model_args/qwen3.5-35B-A3B_lora.txt — the config this very test uses — is unchanged.

  2. This PR's own delta to that command is one pair of quotes, snapshot-pinned:

    -  --moe-layer-freq [1,1,...,1]
    +  --moe-layer-freq '[1,1,...,1]'

    Unquoted that is a bracket glob matching one character from {1, ,}; the launch cwd contains no file named 1 or ,, so bash passed it through literally. Megatron receives the same string either way.

  3. The other shard of the same suite, on this same PR and label, passed. stage-c-8-gpu-h100 (1) ran real tests on novita-host2 and is SUCCESS; the failure is on novita-host4.

Taken together: the launch command is provably unchanged from the .sh baseline, so a cuDNN CUDNN_STATUS_BAD_PARAM inside TransformerEngine's fused-attention descriptor is not something this chain can cause. Still classified as flake / host-specific, but on (1)–(3) rather than on the sibling-PR comparison.

Next step unchanged

Six 2gpu/4gpu h200 lanes have been queued ~4.5h (that fleet is 5/5 busy, and #1683, #1820, #1927 have runs queued behind us). GitHub will not re-run a job while the run is still in progress, so once the round completes I will rerun the failed job. If it fails a second time on the same test I will stop calling it a flake and diagnose it properly against a nightly main data point.

I have deliberately not touched the run-ci-image label: dropping it would turn this lane green by running nothing, which is the same false-green trap as above.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. Both GPU failures on this PR reproduce identically on plain main's nightly from before this chain was rebased. This supersedes my earlier "flake" wording with hard cross-branch evidence. Please push back if any conclusion is off.

Both failures are pre-existing on main

The nightly run on main from 2026-07-28 — i.e. before this chain was rebased onto it, and the only place these lanes get real coverage — fails the same two tests in the same way:

test main nightly 2026-07-28 this PR
tests/e2e/megatron/test_qwen3_5_35b_a3b_lora_ci.py FAIL exit=1 (stage-c-8-gpu-h100 (0)) FAIL (stage-c-8-gpu-h100 (0))
tests/e2e/short/test_qwen3_0.6B_fsdp_colocated_2xGPU.py timeout after 1800s (stage-c-2-gpu-h200) timeout after 1800s (stage-c-2-gpu-h200 (0))

The h100 fingerprint is byte-identical on both sides — the same cuDNN assertion at the same source line:

RuntimeError: /TransformerEngine/transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu:934
  cuDNN Error: detail::set_attribute(reshape_operation.get_raw_desc(), CUDNN_ATTR_OPERATION_RESHAPE_MODE,
  CUDNN_TYPE_RESHAPE_MODE, 1, &cudnn_reshape_mode) failed ... code: CUDNN_STATUS_BAD_PARAM

The fsdp one is not an assertion at all — it is the 1800s cap on a test declared est_time=300, hit on both sides.

Note these are not random flakes: they reproduce on main. I am therefore not rerunning them — a rerun cannot be greener than main, and it would occupy an 8×H100 runner for an hour to re-learn the same fact. Six of the last eight nightlies on main are red.

Positive evidence for this chain

The one test in these lanes that directly exercises what M8 changed — tests/e2e/megatron/model_scripts/test_glm5_744b_a40b_4layer_r3.py, which runs a model script that op8-11 converted from .sh to .pypassed on this PR, as did tests/e2e/fsdp/test_qwen3_4B_fsdp_true_on_policy.py and both stage-c-8-gpu-h200 shards.

Additionally tests/snapshots/model_args/ has a zero-byte diff from op8-17 (where the .sh-era args were frozen) through this PR's head, so the arguments reaching train.py are provably identical to the pre-M8 baseline.

Current state of this PR

stage-a-cpu ×4, stage-b-cpu, stage-b-2-gpu-h200, stage-c-8-gpu-h200 ×2, stage-c-8-gpu-h100 (1), stage-c-2-gpu-h200 (1), stage-c-4-gpu-h200 (1) all pass. Two stage-c-4-gpu-h200 shards are still queued behind a saturated fleet; the two failures above are the pre-existing main ones.

Per the user's standing instruction I am not filing an issue for the two main failures — flagging them here and to the user instead.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from 98b4499 to d8234df Compare July 29, 2026 11:06
@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. A third GPU lane failed, and unlike the other two this one is caused by this chain: op8-9's inline base64: payload needs the matching Megatron-LM change, which CI was not told to check out. Fixed by declaring the dependency in the PR description. Please push back if any conclusion is off.

The failure

stage-c-4-gpu-h200 (2)tests/e2e/megatron/model_scripts/test_deepseek_v4_flash_4layer_ci.py, exit 1 after 619s:

OSError: [Errno 36] File name too long: 'base64:Y29uZmlnczoKICBiZjE2OgogICAgdHJhbnNmb3JtZXJfZW5naW5lX2NvbmZpZ190eXBlOiAiVEVRdWFudGl6YXRpb25QYXJhbXMi...'

The payload decodes to configs:\n bf16:\n transformer_engine_config_type: "TEQuantizationParams"... — it is the --te-precision-config-file value.

Root cause: an undeclared cross-repo dependency

op8-9 replaced the temp-file arguments with inline base64: payloads, and --te-precision-config-file is consumed by Megatron-LM, not by miles. That side is a separate, already-reviewed op living in the Megatron fork: tom/refactor-miles-repo-megatron/op8-13 — one commit, Support inline base64 payloads for te-precision-config-file, adding megatron/core/file_arg_utils.py plus resolve_file_arg() wiring in megatron/core/quantization/utils.py and two unit tests.

This PR's description declared no dependency pins, so _run-ci.yml fell back to the default and the job log shows:

Resolved: megatron=miles-main -> fetch=miles-main, sglang=sglang-miles -> fetch=sglang-miles

miles-main has no resolve_file_arg, so Megatron took the base64:... string as a path and open() failed with ENAMETOOLONG. Real defect, correctly caught by CI — and it stayed invisible until now because this lane only runs under run-ci-image, which only this tip PR carries.

Fix

Added to the PR description, per the mechanism in docs/ci/:

ci-megatron-pr: tom/refactor-miles-repo-megatron/op8-13

Since the workflow reads the description from the event payload, a re-run would have replayed the stale body; I pushed an empty commit and immediately reverted it so a fresh synchronize picks the new description up. The head SHA is unchanged at d8234df3e and the tree is byte-identical — run 30446270231 is the fresh round.

Two things for the human to decide

  1. Merge order. The Megatron commit must land in miles-main before (or together with) the miles side, otherwise main breaks the moment op8-9 merges. The pin makes CI correct; it does not make the merge safe on its own.
  2. The Megatron branch is one commit behind miles-main (missing 9fc14d826 feat(optimizer): NVMe streaming of DistributedOptimizer state (#63)). Harmless for these tests, but worth rebasing before merge.

The other two failures are unchanged and still reproduce on main's nightly — see my previous comment.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. The Megatron pin fixed the real failure — confirmed end to end. Summary of where this PR's CI now stands. Please push back if any conclusion is off.

The base64: failure is fixed

stage-c-4-gpu-h200 (2) in run 30446270231:

Resolved: megatron=tom/refactor-miles-repo-megatron/op8-13 -> fetch=tom/refactor-miles-repo-megatron/op8-13
[1/8] tests/e2e/megatron/model_scripts/test_deepseek_v4_flash_4layer_ci.py  PASS  elapsed=1264s
Test Summary: 8/8 passed

The test that previously died on OSError: [Errno 36] File name too long: 'base64:...' now passes, and the whole shard is 8/8. Diagnosis and fix confirmed: op8-9's inline payloads need the Megatron-side resolve_file_arg, and CI simply had not been told to check that branch out.

Where the PR stands

lane result
stage-a-cpu ×4, stage-b-cpu pass
stage-b-2-gpu-h200 pass
stage-c-2-gpu-h200 (1) pass
stage-c-4-gpu-h200 (0), (2) pass
stage-c-8-gpu-h100 (1) pass
stage-c-8-gpu-h200 (0), (1) pass
stage-c-4-gpu-h200 (1) still running
stage-c-8-gpu-h100 (0) fails — pre-existing on main
stage-c-2-gpu-h200 (0) fails — pre-existing on main

The two remaining failures are the ones that reproduce identically on main's 2026-07-28 nightly (same cuDNN assertion line; same 1800s timeout on a test declared est_time=300). One extra data point since my last comment: the cuDNN failure moved from novita-host4 to novita-host2 between rounds while shard 1 passed on novita-host4, so it tracks the test rather than the host — consistent with the nightly evidence and against my earlier host-specific guess.

Still needs a human decision

Merge order. tom/refactor-miles-repo-megatron/op8-13 must land in miles-main before or with the miles side, or main breaks the moment op8-9 merges. The ci-megatron-pr: line makes CI correct; it does not make the merge safe. That branch is also one commit behind miles-main (9fc14d826), worth rebasing first.

Per the user's standing instruction, no issues filed for the two main failures — raised here and with the user instead.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. Final state of run 30446270231: it has settled, and stage-c-4-gpu-h200 (1) — the last one outstanding when I posted above — passed.

17 pass, 2 fail, 0 outstanding. Every lane is green except the two that reproduce identically on main's 2026-07-28 nightly:

  • stage-c-8-gpu-h100 (0)test_qwen3_5_35b_a3b_lora_ci.py, cuDNN CUDNN_STATUS_BAD_PARAM at fused_attn_f16_arbitrary_seqlen.cu:934
  • stage-c-2-gpu-h200 (0)test_qwen3_0.6B_fsdp_colocated_2xGPU.py, timeout at the 1800s cap on a test declared est_time=300

Both were failing on main before this chain was rebased onto it, so this PR is as green as main currently permits. Everything attributable to this chain is passing, including the base64: payload path once the Megatron dependency was declared.

The merge-order point from my previous comment still stands and needs a human: tom/refactor-miles-repo-megatron/op8-13 has to land in miles-main before or with op8-9.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from e6decb6 to d8234df Compare July 29, 2026 16:53
@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. I stopped accepting "pre-existing on main" as the end of the story and bisected the two remaining failures against the CI image. They correlate exactly with one image jump, so I pinned this PR to the last image that demonstrably passed, as an experiment. Please push back if any conclusion is off.

The two failures arrived together, with one image rebuild

docker-build pushes a rolling dev plus a timestamped dev-<UTC>. Walking the build-and-push outcomes:

build head pushed?
2026-07-24T02:06 7aa6e9117 successdev-202607240207
2026-07-24T07:01 66b39f07a (Bump TransformerEngine to 2.17.0) failure
2026-07-24T19:34 667ffbc86 (install TE from PyPI) failure
2026-07-25T01:42 … 2026-07-26T00:53 several failure / cancelled
2026-07-26T05:08 055548a60 success
2026-07-26T06:02 1e1afc00b (install TE from rolling wheels) success
2026-07-26T12:31 52403d0e3 success

So dev sat at the 07-24T02:07 build for two days while every rebuild failed, then jumped straight past TransformerEngine 2.17 and both TE-install rewrites.

Nightly results across that jump:

nightly image stage-c-8-gpu-h100 (0) stage-c-2-gpu-h200
2026-07-24 07-24T02:07 build success both shards success
2026-07-26 07-26T12:31 build failure both shards failure
2026-07-28 later dev failure one shard failure

(The 2026-07-25 nightly is not a data point: every stage skipped and the run still reported success.)

Both symptoms — the cuDNN CUDNN_STATUS_BAD_PARAM in fused_attn_f16_arbitrary_seqlen.cu and the fsdp test blowing past 1800s on a 300s estimate — appear on the same rebuild. A TransformerEngine build mismatched against the image's cuDNN explains both: TE 2.17's fused attention is exactly what the first one dies in, and losing a fused path is exactly what would make the second one crawl.

The experiment

This PR is now pinned to the last image that demonstrably passed both lanes:

ci-image-tag: dev-202607240207
ci-megatron-pr: tom/refactor-miles-repo-megatron/op8-13

Run 30472852114 resolved radixark/miles:dev-202607240207. If both lanes go green on it, the regression is in the image rather than in any code under test, and the culprit is one of 66b39f07a / 667ffbc86 / 1e1afc00b.

This pin is a diagnostic, not a fix, and must come off before merge — it is two days older than main's current image and predates the mooncake overlay (59c73017c), so an unrelated mooncake test could fail on it. The real repair belongs in the image, not in this PR.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from 36a6024 to d8234df Compare July 29, 2026 18:23
@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. The pinned-image experiment returned a clear answer, and it splits the two failures apart: one is an image regression, the other is a code regression from a specific main commit. Neither is from this chain, and neither can be fixed here. The image pin is now reverted. Please push back if any conclusion is off.

Result of the experiment

Pinned to dev-202607240207 (the last image that passed both lanes, run 30472852114):

  • tests/e2e/megatron/test_qwen3_5_35b_a3b_lora_ci.pyPASSED. Not one occurrence of fused_attn_f16_arbitrary_seqlen.cu:934 anywhere in the job log.
  • tests/e2e/short/test_qwen3_0.6B_fsdp_colocated_2xGPU.pystill timeout after 1800s, in the same job where test_glm5_744b_a40b_4layer_r3.py and test_qwen3_4B_fsdp_true_on_policy.py both passed.
  • The h100 lane still went red on that image, but for the reason I flagged in advance: ImportError: cannot import name 'FieldSchema' from 'mooncake.structured_object_store', because that image predates the mooncake overlay (59c73017c).

So the two failures have different causes

failure cause evidence
cuDNN BAD_PARAM in test_qwen3_5_35b_a3b_lora_ci.py CI image regression passes on dev-202607240207, fails on current dev; dev sat on the 07-24T02:07 build for two days while every rebuild failed, then jumped past TE 2.17 and both TE-install rewrites (66b39f07a / 667ffbc86 / 1e1afc00b)
test_qwen3_0.6B_fsdp_colocated_2xGPU.py timeout code regression, #1469 reproduces on the old image, so not the image. The 2026-07-27 nightly has no 2-gpu failure; the 2026-07-28 nightly has exactly this timeout. 5b809d9d7 [fsdp] fsdp backend support fix (#1469) landed 2026-07-28T00:02Z, between them — and the test that broke is an fsdp test

I should also correct myself: I earlier lumped the 2026-07-26 nightly's 2-gpu failures in with this one. They were different tests (test_qwen2.5_0.5B_gsm8k{,_async}.py, exit 1 after ~45s), not the fsdp timeout. And the 2026-07-25 nightly is not usable as a data point at all — every stage skipped, run still success.

Why this PR cannot be fully green

The two conditions are mutually exclusive from here:

  • current image → mooncake works, cuDNN fails
  • dev-202607240207 → cuDNN works, mooncake fails
  • #1469's fsdp timeout fails under both

I have reverted ci-image-tag: and kept only ci-megatron-pr:, so this PR runs in the honest configuration. The representative result for that configuration is run 30446270231: 17 pass, 2 fail, the two being exactly the failures above.

What actually needs doing, by someone who owns these

  1. Rebuild/repair the CI image so TransformerEngine matches the image's cuDNN — the two-day gap where every docker-build failed is worth looking at on its own, since it let three TE-related changes land in one jump with no green nightly in between.
  2. Investigate the fsdp colocated timeout from [fsdp] fsdp backend fix #1469 — a test declared est_time=300 now runs past the 1800s cap.

No issues filed, per the user's standing instruction; raising it here and with the user.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy

fzyzcjy commented Jul 29, 2026

Copy link
Copy Markdown
Collaborator Author

🤖 Posted autonomously by Claude Code acting on the user's behalf. Final result for this PR, with the classification and evidence for each remaining failure. Both are pre-existing failures that this chain does not cause, but they have different mechanisms, so I am stating each separately. Please push back if any conclusion is off.

Final state

Run 30479714644 on d8234df3e, settled: 17 pass, 2 fail, 0 outstanding. Configuration is ci-megatron-pr: tom/refactor-miles-repo-megatron/op8-13 only — no image pin, i.e. the same radixark/miles:dev everyone else's CI uses.

Passing: stage-a-cpu ×4, stage-b-cpu, stage-b-2-gpu-h200, stage-c-2-gpu-h200 (1), stage-c-4-gpu-h200 ×3, stage-c-8-gpu-h100 (1), stage-c-8-gpu-h200 ×2.

Failure 1 — pre-existing, caused by the shared CI image

stage-c-8-gpu-h100 (0), container_image: radixark/miles:dev:

FAILED: tests/e2e/megatron/test_qwen3_5_35b_a3b_lora_ci.py returned exit code 1
RuntimeError: /TransformerEngine/transformer_engine/common/fused_attn/fused_attn_f16_arbitrary_seqlen.cu:934
  cuDNN Error: ... CUDNN_ATTR_OPERATION_RESHAPE_MODE ... CUDNN_STATUS_BAD_PARAM

Evidence it is the image and not any code under test:

  1. The same test passes on an older image. I pinned ci-image-tag: dev-202607240207 (run 30472852114) and test_qwen3_5_35b_a3b_lora_ci.py PASSED, with zero occurrences of fused_attn_f16_arbitrary_seqlen.cu:934 in the whole job log. On the current dev that string appears 4 times.
  2. It reproduces on plain main. The 2026-07-28 nightly fails the same test with the byte-identical fingerprint, before this chain was rebased onto main.
  3. The image jumped. docker-build pushes a rolling dev plus a timestamped dev-<UTC>. dev sat on the 2026-07-24T02:07 build for two days because every rebuild in between failed, then landed 2026-07-26T05:08 having swallowed TransformerEngine 2.17 and both TE-install rewrites at once (66b39f07a, 667ffbc86, 1e1afc00b). The 2026-07-24 nightly — last one on the old image — passed this lane; the 2026-07-26 nightly — first on the new one — failed it.

Note the older image is not a usable workaround: it predates the mooncake overlay (59c73017c), so it fails elsewhere with ImportError: cannot import name 'FieldSchema' from 'mooncake.structured_object_store'. Current image → mooncake works, cuDNN breaks; old image → the reverse.

Failure 2 — pre-existing, caused by main source, bisected to #1469

stage-c-2-gpu-h200 (0):

Test Summary: 2/3 passed
FAILED: tests/e2e/short/test_qwen3_0.6B_fsdp_colocated_2xGPU.py (timeout after 1800s)

Not an assertion — a test declared est_time=300 running past the 1800s cap. Evidence:

  1. Not the image. It still times out on dev-202607240207, in the same job where test_glm5_744b_a40b_4layer_r3.py and test_qwen3_4B_fsdp_true_on_policy.py both pass.
  2. It reproduces on plain main. Same test, same 1800s timeout, in the 2026-07-28 nightly.
  3. Bisected to one commit. The 2026-07-27 nightly has no 2-gpu failure at all; the 2026-07-28 nightly has exactly this timeout. 5b809d9d7 [fsdp] fsdp backend support fix (#1469) landed 2026-07-28T00:02Z, inside that window — and the test that broke is an fsdp test.

A correction to an earlier comment of mine: I initially grouped the 2026-07-26 nightly's 2-gpu failures with this one. They were different tests (test_qwen2.5_0.5B_gsm8k{,_async}.py, exit 1 after ~45s). Also, the 2026-07-25 nightly is not a usable data point — every stage skipped while the run reported success.

Positive evidence for this chain

  • tests/snapshots/model_args/ has a zero-byte diff from op8-17, where the .sh-era args were frozen, through this head — so what reaches train.py is provably identical to the pre-M8 baseline.
  • tests/e2e/megatron/model_scripts/test_glm5_744b_a40b_4layer_r3.py, which runs a model script op8-11 converted from .sh to .py, passes.
  • tests/e2e/megatron/model_scripts/test_deepseek_v4_flash_4layer_ci.py, which failed on OSError: File name too long: 'base64:...', now passes (8/8 in its shard) after the ci-megatron-pr: dependency was declared.
  • Both stage-a-cpu failures found earlier in this chain were real and are fixed.

Needs someone who owns these

  1. The CI image: get TransformerEngine matched to the image's cuDNN. The two-day window where every docker-build failed is worth a look on its own — it let three TE-related changes land in one jump with no green nightly between them.
  2. [fsdp] fsdp backend fix #1469: the fsdp colocated test now exceeds a 6× margin over its own estimate.

No issues filed, per the user's standing instruction — raised here and with the user.

Status: https://github.com/radixark/miles/pull/1911/checks

@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-11 branch from 5cf6e24 to 23a65d9 Compare August 4, 2026 04:13
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from d8234df to 40fdcfc Compare August 4, 2026 04:13
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-11 branch from 23a65d9 to 1d07063 Compare August 8, 2026 02:38
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from 40fdcfc to d0682a5 Compare August 8, 2026 02:38
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-11 branch from 1d07063 to fbefce1 Compare August 8, 2026 06:36
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from d0682a5 to c95064e Compare August 8, 2026 06:36
@fzyzcjy

fzyzcjy commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator Author
image

fzyzcjy added 17 commits August 9, 2026 17:58
Squashed from:
- Fix PYTHONBUFFERED typo in launch scripts and command utils
- Fix the same typo in the NPU docker patch
- Unbuffer the ray workers, not only the submitting client
- Unbuffer the launchers that submit ray jobs of their own
Squashed from:
- Add a shell launch script test harness
- Make the shell harness report stderr and emit shim stdout correctly
- Drop the deprecated huggingface-cli shim
- Exercise the shim behaviours the single real script never reaches
- Poll the ray cluster the way the real scripts do in the synthetic script
- Group the harness tests by what they exercise
- Make the harness record commands in fork order and refuse to be unfrozen
…r paths

Squashed from:
- Fix launch scripts whose model config path could never resolve
- Fix two launcher entrypoints that raised before issuing any command
- Cover the two regressions this op fixes
… scripts

Squashed from:
- Derive the miles checkout location instead of hardcoding it
- Quote the derived train.py path and pin the invariant
- Find the shell scripts without shelling out to git
Squashed from:
- Snapshot the external commands of every shell launch script
- Apply isort and black to the shell launch script test
- Intercept ps so the recordings do not read the host process list
- Harden the harness against host state the rollout exposed
- Share the snapshot compare-or-update step and stop running each script twice
- Keep the generated snapshots under one obvious tests/snapshots tree
- Group the launch script tests by subject
- Name the shell launcher test after what it covers
- Assert the recorded order for every script, including the concurrent ones
- Regenerate the concurrent launcher's snapshot in its true command order
ExecuteTrainConfig.num_nodes read SLURM_JOB_NUM_NODES into a class-level
default, so the value was fixed when command_utils was imported. A test that
wants a deterministic launch command cannot undo that with monkeypatch, and a
process that sets the variable after import does not see it either.

A default_factory reads it at construction instead, but dataclass_cli copied
the parameter's declared default straight into the click signature, and for a
factory field that default is dataclasses' _HAS_DEFAULT_FACTORY sentinel,
which click then type-casts:

    TypeError: int() argument must be ... not '_HAS_DEFAULT_FACTORY_CLASS'

Every scripts/run_*.py exposes this config through that bridge. Resolve the
factory when the signature is built, the way the argparse bridge already does.
…ript

Squashed from:
- Snapshot the commands built by every python launch script
- Apply isort and black to the python launch script test
- Reuse the shell harness sanitizer and snapshot helper
- Move the python launcher snapshots into the shared tree too
- Share the command recorder with the command_utils tests
- Freeze the launcher environment that the snapshots actually depend on
- Regenerate the launcher snapshots for the ray runtime unbuffering
- Snapshot the config files a launcher generates, not just its commands
- Record the generated precision config in the deepseek-v4 snapshots
Squashed from:
- Cover the public surface of command_utils with unit tests
- Group the command_utils tests by the function under test
- Close the gaps that let the command_utils tests pass on broken behaviour
- Keep the command_utils tests in one file
Squashed from:
- Rename exec_command by the resource its command needs
- Point the nvlink and single-node conversion tests at the gpu helper
- Re-record the multi-node label the rename changed
- Rename the last two exec_command call sites the split missed
- Re-record the multi-node label in the rsync_simple test too
Squashed from:
- Move the shell exec helpers next to their only consumers
- Carry NodeAffinitySchedulingStrategy along with the moved exec helpers
- Stop patching command helpers on a module that no longer has them
…yloads

Squashed from:
- Accept inline base64 payloads for the config file arguments
- Pass config documents inline instead of through a temp file
- Make the inline config payload reach every consumer and fail loudly
- Apply pre-commit import ordering
- Regenerate the deepseek-v4 snapshots for the inline config payload
Squashed from:
- Snapshot the launchers that build their own command line
- Record what the self-executing launchers submit today
- Freeze the pid these launchers embed in their cleanup command
…ures

Squashed from:
- Let a p2p profile's rotary_base reach the model script it configures
- Test the model args command run.py actually builds, not a copy of its logic
- Flip the p2p snapshots to the rotary base each profile declares
The next ops rewrite all 62 scripts/models/*.sh into python. Once the shell
versions are gone there is no source of truth left to prove the rewrite was
faithful, so record the argv each of them expands to now. What these golden
files pin is agreement with the shell era, not merely agreement with today's
behaviour; the rewrite may only change the producer, never these files.

They also give the 18 models that no launcher snapshot reaches their first
coverage of any kind.
Squashed from:
- Expand the model args in python before building the command
- Update the launcher snapshots for the inlined model args
- Point the command_utils tests at the expanded model args
- Freeze the model-args knobs the snapshots now depend on
- Skip non-files when scanning the model scripts for environment knobs
Squashed from:
- Replace the model config shell scripts with python
- Point the run_megatron CLI tests at the model args loader
- Preserve the rotary base override in the 16-node profile launcher
- Convert the shell model configs to python and load them through one CLI
- Apply pre-commit formatting
- Keep the environment overrides and the failure path the sourced scripts had
- Point the NPU docker patch at the python model definitions
- Read each model's environment override where the shell script read it
- Match the shell mask when the model is shorter than its dense prefix
- Require keyword arguments for the moe layer frequency
- Take the model args overrides from the environment the shell already used
- Run the model args loader itself instead of a script that only forwards to it
- Let the model scripts be plain lists and read their environment at call time
- Group the model args utilities by what calls them
- Regenerate the shell launcher snapshots for the model args entry point
- Expect the TypeError an unknown model args keyword now raises
- Drop the override argument the restored environment reading made redundant
- Let a model script declare its arguments as one block of text
- Concatenate the model argument lines instead of parsing them
- Fix the callers that still joined the model args, and pin the contract
- Repair the two paths the model args conversion left behind
- Treat an explicit zero override as a value, not as a missing argument
- Let a model script reach the loader without importing the miles package
- Take the golden model args from the python loader instead of the shell
- Record the model args lookup in the two concurrent launchers' snapshots
- Point the p2p launcher at the loader and let it fail loudly
- Regenerate the self-executing launcher snapshots for the python model args
Squashed from:
- Quote the model args miles inlines into the launch command
- Regenerate the launcher snapshots with the model args quoted
- Quote the model args on the NPU path too
- Quote the model args the formal-math launcher inlines too
- Keep the quoting next to the loader it quotes
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-11 branch from fbefce1 to c77d60a Compare August 9, 2026 10:00
@fzyzcjy
fzyzcjy force-pushed the tom/refactor-miles/op8-18 branch from c95064e to 79e9a3c Compare August 9, 2026 10:00
Base automatically changed from tom/refactor-miles/op8-11 to main August 9, 2026 10:51
@fzyzcjy
fzyzcjy merged commit 9e0b747 into main Aug 9, 2026
8 of 10 checks passed
@fzyzcjy
fzyzcjy deleted the tom/refactor-miles/op8-18 branch August 9, 2026 10:52
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants