Skip to content

CollectiveX MVP v0.1#2004

Open
Oseltamivir wants to merge 200 commits into
mainfrom
collectivex
Open

CollectiveX MVP v0.1#2004
Oseltamivir wants to merge 200 commits into
mainfrom
collectivex

Conversation

@Oseltamivir

@Oseltamivir Oseltamivir commented Jul 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

CollectiveX v1 expert-parallel communication benchmark and its isolated GitHub Actions publication path.

Benchmark and publication contract

  • Covers eight SKUs, six backend families, EP8/EP16, normal and genuine low-latency semantics.
  • Locks 58 shards, 608 requested cases / 1,600 points, including 364 runnable and 244 explicit unsupported cases.
  • Standardizes every measured component at 32 synchronized full-roundtrip warmups and 64 x 8 timed iterations: 512 cross-rank-max observations.
  • Defaults every sweep to release_tag=unversioned; only an explicit v1 tag with the locked full-matrix digest emits a release marker.
  • Uses no results server, Vercel storage, GCP, Neon, managed database, or managed object store.

Validation

  • 151 Python contract/unit tests pass.
  • Matrix SHA-256: f1ca85f9689922b90edd5767b9ff2a902f6b896f32f68b2ca086dde3fd2157d0.
  • Case catalog SHA-256: 8e262178f770b0cdde12b7ec71604afd87251fa55685d4594f29717153ad6bbd.
  • Regeneration confirms 58 shards, 364 runnable cases, 244 unsupported cases, and 1,600 points.
  • Actionlint, ShellCheck, and git diff --check pass.

中文说明

本 PR 完成 CollectiveX v1 专家并行通信基准测试及隔离式 GitHub Actions 发布链路。代码已准备执行三轮完整、无 canary 的 V1 运行;本 PR 目前不声称已有完成 promotion 的实测结果。

基准测试与发布约定

  • 覆盖八种 SKU、六类后端、EP8/EP16,以及常规与真正低延迟语义。
  • 固定为 58 个 shard、608 个请求 case / 1,600 个点位,其中包含 364 个可运行 case 和 244 个显式 unsupported case。
  • 每个被测组件统一执行 32 次同步完整往返预热和 64 x 8 次计时迭代,共 512 个跨 rank 最大值观测。
  • 所有扫描默认使用 release_tag=unversioned;只有显式选择 v1 且匹配固定完整矩阵摘要时才生成 release marker。
  • 发布必须使用来自同一 source SHA 的三个成功且带 tag 的运行。手动 publisher 会重新校验运行 metadata 与精确 marker,在 runner 临时存储中完成 promotion,并仅保留清理后的 cxpublication-v1-* NDJSON。
  • 不使用 results server、Vercel storage、GCP、Neon、managed database 或 managed object store。

验证

  • 151 个 Python 约定/单元测试通过。
  • Matrix SHA-256:f1ca85f9689922b90edd5767b9ff2a902f6b896f32f68b2ca086dde3fd2157d0
  • Case catalog SHA-256:8e262178f770b0cdde12b7ec71604afd87251fa55685d4594f29717153ad6bbd
  • 重生成确认 58 个 shard、364 个可运行 case、244 个 unsupported case 和 1,600 个点位。
  • Actionlint、ShellCheck 与 git diff --check 通过。

Comment on lines +145 to +150
rsync -a --delete --delete-excluded \
--exclude='__pycache__/' --exclude='results/' --exclude='.cx_workloads/' \
--exclude='configs/platforms.yaml' --exclude='private-infra.md' \
--exclude='goal.md' --exclude='notes.md' \
"$repo_root/experimental/CollectiveX" "$stage_dir/experimental/" >/dev/null 2>&1 \
|| cx_die "staging CollectiveX failed"

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.

🔴 The setup step writes the shard JSON to experimental/CollectiveX/results/.shard_${matrix.id}.json and sets CX_SHARD_FILE=results/.shard_${matrix.id}.json (relative), but cx_stage_repo (runtime/common.sh:145-150) rsyncs the CollectiveX tree with --exclude='results/' --delete-excluded and drops the shard file — so for every staged single-tray SKU (b300 always; gb200/gb300 with EP4 via CX_NODES<=1), the [ -f "$CX_SHARD_FILE" ] guard at run_in_container.sh:458 fails and execution falls into the single-bench else branch (line 556+), silently running one wrong-config default (uniform/decode/bf16, empty case_id) instead of the shard's N scheduled cases. Downstream make_bundle will catch this via missing_identity/coverage but only after GPU allocation was spent on the wrong workload. Cheap fix: allow-list the shard file through the rsync (--include='experimental/CollectiveX/results/' --include='experimental/CollectiveX/results/.shard_*.json' before the results/ exclude), copy the shard file into the stage dir after the rsync, or resolve CX_SHARD_FILE against the original repo root in run_in_container.sh's SHARD guard the way the rack (EP8) launchers already do (see launch_gb300-nv.sh:92-93 / launch_gb200-nv.sh cx_ep_cases).

Extended reasoning...

The bug

The sweep workflow's shard-fanout step writes the resolved case list to experimental/CollectiveX/results/.shard_${matrix.id}.json:

# .github/workflows/collectivex-sweep.yml
env:
  CX_SHARD_FILE: results/.shard_${{ matrix.id }}.json   # RELATIVE path
...
- name: Extract shard from matrix artifact
  working-directory: experimental/CollectiveX
  run: |
    ...
    json.dump({...,'cases':s['cases']}, open('results/.shard_${{ matrix.id }}.json','w'))

The physical file therefore lands at $REPO/experimental/CollectiveX/results/.shard_<id>.json, and CX_SHARD_FILE=results/.shard_<id>.json is interpreted relative to the container's cwd, which is /ix/experimental/CollectiveX.

For every SKU that requires CX_STAGE_DIR (b300 always; gb200/gb300 with EP4 via the CX_NODES<=1 delegate path in launch_gb200-nv.sh:57 / launch_gb300-nv.sh:47), the launcher calls:

# launch_b300.sh:34, launch_gb200-nv.sh:52, launch_gb300-nv.sh:24
MOUNT_SRC="$(cx_stage_repo "$REPO_ROOT" "$CX_STAGE_DIR")"

which rsyncs the tree with an exclude that drops results/:

# experimental/CollectiveX/runtime/common.sh:145-150
rsync -a --delete --delete-excluded \
  --exclude='__pycache__/' --exclude='results/' --exclude='.cx_workloads/' \
  --exclude='configs/platforms.yaml' --exclude='private-infra.md' \
  --exclude='goal.md' --exclude='notes.md' \
  "$repo_root/experimental/CollectiveX" "$stage_dir/experimental/"

Both --exclude='results/' and --delete-excluded guarantee that the shard file the workflow just wrote is missing from the stage dir.

The consequence at runtime

The container mounts $MOUNT_SRC:/ix, cwd=/ix/experimental/CollectiveX. Inside run_in_container.sh, the SHARD guard resolves CX_SHARD_FILE relative to that cwd:

# runtime/run_in_container.sh:458
if [ -n "${CX_SHARD_FILE:-}" ] && [ -f "${CX_SHARD_FILE:-/nonexistent}" ]; then
  # SHARD mode — sweep every scheduled case
  ...
else
  # Single-bench (workflow_dispatch) path
  # uses ${CX_MODE:-normal}, ${CX_PHASE:-decode}, ${CX_ROUTING:-uniform},
  # ${CX_DISPATCH_DTYPE:-bf16}, empty CX_CASE_ID/CX_SUITE/CX_WORKLOAD_NAME, ...

The file resolves to /ix/experimental/CollectiveX/results/.shard_<id>.json — which is missing because rsync excluded it — so the test fails and the else branch runs a single default case with none of the shard's identity, N times cheaper than the intended N-case sweep.

Why the rack (EP8) paths escape

The rack-scale launchers iterate cases themselves in the launcher on the SUBMIT host (not inside the container). Their case-list helpers explicitly resolve the shard file against the original checkout when the relative path misses:

# launch_gb300-nv.sh cx_ep8_cases (and launch_gb200-nv.sh cx_ep_cases)
local sf="${CX_SHARD_FILE:-}"
[ -n "$sf" ] && [ ! -f "$sf" ] && [ -f "$CX_DIR/$sf" ] && sf="$CX_DIR/$sf"

The same workaround is absent from run_in_container.sh:458, so the EP4 single-tray path — which shares the b300/gb200-EP4/gb300-EP4 launchers with the staged mount — hits the missing file.

Affected sweeps

Every single-tray staged shard in the v1 promoted matrix, per sweep_matrix.py + configs/suites.yaml platforms:

  • b300 (all shards; launch_b300.sh is single-node)
  • gb200 EP4 (CX_NODES<=1 -> run_in_container.sh)
  • gb300 EP4 (CX_NODES<=1 -> run_in_container.sh)

The h100-dgxc/h200-dgxc/b200-dgxc/mi325x/mi355x paths do not set CX_STAGE_DIR in this workflow (cx_stage_repo becomes a no-op) and are unaffected.

Concrete walk-through (b300 shard)

  1. Setup job resolves matrix; writes experimental/CollectiveX/results/.shard_b300-deepep.json on the checkout with e.g. 24 cases (varied phase/dtype/routing/eplb across ep-core-v1 + ep-routing-v1).
  2. Sweep job on the b300 runner exports CX_SHARD_FILE=results/.shard_b300-deepep.json, checks out the repo, and calls launch_b300.sh.
  3. launch_b300.sh:34 -> cx_stage_repo rsyncs to $CX_STAGE_DIR/job_<id>/experimental/CollectiveX/ with --exclude='results/' --delete-excluded. The shard file is not copied.
  4. srun --container-workdir=$MOUNT_DIR/experimental/CollectiveX ... run_in_container.sh. cwd inside container = /ix/experimental/CollectiveX.
  5. run_in_container.sh:458 tests [ -f "results/.shard_b300-deepep.json" ] -> that resolves to /ix/experimental/CollectiveX/results/.shard_b300-deepep.json -> missing.
  6. Execution falls into the else branch at line 556+. It dispatches ${CX_BENCH} once with CX_MODE=normal, CX_PHASE=decode, CX_ROUTING=uniform, CX_DISPATCH_DTYPE=bf16, empty CX_CASE_ID, empty CX_SUITE, empty CX_WORKLOAD_NAME, empty CX_REQUIRED_PUBLICATION.
  7. One result JSON is produced with no case_id and mismatched identity; the other 23 scheduled cases never run.
  8. Aggregate job's make_bundle.py validate_expected_coverage computes missing_identity + missing + identity_mismatch against matrix_full.json and raise SystemExit(...) — the whole aggregate fails, after b300 GPU-time was spent on the wrong workload.

Impact

For every b300/gb200-EP4/gb300-EP4 shard promoted through v1 (three of the eight SKUs in ep-core-v1 + ep-routing-v1), the sweep silently runs one wrong-config default point instead of the scheduled N-case sweep. Bundle validation catches the divergence but only post-hoc, so the failure is loud yet wasteful: GPU allocations spent, aggregate job red, invalidating the v1 dataset this PR is producing.

Fix

Any one of:

  1. Allow the shard file through the rsync in cx_stage_repo (runtime/common.sh:146):

    rsync -a --delete --delete-excluded \
      --include='experimental/CollectiveX/results/' \
      --include='experimental/CollectiveX/results/.shard_*.json' \
      --exclude='__pycache__/' --exclude='results/' ...
  2. Copy the shard file into the stage dir after the rsync completes:

    [ -n "${CX_SHARD_FILE:-}" ] && [ -f "$repo_root/experimental/CollectiveX/$CX_SHARD_FILE" ] \
      && cp -a "$repo_root/experimental/CollectiveX/$CX_SHARD_FILE" \
              "$stage_dir/experimental/CollectiveX/$CX_SHARD_FILE"
  3. Mirror the rack (EP8) launcher workaround in run_in_container.sh:458:

    sf="${CX_SHARD_FILE:-}"
    # $CX_DIR is not set inside the container; use the fixed workdir instead.
    [ -n "$sf" ] && [ ! -f "$sf" ] && [ -f "/ix/experimental/CollectiveX/$sf" ] \
      && sf="/ix/experimental/CollectiveX/$sf"
    if [ -n "$sf" ] && [ -f "$sf" ]; then ...

Approach (1) or (2) is the smallest change with the least surface area.

Comment thread experimental/CollectiveX/env_capture.py Outdated
Comment on lines +178 to +180
elif _run(["ibstat", "-l"]):
devices = [d.strip() for d in _run(["ibstat", "-l"]).splitlines() if d.strip()]
return {

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.

🟡 _rdma() calls _run(["ibstat", "-l"]) twice at env_capture.py:178-179 — once in the elif condition and once in the comprehension body. If the second invocation returns None (which _run does on shutil.which miss, TimeoutExpired/OSError, or nonzero exit), .splitlines() raises AttributeError and takes down env_capture.py under run_in_container.sh's set -euo pipefail. The trigger is genuinely rare (both calls are microseconds apart on a stable IB stack, and this branch runs only when ibv_devinfo is absent), so nit — but the fix is a one-line refactor mirroring the ibv_devinfo branch just above.

Extended reasoning...

The defect. env_capture._rdma() has an asymmetry between its two RDMA-listing branches:

listing = _run(["ibv_devinfo", "-l"])   # assigned once, iterated once
if listing:
    for line in listing.splitlines()[1:]:
        ...
elif _run(["ibstat", "-l"]):             # called once (as a truthiness check)
    devices = [d.strip() for d in _run(["ibstat", "-l"]).splitlines() if d.strip()]  # called AGAIN

The ibv_devinfo branch just above does the right thing: assign once, reuse. The ibstat branch does not.

Why the crash is theoretical but real. _run() returns None on any of: shutil.which(cmd[0]) failing (line 51), subprocess.TimeoutExpired/OSError (line 57), or out.returncode != 0 (line 59). If the first call returns a truthy string but the second returns None — a transient OS timer glitch, an OOM-killed helper, a stray nonzero exit under load — then None.splitlines() raises AttributeError. Under run_in_container.sh's set -euo pipefail (line 33), that aborts the whole shard step before any GPU benchmark runs.

Step-by-step proof of the theoretical crash path:

  1. Node has ibstat in $PATH but no ibv_devinfo (a real config: MI355X-style stacks with ibstat only).
  2. First call: _run(["ibstat", "-l"]) succeeds → returns "mlx5_0\nmlx5_1\n" → elif condition is truthy.
  3. Second call: a transient nonzero exit (e.g. ibstat racing an IB-driver reload, timer wraparound, PID-namespace hiccup) → out.returncode != 0_run returns None.
  4. None.splitlines()AttributeError: 'NoneType' object has no attribute 'splitlines' → Python exits nonzero → set -e aborts run_in_container.sh → the shard step fails before GPU work.

Why this is nit, not normal. Every verifier converged on the same practical assessment: ibstat -l is a fast local device listing with no network/filesystem dependency, so a transient failure between two back-to-back calls (microseconds apart) is extremely improbable. The elif branch itself only runs when ibv_devinfo is absent, which is uncommon on the target runners since both binaries come from the same InfiniBand userspace stack. And env_capture.py produces a diagnostic/provenance artifact — even a genuine crash here would break provenance capture, not the benchmark measurement. The defect exists but doesn't justify blocking merge.

The fix. One-line refactor to mirror the ibv_devinfo branch:

else:
    listing = _run(["ibstat", "-l"])
    if listing:
        devices = [d.strip() for d in listing.splitlines() if d.strip()]

Same idiom the file uses immediately above. Eliminates the wasted subprocess call and the theoretical None-deref in one change. Worth doing as a follow-up cleanup, but the PR does not need to block for it.

Comment on lines +260 to +286
"required_publication": env("CX_REQUIRED_PUBLICATION") or None,
"backend": backend,
"phase": phase,
"ep": integer("CX_EP", integer("CX_NGPUS", 1)),
"gpus_per_node": integer("CX_GPUS_PER_NODE", integer("CX_NGPUS", 1)),
"scale_up_domain": integer("CX_SCALE_UP_DOMAIN", integer("CX_NGPUS", 1)),
"dispatch_dtype": env("CX_DISPATCH_DTYPE", "bf16"),
"mode": env("CX_MODE", "normal"),
"contract": env("CX_MEASUREMENT_CONTRACT", "layout-and-dispatch-v1"),
"routing": env("CX_ROUTING", "uniform"),
"eplb": enabled("CX_EPLB"),
"combine_quant_mode": env("CX_COMBINE_QUANT_MODE", "none"),
"resource_mode": env("CX_RESOURCE_MODE", "tuned"),
"activation_profile": env("CX_ACTIVATION_PROFILE", "normal"),
"placement": env("CX_PLACEMENT", "packed"),
"routing_step": env("CX_ROUTING_STEP", "0"),
"uneven_tokens": env("CX_UNEVEN_TOKENS", "none"),
"tokens_ladder": env("CX_TOKENS_LADDER"),
"canonical": enabled("CX_CANONICAL"),
"sampling_contract": "fixed-512-v1",
"samples_per_point": integer("CX_SAMPLES_PER_POINT", 512),
"iters": integer("CX_ITERS", 8),
"trials": integer("CX_TRIALS", 64),
"warmup": integer("CX_WARMUP", 32),
"warmup_semantics": env(
"CX_WARMUP_SEMANTICS", "full-roundtrip-per-trial-point-v1"
),

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.

🟡 cx_emit_ep_failed_case (runtime/common.sh:256-287) builds failure.case without the hidden/topk/experts/nodes keys, but every matrix case emitted by sweep_matrix.py always carries all four. On the first sweep where any case exhausts its retries (flashinfer intermittent MNNVL, HybridEP/UCCL empty-rank, any deterministic rc=5), make_bundle's _identity_differences reports the same case_id four times as hidden=None!=7168,topk=None!=8,experts=None!=256,nodes=None!=1, and validate_expected_coverage piles on by re-listing that case in missing, so the aggregate job aborts with a dual-report that hides the real signal (the case failed all retries — the intended fail-closed behavior). Fix in either place is fine: add the four fields to cx_emit_ep_failed_case from CX_HIDDEN/CX_TOPK/CX_EXPERTS (defaults 7168/8/256) and CX_NGPUS/SLURM_NNODES, or make _identity_differences skip these fields when the actual doc is a failed-case.

Extended reasoning...

The observed behavior

With the PR merged and any sweep that produces a failed-case record for a scheduled case, the aggregate job will fail with a message like:

bundle: expected-matrix coverage failed (
  missing_identity=0 missing=['cxv1-...'] extra=[] duplicates=[]
  identity_mismatch=['cxv1-...:hidden=None!=7168,topk=None!=8,experts=None!=256,nodes=None!=1'])

The same case_id appears in both missing and identity_mismatch, and the mismatch string names four fields that have nothing to do with why the case actually failed.

Step-by-step proof

Take a concrete promoted case, say h100-dgxc/deepep/decode under ep-core-v1 (uniform, canonical, deepseek-v3-v1 defaults). sweep_matrix.py:181-186 builds the matrix entry with:

{
  ...,
  "hidden": "",     # h==7168 -> "" sentinel
  "topk": "",       # t==8    -> ""
  "experts": "",    # e==256  -> ""
  "nodes": "1",     # always str
  ...
}

When every one of the 4 flashinfer attempts wedges on the intermittent MNNVL completion-flag deadlock (documented in run_in_container.sh around line 526), the last attempt's cx_emit_ep_failed_case writes a failed_*.json whose failure.case dict is missing the four keys entirely — the emitter reads CX_DISPATCH_DTYPE/CX_MODE/etc. but has no CX_HIDDEN/CX_TOPK/CX_EXPERTS/SLURM_NNODES reads.

aggregate_results.py keeps that failed-case doc as the newest for that case_id. Then make_bundle.py runs validate_expected_coverage:

  1. _expected_case_identity(matrix_case)"hidden" in case is true (value ""), so identity["hidden"] = int("" or 7168) = 7168. Same for topk/experts (8/256). "nodes" in case is true, identity["nodes"] = int("1") = 1. Expected identity contains {hidden: 7168, topk: 8, experts: 256, nodes: 1, ...}.
  2. _actual_case_identity(failed_doc) (the failed-case branch, line 184-195) copies failure.case verbatim, calls _expected_case_identity. None of hidden/topk/experts/nodes are in that dict, so the if field in case: guard skips all four. Actual identity contains everything except the four scheduled shape fields.
  3. _identity_differences iterates the expected identity's items; actual_identity.get("hidden") is None, None != 7168 -> hidden=None!=7168. Same for the other three.
  4. validate_expected_coverage (line 294-298) hits the differences branch, appends the case_id to identity_mismatch, and does not add it to actual{}. Then missing = set(expected) - set(actual) (line 301) also contains that case_id. Line 319 raises the dual-report SystemExit.

validate_results.py:validate_doc's failed-case schema (v5, ~lines 234-243) requires a different, smaller field set that happens to match what the emitter writes, so it stays silent about this desync. Only make_bundle notices, and only in a way that obscures the real cause.

Why this fires in practice

The PR explicitly builds in retry logic — CX_FLASHINFER_RETRIES defaults to 3 attempts, and both the container and rack launchers loop attempts and preserve a failed_*.json when all attempts fail. Retry-exhaustion is expected behavior for known intermittents, but the aggregate step will now report those as identity_mismatch + missing for hidden/topk/experts/nodes — the least informative signal possible.

Impact

Bundle validation still correctly rejects the incomplete run (the intended fail-closed behavior), and no incorrect data ships, so this is a diagnostic-clarity regression rather than a correctness bug. It will, however, cost real triage time in CI: an operator staring at hidden=None!=7168,topk=None!=8,experts=None!=256,nodes=None!=1 will not obviously infer "one flashinfer case exhausted its retries."

Fix

Either add the four fields to cx_emit_ep_failed_case (read CX_HIDDEN/CX_TOPK/CX_EXPERTS with defaults 7168/8/256, and CX_NGPUS/SLURM_NNODES for nodes), or teach _identity_differences/_actual_case_identity to drop these fields when the actual doc is a failed-case. Either way the two validators stay in sync.

@Oseltamivir Oseltamivir force-pushed the collectivex branch 4 times, most recently from 758fa52 to 1c5b901 Compare July 4, 2026 01:11
Comment thread experimental/CollectiveX/tests/test_sampling_contract.py Fixed
@Oseltamivir Oseltamivir force-pushed the collectivex branch 3 times, most recently from 7e5f80a to 28cbac4 Compare July 4, 2026 03:21
@functionstackx functionstackx changed the title CollectiveX v1: cross-vendor EP benchmark suite CollectiveX v1: cross-vendor EP benchmark suite / CollectiveX v1:跨厂商 EP 基准测试套件 Jul 4, 2026
@functionstackx functionstackx changed the title CollectiveX v1: cross-vendor EP benchmark suite / CollectiveX v1:跨厂商 EP 基准测试套件 CollectiveX v1: cross-vendor EP benchmark suite / CollectiveX v1:跨厂商 EP 基准测试套件 / CollectiveX v1: 크로스 벤더 EP 벤치마크 스위트 Jul 4, 2026
@Oseltamivir Oseltamivir changed the title CollectiveX v1: cross-vendor EP benchmark suite / CollectiveX v1:跨厂商 EP 基准测试套件 / CollectiveX v1: 크로스 벤더 EP 벤치마크 스위트 Finalize CollectiveX v1 cross-vendor EP benchmark suite / 完成 CollectiveX v1 跨厂商 EP 基准测试套件 Jul 4, 2026
@Oseltamivir Oseltamivir force-pushed the collectivex branch 15 times, most recently from 4ff5841 to bffd8d6 Compare July 4, 2026 15:05
Remove the two low-latency logfmt10 precision profiles
(d-bf16.c-logfmt10-dynamic64 and d-fp8-e4m3fn-b128-f32-fused.c-logfmt10-dynamic64)
atomically across the identity registry, capability ledger, schema enums,
byte-provenance maps, and the DeepEP/UCCL supported-profile sets. The
logfmt10 wire format is no longer scheduled or publishable; the inert
use_logfmt native-API keyword wiring is kept because it pins the DeepEP/UCCL
low_latency_combine signature independently of any logfmt profile.

Regenerate the frozen digests and counts whose inputs shrank with the two
profiles gone: precision targets 94/59/35 → 62/33/29, the full-matrix case
tuple, shard-count Counter, low-latency case count 80 → 48, unsupported
terminal-outcome count 173 → 167, and the execution-plan / full-matrix /
case-catalog SHA-256 anchors. The publisher precision-cohort fixture moves
from the (now bf16-only) low-latency profiles to the normal-mode
dispatch×combine 2×2, preserving its cohort topology.

移除两个低延迟 logfmt10 精度档位,在身份注册表、能力账本、schema 枚举、
字节溯源映射以及 DeepEP/UCCL 支持档位集合中原子化删除。logfmt10 线格式不再
被排程或发布;保留 use_logfmt 原生 API 关键字接线(它独立于任何 logfmt 档位,
用于固定 DeepEP/UCCL low_latency_combine 签名)。重新生成随之收缩的冻结摘要与
计数:精度目标 94/59/35 → 62/33/29、全矩阵用例元组、分片计数 Counter、低延迟
用例数 80 → 48、不支持终态数 173 → 167,以及执行计划/全矩阵/用例目录 SHA-256。
发布器精度队列夹具从(现仅 bf16 的)低延迟档位改为普通模式 dispatch×combine
2×2,保持其队列拓扑不变。
Reduce ep-precision-normal-v1 from six scheduled profiles to two
(d-fp8-e4m3fn-b128-f32-prequantized.c-bf16 and
d-fp8-e4m3fnuz-b128-f32-prequantized.c-fp8-e4m3fnuz-direct-cast-noscale),
spanning one bf16-combine and one fp8-combine codec across the e4m3fn and
e4m3fnuz families. The four dropped profiles stay defined in the
V1_PRECISION_PROFILES registry (defined-but-unscheduled) so their schema
enums and capability targets survive for republication of already
collected evidence.

- identity.V1_NORMAL_PRECISION_PROFILE_IDS trimmed to the two kept ids
  (order-preserving); the frozen suite contract follows automatically.
- configs/suites.yaml ep-precision-normal-v1.precision_profiles matched
  order-for-order to satisfy the frozen-catalog equality gate.
- test_precision_scheduling registry-equality relaxed to a subset check
  plus an explicit defined-but-unscheduled set; expected-case counters
  filtered to the scheduled profile ids.
- regenerated the execution-plan, full-matrix and case-catalog digests,
  the unsupported terminal-outcome count (167 to 159) and the matrix
  case/point tuples.

221 tests green.

将 ep-precision-normal-v1 的常规精度扫描从六个受调度配置削减为两个
(d-fp8-e4m3fn-b128-f32-prequantized.c-bf16 与
d-fp8-e4m3fnuz-b128-f32-prequantized.c-fp8-e4m3fnuz-direct-cast-noscale),
覆盖 e4m3fn 与 e4m3fnuz 两族各一个 bf16-combine 与 fp8-combine 编解码。
被移除的四个配置仍保留在 V1_PRECISION_PROFILES 注册表中(已定义但不调度),
使其 schema 枚举与能力目标得以留存,以便对已采集证据进行再发布。

- identity.V1_NORMAL_PRECISION_PROFILE_IDS 保序削减为保留的两个 id;
  冻结的套件契约自动跟随。
- configs/suites.yaml 中对应字段按序对齐以通过冻结目录相等性校验。
- 放宽 test_precision_scheduling 的注册表相等断言为子集校验加显式的
  已定义但不调度集合;预期用例计数按受调度配置 id 过滤。
- 重新生成执行计划、完整矩阵与用例目录摘要、不支持终态计数(167 至 159)
  以及矩阵用例/采样点元组。

221 项测试通过。
Introduce tests/ep_backend.py holding the EPBackend abstract base class
plus the WorkloadSpec / RankInputs dataclasses. This is the target shape
for the EP dispatch/combine benchmark: the base owns input generation
(make_inputs), problem encoding (make_problem), and the two Pass-2 timing
branch rules (benchmark_dispatch/benchmark_combine), while subclasses
supply only the transport (create_buffer/dispatch/stage/combine).

make_inputs runs before create_buffer, resolving the historical inversion
where buffers were sized in __init__ before any input existed: buffer
sizing needs the ladder numbers, not the input tensors.

The module has zero consumers yet — nothing imports it at runtime, so the
221-test baseline is unchanged. torch and routing are imported lazily
inside device-touching methods (mirroring ep_harness) so the module
imports under the CPU-only test environment.

新增惰性的 EPBackend 抽象基类

引入 tests/ep_backend.py,包含 EPBackend 抽象基类以及 WorkloadSpec /
RankInputs 数据类。这是 EP dispatch/combine 基准测试的目标结构:基类负责
输入生成(make_inputs)、问题编码(make_problem)以及两条 Pass-2 计时分支
规则(benchmark_dispatch/benchmark_combine),子类只需提供传输实现
(create_buffer/dispatch/stage/combine)。

make_inputs 在 create_buffer 之前运行,纠正了以往在任何输入存在之前就于
__init__ 中构建缓冲区的顺序倒置:缓冲区大小只需要阶梯数值,而非输入张量。

该模块目前没有任何调用方——运行时不会被导入,因此 221 项测试基线保持不变。
torch 与 routing 在触及设备的方法内惰性导入(与 ep_harness 一致),使该模块
可在仅 CPU 的测试环境中导入。
Restructure the EP dispatch/combine benchmark so every backend subclasses
the EPBackend ABC and the driver drives a real lifecycle instead of a
duck-typed protocol. Buffer construction moves out of __init__ into
create_buffer(spec), which consumes what make_inputs(args) produces —
resolving the historical inversion where buffers were sized before inputs
existed. run_sweep now calls make_inputs -> create_buffer and reads all
provenance/contract flags off the backend after the buffer is built.

All six adapters (nccl, deepep, deepep-v2, mori, deepep-hybrid, uccl)
subclass EPBackend via super().__init__, which sets self.mode and validates
SUPPORTED_MODES. Buffer bodies are moved verbatim behind a local-alias line
with an `assert spec.max_tokens_per_rank <= cap` guard. probe_precision, the
second protocol consumer, calls create_buffer(make_inputs(args)) after
construction. Drop the unused abstract supported_profiles hook — each adapter
resolves its own profile set inline via ep_precision.resolve_precision.

Scrape-anchor tests updated in lockstep: the ElasticBuffer/JIT/cache-dir and
HybridEPBuffer ordering pins now walk create_buffer, and the combine-timing
and post=finish_dispatch fragments target ep_backend.py.

将 EP dispatch/combine 基准重构为每个后端都继承 EPBackend 抽象基类,驱动器执行
真正的生命周期,而非鸭子类型协议。缓冲区构造从 __init__ 移到 create_buffer(spec),
由 make_inputs(args) 的产物驱动,纠正了过去"先建缓冲区后有输入"的顺序倒置。
run_sweep 现按 make_inputs -> create_buffer 顺序调用,并在缓冲区建成后从后端读取
全部 provenance/契约标志。

六个适配器(nccl、deepep、deepep-v2、mori、deepep-hybrid、uccl)均通过
super().__init__ 继承 EPBackend,由基类设置 self.mode 并校验 SUPPORTED_MODES。
缓冲区代码体在局部别名行后原样移动,并加 `assert spec.max_tokens_per_rank <= cap`
守卫。第二个协议消费方 probe_precision 在构造后调用 create_buffer(make_inputs(args))。
移除未实现的抽象 supported_profiles 钩子——各适配器在 __init__ 中经
ep_precision.resolve_precision 内联解析各自的 profile 集合。

同步更新源码抓取测试:ElasticBuffer/JIT/缓存目录 及 HybridEPBuffer 的顺序断言
现遍历 create_buffer;combine 计时与 post=finish_dispatch 片段改指向 ep_backend.py。
Pure dedup after the ABC cutover: delete per-adapter methods whose bodies the
base class now reproduces exactly.

- oracle_dispatch_payload: deleted from deepep, deepep-v2, mori, hybrid, uccl
  (all were the base's encode_dispatch(...).semantic). nccl keeps its identity
  override (the wire payload is what the oracle built).
- precision_evidence: deleted from every adapter. The base threads
  uint8_storage=self._uint8_scale_storage, which is False by default and True
  for hybrid (via its class flag) — matching each adapter's former literal.
- make_problem: deleted from nccl, deepep, deepep-v2, uccl (base reproduces
  them; deepep-v2 keeps its int-dtype via a new _topk_idx_dtype override
  returning deep_ep.topk_idx_t). mori/hybrid keep make_problem (extra
  scales/indices fields and conditional dispatch_x).
- buffer_cap/finalize: deleted from nccl (both matched the base). deepep/uccl
  keep buffer_cap (low-latency cap) and finalize (LL teardown / os._exit);
  mori keeps finalize (os._exit); deepep-v2/hybrid keep finalize (rc=1 / JIT
  teardown).

No behavioral change: every deletion was verified equal to the base, and the
dead problem.layout field (never read) is dropped with nccl.make_problem.

ABC 切换后的纯去重:删除那些基类已逐字复现的各适配器方法。

- oracle_dispatch_payload:从 deepep、deepep-v2、mori、hybrid、uccl 删除(均为基类的
  encode_dispatch(...).semantic)。nccl 保留其恒等重写(线上载荷即 oracle 所构造)。
- precision_evidence:从所有适配器删除。基类透传
  uint8_storage=self._uint8_scale_storage,默认 False、hybrid 经类标志为 True,
  与各适配器原有字面量一致。
- make_problem:从 nccl、deepep、deepep-v2、uccl 删除(基类可复现;deepep-v2 通过新增
  _topk_idx_dtype 重写返回 deep_ep.topk_idx_t 保留其整型 dtype)。mori/hybrid 保留
  make_problem(含额外 scales/indices 字段与条件式 dispatch_x)。
- buffer_cap/finalize:从 nccl 删除(均与基类一致)。deepep/uccl 保留 buffer_cap
  (低延迟容量)与 finalize(LL 拆除 / os._exit);mori 保留 finalize(os._exit);
  deepep-v2/hybrid 保留 finalize(rc=1 / JIT 拆除)。

无行为变化:每处删除均已核验与基类等价;随 nccl.make_problem 一并移除从未被读取的
无用 problem.layout 字段。
Add torch-free unit tests for the shared base-class plumbing the driver relies
on, using a minimal in-process fake backend:

- make_inputs: rc=2 early-return when the buffer cap can't serve the conditioning
  ladder and when the token ladder is empty; on success, ladder/conditioning
  sizing, max-tokens-per-rank span, and canonical-measured workload registration
  (conditioning shapes never register). _build_rank_inputs is overridden so the
  path needs no routing/activation tensors.
- timed_components: the roundtrip / +dispatch,combine / +stage / roundtrip-only
  matrix, including roundtrip_only suppressing the stage flag.
- benchmark_dispatch/combine branch selection: post=cleanup only when
  dispatch_needs_combine_cleanup; combine supplies a per-iter re-dispatch `pre`
  when combine_needs_redispatch, else dispatches once and reuses the handle.
  time_us is stubbed to capture pre/post without running a timing loop.
- __init__ mode validation (SUPPORTED_MODES) and __init_subclass__ empty-name
  enforcement.

为驱动器所依赖的基类公共管线新增无需 torch 的单元测试,使用进程内最小假后端:

- make_inputs:当缓冲区容量无法承载 conditioning 阶梯、以及 token 阶梯为空时的
  rc=2 提前返回;成功路径下的阶梯/conditioning 定尺、max-tokens-per-rank 跨度,
  以及仅对 canonical 实测点登记 workload(conditioning 形状不登记)。重写
  _build_rank_inputs 使该路径无需 routing/activation 张量。
- timed_components:roundtrip / +dispatch,combine / +stage / 仅 roundtrip 矩阵,
  含 roundtrip_only 抑制 stage 标志。
- benchmark_dispatch/combine 分支选择:仅当 dispatch_needs_combine_cleanup 时
  post=清理;combine 在 combine_needs_redispatch 时提供逐迭代重派发的 pre,
  否则派发一次并复用句柄。time_us 被打桩以捕获 pre/post 而不运行计时循环。
- __init__ 模式校验(SUPPORTED_MODES)与 __init_subclass__ 空 name 强制。
UCCL's uccl_deepep.Buffer is a DeepEP-API clone, so ep_deepep.py and
ep_uccl.py carried a byte-identical ~250-line operational surface (mode
handling, dispatch/combine, expert-packed inspection, fp8 dequantization).
Extract it into a new deep_ep-free DeepEPFamilyBackend base; each concrete
adapter keeps only its native buffer import, create_buffer provenance, and
teardown. The base imports no vendor module, so importing ep_uccl never
transitively requires deep_ep (absent on the UCCL image).

Re-target the three adapter-genuineness scrape-tests to follow the moved
code into the base file rather than deleting them, preserving all CPU-side
coverage of the un-runnable native low-latency calls. Suite stays 239 green.

重构:通过 family 基类共享 DeepEP/UCCL 适配器公共代码

UCCL 的 uccl_deepep.Buffer 是 DeepEP API 的克隆,因此 ep_deepep.py 与
ep_uccl.py 存在约 250 行逐字节相同的操作代码(模式处理、dispatch/combine、
expert-packed 检查、fp8 反量化)。将其提取到不依赖 deep_ep 的
DeepEPFamilyBackend 基类;各具体适配器仅保留各自的原生 buffer 导入、
create_buffer 溯源与清理逻辑。基类不导入任何厂商模块,因此导入 ep_uccl
不会间接引入 deep_ep(UCCL 镜像中并不存在)。

将三个适配器真实性源码扫描测试重定向到基类文件以跟随迁移后的代码,而非删除,
从而保留对不可运行的原生 low-latency 调用的全部 CPU 侧覆盖。测试保持 239 通过。
Lift the B300 EP16 topology wall now that cross-node dispatch/combine is
proven over the 400G RoCE GPU-fabric rails (world=16 all-reduce PASS on
adjacent B300 nodes); the prior wall tested only the storage-IB rails.

- capability.py: clear TOPOLOGY_CELL_OVERRIDES (B300 EP16 -> runnable for
  deepep, deepep-v2, deepep-hybrid, nccl-ep; uccl/mori stay unsupported)
- collectivex-sweep.yml: add B300 network overlay slot (RoCE selector kept
  in the write-only per-SKU secret, never in-tree)
- publisher.py: recompute CANONICAL_FULL_V1 matrix + case-catalog digests
- tests: update matrix count contract (49->53 shards, 172 runnable) and
  B300 EP16 disposition expectations

启用 B300 EP16 跨节点(经 RoCE 网络)

已验证跨节点 dispatch/combine 可经 400G RoCE GPU 网络链路运行(相邻 B300
节点 world=16 all-reduce 通过);此前的限制仅测试了存储 IB 链路。

- capability.py:清空 TOPOLOGY_CELL_OVERRIDES(B300 EP16 对 deepep、
  deepep-v2、deepep-hybrid、nccl-ep 变为可运行;uccl/mori 仍不支持)
- collectivex-sweep.yml:新增 B300 网络覆盖槽位(RoCE 选择器仅存于只写的
  分 SKU secret,绝不入库)
- publisher.py:重新计算 CANONICAL_FULL_V1 矩阵与用例目录摘要
- tests:更新矩阵计数契约(49->53 个分片,172 个可运行)及 B300 EP16
  处置预期
B300 EP16 runs over the RoCE GPU-fabric: nccl-ep and the DeepEP Hybrid
control path (native DOCA transport) execute there and stay runnable.
DeepEP V1/V2 cannot -- their internode path is NVSHMEM-IBGDA, which needs
GDRCopy /dev/gdrdrv, and that char device is unprovisioned on B300 hosts
(module loaded, no device node; gdr_open returns NULL). Wall (b300,
deepep|deepep-v2, 16) at the topology layer and keep their precision cells
unsupported. Cascade the frozen digests/counts: full V1 matrix is now 51
shards / 322 cases / 167 runnable / 155 unsupported; refresh the publisher
canonical matrix + case-catalog digests, the qualification execution-plan
digests, and the pinned CPU-test expectations.

B300 EP16 在 RoCE GPU 光纤上运行:nccl-ep 与 DeepEP Hybrid 控制路径
(原生 DOCA 传输)可执行,保持可运行。DeepEP V1/V2 不行——其跨节点
路径为 NVSHMEM-IBGDA,需要 GDRCopy /dev/gdrdrv,而该字符设备在 B300
主机上未配置(模块已加载但无设备节点;gdr_open 返回 NULL)。在拓扑层
封禁 (b300, deepep|deepep-v2, 16),并将其精度单元保持为 unsupported。
级联更新冻结摘要与计数:完整 V1 矩阵现为 51 分片 / 322 用例 /
167 可运行 / 155 不支持;刷新 publisher 规范矩阵与用例目录摘要、
资格执行计划摘要,以及固定的 CPU 测试期望值。
Collapse the publication requirement from three independent allocations
to one. Set REQUIRED_ALLOCATIONS=1 so the count-based eligibility,
promotion-coverage, promote, and bundle-id gates all repoint to a single
qualified run at qualification index 1. Drop the cross-allocation
stability apparatus entirely (it cannot be measured from one allocation):
remove the per-point stability block, the stable_p50/stable_p99/
p50_max_min_ratio/p99_max_min_ratio eligibility fields, the unstable-p50/
unstable-p99 reasons, the _ratio helper, and the pointStability schema
def. Keep within-run trial drift/outlier diagnostics and cross-backend
cohort ranking, collapsing their run dimension to REQUIRED_ALLOCATIONS.

The qualification-index machinery (indices 1..3) is untouched: it defines
deterministic execution orderings, not a publication count. v1 publication
pins to index 1.

Full unittest battery: 239 OK.

从三次独立分配收敛为单次发布 v1。将 REQUIRED_ALLOCATIONS 置为 1,使
基于计数的资格、晋升覆盖、晋升与 bundle-id 门槛统一指向资格索引 1 的
单次合格运行。彻底移除跨分配稳定性机制(单次分配无法测量):删除
每点 stability 块、stable_p50/stable_p99/p50_max_min_ratio/
p99_max_min_ratio 资格字段、unstable-p50/unstable-p99 原因、_ratio
辅助函数以及 pointStability schema 定义。保留运行内 trial 漂移/离群
诊断与跨后端 cohort 排名,将其运行维度收敛为 REQUIRED_ALLOCATIONS。

资格索引机制(索引 1..3)保持不变:它定义确定性执行顺序,而非发布
计数。v1 发布固定使用索引 1。
Collapse the publish-v1 job to a single source run. Replace the
publish_run_ids input (three comma-separated IDs) with publish_run_id,
drop the count-of-three and uniqueness checks and the shared-source-SHA
loop, pin the per-run release marker to qualification_index == 1, and
require the aggregate qualification-index set to be exactly {1}. The
setup job's qualification-index input and per-index release-marker
digest check are unchanged (layer 2). Update test_sampling_contract.py
publish-job string assertions to the single-run messages.

Full unittest battery: 239 OK.

将 publish-v1 作业收敛为单一来源运行。以 publish_run_id 替换
publish_run_ids(三个逗号分隔的 ID),移除计数为三与唯一性检查以及
共享来源 SHA 的循环,将每次运行的发布标记固定为 qualification_index
== 1,并要求聚合资格索引集合恰为 {1}。setup 作业的资格索引输入与
按索引的发布标记摘要校验保持不变(第二层)。同步更新
test_sampling_contract.py 中 publish 作业的字符串断言为单次运行消息。
Update the tracked docs to match the single-allocation publication:
README, methodology, and README_zh now describe accepting one
successful first-attempt run ID at qualification index 1 (not three
run IDs at indices 1, 2, 3). Replace the cross-allocation p50/p99
repeat-stability language in the decision-grade contract with the
retained within-run trial drift/outlier diagnostics.

更新受版本管理的文档以匹配单次分配发布:README、methodology 与
README_zh 现描述接受一个位于 qualification index 1 的成功首次尝试
run ID(而非索引 1、2、3 的三个 run ID)。将 decision-grade 契约中
的跨分配 p50/p99 重复稳定性表述替换为保留的运行内 trial 漂移/离群
诊断。
Relax v1 promotion so a v1-tagged run can publish any subset of the
canonical matrix, not only the full 8-SKU matrix. A partial promotion
carries coverage_scope="partial" + covered_skus and is validated by
canonical membership (every case is a real canonical case with the
canonical disposition, proper subset) rather than the full catalog-sha /
matrix-id pin. Quality gates are unchanged: partial cohorts must still be
decision-grade and runnable cases must still succeed. Full promotions keep
the exact catalog pin, fixed cohort counts, and non-empty rankings.

放宽 v1 发布门槛:带 v1 标签且成功的运行可发布规范矩阵的任意子集,
而不再要求覆盖全部 8 种 SKU。部分发布携带 coverage_scope="partial"
与 covered_skus,通过规范成员校验(每个用例均为真实规范用例、判定一致、
且为真子集)而非完整目录哈希/矩阵 ID 绑定。质量门槛不变:部分队列仍须
达到 decision-grade,可运行用例仍须成功;完整发布保留精确目录绑定、
固定队列数量与非空排名要求。
Model the release version as a first-class numeric field N (1, 2, 3, ...),
not a hardcoded "v1" tag. The release_tag workflow input becomes a string
validated as 'unversioned' or a positive integer; the release marker carries
"version": N plus a coverage_scope ("full"/"partial"); publish and refresh
discover the version version-agnostically from the marker/artifact name via
regex and re-emit it in cxrelease-<N>-* / cxpublication-<N>-* names. A new
autopublish job self-publishes a successful first-attempt versioned sweep so
the frontend can discover many runs of a version JIT. The frozen data-format
literals (collectivex.release-tag.v1, collectivex.public.v1, the
collectivex_public_v1_<digest>.ndjson filename) are schema-version, shared
across releases, and stay unchanged.

将发布版本建模为一等的数值字段 N(1、2、3……),而非硬编码的 “v1” 标签。
release_tag 工作流输入改为字符串,校验为 'unversioned' 或正整数;发布标记
携带 "version": N 及 coverage_scope("full"/"partial");publish 与 refresh
通过正则从标记/产物名中与版本无关地发现版本号,并在 cxrelease-<N>-* /
cxpublication-<N>-* 名称中重新输出。新增 autopublish 作业让一次成功的
首次尝试带版本 sweep 自行发布,便于前端即时发现某版本的多个运行。冻结的
数据格式字面量(collectivex.release-tag.v1、collectivex.public.v1、
collectivex_public_v1_<digest>.ndjson 文件名)属于 schema 版本,跨发布共享,
保持不变。
Add an --exclude-skus comma-list to sweep_matrix.resolve_matrix (and the
sweep_matrix.py CLI + collectivex-sweep.yml exclude_skus dispatch input) so a
versioned "tagged + success" run can drop whole runner pools whose clusters are
unavailable and still publish the SKUs it can exercise. Excluding a pool only
omits its cells; every surviving requested case stays byte-identical to the full
matrix, so the publisher's canonical-catalog binding still forbids inventing or
reclassifying cases. Disjoint from --only-sku; unknown pools are rejected.

The release marker already records coverage_scope=partial for any non-canonical
matrix digest and autopublish already accepts full OR partial, so this completes
the incomplete-matrix gate end to end.

为部分版本化 sweep 增加 --exclude-skus:允许在集群不可用时剔除整个 runner
池,仍以"打标签 + 成功"发布可运行的 SKU。剔除只省略对应单元格,存活的请求用例
与完整矩阵逐字节一致,发布器的规范目录绑定仍禁止伪造或重新分类用例。与
--only-sku 互斥,未知池会被拒绝。
Drop the first-attempt-only gate on promotion/autopublish so a re-run
whose legs re-emit at one consistent attempt (e.g. attempt 2) can still
publish. Provenance stays single-attempt: every included shard must share
the same run and attempt; only the "attempt must be 1" restriction is
removed.

- publisher: remove the run_attempt != 1 term from the promote gate;
  message now "qualification index 1 from a single versioned run"
- workflow: drop github.run_attempt == '1' from the autopublish `if`;
  manual publish accepts any positive attempt
- tests: cover promotion of a non-first-attempt run; update workflow
  string assertions

放开仅限首次尝试的发布门槛:重跑(各腿在同一次一致的尝试号,如第 2 次)
也可发布。来源仍为单次尝试——所有分片须同属一次运行且尝试号一致,仅移除
"尝试号必须为 1"的限制。
Promotion no longer aborts a whole run because some series or cohorts are
non-decision-grade. The publisher computes each series' and cohort's quality
verdict and attaches it to every one of them in the published dataset, then
publishes everything: diagnostic series/cohorts ship labelled
(status:"diagnostic" / decision_grade:false) with their eligibility.reasons,
never dropped. The consumer (frontend) decides what to surface and may reveal
flagged series. This splits selection (what to show) from validation (is the
number real): validation stays on the backend, the display decision moves out.

Honesty floor kept: _require_runnable_promotion_success (every runnable case
physically produced clean data) and at least one decision-grade series are still
required for a promoted "v1" to mean something. _require_promotion_cohorts full
scope keeps only structural checks (required kinds present, exact counts,
derived-chip count); the partial scope no longer requires surfaced cohorts to be
decision-grade.

Also fixes three latent bugs unmasked once the veto stopped aborting first:
- schema trial_count const 192 -> 64 (single-run migration leftover; real data
  emits 64), plus the same stale value in a test fixture.
- zero-logical-byte measured components (UCCL host-staging "stage": real
  latency, total_logical_bytes:0 -> 0.0 GB/s) could not satisfy the rate schema
  (percentiles, exclusiveMinimum:0) yet the validator requires a rate. Add
  ratePercentiles (minimum:0) for the two component rate fields; latency stays
  strict.
- rankings-coverage validator now skips precision-pair cohorts (display-only
  paired contrasts) to match the ranking generator, which already skips them.

Tests: the three promote-gate tests that asserted abort-on-non-decision-grade
now assert mixed bundles promote and ship both verdicts, plus the retained
all-diagnostic -> floor rejection; new schema contract test locks zero-byte
rates and trial_count=64. Full CPU battery 243 green. Verified end-to-end on a
real green run: promote -> schema-valid dataset shipping decision-grade +
diagnostic series each with its verdict.

发布不再因为部分 series 或 cohort 非 decision-grade 就整体中止。publisher 计算
每个 series 和 cohort 的质量判定并将其附加到已发布数据集中的每一项上,然后发布
全部内容:诊断类 series/cohort 带标签发布(status:"diagnostic" /
decision_grade:false)并附带 eligibility.reasons,绝不丢弃。由消费端(前端)
决定展示哪些,并可显示被标记的 series。这将“选择展示什么”与“校验数值是否真实”
分离:校验留在后端,展示决策移出。

保留诚实底线:_require_runnable_promotion_success(每个可运行 case 确实产出了
干净数据)以及至少一个 decision-grade series 仍是已发布“v1”的必要条件。同时
修复 veto 移除后暴露的三个潜藏缺陷:schema trial_count 常量 192->64、零逻辑
字节测量分量的 0.0 GB/s 速率(新增 ratePercentiles,minimum:0)、rankings
覆盖校验跳过 precision-pair cohort。全量 CPU 测试 243 通过。
… runs

Mirror --max-nodes with an --ep-sizes / ep_sizes filter in
sweep_matrix.resolve_matrix and the sweep workflow: a dispatch-time narrower
that keeps only shards whose expert-parallel degree is in the given comma-list.
Passing "8" drops EP16 so a single comprehensive run can co-schedule the 8-GPU
SKUs' single-node EP8 with the GB200/GB300 two-node EP8, without dispatching any
EP16 leg (EP16 walls on H100's zero-RDMA topology and would block all-green
autopublish). Blank keeps every degree, so the canonical matrix stays
byte-identical and its frozen digest does not drift; a filtered run publishes
as a partial subset, exactly like --exclude-skus / --max-nodes.
…on probe

Front-load the deletions that would otherwise break the CPU battery on
every subsequent refactor commit, leaving a stable keep-set.

- delete publisher.py (pure CLI leaf; only test_publisher imported it)
- delete test_publisher, test_sampling_contract, test_deepep_v2_contract,
  test_precision_scheduling (AST/source-text/frozen-digest suites)
- delete probe_precision.py (swept precision-dimension probe)
- trim test_ep_precision_adapters to the behavioral resolution tests

Battery: 48 tests OK.
…rtifacts

Reset scope to the MVP: schedule benchmarks, execute, validate emissions, and
upload neutral artifacts. Remove promotion, ranking, recommendation, channels,
bundles, and the qualification-repeat machinery. The frontend owns all display
and coverage decisions from the neutral matrix/result/summary artifacts. This
deliberately breaks the current frontend publication reader; that migration is a
separate effort.

Schemas: delete the channel, private-bundle, and public-dataset schemas. Strip
the precision, qualification-index, and allocation-stratum dimensions from
raw-case and terminal-outcome. Keep raw-case, samples, and terminal-outcome as
the neutral contract.

Contracts: validate document shape with jsonschema and keep only the cross-file
Python checks (scheduled-case-ID equality, detached-sample path/hash, one
terminal per scheduled case, complete shard delivery). Fold the artifact-safety
privacy boundary into validate_delivery and terminal emission, then delete
artifact_safety.py.

Matrix/capability/identity: drop the precision dimension and min/max_nodes;
schedule EP8/EP16 via ep_degrees plus an ep-sizes filter. Relocate the FP8
precision catalog from identity to ep_precision, where it stays callable but
dormant (default sweep is BF16 only). Remove required_publication from harness
emission so its case identity matches the matrix.

Tests trimmed to the neutral contract (four files, 731 LOC). CPU battery green;
matrix gen -> extract -> validate-control round-trips clean.
Drop the probe-summary, autopublish, publish, and refresh jobs together
with the release-marker/digest logic and the 11 publication-oriented
inputs. The workflow now only resolves the shard matrix and executes each
cell, uploading neutral matrix and per-shard result artifacts. Display,
promotion, ranking, and dataset building move out of this repo.
Relocate the 15 runnable EP modules (adapters, harness, routing,
workload, eplb, precision codec, run_ep/make_workloads entrypoints) from
tests/ into a dedicated bench/ directory so tests/ holds only unit tests.
Retarget sweep_matrix's importer, the two unit tests' path setup, the
runtime shell launch paths, and run_ep's stamped reproduction command.
Sibling and top-level imports resolve via file-relative sys.path, so the
move is mechanical; probe_precision paths stay pending removal in the
runtime prune.
…rovenance.py

The emitter's implementation-provenance evidence builders (DeepEP MNNVL
resolution, NCCL/RCCL lineage, resource projection, backend-provenance issue
detection, hybrid/deepep-v2 JIT evidence checks, content-manifest hashing) are
cross-field facts a JSON Schema cannot express, but they are an emitter concern,
not part of the neutral document/delivery validation boundary. Relocate the
twelve-function cluster beside the bench modules that call it, so contracts.py
holds only neutral validation. Rewire the five emitter modules (ep_deepep,
ep_deepep_hybrid, ep_harness, ep_nccl, ep_uccl) to ep_provenance. Drop two dead
helpers (backend_version, hybrid_communication_domains) with no callers.

contracts.py now holds only neutral validation (1009 LOC).
The publication-subsystem strip (b09a270) removed four emit-side projection
helpers from contracts.py on the premise that their only consumers were the
precision validator and publisher.py. run_sweep's emit tail still calls all of
them, so the real (GPU) emit path raised AttributeError at runtime; the
FakeBackend unit smoke never reaches that tail, so nothing surfaced it.

Relocate the emit-side projection helpers next to the other backend-provenance
projections in bench/ep_provenance.py and rewire the ep_harness call sites:
- series_provenance (privacy projection: drops host path/jit_cache_key/
  jit_shared_objects/sm_fraction, projects source-built libs + jit cubins)
- public_series_config / public_series_config_sha256
- routing_implementation_control_sha256
- backend_version (+ a local canonical _sha256_json)

series_id stays load-bearing: validate_samples_document derives the detached
sample point path from it (identity.point_id(series=...)), and identity + the
schemas still require it. This is completed relocation, not removal.

Also remove the emitter's publication-disposition logic: _derive_publication_status
and the outcome.publication_status field. The raw-case schema's outcome object is
additionalProperties:false over {reasons,status,validity}, so any real emission
carrying publication_status would have failed validation; deciding publication
disposition is out of scope for the sweep backend.
… shell

The MVP sweep schedules, executes, validates, and uploads neutral
artifacts only; it does not run the swept precision-probe path and no
longer carries a required-publication signal. Remove the precision-probe
dispatch, control-field parsing, manifest validation, and failure-class
branches from runtime/common.sh and runtime/run_in_container.sh, and drop
the CX_REQUIRED_PUBLICATION / --required-publication plumbing (the arg no
longer exists on the harness). Keep the BF16-default --precision-profile
and --qualification-index paths (FP8 stays callable-but-dormant), and
preserve every cluster execution fix untouched.
CollectiveX now schedules, executes, validates, and uploads neutral
artifacts and no longer promotes, ranks, recommends, or decides display.
Rewrite README.md and docs/methodology.md to describe the setup+sweep
workflow, BF16-default measurement (FP8 codec callable but not swept),
per-dispatch matrix (no frozen digest/qualification), and neutral
validate-delivery + artifact upload. Drop all publication/promotion/
dev-latest/publisher/cohort/recommendation content; preserve the
measurement, correctness, execution-isolation, and image-pinning
contracts unchanged. Remove the language switcher from the English docs
(existing _zh files left untouched per the English-only override).
The P1 schema strip removed publication/precision/qualification fields
from raw-case-v1, but the emitter in bench/ep_harness.py still wrote
them, so contracts.validate_raw_document would reject every emitted case
(including the default BF16 sweep) on the cluster.

Remove the fields the stripped schema no longer accepts:
- case.shape: precision_profile, dispatch_precision, combine_precision
- identity.series_factors: implementation_contract_sha256,
  public_config_sha256, routing_control_sha256, runtime_fingerprint_sha256
- identity.allocation_factors / measurement / samples: qualification_index
- measurement.execution_order_sha256 (+ dead observed_component_orders)
- provenance.allocation_stratum_sha256
- provenance.git_run.qualification_index (bench/run_ep.py)

Add tests/test_raw_document_roundtrip.py: a hermetic regression test that
builds a full raw + samples document from the real identity module and
drives contracts.validate_raw_document, asserting the BF16 fixed facts
and rejecting each removed drift field. Closes the FakeBackend coverage
gap where no test exercised the emitter->validate contract on CPU.
sweep_matrix.py imports contracts unconditionally, and its
--validate-control / --extract-from paths are documented (and relied on
by cx_validate_shard_control in runtime/common.sh) to run on a minimal
GPU-runner python3 with only the standard library. The P2 rewrite added
a module-level `from jsonschema import Draft202012Validator` to
contracts.py, which made importing contracts (hence sweep_matrix) fail on
any host without jsonschema, breaking host-side shard validation before
allocation.

Defer the jsonschema import into contracts._validator(), which is the
only runtime user of the symbol (all annotations are strings under
`from __future__ import annotations`). Importing contracts no longer
requires jsonschema; document validation still imports it on first use.
…hema

The schema/publication trim removed the precision-evidence key and the
allocation_stratum_sha256 provenance parameter, but the raw-document emitter
still wrote correctness.precision (rejected by raw-case schema
additionalProperties) and still passed allocation_stratum_sha256 to
provenance_complete() (now a TypeError). It also left two orphaned probe_stage
branches in cx_fail_stage() that abort the launcher under set -u.

These desyncs are invisible to the CPU unit battery (run_sweep imports torch and
never executes off-GPU); a live 8xH100 nccl-ep sweep surfaced all three. Fix:
drop the dead precision gather + correctness.precision emit, drop the
allocation_stratum_sha256 kwarg, and remove the probe_stage branches. Full
offline schema reconciliation confirms zero remaining forbidden keys.

Verified green on 8xH100 (nccl-ep, EP8, BF16): both prefill and decode emit
status=success case-attempt docs + detached samples; summarize produces the
neutral table.
contracts.py now depends on jsonschema (P2 moved document validation onto it).
The setup job and self-hosted sweep runners ship a bare python3 with no system
pip, so validate-delivery / emit-terminal / summarize invocations of bare
python3 could not resolve jsonschema. Add jsonschema to the matrix-deps install
and build a host venv (venv+ensurepip, available where system pip is not) on
PATH for every sweep step.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

Development

Successfully merging this pull request may close these issues.

2 participants