Files
bc815b3272 feat(byo): apply-swap — download + serve a Route-C fine-tune (#628)
* feat(byo): apply-swap — download + serve a Route-C fine-tune

A curated-arch fine-tune (e.g. josefprusa/ThinkingCap-Qwen3.6-27B AutoRound
→ qwen3.6-27b) hard-stops at the pull gate's stratum-5 no-fit-model (correct
— nothing to price), so the c3 Bring [D] download failed with "download did
not complete". Route-C was pure guidance ("clone the compose, point --model
at your weights") with no code behind it — the deferred "swap_path apply".

Build it as a DISTINCT action that never touches the locked 6-stratum gate:

- scripts/lib/profiles/swap_apply.py (new): resolve the Route-C swap
  (arch→curated sibling + has_mtp_head from deriver), download the brought
  weights SHA-verified via downloader.download_model (a minimal EInput), and
  emit a serve-locally compose that CLONES the --profile-like sibling's REAL
  compose (keeping its curated chat-template / reasoning+tool parsers / MTP
  wiring — NOT the derived-vllm template, which drops all that) with --model
  re-pointed at a `<weights>:/brought-model:ro` mount, --served-model-name set
  to the brought basename, and --speculative-config kept iff the checkpoint
  carries an MTP head (else dropped). Written alongside the sibling compose as
  `_brought-<san>.yml` (gitignored) so its relative ../ mounts resolve.
- scripts/pull.sh: intercept `--apply-swap` in the wrapper (strictly additive,
  like --json) → run the apply-swap action; prints `[apply-swap] compose: …`.
  The gate path is byte-unchanged without the flag.
- c3: services.run_bring_download gains `apply_swap=` (appends --apply-swap +
  captures the emitted compose via last_swap_compose()); the [D] worker passes
  it on route==C and stashes the compose; ② Serve serves that swap compose
  directly (serve_generated) instead of a reproduction of the sibling's own
  catalog compose.
- pull.py: fix the stale NOTE — "the bf16 base won't fit and lacks the MTP
  head" → the base HAS the head; now just the size reason.

Tests: test-pull-swap.sh section 4 (emit: --model repointed, MTP kept/dropped
by head presence, curated flags + JSON args survive the YAML round-trip,
distinct container_name); test_services apply_swap flag + compose capture. The
locked-gate hard-stop tests stay green; --json fit-check byte-identical.
Validated live: pull.sh --json still no-fit-model+route-C, and apply_swap emits
a correct ThinkingCap swap compose (MTP kept).

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EfF565T9eSLaqGzidyJ1Pm

* c3: reframe Route-C fit-check card as ✓ Servable + a clear [D] action

The fit-check card for a curated-arch fine-tune (Route-C swap) was
self-contradicting: a RED "not eligible" + a bare "no-fit-model" token at
the top, then a GREEN "② Serve is armed with <sibling>" at the bottom —
which read as a dead-end AND named the wrong model (the sibling, not the
brought fine-tune). The engine's "no-fit-model" is the *means* (generic
fit-math can't price a curated-hybrid arch), not the user's answer; the
outcome is servable via the sibling's recipe.

Reframe the Route-C case in _byo_result_text:
- green "✓ Servable — a fine-tune of <sibling>" instead of red "not eligible",
- one plain "How it serves" line (reuses the sibling's chat-template / tools /
  spec-dec with your weights) instead of the --quantization/impl bullets,
- MTP kept/dropped line from has_mtp_head,
- the next-step is an explicit "→ Press [D] to download + serve <BROUGHT model>"
  (the fine-tune, not the sibling) — which #628's --apply-swap now fulfils,
- raw verdict + backend note dimmed for debugging, not the headline.

Non-swap cases (eligible / Route A / B / plain no-fit) are byte-unchanged.
Regression test added; c3 fast suite green.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EfF565T9eSLaqGzidyJ1Pm

---------

Co-authored-by: noonghunna <[email protected]>
Co-authored-by: Claude Opus 4.8 (1M context) <[email protected]>
2026-07-08 05:50:16 +05:00

1878 lines
84 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""v0.8.0 Pull-Gate — STEP P4: the `pull` orchestrator.
This is the keystone STEP. It chains the *frozen* predecessor slices into
the locked 6-stratum abort taxonomy, implements the two P4-owned decision
units (stratum-5 `no-fit-model` + the `[C1]` §4.1 total function), runs the
Path-A stratum-6 `[D]` dry-run, and emits via the *existing* `[D]`
generator. It owns the CLI flags. It NEVER edits a frozen module — it
imports P1 (`tools/kv-calc.py`), P2 (`deriver`), P3 (`gates`) and `[D]`
(`generate_compose`) read-only.
pull.py / pull.sh split
-----------------------
`scripts/pull.sh` is a thin argv pass-through (the established
`generate-compose.sh` / `diagnose-profile.sh` pattern): it resolves
`ROOT_DIR` and `exec`s `python3 scripts/lib/profiles/pull.py "$@"`. ALL
decision logic lives here in `pull.py` so it is unit-testable hermetically
(injected hardware-SM + injected fetcher + injected statvfs + injected
`[D]` runner — no live network, no GPU, no real emit in tests).
Public API (stable; the test consumes `run_pull`)
-------------------------------------------------
from scripts.lib.profiles import pull
res = pull.run_pull(
slug, profile_like, *,
path=None, # None -> auto (A if curated+--out else B)
dry_run=False, # force Path B
yes=False, # satisfy `confirm→proceed` --yes
force_download=False, # no-op + notice this phase
experimental_arch=False, # bypass ONLY no-arch-row
trust_remote_code=False, # bypass needs-trust-remote-code-ack
hf_home=None,
out=None, # Path A emit target
hardware_sm=None, # INJECTABLE (real detect when None)
fetcher=None, # INJECTABLE (real HTTP when None)
statvfs=None, # INJECTABLE (real os.statvfs when None)
d_runner=None, # INJECTABLE [D] dry-run/emit (real gc.generate)
profiles=None,
root=None,
) -> PullResult
`PullResult` is a frozen-ish dataclass carrying the terminal outcome, the
stratum at which the run stopped, the structured reason, and (Path A only,
on success) the emitted compose text. The truth-table test asserts against
its fields; the CLI renders it to stdout + an exit code.
`[C1]` §4.1
-----------
`c1_terminal(confidence, raw_verdict, flags)` reproduces the
`v0.8.x-design.md` §4.1 3×3 table EXACTLY as DATA (`_C1_TABLE`, a
`dict[(confidence, raw_verdict)] -> _Cell`). It is TOTAL over
`{exact, derived, estimated-lower-bound} × {fits-clean, fits-constrained,
wont-fit}`. The table is reproduced from `v0.8.x-design.md` lines 62-66
(the `### 4.1` table block). No cell was ambiguous.
"""
from __future__ import annotations
import importlib.util
import os
import sys
from dataclasses import dataclass, field
from enum import Enum
from pathlib import Path
from types import SimpleNamespace
from typing import Any, Callable, Optional
REPO_ROOT = Path(__file__).resolve().parents[3]
# Repo root on sys.path so `scripts.lib.profiles.*` absolute imports resolve
# whether this is imported as a module (tests) OR exec'd as a script
# (pull.sh) — same bootstrap pattern as generate_compose.py.
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from scripts.lib.profiles import deriver as D # noqa: E402 (P2, frozen — RO)
from scripts.lib.profiles import gates as G # noqa: E402 (P3, frozen — RO)
# ---------------------------------------------------------------------------
# P1 — tools/kv-calc.py via the documented sys.modules contract.
# ---------------------------------------------------------------------------
_KV = None
def _kv():
"""Load `tools/kv-calc.py` per the in-file import contract: register in
`sys.modules["kv_calc"]` BEFORE `exec_module` (kv-calc.py uses
@dataclass, which resolves `cls.__module__` via sys.modules during class
creation)."""
global _KV
if _KV is not None:
return _KV
if "kv_calc" in sys.modules:
_KV = sys.modules["kv_calc"]
return _KV
kv_path = REPO_ROOT / "tools" / "kv-calc.py"
spec = importlib.util.spec_from_file_location("kv_calc", kv_path)
mod = importlib.util.module_from_spec(spec)
sys.modules["kv_calc"] = mod # MUST precede exec_module
spec.loader.exec_module(mod)
_KV = mod
return _KV
# ---------------------------------------------------------------------------
# [D] — generate_compose, imported read-only (engine-pin resolver, scope-gate,
# and the full generate() path for the stratum-6 dry-run + real emit).
# ---------------------------------------------------------------------------
def _gc():
if str(REPO_ROOT) not in sys.path:
sys.path.insert(0, str(REPO_ROOT))
from scripts.lib import generate_compose as gc # noqa: E402
return gc
# ===========================================================================
# Terminal + stratum vocabulary (design-locked — never extended)
# ===========================================================================
class Terminal(str, Enum):
"""LOCKED §4.1 / §5.3 terminal set — EXACTLY these four, never more."""
PROCEED = "proceed"
CONFIRM_PROCEED = "confirm→proceed"
HARD_BLOCK = "hard-block"
OVERRIDE_ACCEPTED = "override-accepted"
# Frozen design-lock assertion target.
LOCKED_TERMINALS = frozenset(t.value for t in Terminal)
class Stratum(int, Enum):
"""Where a run stopped. 0 == ran to a [C1] terminal / Path-B verdict."""
DERIVER = 1 # stratum-1: deriver structured errors
PROFILE_LIKE = 2 # stratum-2: --profile-like precondition
C0 = 3 # stratum-3: [C0] engine-support / runtime / SM
C2A_DISK = 4 # stratum-4: [C2a] disk pre-gate
ELIGIBILITY = 5 # stratum-5: pre-[B] generic-dense eligibility
D_DRY_RUN = 6 # stratum-6: Path-A [D] dry-run refusal
DECIDED = 0 # reached [C1] / Path-B verdict (no abort)
@dataclass
class PullResult:
slug: str
profile_like: str
path: str # "A" | "B"
ok: bool # download-eligible / clean verdict
stratum: Stratum # where it stopped (DECIDED=ran through)
abort_reason: Optional[str] = None # structured machine reason
detail: str = ""
confidence: Optional[str] = None
raw_verdict: Optional[str] = None
terminal: Optional[str] = None # [C1] terminal (when [B] reached)
emitted: bool = False # Path A only: [D] actually emitted
compose_text: Optional[str] = None # Path A only, on emit
notices: list[str] = field(default_factory=list)
diagnostics: dict[str, Any] = field(default_factory=dict)
# ----- v0.8.0 [E] E4 additive outcome fields (CONTRACT-1) -------------
# None == the [E] stage did NOT run (curated Path-A, --dry-run Path-B,
# not-download-eligible, or CONTRACT-5 reject). Existing PullResult
# fields/semantics are UNCHANGED — these are purely additive so every
# pre-E4 test-pull.sh assertion is byte-unaffected.
download_ok: Optional[bool] = None # E2 download stage outcome
boot_ok: Optional[bool] = None # E3 derived boot outcome
smoke: Optional[dict[str, Any]] = None # E3 derived smoke result
capture_paths: list[str] = field(default_factory=list) # §6 artifacts
# ===========================================================================
# [C1] — §4.1 3×3 confidence × raw-verdict → terminal TOTAL FUNCTION.
#
# Reproduced EXACTLY (as DATA, not branching prose) from the locked
# v0.8.x design §4.1 (the confidence × raw-verdict table):
#
# | Confidence | fits-clean | fits-constrained | wont-fit |
# | exact | proceed(silent) | confirm→proceed | hard-block|
# | derived | confirm→proceed | confirm→proceed | advisory→ |
# | | (--yes) | (--yes + notice) | --force- |
# | | | | download→ |
# | | | | override- |
# | | | | accepted |
# | estimated-lower-bound | confirm→proceed | confirm→proceed | advisory→ |
# | | (--yes + floor) | (--yes + floor + | --force- |
# | | | notice) | download→ |
# | | | | override- |
# | | | | accepted |
#
# §4.1 footnote (design line 68): "Never silently gate-pass means precisely:
# only exact × fits-clean reaches proceed without --yes." No cell was
# ambiguous — every (confidence, raw_verdict) pair has exactly one row text.
# ===========================================================================
class _Need(str, Enum):
"""The flag a cell requires to reach its terminal."""
NONE = "none" # silent (only exact×fits-clean)
YES = "--yes" # confirm→proceed gate: --yes accepts
FORCE = "--force-download" # advisory: --force-download → override-accepted
BLOCK = "block" # unconditional hard-block (no flag clears it)
@dataclass(frozen=True)
class _Cell:
"""One §4.1 table cell, as data."""
base_terminal: Terminal # terminal the cell resolves to *when satisfied*
need: _Need # what flag (if any) the cell requires
note: str # the exact §4.1 parenthetical, surfaced to UX
_C = D.Confidence # exact / estimated-lower-bound (derived RESERVED, still mapped)
# The 9-cell table. KEY = (confidence-value, raw-verdict-string).
# This dict IS the spec; c1_terminal() is a pure lookup + flag interaction.
_C1_TABLE: dict[tuple[str, str], _Cell] = {
# --- exact -------------------------------------------------------------
(_C.EXACT.value, "fits-clean"): _Cell(
Terminal.PROCEED, _Need.NONE, "proceed (silent)"
),
(_C.EXACT.value, "fits-constrained"): _Cell(
Terminal.CONFIRM_PROCEED, _Need.YES,
"constraint changed the requested config — user must accept the "
"applied ctx/KV constraint even though math is trusted",
),
(_C.EXACT.value, "wont-fit"): _Cell(
Terminal.HARD_BLOCK, _Need.BLOCK,
"math trusted; suggest closest-fit",
),
# --- derived (RESERVED for the future override-registry phase; still a
# total-function row per §4.1 so the table is exhaustive) -----------
(_C.DERIVED.value, "fits-clean"): _Cell(
Terminal.CONFIRM_PROCEED, _Need.YES,
"best-effort, validate post-boot",
),
(_C.DERIVED.value, "fits-constrained"): _Cell(
Terminal.CONFIRM_PROCEED, _Need.YES,
"best-effort + constraint notice",
),
(_C.DERIVED.value, "wont-fit"): _Cell(
Terminal.OVERRIDE_ACCEPTED, _Need.FORCE,
"advisory → --force-download → override-accepted",
),
# --- estimated-lower-bound --------------------------------------------
(_C.ESTIMATED_LOWER_BOUND.value, "fits-clean"): _Cell(
Terminal.CONFIRM_PROCEED, _Need.YES,
"VRAM is a floor; likely under-modeled",
),
(_C.ESTIMATED_LOWER_BOUND.value, "fits-constrained"): _Cell(
Terminal.CONFIRM_PROCEED, _Need.YES,
"VRAM is a floor + constraint notice",
),
(_C.ESTIMATED_LOWER_BOUND.value, "wont-fit"): _Cell(
Terminal.OVERRIDE_ACCEPTED, _Need.FORCE,
"advisory → --force-download → override-accepted",
),
}
# Domain (used by the test to assert totality without re-deriving it here).
C1_CONFIDENCE_DOMAIN = (
_C.EXACT.value,
_C.DERIVED.value,
_C.ESTIMATED_LOWER_BOUND.value,
)
C1_RAW_VERDICT_DOMAIN = ("fits-clean", "fits-constrained", "wont-fit")
@dataclass(frozen=True)
class C1Outcome:
terminal: Terminal
satisfied: bool # did the present flags satisfy the cell?
note: str
needs: str # the flag still required (or "" when satisfied/blocked)
def c1_terminal(confidence: str, raw_verdict: str, flags: dict) -> C1Outcome:
"""The §4.1 total function. Pure: (confidence, raw_verdict, flags) ->
C1Outcome. `flags` carries booleans `yes` / `force_download`.
Resolution per §4.1 (the dict above is the authority — this only
encodes the flag interaction the table prescribes, never new policy):
- `_Need.NONE` : reaches `proceed` with no flag (only exact×clean).
- `_Need.YES` : `confirm→proceed`; reached when `--yes` present,
else NOT satisfied (advisory: "re-run with --yes").
- `_Need.FORCE` : low-confidence wont-fit advisory → `override-accepted`
ONLY with `--force-download`; else NOT satisfied.
- `_Need.BLOCK` : `hard-block`, no flag clears it (exact×wont-fit).
"""
key = (confidence, raw_verdict)
cell = _C1_TABLE.get(key)
if cell is None: # pragma: no cover — totality is test-asserted
raise KeyError(f"§4.1 has no cell for {key!r} (table is TOTAL)")
if cell.need is _Need.NONE:
return C1Outcome(cell.base_terminal, True, cell.note, "")
if cell.need is _Need.BLOCK:
# hard-block is itself the terminal; not a gate-pass, no flag clears.
return C1Outcome(Terminal.HARD_BLOCK, False, cell.note, "")
if cell.need is _Need.YES:
if flags.get("yes"):
return C1Outcome(cell.base_terminal, True, cell.note, "")
return C1Outcome(cell.base_terminal, False, cell.note, "--yes")
if cell.need is _Need.FORCE:
if flags.get("force_download"):
# override-accepted is NOT a gate-pass (§5.3 / design line 106):
# state + telemetry notice only, NO download this phase.
return C1Outcome(
Terminal.OVERRIDE_ACCEPTED, True, cell.note, ""
)
return C1Outcome(
Terminal.OVERRIDE_ACCEPTED, False, cell.note, "--force-download"
)
raise AssertionError(f"unreachable _Need {cell.need!r}") # pragma: no cover
# ===========================================================================
# §7 boot-fit ≠ runtime caveat (printed on every download-eligible AND every
# Path-B verdict; presentation-only, decision-logic-neutral).
# ===========================================================================
CAVEAT_S7 = (
"boot-fit satisfied; this does NOT guarantee stability under "
"sustained / accumulated-context workloads — validate with "
"soak-continuous before relying on it (recommend: scripts/soak.sh "
"SOAK_MODE=continuous)."
)
# ===========================================================================
# Hardware-SM detection (real path, INJECTABLE so tests are hermetic).
# ===========================================================================
def detect_hardware_sm() -> Optional[float]:
"""Real detection via the existing preflight path
(`nvidia-smi --query-gpu=...,compute_cap`). Returns the MIN sm across
visible GPUs (the binding constraint for a multi-GPU runtime), or None
when nvidia-smi is unavailable. NEVER called in tests (they inject)."""
import shutil
import subprocess
if not shutil.which("nvidia-smi"):
return None
try:
out = subprocess.run(
["nvidia-smi", "--query-gpu=compute_cap",
"--format=csv,noheader,nounits"],
capture_output=True, text=True, timeout=15,
)
except (OSError, subprocess.SubprocessError): # pragma: no cover
return None
if out.returncode != 0:
return None
caps = []
for line in out.stdout.splitlines():
line = line.strip()
if not line:
continue
try:
caps.append(float(line))
except ValueError: # pragma: no cover
continue
return min(caps) if caps else None
# ===========================================================================
# GPU / topology detection (v0.8.0 [E] E4 — additive; reuses the SAME
# nvidia-smi detection discipline as detect_hardware_sm(); INJECTABLE so
# tests are hermetic — NO real GPU in CI). gates.py has no GPU-count/VRAM
# helper (only the compute_cap SM path), so the additive-behaviour-preserving
# option per the brief is a new pull.py helper here (frozen modules
# untouched; curated Path-A unaffected — this is only consulted on the
# post-[C1] derived [E] path).
#
# Returns (visible_gpu_count, per_gpu_vram_mib, gpu_names) over ALL visible
# GPUs; None when nvidia-smi is unavailable AND no override was given (the
# derived [E] path then refuses honestly — never fabricates a topology).
# ===========================================================================
def detect_gpu_topology() -> Optional[tuple[int, list[int], list[str]]]:
"""Real detection via `nvidia-smi --query-gpu=memory.total,name`.
Returns (count, per_gpu_vram_mib, gpu_names) for ALL visible GPUs, or
None when nvidia-smi is absent. NEVER called in tests (they inject)."""
import shutil
import subprocess
if not shutil.which("nvidia-smi"): # pragma: no cover - env dependent
return None
try: # pragma: no cover - exercised on-rig (E5), injected in CI
out = subprocess.run(
["nvidia-smi", "--query-gpu=memory.total,name",
"--format=csv,noheader,nounits"],
capture_output=True, text=True, timeout=15,
)
except (OSError, subprocess.SubprocessError): # pragma: no cover
return None
if out.returncode != 0: # pragma: no cover
return None
vram: list[int] = []
names: list[str] = []
for line in out.stdout.splitlines(): # pragma: no cover - on-rig
line = line.strip()
if not line:
continue
parts = [p.strip() for p in line.split(",", 1)]
try:
vram.append(int(float(parts[0])))
except (ValueError, IndexError):
continue
names.append(parts[1] if len(parts) > 1 else "GPU")
if not vram: # pragma: no cover
return None
return len(vram), vram, names # pragma: no cover
def _topology_summary_canonical(names: list[str], vram_mib: list[int]) -> str:
"""§6.2 verbatim — deterministic serialization of SORTED
(gpu_name, vram_mib) tuples. Stable across runs/rigs for the
capture consensus key."""
tuples = sorted(
(str(n), int(v)) for n, v in zip(names, vram_mib)
)
return "[" + ", ".join(f"({n}, {v})" for n, v in tuples) + "]"
def _weights_oversize_advisory(weights_total_gb, gpu_topology) -> str:
"""If the raw weights ALONE exceed the detected topology's total VRAM,
return a one-line advisory explaining the won't-fit. Used to enrich the
eligibility `no-fit-model` abort for an uncurated derive — e.g. a bf16
full-weight abliterated repo of a model we serve only quantized — so the
user sees WHY their specific repo can't run, not just a generic stop.
Pure / total: empty string when it fits, when size or topology is unknown
(headless / injected None), or on any malformed input — never raises."""
try:
wgb = float(weights_total_gb)
except (TypeError, ValueError):
return ""
if wgb <= 0 or not gpu_topology:
return ""
try:
gcount, vram_mib, gnames = gpu_topology
total_vram_gb = sum(float(v) for v in vram_mib) / 1024.0
except (TypeError, ValueError):
return ""
if total_vram_gb <= 0 or wgb <= total_vram_gb:
return ""
gpu_label = gnames[0] if gnames else "GPU"
return (
f" SIZE: the weights are ~{wgb:.0f} GB but the detected topology has "
f"~{total_vram_gb:.0f} GB total VRAM ({gcount}× {gpu_label}) — they "
f"won't fit even before KV cache. A bf16 full-weight repo is too large "
f"for this rig; use an int4 (AWQ / AutoRound, ~1/4 the size) or GGUF variant."
)
# ===========================================================================
# v0.8.2 CONTRACT-1.1 — capture-on-hard-block PASS-THROUGH.
#
# Emits a pt1-gate-only redacted bundle on a terminal hard-block, BEFORE the
# existing `return res`. This is a PURE PASS-THROUGH: it changes NONE of the
# `[C1]`/6-stratum decision logic — every `return res` decision (ok / stratum
# / abort_reason / detail) is exactly what it was; we only emit a bundle (a
# local file op, no network) and record the bundle dir on `res`. ANY emit
# failure is swallowed structurally — a capture problem must NEVER change a
# gate decision or raise out of `run_pull` (the locked `[F]`-offline / I/O-
# free-gate boundary; pre-existing test-pull.sh assertions byte-unaffected).
#
# `res.capture_paths` (an existing additive PullResult field) is appended so
# the surface step (V2) + tests can find the bundle; nothing else on `res`
# is touched.
# ===========================================================================
def _gate_capture_passthrough(
res, *, slug, profile_like, der, hardware_sm, gpu_topology, root,
gate_capture_fn,
):
"""Emit the pt1-gate-only bundle for a terminal hard-block, then return
`res` UNCHANGED (pass-through). Swallows every exception — a capture
failure never alters a gate decision."""
try:
from scripts.lib.profiles import capture as _CAP
fn = gate_capture_fn or _CAP.emit_gate_capture
# club3090 commit captured on the HOST (mirrors _build_einput; never
# git-in-container) — best-effort, "unknown" on any failure.
commit = "unknown"
try:
import subprocess
cp = subprocess.run(
["git", "-C", str(root or REPO_ROOT), "rev-parse", "HEAD"],
capture_output=True, text=True, timeout=10, check=False,
)
if cp.returncode == 0 and cp.stdout.strip():
commit = cp.stdout.strip()
except Exception: # pragma: no cover - git always present on rig
commit = "unknown"
out = fn(
slug=slug,
profile_like=profile_like,
abort_reason=res.abort_reason or "",
confidence=(
SimpleNamespace(name=res.confidence)
if res.confidence else None
),
raw_verdict=res.raw_verdict,
detail=res.detail,
der=der,
hardware_sm=hardware_sm,
gpu_topology=gpu_topology,
club3090_commit=commit,
predicted_b_breakdown=res.diagnostics.get("b_breakdown"),
repo_root=root or REPO_ROOT,
)
d = out.get("dir") if isinstance(out, dict) else None
if d:
res.capture_paths.append(str(d))
res.diagnostics["gate_capture"] = {"dir": str(d)}
except Exception as exc: # pragma: no cover - capture is best-effort
res.diagnostics["gate_capture"] = {"error": repr(exc)}
return res
# ===========================================================================
# The orchestrator — chains the frozen slices in the LOCKED stratum order.
# ===========================================================================
def run_pull(
slug: str,
profile_like: str,
*,
path: Optional[str] = None,
dry_run: bool = False,
yes: bool = False,
force_download: bool = False,
experimental_arch: bool = False,
trust_remote_code: bool = False,
hf_home: Optional[str] = None,
out: Optional[str] = None,
hardware_sm: Optional[float] = None,
fetcher=None,
statvfs: Optional[Callable[[str], Any]] = None,
d_runner: Optional[Callable[[Path, str, bool], tuple]] = None,
profiles=None,
root: Optional[Path] = None,
# ----- v0.8.0 [E] E4 — INJECTABLE GPU/topology + [E]-stage hooks ------
# gpu_topology: (count, per_gpu_vram_mib, gpu_names) — None -> real
# detect_gpu_topology(); tests inject. Mirrors hardware_sm's
# inject-or-detect pattern (the brief's --hardware-gpus-style override).
gpu_topology: Optional[tuple[int, list[int], list[str]]] = None,
# The 5 [E]-stage funcs are injectable so test-pull.sh stays mock-only
# (NO real Docker / GPU / network — real on-rig is E5). None -> the real
# shipped E1/E2/E3 functions.
emit_fn: Optional[Callable] = None, # E1 generate_from_profile
download_fn: Optional[Callable] = None, # E2 download_model
# E3 boot lifecycle CM factory (the E3/E4-fix seam): a context-manager
# factory `boot_cm(ei, compose_text, runner=...) -> ctx[BootResult]`
# that keeps the server ALIVE for the `with` body (boot -> smoke ->
# capture) and tears it down on __exit__. Default = booted_derived.
boot_cm: Optional[Callable] = None, # E3 booted_derived (CM)
smoke_fn: Optional[Callable] = None, # E3 smoke_derived
capture_fn: Optional[Callable] = None, # E3 emit_capture
override_capture_fn: Optional[Callable] = None, # E4 emit_override_capture
# v0.8.2 CONTRACT-1.1 — the pt1-gate-only emitter, wired as a pure
# PASS-THROUGH on the terminal hard-block return paths (it emits a
# bundle BEFORE the existing `return res`; the decision is UNCHANGED).
# Injectable so test-pull.sh stays mock-only; None -> the real shipped
# capture.emit_gate_capture.
gate_capture_fn: Optional[Callable] = None,
# v0.8.2 CONTRACT-3 — OPTIONAL non-NVIDIA hardware-enumeration augment.
# Consulted ONLY when `nvidia-smi` already returned nothing (so the
# NVIDIA majority NEVER enters this seam — that path is byte-identical
# with this absent OR present-but-degrading). hw-detect-ONLY: it yields
# an SM-equivalent for the [C0] SM gate; it NEVER feeds kv-calc. None
# -> the real shipped hwdetect.detect_non_nvidia_sm (optional bounded
# `whichllm` subprocess; absent/failed -> graceful degrade to today's
# exact `hardware-sm-undetermined` behaviour). Injectable so
# test-pull.sh / test-hwdetect.sh stay hermetic (no `whichllm`, no
# non-NVIDIA GPU in CI).
hwdetect_fn: Optional[Callable] = None,
) -> PullResult:
"""Execute the 6-stratum Pull-Gate state machine.
Order is STRICT and monotonic (the design's locked taxonomy):
stratum-1 deriver structured errors (P2; already structured)
stratum-2 --profile-like precondition (P3 gates.stratum2)
stratum-3 [C0] engine-support/runtime/SM (P3 gates.c0) + flag bypass
stratum-4 [C2a] disk pre-gate (P3 gates.c2a)
stratum-5 pre-[B] generic-dense eligibility (P4; this module)
[B] kv.raw_verdict (P1)
[C1] §4.1 total function (P4; this module)
stratum-6 Path A only: [D] dry-run (read-only existing generator)
emit Path A only, on a clean dry-run: real [D] generate()
Path B (universal evaluate / --dry-run) NEVER reaches stratum-6, NEVER
calls [D], NEVER downloads. `--force-download` is a no-op + notice this
phase (emit/download deferred to the Loop phase).
"""
root = root or REPO_ROOT
gc = _gc()
kv = _kv()
flags = {"yes": yes, "force_download": force_download}
# ----- Path selection -------------------------------------------------
# Explicit `path=` wins (test driver). Else: --dry-run forces Path B;
# otherwise Path A iff a curated tier-1 hit AND an --out target, else B.
forced_path = path
if profiles is None:
from scripts.lib.profiles.compat import load_profiles
profiles = load_profiles()
# ----- stratum-1: deriver (P2, frozen) --------------------------------
der = D.derive(
slug, hf_home=hf_home, fetcher=fetcher, profiles=profiles
)
if der.error is not None:
_der_res = PullResult(
slug=slug, profile_like=profile_like, path="?",
ok=False, stratum=Stratum.DERIVER,
abort_reason=der.error.kind.value, detail=str(der.error),
)
# v0.8.2 CONTRACT-1.1 pass-through (pre-deriver: no der.profile ->
# model/arch/quant null). Decision UNCHANGED.
return _gate_capture_passthrough(
_der_res, slug=slug, profile_like=profile_like, der=None,
hardware_sm=hardware_sm, gpu_topology=gpu_topology, root=root,
gate_capture_fn=gate_capture_fn,
)
is_curated = der.tier1 is not None
if forced_path in ("A", "B"):
eff_path = forced_path
elif dry_run:
eff_path = "B"
elif is_curated and out is not None:
eff_path = "A"
else:
eff_path = "B"
res = PullResult(
slug=slug, profile_like=profile_like, path=eff_path,
ok=False, stratum=Stratum.DECIDED,
confidence=der.confidence.value if der.confidence else None,
)
# ----- stratum-2: --profile-like precondition (P3, frozen) ------------
s2 = G.stratum2_profile_like(
profile_like, derive_result=der, path=eff_path, root=root,
)
if not s2.ok:
res.ok = False
res.stratum = Stratum.PROFILE_LIKE
res.abort_reason = s2.refusal.reason
res.detail = s2.refusal.detail
return _gate_capture_passthrough(
res, slug=slug, profile_like=profile_like, der=der,
hardware_sm=hardware_sm, gpu_topology=gpu_topology, root=root,
gate_capture_fn=gate_capture_fn,
)
# ----- stratum-3: [C0] engine-support / runtime / SM (P3, frozen) -----
if hardware_sm is None:
hardware_sm = detect_hardware_sm()
if hardware_sm is None:
# v0.8.2 CONTRACT-3 (hw-detect-ONLY) — OPTIONAL non-NVIDIA augment.
# Reached ONLY when `nvidia-smi` gave nothing, so the NVIDIA
# majority never executes this and that path is byte-identical
# whether this augment is absent or present-but-degrading. An
# absent/failed/non-applicable `whichllm` returns None and we fall
# straight through to the unchanged `hardware-sm-undetermined`
# terminal below (safety half). A recognised non-NVIDIA device
# (AMD ROCm / Apple) yields an SM-equivalent so the eval path can
# run the [C0] SM gate instead of refusing blind (delivery half).
# This SM-equivalent feeds ONLY the [C0] gate — NEVER kv-calc
# (kv-calc stays the sole fit authority; CONTRACT-3 is
# enumeration-only). Any exception is swallowed by hwdetect itself
# (leaf module never raises out).
if hwdetect_fn is None:
from scripts.lib.profiles.hwdetect import detect_non_nvidia_sm
hwdetect_fn = detect_non_nvidia_sm
_aug_sm = hwdetect_fn()
if _aug_sm is not None:
# Additive-only: mutate the existing `res` (created above;
# stratum-2 already passed). NO decision field is touched —
# only `hardware_sm` (so the EXISTING [C0] gate now runs
# instead of the unchanged blind-refuse terminal) + an
# additive notice/diagnostic. kv-calc is NOT consulted.
hardware_sm = float(_aug_sm)
res.notices.append(
"[hw] nvidia-smi absent; non-NVIDIA accelerator "
"enumerated via the optional hardware-detect subprocess "
"(enumeration only — kv-calc remains the sole fit "
"authority; validate with soak-continuous)"
)
res.diagnostics["hwdetect"] = {
"augmented": True, "sm_equiv": float(_aug_sm),
}
if hardware_sm is None:
# No GPU detected and none injected: cannot honestly run the SM
# gate. Fail closed (never fabricate a fit per §1).
res.ok = False
res.stratum = Stratum.C0
res.abort_reason = "hardware-sm-undetermined"
res.detail = (
"could not detect GPU compute capability (nvidia-smi absent) "
"and no --hardware override given; refusing to run the SM gate "
"blind"
)
# topology GUARANTEED null here (nvidia-smi absent — CONTRACT-1.1):
# pass gpu_topology=None so the bundle's topology is null even if
# an unrelated override was supplied.
return _gate_capture_passthrough(
res, slug=slug, profile_like=profile_like, der=der,
hardware_sm=None, gpu_topology=None, root=root,
gate_capture_fn=gate_capture_fn,
)
c0 = G.c0_engine_support(
profile_like, der, path=eff_path, hardware_sm=float(hardware_sm),
root=root,
)
if c0.state != G.C0State.ENGINE_SUPPORTED:
# Apply ONLY the bypasses [C0] explicitly tagged on `.bypassable_by`.
# --experimental-arch bypasses ONLY no-arch-row (never
# runtime-incompatible — its bypassable_by is () so the membership
# test below can never let it through).
bypassed = False
provided = set()
if experimental_arch:
provided.add(G.BYPASS_EXPERIMENTAL_ARCH)
if trust_remote_code:
provided.add(G.BYPASS_TRUST_REMOTE_CODE)
tags = set(c0.bypassable_by)
if tags and tags.issubset(provided):
# Every condition [C0] flagged is covered by a provided flag.
# (auto_map+no-arch-row tags BOTH; requires BOTH flags — the
# subset test enforces that automatically.)
bypassed = True
if not bypassed:
res.ok = False
res.stratum = Stratum.C0
res.abort_reason = c0.state.value + (
"/" + c0.sub_reason.value if c0.sub_reason else ""
)
res.detail = c0.detail
res.diagnostics["c0_bypassable_by"] = list(c0.bypassable_by)
return _gate_capture_passthrough(
res, slug=slug, profile_like=profile_like, der=der,
hardware_sm=hardware_sm, gpu_topology=gpu_topology,
root=root, gate_capture_fn=gate_capture_fn,
)
res.notices.append(
f"[C0] {c0.state.value}"
+ (f"/{c0.sub_reason.value}" if c0.sub_reason else "")
+ f" bypassed by {sorted(provided & tags)} (Path B only this "
f"phase; outcome capture deferred to Loop)"
)
# ----- stratum-4: [C2a] disk pre-gate (P3, frozen) — AFTER [C0] -------
c2a = G.c2a_disk(der, hf_home=hf_home, statvfs=statvfs)
if c2a.state != G.C2aState.DISK_OK:
res.ok = False
res.stratum = Stratum.C2A_DISK
res.abort_reason = c2a.state.value # "disk-short"
res.detail = c2a.detail
return _gate_capture_passthrough(
res, slug=slug, profile_like=profile_like, der=der,
hardware_sm=hardware_sm, gpu_topology=gpu_topology, root=root,
gate_capture_fn=gate_capture_fn,
)
# ----- stratum-5: pre-[B] generic-dense eligibility (P4) --------------
# Separate pre-[B] abort — NOT a [C0] rewrite ([C0] already emitted
# engine-supported / a bypassed unknown and stays). Non-bypassable:
# there is no fit model to force. Tier-1 curated hits are eligible by
# construction (the curated profile encodes a priced model); for a
# derived model, ineligible iff deriver said NOT tier-1 AND
# kv.is_generic_dense_eligible(config) is False (deriver already ran the
# predicate into .generic_dense_eligible).
if not is_curated:
eligible = bool(der.generic_dense_eligible)
if not eligible:
res.ok = False
res.stratum = Stratum.ELIGIBILITY
res.abort_reason = "no-fit-model"
_arch = (der.profile or {}).get("arch")
# If the (uncurated) arch resolves via the xref alias to a family we
# serve CURATED (hybrid/MoE — not generic-dense, so unfit-priceable
# here), point at the curated-swap path instead of dead-ending. The
# generic derive genuinely can't fit these; the curated compose can.
_hint = ""
try:
# `gc` (the generate_compose module) is already loaded above via
# `gc = _gc()`; reuse it — do NOT re-import as a local (that
# shadows the module-level `_gc` loader -> UnboundLocalError).
_rt = gc._load_yaml(
root, "scripts/lib/profiles/profile_runtime.yml"
)
_canon, _row = gc.resolve_arch_from_config(
_rt, gc.load_arches(root), _arch
)
if _row is not None:
_slugs = (
((_rt.get("arch_model_xref") or {}).get(_canon) or {})
.get("model_slugs") or []
)
if _slugs:
_hint = (
f" NOTE: this arch is the curated '{_slugs[0]}' "
f"({_row.get('family')}) — generic derive can't fit it, "
f"but the curated path can: clone that model's compose and "
f"point --model at your weights (use a quantized "
f"variant; the bf16 base is too large to fit). "
f"See docs/BRING_YOUR_OWN.md → 'Swap a curated model for a "
f"fine-tune / abliterated variant'."
)
except Exception:
_hint = ""
# Coarse weights-only VRAM verdict: even when we can't kv-calc-price
# a hybrid/MoE, if the raw weights exceed total VRAM it won't fit at
# ANY KV — surface that concretely (the huihui abliterated bf16 ~54GB
# vs 2×24GB case). Empty when it fits / size unknown / headless.
_size_hint = _weights_oversize_advisory(
(der.profile or {}).get("weights_total_gb"), gpu_topology
)
res.detail = (
f"{slug}: not Tier-1 curated and not generic-dense eligible "
f"(arch {_arch!r}); no fit model "
f"to price — pre-[B] hard-stop (non-bypassable; "
f"--experimental-arch does NOT apply — there is no model)."
+ _size_hint
+ _hint
)
return _gate_capture_passthrough(
res, slug=slug, profile_like=profile_like, der=der,
hardware_sm=hardware_sm, gpu_topology=gpu_topology,
root=root, gate_capture_fn=gate_capture_fn,
)
# ----- [B]: raw fit verdict (P1 kv.raw_verdict) -----------------------
entry = s2.registry_entry or {}
spec = der.spec
if spec is None:
# Tier-1 curated hit: build the curated-exact kv-calc spec (real
# model_family preserved) so [B] prices it through P1's authoritative
# family branch, not generic-dense's conservative lower-bound.
spec = _curated_spec(profiles, der)
rv = kv.raw_verdict(
spec=spec,
kv_format=entry.get("kv_format", "fp8_e5m2"),
max_ctx=int(entry.get("max_ctx") or spec.get("max_ctx_supported")
or 131072),
max_num_seqs=int(entry.get("max_num_seqs") or 1),
tp=int(entry.get("tp") or 1),
mem_util=float(entry.get("mem_util") or 0.95),
)
raw = rv["raw_verdict"]
res.raw_verdict = raw
res.diagnostics["b_breakdown"] = rv.get("breakdown_gb")
# ----- [C1]: §4.1 total function (P4) ---------------------------------
conf = der.confidence.value
c1 = c1_terminal(conf, raw, flags)
res.terminal = c1.terminal.value
res.diagnostics["c1_note"] = c1.note
if c1.terminal is Terminal.HARD_BLOCK:
res.ok = False
res.stratum = Stratum.DECIDED
res.abort_reason = "hard-block"
res.detail = f"[C1] {conf}×{raw} → hard-block ({c1.note})"
# v0.8.2 CONTRACT-1.1 — the C1 exact×wont-fit terminal (the genuine
# kv-calc VRAM refusal). predicted_b_breakdown is in scope
# (res.diagnostics["b_breakdown"] set above) — the pass-through
# carries it. Decision UNCHANGED.
return _gate_capture_passthrough(
res, slug=slug, profile_like=profile_like, der=der,
hardware_sm=hardware_sm, gpu_topology=gpu_topology, root=root,
gate_capture_fn=gate_capture_fn,
)
if not c1.satisfied:
# confirm→proceed without --yes, or low-conf wont-fit advisory
# without --force-download. Honest non-pass: state + the flag the
# user must add. NEVER a silent gate-pass.
res.ok = False
res.stratum = Stratum.DECIDED
res.abort_reason = f"{c1.terminal.value}:needs {c1.needs}"
res.detail = (
f"[C1] {conf}×{raw}{c1.terminal.value} ({c1.note}); "
f"re-run with {c1.needs} to accept"
)
return res
if c1.terminal is Terminal.OVERRIDE_ACCEPTED:
# NOT a fit (design line 106). v0.8.0 [E] E4 trigger semantics:
# --force-download (which is what SATISFIED this terminal) now
# ACTIVATES the derived [E] stage for a non-curated model that is
# NOT --dry-run (was a no-op pre-[E]). The §5.3 override-accepted
# force-capture (pt5) is emitted on this path. Curated / --dry-run
# stay verdict-only (unchanged).
res.ok = True
res.stratum = Stratum.DECIDED
res.abort_reason = None
res.detail = (
f"[C1] {conf}×{raw} → override-accepted ({c1.note}); "
f"override-accepted is NOT a fit (calibration signal, never "
f"recorded as fit-validated — §5.3)"
)
res.notices.append(
"override-accepted: forced low-confidence download is a "
"calibration signal, NOT a validated fit (§5.3)"
)
res.notices.append(CAVEAT_S7)
if not is_curated and not dry_run:
# Trigger C: --force-download activates [E] ONLY for the
# override-accepted terminal. The §5.3 pt5 force-capture is
# emitted (is_override_accepted=True).
_run_derived_e_stage(
res, slug=slug, profile_like=profile_like,
der=der, s2=s2, c2a=c2a, conf=conf, raw=raw,
terminal=c1.terminal.value, is_override_accepted=True,
hf_home=hf_home, hardware_sm=float(hardware_sm),
gpu_topology=gpu_topology, root=root, fetcher=fetcher,
emit_fn=emit_fn, download_fn=download_fn, boot_cm=boot_cm,
smoke_fn=smoke_fn, capture_fn=capture_fn,
override_capture_fn=override_capture_fn,
)
else:
res.notices.append(
"override-accepted: download/telemetry capture deferred "
"(curated or --dry-run — verdict-only this run)"
)
return res
# ----- terminal is proceed / confirm→proceed (satisfied) --------------
if raw == "fits-constrained" and eff_path == "A":
res.notices.append(
"known effective-cap warning: vLLM internally caps effective "
"KV on this hardware; [D] emits the chosen registry profile "
"UNCHANGED (no compose config rewritten)"
)
# ----- Path B: print the §7-caveated verdict ---------------------------
if eff_path == "B":
res.ok = True
res.stratum = Stratum.DECIDED
res.detail = (
f"Path B verdict: [C1] {conf}×{raw}{c1.terminal.value} "
f"({c1.note})"
)
res.notices.append(CAVEAT_S7)
# v0.8.0 [E] E4 trigger semantics (the new user-facing contract,
# ADDITIVE — supersedes "Path B never emits/downloads" ONLY for the
# non-curated derived download-eligible no-dry-run case):
# * non-curated + download-eligible [C1] terminal + NO --dry-run
# -> continue into the derived [E] stage;
# * --dry-run stays verdict-only (NEVER enters [E]);
# * curated forced-B (e.g. --dry-run on a curated slug) is
# verdict-only — curated weights are local, no download stage.
# is_curated is True for a Tier-1 curated hit; a curated slug only
# reaches Path B here via --dry-run, which the guard below excludes.
if not is_curated and not dry_run:
_run_derived_e_stage(
res, slug=slug, profile_like=profile_like,
der=der, s2=s2, c2a=c2a, conf=conf, raw=raw,
terminal=c1.terminal.value, is_override_accepted=False,
hf_home=hf_home, hardware_sm=float(hardware_sm),
gpu_topology=gpu_topology, root=root, fetcher=fetcher,
emit_fn=emit_fn, download_fn=download_fn, boot_cm=boot_cm,
smoke_fn=smoke_fn, capture_fn=capture_fn,
override_capture_fn=override_capture_fn,
)
else:
if force_download:
res.notices.append(
"--force-download is a no-op here (curated / --dry-run "
"verdict-only; download deferred)"
)
return res
# ----- Path A: stratum-6 [D] dry-run, then real emit ------------------
runner = d_runner or (lambda r, p, ad: gc.generate(r, p, accept_degraded=ad))
try:
compose_text, meta = runner(root, profile_like, False)
except gc.Refuse as r:
# [D] refused at one of its LATER points (pin mismatch / TP·KV /
# trc / foundational-or-degraded patch drift). Surface as a Path-A
# abort — do NOT report download-eligible (Codex-r5 Med-1: the
# stratum-2 scope-gate is necessary-not-sufficient for [D] emit).
res.ok = False
res.stratum = Stratum.D_DRY_RUN
res.abort_reason = f"d-refused:{_short_refuse(str(r))}"
res.detail = (
f"Path-A [D] dry-run refused: {r} — NOT reported "
f"download-eligible (stratum-2 scope-gate is necessary but not "
f"sufficient for [D] emit)"
)
return res
# Clean dry-run: the validated registry key is handed to the existing
# [D] for real emission. The dry-run already produced the exact compose
# text (gc.generate is pure); honor --out (COMPOSE_GENERATOR.md
# --project-directory correctness: the emitted compose's relative
# overlay mounts resolve from the compose file's own directory, so the
# consumer must `docker compose --project-directory <repo-root>` — we
# surface that requirement as a notice and write where --out points).
res.ok = True
res.stratum = Stratum.DECIDED
res.emitted = True
res.compose_text = compose_text
res.diagnostics["d_meta"] = meta
res.detail = (
f"Path A download-eligible: [C1] {conf}×{raw} → "
f"{c1.terminal.value}; [D] dry-run clean, compose emitted "
f"(pin={meta.get('engine_pin')})"
)
if out is not None:
out_path = Path(out)
out_path.parent.mkdir(parents=True, exist_ok=True)
out_path.write_text(compose_text, encoding="utf-8")
res.diagnostics["out_written"] = str(out_path)
res.notices.append(
"run the emitted compose with `docker compose "
"--project-directory <repo-root> -f <out> up` so the relative "
"overlay mounts resolve (see COMPOSE_GENERATOR.md)"
)
res.notices.append(CAVEAT_S7)
return res
# ===========================================================================
# v0.8.0 [E] E4 — the post-`[C1]` derived `[E]` stage.
#
# ADDITIVE: wired ONLY from the two post-`[C1]` non-curated download-eligible
# return points (override-accepted-with-force-download, and the satisfied
# proceed/confirm→proceed Path-B verdict), and ONLY when `not is_curated and
# not dry_run`. It NEVER touches the `[C1]` table, the 6-stratum logic, or
# curated Path-A — those are CONSUMED, not modified. It mutates ONLY the
# additive `PullResult` fields (download_ok / boot_ok / smoke /
# capture_paths) + notices/diagnostics; ok/terminal/raw_verdict/emitted/
# stratum/abort_reason are left exactly as the [C1] decision set them.
#
# Sequence (CONTRACT-1..5):
# 1. populate EInput from in-scope run_pull state (CONTRACT-1)
# 2. derived_emittable(einput) — CONTRACT-5 pre-[E] precondition; on
# refuse -> structured `derived-runtime-unsupported:<reason>` notice +
# diagnostics, NO download / NO boot.
# 3. generate_from_profile -> download_model -> `with booted_derived(...)
# as bt:` [server UP] -> smoke_derived -> emit_capture (pt1-4 +
# manifest) [-> pt5 if override] -> CM __exit__: teardown (ALWAYS,
# AFTER capture). The E3/E4-fix: smoke + capture run against a LIVE
# server inside the `with`; teardown is on context-manager exit, not
# before the boot call returns (on-rig E5 caught the prior teardown-
# in-finally-before-smoke -> ConnectionRefused defect).
# 4. if is_override_accepted: emit_override_capture (§5.3 pt5) — also
# inside the `with` (before teardown).
#
# Every stage func is INJECTABLE (test-pull.sh mocks them; real on-rig is
# E5). All stage exceptions are caught + recorded structurally — the [E]
# stage NEVER raises out of run_pull and NEVER mutates the [C1]-owned
# fields, so every pre-existing test-pull.sh assertion is byte-unaffected.
# ===========================================================================
def _build_einput(
*, slug, der, s2, c2a, terminal, is_override_accepted,
hf_home, hardware_sm, gpu_topology, root, fetcher,
):
"""CONTRACT-1 — construct the EInput from the in-scope run_pull state.
GPU/topology via the injectable detect_gpu_topology() (None when no
nvidia-smi and no override -> the caller refuses honestly)."""
from scripts.lib.profiles.einput import EInput
runtime = dict(s2.registry_entry or {})
der_prof = getattr(der, "profile", None) or {}
selected = list(
der_prof.get("download_set")
or der_prof.get("selected_weight_files")
or []
)
resolved_hf_home = D.resolve_hf_home(hf_home)
topo = gpu_topology
if topo is None:
topo = detect_gpu_topology()
if topo is None:
return None, "gpu-topology-undetermined"
vis_count, per_gpu_vram_mib, gpu_names = topo
tp = int(runtime.get("tp") or 1)
# The EXACT tp GPUs the derived compose binds — the contiguous first-tp
# (the curated `gpu_assignment_mode: contiguous` convention; CONTRACT-5
# also asserts len(selected_gpu_indices) == tp).
selected_gpu_indices = list(range(min(tp, vis_count)))
selected_gpu_vram_mib = [
per_gpu_vram_mib[i] for i in selected_gpu_indices
if i < len(per_gpu_vram_mib)
]
sel_names = [
gpu_names[i] for i in selected_gpu_indices if i < len(gpu_names)
]
topology_summary = _topology_summary_canonical(
sel_names, selected_gpu_vram_mib
)
# club3090 commit captured on the HOST (NEVER git-in-container).
commit = "unknown"
try:
import subprocess
cp = subprocess.run(
["git", "-C", str(root or REPO_ROOT), "rev-parse", "HEAD"],
capture_output=True, text=True, timeout=10, check=False,
)
if cp.returncode == 0 and cp.stdout.strip():
commit = cp.stdout.strip()
except (OSError, Exception): # pragma: no cover - git always present
commit = "unknown"
ei = EInput(
slug=slug,
terminal=terminal,
is_override_accepted=is_override_accepted,
der=der,
runtime=runtime,
selected_files=selected,
hf_home=resolved_hf_home,
c2a=c2a,
hardware_sm=float(hardware_sm),
visible_gpu_count=int(vis_count),
per_gpu_vram_mib=list(per_gpu_vram_mib),
selected_gpu_indices=selected_gpu_indices,
selected_gpu_vram_mib=selected_gpu_vram_mib,
topology_summary=topology_summary,
club3090_commit=commit,
diagnostics={"_root": str(root or REPO_ROOT)},
)
return ei, None
def _run_derived_e_stage(
res, *, slug, profile_like, der, s2, c2a, conf, raw, terminal,
is_override_accepted, hf_home, hardware_sm, gpu_topology, root, fetcher,
emit_fn, download_fn, boot_cm, smoke_fn, capture_fn,
override_capture_fn,
):
"""Run the post-`[C1]` derived `[E]` stage. Mutates ONLY the additive
`res` fields + notices/diagnostics — NEVER ok/terminal/raw_verdict/
emitted/stratum/abort_reason (those are the [C1] decision, CONSUMED).
Every stage func is injectable; all exceptions are recorded
structurally so this never raises out of run_pull (pre-existing
test-pull.sh assertions byte-unaffected)."""
from datetime import datetime, timezone
# Lazy imports — the real E1/E2/E3 funcs (only when not injected). This
# keeps the import surface of pull.py unchanged for callers that never
# reach [E] (curated Path-A / --dry-run).
from scripts.lib import generate_compose as gc
from scripts.lib.profiles import booter as _B
from scripts.lib.profiles import capture as _CAP
from scripts.lib.profiles import downloader as _DL
emit_fn = emit_fn or gc.generate_from_profile
download_fn = download_fn or _DL.download_model
# E3/E4-fix: the boot lifecycle is a CONTEXT MANAGER factory — the
# server stays ALIVE for the `with` body (smoke + capture run against a
# live server); teardown is on __exit__, AFTER capture. Default =
# booted_derived. Injectable so test-pull.sh stays mock-only.
boot_cm = boot_cm or _B.booted_derived
smoke_fn = smoke_fn or _CAP.smoke_derived
capture_fn = capture_fn or _CAP.emit_capture
override_capture_fn = override_capture_fn or _CAP.emit_override_capture
root = root or REPO_ROOT
# ----- CONTRACT-1: populate EInput from in-scope state ----------------
ei, topo_err = _build_einput(
slug=slug, der=der, s2=s2, c2a=c2a, terminal=terminal,
is_override_accepted=is_override_accepted, hf_home=hf_home,
hardware_sm=hardware_sm, gpu_topology=gpu_topology, root=root,
fetcher=fetcher,
)
if ei is None:
res.notices.append(
f"[E] not run: {topo_err} (no nvidia-smi and no GPU-topology "
f"override; refusing to fabricate a topology — verdict stands)"
)
res.diagnostics["e_stage"] = {"ran": False, "reason": topo_err}
return
# ----- CONTRACT-5: derived-emittable pre-[E] precondition -------------
try:
ok, reason = gc.derived_emittable(ei)
except Exception as exc: # pragma: no cover - gate is pure
ok, reason = False, f"derived-runtime-unsupported:gate-error:{exc!r}"
if not ok:
res.notices.append(
f"[E] CONTRACT-5 refuse: {reason} — NO download / NO boot"
)
res.diagnostics["e_stage"] = {
"ran": False, "contract5_refuse": reason,
}
return
# E2's standardized header-probe fetcher wiring (no-op on the dtype
# resolution ORDER; just supplies the object E1 looks for).
try:
_DL.set_probe_fetcher(ei, fetcher)
except Exception: # pragma: no cover - defensive
pass
ts = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
stage = {"ran": True, "contract5_refuse": None}
# ----- E1: generate_from_profile --------------------------------------
try:
compose_text, compose_meta = emit_fn(root, ei)
except gc.Refuse as r:
res.notices.append(f"[E] generate refuse: {r}")
stage["emit_ok"] = False
res.diagnostics["e_stage"] = stage
return
except Exception as exc:
res.notices.append(f"[E] generate failed: {exc!r}")
stage["emit_ok"] = False
res.diagnostics["e_stage"] = stage
return
stage["emit_ok"] = True
# ----- E2: download_model ---------------------------------------------
try:
dl = download_fn(ei, fetcher=fetcher)
except TypeError:
# an injected mock may not accept the fetcher kwarg.
dl = download_fn(ei)
except Exception as exc:
res.notices.append(f"[E] download failed: {exc!r}")
res.download_ok = False
stage["download_failure"] = repr(exc)
res.diagnostics["e_stage"] = stage
return
res.download_ok = bool(getattr(dl, "ok", False))
if not res.download_ok:
res.notices.append(
f"[E] download not ok: failure="
f"{getattr(dl, 'failure', None)!r}"
)
# ----- E3/E4-fix: boot -> smoke -> capture, ALL inside the live-server
# context manager. The server is ALIVE for the entire `with` body
# (booted_derived yields the handle while UP); teardown (down +
# rmtree) happens on `__exit__`, AFTER capture+pt5, ALWAYS (the
# CM's own finally — no-orphan guarantee preserved at the CORRECT
# scope). On-rig E5 caught the prior teardown-in-finally-before-
# smoke defect: smoke ran against an already-destroyed server ->
# ConnectionRefused, every time. Now smoke runs while UP.
#
# The whole `with` is wrapped so a smoke/capture exception is
# caught structurally here (never raises out of run_pull — every
# pre-existing test-pull.sh assertion byte-unaffected); the CM's
# teardown STILL runs on the exceptional __exit__ before this
# except is reached. ------------------------------------------
try:
with boot_cm(ei, compose_text) as bt:
res.boot_ok = bool(getattr(bt, "ok", False))
# --- E3: smoke_derived (server is UP here) -------------------
sm = _CAP.SmokeResult()
endpoint = getattr(bt, "endpoint", None)
if res.boot_ok and endpoint:
try:
sm = smoke_fn(ei, endpoint)
except Exception as exc: # pragma: no cover - smoke defensive
res.notices.append(f"[E] smoke failed: {exc!r}")
res.smoke = {
"smoke_capability_set": list(
getattr(sm, "smoke_capability_set", []) or []
),
"results": dict(getattr(sm, "results", {}) or {}),
"partial": bool(getattr(sm, "partial", False)),
}
# --- E3: emit_capture (pt1-4 + manifest) — emitted while/just
# after smoke, BEFORE teardown (which is the CM __exit__) --
try:
cap = capture_fn(
ei,
confidence=SimpleNamespace(name=str(conf)),
raw_verdict=raw,
profile_like=profile_like,
download_result=dl,
boot_result=bt,
smoke_result=sm,
compose_meta=compose_meta,
kv_calc_version=_KV_CALC_VERSION,
repo_root=root,
ts=ts,
# F3/G6-A — PURE pass-through wiring (NO decision-logic
# change; gate/terminal/[B]/[C1]/is_override_accepted
# untouched). A-i: the predicted [B] breakdown that
# already exists at pull.py:638 (== raw_verdict()
# ["breakdown_gb"]), now persisted into pt1 on every
# post-[B] capture (was disk-only in pt5). A-ii: the
# booter's bounded redacted container-log excerpt
# (None on success / when unavailable — emit_capture
# writes failure_log_excerpt + parses pt3.actual ONLY
# when boot is not ok).
predicted_b_breakdown=res.diagnostics.get(
"b_breakdown"
),
failure_log_excerpt=getattr(
bt, "failure_log_excerpt", None
),
)
res.capture_paths = list((cap.get("paths") or {}).values())
res.diagnostics["capture_dir"] = cap.get("dir")
except Exception as exc:
res.notices.append(f"[E] capture emit failed: {exc!r}")
stage["capture_ok"] = False
res.diagnostics["e_stage"] = stage
# Leave the `with` -> CM teardown still runs on __exit__.
return
stage["capture_ok"] = True
# --- CONTRACT-4 pt5: override-accepted force-capture (§5.3) --
# Emitted ONLY when is_override_accepted (the §5.3 trigger).
# The literal calibration_signal_not_validated:true is enforced
# inside the emitter. Still inside the `with` (before teardown).
if is_override_accepted:
boot_peak = None
gpu_worker = None
exit_summary = None
if not res.boot_ok:
# boot never reached allocation -> actual null; the WHY
# is the boot failure reason (CONTRACT-4 pt5).
exit_summary = getattr(bt, "failure", None) or (
"boot did not reach allocation"
)
try:
p5 = override_capture_fn(
ei,
predicted_b_breakdown=res.diagnostics.get(
"b_breakdown"
),
boot_peak_mib=boot_peak,
gpu_worker_reported_mib=gpu_worker,
exit_error_summary=exit_summary,
repo_root=root,
ts=ts,
)
res.capture_paths.append(p5)
res.notices.append(
"[E] §5.3 override-accepted force-capture (pt5) "
"emitted (calibration signal, NOT fit-validated)"
)
except Exception as exc: # pragma: no cover - emitter
res.notices.append(
f"[E] pt5 override-capture failed: {exc!r}"
)
# <- CM __exit__ HERE: runner.down + rmtree ALWAYS ran (success OR
# exception in the with-body), AFTER smoke + capture + pt5.
except Exception as exc:
# Boot CM raised (defensive — booted_derived yields an ok=False
# handle for an EXPECTED boot failure, so this is the unexpected
# path). The CM's finally already tore down. Record structurally;
# NEVER raise out of run_pull.
res.notices.append(f"[E] boot failed: {exc!r}")
if res.boot_ok is None:
res.boot_ok = False
res.diagnostics["e_stage"] = stage
# kv-calc has no version constant; this is the stable label E4 stamps into
# the §6.2 capture manifest's `kv_calc_version` (a [F] consensus-key input).
# Kept here (pull.py, the orchestrator) — NOT a frozen-module edit.
#
# CONTRACT-5 (ii) — G2: a calibration-affecting kv-calc math/corpus edit
# that does NOT change this string lets materially-different runs collide on
# the §6.2 consensus key / §6.3 dedup key / submission_fingerprint. Removing
# that defect by *deriving* the version from the actual content surface
# instead of a hand-bumped literal: `kvcalc-v0.8.0+<sha256(content)[:12]>`
# where <content> = the bytes of `tools/kv-calc.py` (the kv-calc math) + the
# sorted concatenation of `scripts/lib/profiles/calibration/*.yml` (the
# calibration corpus). A math OR corpus edit now changes the hash
# automatically — no manual bump discipline needed (closes G2; satisfies the
# (iii) regression). This is purely how the version STRING is computed: zero
# decision-logic touch (gate states / terminals / [B] / [C1] / verdicts /
# is_override_accepted / the §4.1 table are all untouched).
#
# Hashing convention is parity with `[E]`/§6.2's `submission_fingerprint`
# (`capture.py` `_fingerprint`): sha256 over `\x1f`-joined parts, utf-8.
# Computed ONCE at import and cached in the module-level constant (never
# recomputed per call). If the content files are unreadable for ANY reason
# the producer degrades to the bare `"kvcalc-v0.8.0"` and NEVER raises —
# `[E]`/pull must not crash over a provenance-label refinement.
_KV_CALC_VERSION_BASE = "kvcalc-v0.8.0"
def _kv_calc_content_hash(repo_root: Path) -> Optional[str]:
"""sha256[:12] over the kv-calc math + sorted calibration corpus.
<content> parts, in this fixed order:
1. raw bytes of `tools/kv-calc.py`
2. for each `scripts/lib/profiles/calibration/*.yml` sorted by name:
the file NAME then its raw bytes (name included so a rename / add /
remove of a corpus file also moves the hash).
Parts joined with the `\\x1f` unit-separator (same convention as
`capture.py`'s `submission_fingerprint`), sha256, first 12 hex.
Returns None (caller degrades to the bare base) if anything is
unreadable — robust, never raises."""
import hashlib
try:
kv_calc = (repo_root / "tools" / "kv-calc.py").read_bytes()
calib_dir = repo_root / "scripts" / "lib" / "profiles" / "calibration"
calib_files = sorted(
calib_dir.glob("*.yml"), key=lambda p: p.name
)
parts: list[bytes] = [kv_calc]
for cf in calib_files:
parts.append(cf.name.encode("utf-8"))
parts.append(cf.read_bytes())
h = hashlib.sha256()
h.update(b"\x1f".join(parts))
return h.hexdigest()[:12]
except Exception: # pragma: no cover - robust degrade, never crash [E]
return None
def _derive_kv_calc_version(repo_root: Path) -> str:
"""CONTRACT-5 (ii): `kvcalc-v0.8.0+<12hex>` from content, or the bare
base when the content surface is unreadable (honest degrade)."""
digest = _kv_calc_content_hash(repo_root)
if digest is None:
return _KV_CALC_VERSION_BASE
return f"{_KV_CALC_VERSION_BASE}+{digest}"
# Computed once at import from the REAL repo content surface; cached here so
# every [E] emit stamps the identical derived label (the per-call site at
# the `capture_fn(... kv_calc_version=_KV_CALC_VERSION ...)` call is byte-
# unchanged — it still reads this module-level name).
_KV_CALC_VERSION = _derive_kv_calc_version(REPO_ROOT)
def _short_refuse(msg: str) -> str:
"""Compact a [D] Refuse message into a stable machine token."""
low = msg.lower()
if "loads" in low or "engine pin" in low:
return "pin-mismatch"
if "tp=" in low or "kv_format" in low:
return "tp-or-kv"
if "trust_remote_code" in low or "security refusal" in low:
return "trc"
if "foundational" in low:
return "foundational-drift"
if "degraded" in low:
return "degraded-drift"
if "out of scope" in low or "genesis" in low:
return "scope"
return "other"
# Per-family map: which weight field(s) `kv._weights_per_card_gb` reads on
# the DEFAULT (weights_variant="default") path that `[B]`/raw_verdict uses.
# We substitute the actually-resolved curated-variant blob size onto exactly
# these field(s) so pricing reflects the resolved variant WITHOUT ever
# touching `model_family` (which selects the authoritative family pricing
# branch in kv-calc). Keyed by the curated-exact spec's own model_family.
_FAMILY_WEIGHT_FIELDS = {
"qwen3-next-hybrid": ("weights_total_gb",),
"qwen3-next-moe": ("weights_total_gb",),
"gemma4-swa-dense": ("weights_int4_gb",),
"gemma4-swa-moe": ("weights_int4_gb", "weights_awq_gb"),
}
def _curated_spec(profiles, der) -> dict:
"""Build the **curated-exact** kv-calc spec for a Tier-1 curated hit so
P1's raw_verdict prices it through the model's authoritative
family-specific branch (NOT generic-dense's conservative lower-bound).
Start from `kv.MODEL_SPECS[der.tier1.model_id]` — the SAME specs
`tools/kv-calc.py --calibration` validates at 22/22 — preserving its real
`model_family` and every family-specific field, then substitute ONLY the
weight size for the actually-resolved variant (`der.tier1.weights_variant`)
read from the curated ModelProfile (the ModelProfile remains the weight
authority; we never recompute weight size). NEVER emit
`model_family="generic-dense"` for a curated model."""
t1 = der.tier1
kv = _kv()
model_specs = getattr(kv, "MODEL_SPECS", None)
if not isinstance(model_specs, dict) or t1.model_id not in model_specs:
raise RuntimeError(
f"_curated_spec: Tier-1 model_id {t1.model_id!r} not in "
f"kv.MODEL_SPECS (keys={sorted((model_specs or {}).keys())}). "
f"This is a hard error — refusing to fall back to generic-dense "
f"for a curated model."
)
# Defensive copy of the authoritative curated-exact spec (preserve
# model_family + all family fields verbatim — do NOT recompute/adjust).
spec = dict(model_specs[t1.model_id])
family = spec.get("model_family")
if family == "generic-dense" or not family:
raise RuntimeError(
f"_curated_spec: curated-exact spec for {t1.model_id!r} has "
f"unexpected model_family={family!r}; refusing to price a "
f"curated model as generic-dense."
)
# Resolved-variant weight blob size from the curated ModelProfile
# (authoritative; same source the old code read).
model = profiles.models[t1.model_id]
vmeta = model.weights.get(t1.weights_variant, {}) or {}
size_gb = (
vmeta.get("size_gb")
or (der.profile or {}).get("weights_variant_size_gb")
)
weight_fields = _FAMILY_WEIGHT_FIELDS.get(family)
if weight_fields is None:
raise RuntimeError(
f"_curated_spec: no weight-field mapping for curated family "
f"{family!r} ({t1.model_id!r}). STOP — do not silently fall "
f"back to generic-dense."
)
# Only substitute when the curated ModelProfile gives a usable numeric
# blob size for the resolved variant. If it does not (e.g. "variable"
# GGUF), keep the authoritative spec's own validated weight size rather
# than zeroing it — never weaken the curated-exact pricing.
if isinstance(size_gb, (int, float)) and float(size_gb) > 0:
for f in weight_fields:
spec[f] = float(size_gb)
spec["model_id"] = t1.slug
return spec
# ===========================================================================
# v0.8.2 CONTRACT-4 — the `recommend` UX (success-path counterpart to the
# CONTRACT-1 failure on-ramp).
#
# This is PURE PRESENTATION / AGGREGATION over the SHIPPED `run_pull`
# verdict. It introduces NO decision logic: every field it renders is read
# straight off the real `PullResult` (`ok` / `confidence` / `raw_verdict` /
# `terminal` / `stratum` / `abort_reason` / `notices` / `emitted`) that the
# locked 6-stratum + §4.1 machinery produced for THIS run. The recommendation
# therefore tracks the real verdict by construction — a model that fits, one
# that hard-blocks, and one that is `confirm→proceed` each yield a different
# block because each carries a different real `res`. It is NOT a static or
# templated summary: remove the gate and the recommendation changes with it.
#
# Honesty invariants (CONTRACT-4):
# * vLLM-only by construction (the shipped gate is vLLM-only; we add
# nothing — we only echo what the gate decided).
# * Carries the §7 boot-fit≠runtime caveat verbatim (CAVEAT_S7) + the
# soak-continuous pointer, on any verdict the gate itself marked
# boot-fit-satisfied (detected via CAVEAT_S7 ∈ res.notices — the SAME
# signal the gate already emits; we do not re-derive it).
# * States WHICH gate decided (`res.stratum`) — never silently summarised.
# * Honest about the confidence tier (echoes res.confidence; an
# estimated-lower-bound floor is stated, never hidden).
# * NEVER implies a non-emitted artifact: a compose line is printed ONLY
# when `res.emitted` is true (Path A actually emitted one).
# ===========================================================================
def _render_recommendation(res: "PullResult") -> list[str]:
"""Aggregate the SHIPPED `run_pull` verdict into an honest, human
recommendation. Pure presentation: derives every line from `res`;
introduces no decision logic. Returns the lines to print (so the
test can assert the recommendation tracks a *real* differing verdict
without driving a tty)."""
out: list[str] = ["[recommend] ----- recommendation -----"]
out.append(
f"[recommend] model={res.slug} profile-like={res.profile_like} "
f"engine=vLLM (the pull gate is vLLM-only by construction)"
)
if res.ok:
# The gate said download-eligible / clean. Echo the real terminal +
# confidence; never upgrade the gate's honesty.
verdict = res.terminal or "proceed"
out.append(
f"[recommend] verdict: FITS → {verdict} "
f"(confidence={res.confidence or 'n/a'}, "
f"raw_verdict={res.raw_verdict or 'n/a'})"
)
if res.confidence == "estimated-lower-bound":
out.append(
"[recommend] note: this is an ESTIMATED LOWER BOUND — the "
"modelled VRAM is a floor and is likely under-modelled; "
"treat the fit as the optimistic edge, not a guarantee."
)
if res.emitted:
out.append(
"[recommend] a ready compose was emitted for this config "
"(see the --out path / stdout above)."
)
else:
# Honesty: do NOT imply a compose that was not produced.
out.append(
"[recommend] no compose was emitted this run (Path B "
"evaluate / --dry-run / not download-eligible) — this is a "
"verdict only, not a launchable artifact."
)
else:
# `res.ok` is False, but that means "the run did not produce a
# launchable artifact" — NOT necessarily "does not fit". Distinguish
# the gate's *acceptance* terminals (it fits the modelled budget; the
# run merely needs an explicit flag) from a genuine hard-block.
# Honesty (CONTRACT-4): never label a fits-clean model "DOES NOT FIT".
# Pure presentation: still derives only from `res`, no decision logic.
reason = res.abort_reason or "see the gate output above"
_rv = res.raw_verdict or ""
_ar = res.abort_reason or ""
_needs_accept = _rv == "fits-clean" and (
_ar.startswith("confirm→proceed")
or _ar.startswith("override-accepted")
or "needs --yes" in _ar
or "needs --force-download" in _ar
)
if _needs_accept:
out.append(
f"[recommend] verdict: FITS (estimated) — NOT YET ACCEPTED: "
f"{reason} (confidence={res.confidence or 'n/a'}, "
f"raw_verdict={res.raw_verdict or 'n/a'})"
)
out.append(
"[recommend] the model fits the gate's modelled budget — this "
"is an ACCEPTANCE gate, not a fit failure. Re-run with the "
"flag named in the reason above to proceed."
)
if res.confidence == "estimated-lower-bound":
out.append(
"[recommend] note: this is an ESTIMATED LOWER BOUND — the "
"modelled VRAM is a floor and is likely under-modelled; "
"treat the fit as the optimistic edge, not a guarantee."
)
else:
# Genuine hard-block / needs-a-flag. Carry the precise reason and
# WHICH gate decided; point at the failure on-ramp (CONTRACT-1).
out.append(
f"[recommend] verdict: DOES NOT FIT / BLOCKED — {reason}"
)
out.append(
"[recommend] this is an honest stop, not a stack failure. The "
"diagnostics (if a redacted bundle was captured) can be sent "
"back with `scripts/pull.sh --submit-last` — see the failure "
"on-ramp section of docs/PULL.md."
)
# WHICH gate decided — always stated (CONTRACT-4: never silently
# summarised). Read straight off the real result.
out.append(
f"[recommend] decided at: stratum={res.stratum.name}"
)
# §7 boot-fit ≠ runtime caveat + soak-continuous pointer — carried
# verbatim ONLY when the gate itself marked this run boot-fit-satisfied
# (CAVEAT_S7 ∈ res.notices is the gate's own signal; we echo, not
# re-derive). A blocked verdict that never reached [B] has no such
# notice and (correctly) gets no false soak pointer.
if CAVEAT_S7 in res.notices:
out.append(f"[recommend] §7 caveat: {CAVEAT_S7}")
out.append(
"[recommend] before relying on this in production run: "
"scripts/soak.sh SOAK_MODE=continuous (the only test that "
"catches Cliff 2b)."
)
out.append("[recommend] -------------------------------")
return out
# ===========================================================================
# CLI (thin; pull.sh execs this). Renders PullResult to stdout + exit code.
# ===========================================================================
_EXIT_OK = 0
_EXIT_ABORT = 2 # any honest hard-stop (stratum 1-6 / hard-block)
_EXIT_NEEDS_FLAG = 3 # confirm→proceed / advisory not yet satisfied
_EXIT_USAGE = 64
def _maybe_submit_verb(argv: list[str]) -> Optional[int]:
"""v0.8.2 CONTRACT-1.3 — the `--submit-last` / `--submit <dir>` verb is
a DISTINCT top-level verb, parsed BEFORE the positional-slug /
`--profile-like` requirement (so `--submit*` needs NEITHER a slug NOR
`--profile-like`). Returns an exit code when the submit verb was
invoked, else `None` (fall through to the normal gate CLI).
This is intentionally a pre-argparse intercept: the shipped gate parser
hard-requires `slug` + `--profile-like`, and the submit verb is a
different invocation grammar entirely (the on-ramp, not a gate run).
The submit path is the ONLY place network ever happens, and only after
an explicit `y` (the locked `[F]`-offline boundary — `run_pull` itself
stays I/O-free).
"""
if "--submit-last" not in argv and "--submit" not in argv:
return None
from scripts.lib.profiles.submit_pull import submit_pull
submit_last = "--submit-last" in argv
capture_dir: Optional[str] = None
if "--submit" in argv:
i = argv.index("--submit")
if i + 1 < len(argv):
capture_dir = argv[i + 1]
return submit_pull(
capture_dir=capture_dir,
submit_last=submit_last,
repo_root=REPO_ROOT,
)
def main(argv: list[str]) -> int:
import argparse
# v0.8.2 CONTRACT-1.3 — the submit verb is parsed BEFORE the gate
# parser's slug/--profile-like requirement (a distinct top-level verb).
_sub_rc = _maybe_submit_verb(argv)
if _sub_rc is not None:
return _sub_rc
class _UsageExit64Parser(argparse.ArgumentParser):
"""argparse's default `error()` hard-exits 2 — which collides with
`_EXIT_ABORT` (honest gate hard-stop), so a typo and a legitimate
block are indistinguishable to callers/automation. Override to exit
`_EXIT_USAGE` (64) on argument/usage errors, restoring the
documented contract. `--help` is unaffected (it goes through
`exit()`, not `error()`, and still returns 0)."""
def error(self, message):
self.print_usage(sys.stderr)
self.exit(_EXIT_USAGE, f"{self.prog}: error: {message}\n")
ap = _UsageExit64Parser(
prog="pull.sh",
description="v0.8.0 Pull-Gate — derive an HF repo, gate it through "
"the locked 6-stratum taxonomy, and (Path A, curated+emittable) "
"emit a compose via the #141 generator. Honest about confidence; "
"never silently gate-passes.",
)
ap.add_argument("slug", help="HF repo slug (e.g. org/Model-Name)")
ap.add_argument(
"--profile-like", required=True, dest="profile_like",
help="REQUIRED curated COMPOSE_REGISTRY key supplying the runtime "
"shape (Path A: must name the curated model+variant & be "
"[D]-emittable; Path B: any vLLM profile, shape only)",
)
ap.add_argument("--dry-run", action="store_true",
help="force Path B (evaluate only; never emit/download)")
ap.add_argument("--yes", action="store_true",
help="accept a confirm→proceed terminal (§4.1)")
ap.add_argument(
"--force-download", action="store_true",
help="advisory low-confidence wont-fit → override-accepted "
"(NO-OP + notice this phase; download deferred to Loop)",
)
ap.add_argument(
"--experimental-arch", action="store_true",
help="bypass ONLY [C0] engine-support-unknown/no-arch-row "
"(never runtime-incompatible; Path B only this phase)",
)
ap.add_argument("--trust-remote-code", action="store_true",
help="bypass [C0] needs-trust-remote-code-ack")
ap.add_argument("--hf-home", help="override the HF_HOME resolution chain")
ap.add_argument("--out", help="Path A: write the emitted compose here")
ap.add_argument(
"--hardware", type=float, default=None,
help="override detected GPU compute capability (e.g. 8.6 for "
"RTX 3090); default = nvidia-smi detection",
)
ap.add_argument(
"--hardware-gpus", default=None,
help="v0.8.0 [E]: override detected GPU topology for the derived "
"[E] stage as 'VRAM_MIB:NAME,VRAM_MIB:NAME' (e.g. "
"'24576:RTX 3090,24576:RTX 3090'); default = nvidia-smi detection",
)
ap.add_argument(
"--recommend", action="store_true",
help="v0.8.2 CONTRACT-4: after the gate runs, print an honest "
"aggregated recommendation (fits? which quant/variant; the §7 "
"boot-fit≠runtime caveat; which gate decided). Presentation only "
"— derives entirely from the real gate verdict, changes no "
"decision; vLLM-only by construction",
)
args = ap.parse_args(argv)
gpu_topology = None
if args.hardware_gpus:
vram: list[int] = []
names: list[str] = []
for tok in args.hardware_gpus.split(","):
tok = tok.strip()
if not tok:
continue
if ":" in tok:
v, n = tok.split(":", 1)
else:
v, n = tok, "GPU"
vram.append(int(float(v)))
names.append(n.strip() or "GPU")
if vram:
gpu_topology = (len(vram), vram, names)
res = run_pull(
args.slug, args.profile_like,
dry_run=args.dry_run, yes=args.yes,
force_download=args.force_download,
experimental_arch=args.experimental_arch,
trust_remote_code=args.trust_remote_code,
hf_home=args.hf_home, out=args.out,
hardware_sm=args.hardware,
gpu_topology=gpu_topology,
)
tag = "OK" if res.ok else "ABORT"
print(f"[pull] {tag} path={res.path} stratum={res.stratum.name} "
f"slug={res.slug} profile-like={res.profile_like}")
if res.confidence:
print(f"[pull] confidence={res.confidence} "
f"raw_verdict={res.raw_verdict} terminal={res.terminal}")
if res.abort_reason:
print(f"[pull] reason={res.abort_reason}")
if res.detail:
print(f"[pull] {res.detail}")
for n in res.notices:
print(f"[pull] note: {n}")
if res.emitted and not args.out:
sys.stdout.write(res.compose_text or "")
# v0.8.2 CONTRACT-1.2 — surface the failure on-ramp pointer (gate path,
# I/O-FREE: a single print; NO network, NO blocking prompt, NO auto-send
# — submission is the separate, explicit, consented `--submit-last`
# step). The TRIGGER is "a gate bundle was emitted for this run", keyed
# on the capture dir the V1 pass-through recorded on `res.diagnostics`
# — EXPLICITLY NOT gated on the exit code. The single most important
# §10-R9 lever (`engine-support-unknown/no-arch-row`) is the
# `--experimental-arch`-bypassable advisory path that exits 0 yet emits
# a bundle; gating this pointer on `exit==2` would silently suppress the
# submit prompt for exactly that class. It does NOT classify (that is
# loop-side at submit) — it prints unconditionally on any emitted gate
# bundle; suppression (no public issue) is enforced loop-side. The
# surfaced message points ONLY at the redacted `.pull-captures/`
# artifact + the exact one-command — never "paste your terminal output"
# (console is not a safe submission source).
_gc = res.diagnostics.get("gate_capture") if res.diagnostics else None
_gc_dir = _gc.get("dir") if isinstance(_gc, dict) else None
if _gc_dir:
print(
f"[pull] Diagnostics captured (redacted, no paths/tokens): "
f"{_gc_dir}"
)
print(
"[pull] Help improve the fit math — submit with: "
"scripts/pull.sh --submit-last"
)
# v0.8.2 CONTRACT-4 — the `recommend` UX. Pure presentation/aggregation
# over the SHIPPED verdict (`res`); changes NO decision and NOT the exit
# code (the recommendation only RENDERS what the gate already decided).
# Emitted last so it summarises the full verdict the user just saw.
if args.recommend:
for ln in _render_recommendation(res):
print(ln)
if res.ok:
return _EXIT_OK
if res.abort_reason and (
res.abort_reason.startswith("confirm→proceed")
or res.abort_reason.startswith("override-accepted")
):
return _EXIT_NEEDS_FLAG
return _EXIT_ABORT
if __name__ == "__main__":
raise SystemExit(main(sys.argv[1:]))