8000 Polish v1 readiness and release evidence layout by DaoyuanLi2816 · Pull Request #88 · DaoyuanLi2816/mini-verl · GitHub
[go: up one dir, main page]

Skip to content
  • Merged
    Show file tree
    Hide file tree
    Changes from all commits
    Commits
    File filter

    Filter by extension

    Filter by extension


    Conversations
    Failed to load comments.
    Loading
    Jump to
    Jump to file
    Failed to load files.
    Loading
    Diff view
    Diff view
    80 changes: 29 additions & 51 deletions .github/workflows/release.yml
    < 8000 th scope="col">Diff line number
    Original file line number Diff line change
    Expand Up @@ -126,57 +126,26 @@ jobs:
    with:
    name: verified-qualified-candidate
    path: accepted
    - name: Revalidate and copy the identical candidate bytes
    - name: Revalidate and prepare canonical identical assets
    run: |
    python -m pip install .
    python scripts/build_release_candidate.py --output accepted/candidate --commit "$GITHUB_SHA" --check
    python - <<'PY'
    import hashlib, json, pathlib, shutil
    root = pathlib.Path("accepted")
    candidate = root / "candidate"
    qualification_root = root / "qualification"
    qualification = qualification_root / "qualification.json"
    manifest_path = candidate / "candidate-manifest.json"
    manifest = json.loads(manifest_path.read_text(encoding="utf-8"))
    manifest_sha = hashlib.sha256(manifest_path.read_bytes()).hexdigest()
    record = json.loads(qualification.read_text(encoding="utf-8"))
    if record["candidate"]["manifest_sha256"] != manifest_sha:
    raise SystemExit("qualification candidate-manifest binding changed")
    if record["wheel"]["sha256"] != manifest["wheel"]["sha256"]:
    raise SystemExit("qualification wheel binding changed")
    output = pathlib.Path("release-distributions")
    (output / "dist").mkdir(parents=True)
    for key in ("wheel", "sdist"):
    shutil.copy2(candidate / manifest[key]["filename"], output / "dist")
    shutil.copy2(candidate / "SHA256SUMS", output / "SHA256SUMS")
    shutil.copy2(manifest_path, output / "candidate-manifest.json")
    shutil.copy2(qualification, output / "qualification.json")
    shutil.copy2(root / "verification.json", output / "verification.json")
    evidence_output = output / "qualification-evidence"
    evidence_output.mkdir()
    version = manifest["miniverl_version"]
    evidence_files = [("full-qualification", qualification)]
    evidence_files.extend(
    (item["name"].replace("_", "-"), qualification_root / item["path"])
    for item in record["artifacts"]
    )
    sums = []
    for label, source in evidence_files:
    suffix = source.suffix or ".bin"
    target = evidence_output / f"miniverl-{version}-{label}{suffix}"
    shutil.copy2(source, target)
    digest = hashlib.sha256(target.read_bytes()).hexdigest()
    sums.append(f"{digest} {target.name}")
    (output / "qualification-full-SHA256SUMS").write_text(
    "\n".join(sums) + "\n", encoding="utf-8", newline="\n"
    )
    for key in ("wheel", "sdist"):
    copied = output / "dist" / manifest[key]["filename"]
    if hashlib.sha256(copied.read_bytes()).hexdigest() != manifest[key]["sha256"]:
    raise SystemExit(f"copy changed {key} bytes")
    PY
    python scripts/prepare_release_assets.py \
    --candidate-dir accepted/candidate \
    --candidate-manifest accepted/candidate/candidate-manifest.json \
    --qualification-root accepted/qualification \
    --qualification accepted/qualification/qualification.json \
    --verification accepted/verification.json \
    --output release-distributions
    python scripts/prepare_release_assets.py \
    --candidate-dir accepted/candidate \
    --candidate-manifest accepted/candidate/candidate-manifest.json \
    --qualification-root accepted/qualification \
    --qualification accepted/qualification/qualification.json \
    --verification accepted/verification.json \
    --output release-distributions --check
    (cd release-distributions/dist && sha256sum --check ../SHA256SUMS)
    (cd release-distributions/qualification-evidence && sha256sum --check ../qualification-full-SHA256SUMS)
    (cd release-distributions && sha256sum --check qualification-SHA256SUMS)
    - name: Upload the identical qualified distributions
    uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
    with:
    Expand Down Expand Up @@ -208,7 +177,7 @@ jobs:
    - name: Verify bytes immediately before publication
    run: |
    (cd release-artifacts/dist && sha256sum --check ../SHA256SUMS)
    (cd release-artifacts/qualification-evidence && sha256sum --check ../qualification-full-SHA256SUMS)
    (cd release-artifacts && sha256sum --check qualification-SHA256SUMS)
    - name: Determine whether exact candidate bytes already exist on PyPI
    id: pypi-state
    run: |
    Expand Down Expand Up @@ -286,12 +255,21 @@ jobs:
    GH_TOKEN: ${{ github.token }}
    run: |
    (cd release-artifacts/dist && sha256sum --check ../SHA256SUMS)
    WHEEL=$(python -c 'import json; print(json.load(open("release-artifacts/candidate-manifest.json"))["wheel"]["filename"])')
    SDIST=$(python -c 'import json; print(json.load(open("release-artifacts/candidate-manifest.json"))["sdist"]["filename"])')
    gh release create "$GITHUB_REF_NAME" \
    release-artifacts/dist/* \
    "release-artifacts/dist/$WHEEL" \
    "release-artifacts/dist/$SDIST" \
    release-artifacts/SHA256SUMS \
    release-artifacts/qualification-full-SHA256SUMS \
    release-artifacts/qualification-evidence/* \
    release-artifacts/candidate-manifest.json \
    release-artifacts/release-verification.json \
    release-artifacts/qualification.json \
    release-artifacts/qualification-SHA256SUMS \
    release-artifacts/qualification-release-smoke.json \
    release-artifacts/qualification-direct-gkd.json \
    release-artifacts/qualification-pg-k1.json \
    release-artifacts/qualification-smollm2.json \
    release-artifacts/qualification-evidence.tar.gz \
    release-artifacts/qualification-evidence-manifest.json \
    --repo "$GITHUB_REPOSITORY" --verify-tag \
    --title "miniVERL $GITHUB_REF_NAME" --generate-notes
    12 changes: 12 additions & 0 deletions CHANGELOG.md
    8000
    Original file line number Diff line number Diff line change
    Expand Up @@ -6,6 +6,18 @@ All notable changes to miniVERL are recorded here. The format follows

    ## [Unreleased]

    ### Release evidence polish

    - Corrected the v1-readiness record after the completed v0.10.1 attempt-1 RTX
    4080 qualification, dry-run and publication while keeping the remaining v1
    stability gates explicitly unsatisfied.
    - Added a torch-free canonical future release-asset builder with explicit
    principal-result mapping, deterministic subordinate-evidence archive,
    separate checksum scopes and fail-closed filename/path validation.
    - Replaced future wildcard evidence uploads and inline filename flattening with
    one validated, explicit GitHub Release asset list. Existing v0.10.1 assets and
    all published bytes remain immutable.

    ## [0.10.1] - 2026-08-16

    ### Product maturity and release qualification
    Expand Down
    9 changes: 6 additions & 3 deletions PROJECT_STATE.md
    Original file line number Diff line number Diff line change
    Expand Up @@ -37,9 +37,12 @@ exports bind an independent identity. The direct-GKD and sampled-k1 vanilla
    policy-loss profiles both have pinned conformance and measured RTX 4080 paths.
    Portable hardware records preserve measured, estimated and unknown states;
    schema-valid community submissions remain unreviewed until maintainer validation.
    The development release chain now builds one hosted-runner candidate and binds
    the private RTX 4080 qualification to that exact manifest and wheel. Release
    jobs may publish only the same verified bytes and never rebuild them.
    The release chain builds one hosted-runner candidate and binds the maintainer's
    RTX 4080 qualification to that exact manifest and wheel. Release jobs may
    publish only the same verified bytes and never rebuild them. Future GitHub
    Releases use a canonical, explicit evidence layout with four principal JSON
    records and one deterministic subordinate-evidence archive; historical v0.10.1
    asset names remain immutable.

    Arbitrary verl YAML, other policy-gradient modes, rewards, PPO/GRPO, Ray, FSDP,
    Megatron, multi-GPU and distributed execution remain unsupported. The legacy
    Expand Down
    11 changes: 10 additions & 1 deletion docs/release-checklist.md
    Original file line number Diff line number Diff line change
    Expand Up @@ -8,6 +8,13 @@ after the exact release commit and its remote checks are green.

    - [ ] Define and review the next focused release scope before changing product
    behavior or scientific evidence.
    - [x] Correct v1-readiness prose to record the completed v0.10.1 same-run
    attempt-1 qualification while retaining explicit unsatisfied v1 gates.
    - [x] Prepare future GitHub Release evidence with the torch-free canonical
    builder: four principal JSON files, one deterministic subordinate archive,
    one archive manifest and separate distribution/qualification checksums.
    - [x] Require the future publisher to enumerate every release asset explicitly;
    no raw qualification directory or wildcard evidence upload is accepted.

    ## v0.10.1 release

    Expand All @@ -29,7 +36,9 @@ after the exact release commit and its remote checks are green.
    `candidate-distributions` and `gpu-full-qualification`, preserve the
    complete evidence set and perform no distribution rebuild.
    - [x] Require release assets to contain versioned full qualification records
    and `qualification-full-SHA256SUMS`; release smoke alone is not accepted.
    and the historical `qualification-full-SHA256SUMS`; release smoke alone
    is not accepted. These v0.10.1 names remain immutable, while future
    releases use the canonical layout documented above.

    ### Maintainer self-review for v0.10.1

    Expand Down
    54 changes: 46 additions & 8 deletions docs/release-qualification.md
    Original file line number Diff line number Diff line change
    Expand Up @@ -52,12 +52,47 @@ artifact digest when provided, wheel byte hash and qualification bindings. It
    also checks each declared evidence file's regular-file type, byte count and
    SHA-256. Cross-origin artifact redirects retain ordinary API headers but strip
    authorization, proxy authorization and cookies. It publishes the accepted
    wheel and sdist without rebuilding them. The workflow and GitHub Release retain
    the full qualification record, declared evidence files and a separate checksum
    inventory under versioned names. A committed
    wheel and sdist without rebuilding them. Future release runs retain the full
    qualification record, four principal workload JSON files and a deterministic
    subordinate-evidence archive in the canonical layout below. A committed
    JSON file, manual upload, fork run, different workflow or cross-run artifact
    pair cannot satisfy this gate.

    ## Canonical future Release assets

    The release-asset builder uses an explicit evidence-role mapping. It never
    derives public filenames from internal paths or appends a guessed extension.
    The top level is exactly:

    ```text
    dist/<wheel and sdist>
    SHA256SUMS
    candidate-manifest.json
    release-verification.json
    qualification.json
    qualification-SHA256SUMS
    qualification-release-smoke.json
    qualification-direct-gkd.json
    qualification-pg-k1.json
    qualification-smollm2.json
    qualification-evidence.tar.gz
    qualification-evidence-manifest.json
    ```

    The four principal workload records remain directly inspectable. Adapter
    configuration and safetensors, its miniVERL manifest, input prompts and the run
    summary live once in the deterministic archive; its manifest binds every member
    to its semantic role, byte count and SHA-256. `SHA256SUMS` covers only the wheel
    and sdist. `qualification-SHA256SUMS` covers the nine provenance and evidence
    assets in canonical order. Hashes prove byte integrity, not code signing or
    third-party endorsement.

    The historical v0.10.1 assets are immutable and retain the names emitted by the
    original flattening step, including duplicated suffixes on some subordinate
    files. They are not rewritten or re-uploaded. The canonical layout applies to
    future releases only and does not prove distributed verl execution or hardware
    beyond the one measured RTX 4080.

    Local validation is torch-free:

    ```bash
    Expand Down Expand Up @@ -92,9 +127,12 @@ portable bounded artifacts, and checks cleanup targets remain under
    `GITHUB_WORKSPACE`. Model caches remain runner-local and are not uploaded.
    Rotate the runner token after suspected exposure.

    ## Activation state
    ## Measured release state

    Repository code, schema, validator and workflows can be complete while runner
    registration remains incomplete. Until the first successful exact-commit run
    exists, report `blocked_external_setup`; do not write “exact-commit
    qualification passed” or “continuous GPU CI”.
    The first exact-commit full run completed for v0.10.1 on attempt 1:
    [GPU qualification 31932226695](https://github.com/DaoyuanLi2816/mini-verl/actions/runs/31932226695),
    [dry-run 31933844796](https://github.com/DaoyuanLi2816/mini-verl/actions/runs/31933844796)
    and [tag publication 31934196365](https://github.com/DaoyuanLi2816/mini-verl/actions/runs/31934196365).
    Future release commits still require their own new exact-SHA, same-run, attempt-1
    full qualification; the v0.10.1 record cannot authorize them. This remains a
    manual maintainer process, not continuous GPU CI.
    25 changes: 24 additions & 1 deletion docs/troubleshooting.md
    Original file line number Diff line number Diff line change
    Expand Up @@ -39,6 +39,7 @@ Contents:
    - [Resume refused: config digest mismatch](#resume-refused-config-digest-mismatch)
    - [Known-good environment mismatch](#known-good-environment-mismatch)
    - [Release qualification not found](#release-qualification-not-found)
    - [Release asset layout check failed](#release-asset-layout-check-failed)

    ## Known-good environment mismatch

    Expand Down Expand Up @@ -70,18 +71,40 @@ no successful gpu.yml workflow_dispatch run exists for <sha>
    whose `head_sha` is the exact release commit. A successful run for a parent,
    another branch or a rebuilt local JSON is intentionally insufficient.

    **Fix.** Register the private runner with labels `[self-hosted, cuda,
    **Fix.** For a future release commit, register the private runner with labels `[self-hosted, cuda,
    rtx4080]`, review the candidate SHA, dispatch `gpu.yml` at that SHA, and wait
    for both `candidate-distributions` and `gpu-full-qualification` from that one
    run. Validate the full record with `miniverl qualification validate`.
    `gpu-release-smoke` is diagnostic only. Until the full record exists, the
    correct status is `blocked_external_setup`, not passed GPU CI.

    The v0.10.1 release already completed this process in attempt-1 run
    `31932226695`; that evidence is immutable and cannot qualify a later commit.

    If the run fails, do not use **Re-run jobs**. Qualification requires
    `GITHUB_RUN_ATTEMPT=1` to prevent cross-attempt artifact reuse. Fix the cause,
    make a fresh ephemeral runner available, then create a new
    `workflow_dispatch` run for the same reviewed SHA.

    ## Release asset layout check failed

    **Symptom**

    ```text
    unexpected release asset file set
    ```

    **Cause.** A future release output contains an extra file, a missing principal
    record, a checksum mismatch, an unsafe archive member or a noncanonical name.
    The builder deliberately rejects guessed extensions, case-insensitive filename
    collisions, symlinks and undeclared evidence.

    **Fix.** Do not rename or flatten evidence manually. Re-run
    `scripts/prepare_release_assets.py` from the accepted candidate, qualification
    root and verification record, then run the same command with `--check`. Keep
    PyPI publication restricted to `dist/` and enumerate the GitHub Release assets
    explicitly. Historical v0.10.1 filenames are preserved as-is.

    ## Missing extras

    **Symptom**
    Expand Down
    Loading
    0