Cloud provider preflight (Modal, Azure) + abliterix wiring - #6
Conversation
Rented compute is a new axis for forge: fleet nodes come through the lease
system, but cloud GPUs are paid by the second and uncontended, so a cloud run
is an --off-fleet run that dispatches to a provider.
Credentials for both accounts are still pending, so this lands the layer that
is useful *without* them: a declarative statement of what each provider needs
and a preflight that names exactly what is absent.
- src/forge/providers/: Provider/Credential base, plus modal and azure.
Preflight is local-only (no network, no spend) and treats a provider's own
config file (~/.modal.toml, ~/.azure) as satisfying a credential, since
`modal token new` and `az login` export nothing.
- forge cloud providers|preflight, --json throughout. Preflight exits 0 even
when not ready — a red report before the creds arrive is information, not a
command failure.
- GPU shapes verified against vendor docs 2026-08-07, not asserted: Modal
takes B200/B300 up to 8 per container; Azure's Blackwell SKUs are 4-GPU
Grace-Blackwell VMs (ND GB300 v6 / ND GB200 v6), so 8x on Azure is a two-VM
job. Availability and quota are deliberately NOT claimed.
Abliteration:
- recipes/abliterate-abliterix.yaml wires abliterix (Optuna search over
abliteration params, scored refusal-rate vs KL divergence). tool_cmd is
pre-filled from the project README and marked UNVERIFIED — reconcile
against `abliterix --help` on the node before the first run.
- bootstrap/fleet-node.sh installs abliterix into the profile env (outside the
lock; it pulls vLLM) and prints its --help so that reconciliation happens.
- common.render_tool_cmd() adds {model_path} to the surgery wrappers, so one
recipe templates across models. Relative paths resolve under model_root only
when they exist on disk; HF repo ids pass through unmangled. The null
tool_cmd refusal contract is unchanged and still tested.
114 tests pass.
colbyt
left a comment
There was a problem hiding this comment.
forge PR #6 review — Cloud provider preflight (Modal, Azure) + abliterix wiring
Reviewed: gh pr diff 6, full diff (632 lines) read in context; ran pytest tests/test_cloud.py tests/test_surgery_tool_cmd.py against the PR branch on this machine; checked recipes.py,
envs/profiles.yaml, config.py, output.py, cli.py exit-code conventions across the repo;
checked PR against projects/forge/WEIGHT-SOURCES.md (commit b1079ac46, dual weight-source
requirement for rented compute).
Verdict: mergeable-after-nits. Design and scope discipline are good (preflight-only, no spend,
no network I/O, job submission explicitly deferred — this correctly respects the dual
weight-source requirement by not building job submission at all yet). One real, currently
reproducing test bug needs a fix before merge; one design gap (Azure readiness signal) is worth
a follow-up issue, not a blocker.
Findings, ranked
1. (Medium-High, currently reproducing) Credential path-check isn't test-isolated — a real ~/.modal.toml on the machine makes test_preflight_reports_missing_credentials_by_name fail locally although CI is green
src/forge/providers/base.py:291-296:
def satisfied_by(self, env):
if env.get(self.env):
return "env"
if self.path and Path(self.path).expanduser().exists():
return "file"
return NoneCredential.satisfied_by takes an env dict for the env-var check but the file-existence check
always reads the real filesystem (Path(self.path).expanduser() — i.e. real $HOME), regardless
of what env was passed to preflight(). tests/test_cloud.py:26-31 calls
providers.get("modal").preflight(env={}) and asserts ok is False, expecting env={} to mean
"nothing satisfies this provider." That's not what happens if the machine running the tests
happens to have ~/.modal.toml on disk.
Reproduced: checked out pr6 in the actual forge checkout and ran the test suite —
FAILED tests/test_cloud.py::test_preflight_reports_missing_credentials_by_name
assert True is False
~/.modal.toml exists on this machine (dated 2026-08-07 14:39, this is Colby's real Modal token
file — worth confirming that's expected/current, raw observation only). GitHub Actions' ubuntu
runner has no such file, so CI is green while local pytest fails — the PR's "114 tests pass"
claim held in CI/on the author's clean environment but does not hold on a dev machine with a real
credential file already in place, which given the whole point of this layer (rented-compute
credentials arriving) is exactly the environment forge devs will be running in soon.
Fix: isolate the filesystem in the affected tests — e.g. monkeypatch.setenv("HOME", str(tmp_path)) (or monkeypatch.setattr(Path, "home", ...)/patch os.path.expanduser) so
~/.modal.toml and ~/.azure resolve under an empty tmp dir regardless of the host's real
dotfiles. Affects test_preflight_reports_missing_credentials_by_name and
test_human_preflight_marks_missing_lines (same class of leak for Azure's ~/.azure) at minimum.
2. (Medium, design gap — flag for follow-up, not necessarily this PR) Azure readiness is checked as "does ~/.azure exist," which is a much weaker signal than the Modal token-file check
src/forge/providers/azure_provider.py:217,227-231:
AZURE_CONFIG = "~/.azure"
...
Credential("AZURE_SUBSCRIPTION_ID", "...", path=AZURE_CONFIG),
Credential("AZURE_TENANT_ID", "...", path=AZURE_CONFIG),The docstring says "az login writes ~/.azure/, which satisfies the credential without env
vars" — but the Azure CLI is documented to create the ~/.azure directory (config, telemetry
cache, etc.) on first invocation of any az command, not only after a successful az login.
If that's accurate, az --version alone (something bootstrap/CI tooling might run) would flip
ok to True with no real subscription access behind it — the opposite failure mode from
Modal's check (Modal's ~/.modal.toml is written specifically by modal token new, so it's a
much stronger signal). This is exactly the "honesty" property the module's docstring and PR body
claim ("a red report is information... not a failed command" / preflight should be trustworthy) —
worth verifying against a real az install before relying on it, and if confirmed, checking a
login-specific artifact (e.g. ~/.azure/azureProfile.json) instead of the bare directory.
Not blocking since Azure isn't wired to spend anything yet, but flag it now since it's the kind
of thing that's easy to forget once credentials actually land.
3. (Low, consistency nit) cmd_providers's human-format loop duplicates a variant of _human_preflight's row formatting
src/forge/cloud.py:104-120 (_human_preflight) and src/forge/cloud.py:123-130
(cmd_providers) both build f"{provider:8} ..." header lines and a gpus line independently
with slightly different formatting (gpus vs gpus ). Not a bug, but two near-duplicate
formatters for the same data will drift. Minor; could factor a shared _row_header(r) helper if
touched again.
4. (Informational, not a finding) forge cloud preflight exits 0 even when nothing is ready
Confirmed intentional and well-documented (module docstring, inline comment at
src/forge/cloud.py:147-149, PR body, and a dedicated test
test_preflight_exits_zero_even_when_not_ready). Reasonable given the JSON-first contract
(emit's ok field is the real signal) — just flagging since "failure modes that exit 0" was
explicitly in scope for this review. Any future shell wrapper around this command must check the
JSON ok field, not the process exit code.
Dual weight-source requirement (commit b1079ac46 / WEIGHT-SOURCES.md)
Respected. This PR ships preflight only — no job submission, no weight-fetch code — which is
exactly what WEIGHT-SOURCES.md says should exist today ("Status: requirement captured
2026-08-07, nothing built. The forge cloud layer today is preflight-only"). No --weights
resolver appears here, so there's nothing yet that could bake in a single weight source. Fine to
merge without addressing the dual-source design — it's out of scope by the PR's own stated
boundary, and that boundary is honest.
Other categories checked, no issues found
- Error handling on cloud API calls: N/A — this PR makes no network/API calls anywhere
(verified: norequests/subprocesscalls tomodal/azin the diff; onlyshutil.which
andPath.exists). Deliberately scoped that way per the module docstring. - Credential handling / no secrets in logs: clean.
Credential.describe()and
_human_preflightonly ever emit variable names, descriptions, and source (env/file),
never values. Confirmed by readingbase.py:298-303andcloud.py:104-120. - Idempotency: preflight is read-only and stateless; same inputs (env + filesystem) always
produce the same result (modulo Finding 1's test isolation issue). render_tool_cmd/model_pathtemplating (train/common.py:37-58): reasonable, tested
from multiple angles (relative-path-under-model_root, HF-repo-id passthrough, absolute-path
passthrough); the null-tool_cmdrefusal contract inabliterate.py/reap.pyis preserved
and re-tested (test_null_tool_cmd_still_refuses).- Recipe validity:
recipes/abliterate-abliterix.yamlsatisfiesrecipes.py'sREQUIRED
keys andMETHODS/profile checks (cuda-datacenterexists inenvs/profiles.yaml); test
test_abliterix_recipe_is_valid_and_wiredexercisesload_recipeagainst it directly. - bootstrap/fleet-node.sh: new abliterix install block follows the existing file's own
pattern exactly (|| echo "WARN: ...", noset -etrip), consistent with the llama.cpp block
immediately above it.
…hen Azure readiness check Review finding 1: satisfied_by() checks Path(self.path).expanduser() against the real filesystem regardless of the env dict passed to preflight(), so test_preflight_reports_missing_credentials_by_name and test_human_preflight_marks_missing_lines silently depended on the dev machine's real ~/.modal.toml / ~/.azure being absent. Reproduced locally (real ~/.modal.toml on this machine flipped the Modal test's assertion). Fixed by monkeypatching HOME to an empty tmp_path in both tests. Review finding 2: Azure's readiness check treated the mere existence of ~/.azure/ as a login signal, but the Azure CLI creates that directory (config, telemetry cache) on the first invocation of ANY az command, not just az login -- so e.g. bootstrap tooling running az --version would flip ok to True with no real subscription access behind it. Point the credential at ~/.azure/azureProfile.json instead, which az login writes specifically, and add tests pinning both the negative (bare dir alone) and positive (profile file present) cases.
Two additions, both requested ahead of the credentials/hardware that will exercise them.
forge cloud— rented-compute preflightFleet nodes come through the lease system; cloud GPUs don't (paid by the second, uncontended), so a cloud run is an
--off-fleetrun that dispatches to a provider. Modal and Azure account details are still pending, so this lands the layer that is useful without them — a declarative statement of what each provider needs, and a check naming exactly what's absent:$PATH.modal token newandaz loginexport nothing, and reporting those as missing would send an operator chasing a non-problem.GPU shapes are verified against vendor docs (2026-08-07), not asserted: Modal accepts B200/B300 at up to 8 per container; Azure's Blackwell SKUs are 4-GPU Grace-Blackwell VMs (ND GB300 v6 / ND GB200 v6), so an 8-GPU job there is a two-VM NVLink-domain problem. Live availability and quota are explicitly not claimed —
az vm list-skusagainst the real subscription is the only truth.abliterix
abliterix runs an Optuna search over abliteration parameters, scoring refusal rate against KL divergence from the base model, and exports a reversible LoRA or baked projections with a SHA256 reproducibility manifest.
recipes/abliterate-abliterix.yaml—profile: cuda-datacenter, since the models this was wired in for exceed every Berkeley GPU.tool_cmdis pre-filled from the project README and marked UNVERIFIED. The repo's rule stands: runabliterix --helpon the node and reconcile before the first real run. It's pre-filled rather than null because the shape is documented and stable — it is still an unverified claim until someone looks.bootstrap/fleet-node.shinstalls it into the profile env (outside the lock — it pulls vLLM) and prints--helpso that reconciliation actually happens.common.render_tool_cmd()adds{model_path}to both surgery wrappers so one recipe templates across models. Relative paths resolve undermodel_rootonly when they exist on disk; HF repo ids pass through unmangled. The null-tool_cmdrefusal contract is unchanged and still tested.Not tested on hardware yet — abliterix needs vLLM, so it won't install on macOS, and testing was deferred rather than risk disturbing GPUs currently serving.
114 tests pass.