Upgrades SpectralQuant from uniform bit allocation to greedy water-filling inside the semantic subspace, with per-dimension Lloyd-Max codebooks fit to the empirical scalar marginal. Adds full three-way evaluation harness (perplexity, generation, latency, LongBench) on Modal H200 GPUs, comprehensive documentation, and the consolidated arxiv-ready manuscript. v1 is preserved on the 'v1' branch. Co-Authored-By: Anirudh B. Vangara <anirudh@sentra.app>
33 KiB
SpectralQuant v2 — Modal Safety Protocol
This document is the operational safety contract for SpectralQuant v2 runs on
Modal. It is the companion to docs/execution_audit_and_modal_runbook.md and
must be read before any Modal run that loads weights or spends GPU credit.
Today's anchor date. 2026-04-29.
The rules below are written conservatively. They cost a few minutes of preflight on every run; in exchange they prevent leaked secrets, silent ratelimits, half-written JSONs, and unrecoverable mid-sweep crashes.
1. Credential handling
1.1 What counts as a credential
- Hugging Face access token (
HF_TOKEN,HUGGING_FACE_HUB_TOKEN). - Modal token id and secret (
MODAL_TOKEN_ID,MODAL_TOKEN_SECRET). - W&B API key if used (
WANDB_API_KEY). - Any OAuth or personal-access-token equivalent.
1.2 Hard rules
-
Never commit credentials. No token value may appear in any file under git — not in
.env, not in scripts, not in JSON output, not in commit messages, not in PR descriptions. The.gitignoreincludes.env,.env.*(except.env.example),modal_token*,hf_token*,secrets/,*.pem,*.key,.modal/, and.huggingface/so a stray file in the working tree is not staged by accident. -
Never echo credentials. Scripts must not
print(os.environ["HF_TOKEN"]), not even partially. The preflight script (scripts/preflight_modal.py) reports presence/absence only —[ok] HF_TOKEN is set (length: N)is acceptable; the value is not. -
Never log credentials. When invoking shell commands that take secrets as arguments, use environment variables instead of CLI flags so the value does not appear in process listings or shell history. Example:
huggingface-cli loginreads from stdin or env, not--token <value>. -
Pass tokens via Modal secrets, not via image baking. In a Modal app:
import modal image = modal.Image.from_registry("nvidia/cuda:12.4.0-devel-ubuntu22.04") app = modal.App("spectralquant-v2") @app.function( image=image, gpu="H200", secrets=[ modal.Secret.from_name("hf-token"), # exposes HF_TOKEN modal.Secret.from_name("wandb-api-key"), # if used ], timeout=60 * 60, ) def run_three_way(...): ...The Modal CLI itself authenticates via
~/.modal.toml, which is gitignored and lives outside the repo. -
If a secret leaks (committed by accident, posted in a log, sent in a message), treat it as compromised: rotate the token immediately on the issuing service, force-push remediation only with the maintainer's approval, and add a note to the audit doc.
1.3 Local development convention
- Keep an
.env.example(committed) listing the names of variables only:HF_TOKEN=,MODAL_TOKEN_ID=,MODAL_TOKEN_SECRET=. Real values live in.env(gitignored). - Source
.envwithset -a; source .env; set +aordirenvfor local shells. Do not export secrets from the global shell rc — they will leak into unrelated processes.
2. Hugging Face license verification
Mistral-7B-v0.3 is a gated model. Without a license-accepting account,
AutoModelForCausalLM.from_pretrained("mistralai/Mistral-7B-v0.3") returns
403.
Preflight requirements before a Mistral run:
- The HF account behind
HF_TOKENmust have visitedhttps://huggingface.co/mistralai/Mistral-7B-v0.3and accepted the community license while logged in. - A dry, weight-free check —
huggingface_hub.HfApi().model_info(model_id, token=os.environ["HF_TOKEN"])— succeeds. If this returns 403, abort the run before model load; do not pay for a GPU minute on a run that will fail at weight download. - Record the tokenizer revision and model revision (commit SHA on the HF
side) into the result JSON's
softwarefield so the run is reproducible if the model is later updated upstream.
For Qwen2.5-7B (public): no license gate, but rate limits apply. Authenticate anyway to get higher quotas.
3. GPU choice
- Default: H200 SXM — has enough VRAM for Mistral-7B-v0.3 with margin and is meaningfully faster than H100 SXM for FP16/bfloat16 matmul. H100 SXM is acceptable as fallback.
- B200 / GB200: only if explicitly requested. The image in
scripts/setup_b200.shtargets a B200; check that the cutile baseline builds against that arch before launching a sweep. - A100 80GB: acceptable for smoke tests only. Do not run the full sweep on A100 — calibration timing will be unrepresentative of the production target.
- L40S / A10 / T4: refuse. Insufficient VRAM for 7B FP16 weights + KV cache
- intermediate activations.
Pick the smallest GPU that fits the run and matches the production target. Bigger does not mean safer — it means more dollars per crashed run.
4. Resumable result paths
The Phase 6 sweep is multiple runs. A crash mid-sweep must not lose previous runs.
-
Per-run JSON path is deterministic. The output file name encodes
model_short,bits,seed, andn_calibso that if a run is repeated it overwrites itself rather than collides with a different run:results/three_way/{model_short}_b{bits}_calib{n_calib}_seed{seed}.json -
Skip-if-exists by default.
experiments/run_three_way.pyaccepts--skip-if-exists(default true),--resume(alias), and--force(default false). With skip, a half-completed sweep can be resumed by re-running the same shell loop without redoing finished cells;--forcere-runs and overwrites. -
Per-run scratch directory. Intermediate state during a single run (compressed key tensors, calibration matrices) lives under
local_results/scratch/<run_id>/(gitignored). Clean up on success; leave intact on failure for postmortem. -
Dry-run / synthetic-smoke before any Modal call.
experiments/run_three_way.pyexposes three safe modes:--dry-runvalidates argv, prints the run plan and the deterministic output path, optionally schema-checks an in-memory placeholder payload, and writes nothing.--synthetic-smokeruns the three engines (TurboQuant baseline, SpectralQuant v1, SpectralQuant v2) end-to-end on small synthetic Q/K/V tensors and writes a schema-valid JSON underresults/three_way_smoke/(prefixedsynthetic_smoke__…so it can never be confused with a real Modal artifact).--inline-corpus-smokeruns the full HF model path (real model load, adapter discovery, hooks, calibration, quantization, eval) but swaps the WikiText /datasets.load_datasetcorpus for a tiny deterministic in-memory list. It is intended to validate the harness plumbing on a small Qwen variant (e.g.Qwen/Qwen2.5-0.5B) when the dataset download is hanging or unavailable. Outputs are prefixedinline_corpus_smoke__…, markmode: "inline-corpus-smoke", setpaper_valid: false, and usecalibration_corpus: "inline_smoke"so they are never mistaken for benchmark evidence. Full sweeps must still use the WikiText / proper eval corpus.
--dry-runand--synthetic-smokerefuse to call HuggingFace, never download a model, and never read or echoHF_TOKEN.--inline-corpus-smokedoes load the model (the whole point is to validate the model-load → hook → quant → eval path) but skips the dataset download, so it cannot stand in for benchmark evidence.The full HF model path (
run_three_way.pywithout--dry-run/--synthetic-smoke) lazy-importstransformersanddatasets. Locally, with those packages absent, it raises a clearRuntimeErrorsaying so — never a silent fallback. On Modal, where the image installs both, the path:- Loads tokenizer +
AutoModelForCausalLMin the requested dtype.HF_TOKENis consumed byhuggingface_hubautomatically; the script never reads its value. - Discovers attention layers via
experiments/model_adapters.py(Mistral / Llama / Qwen2 split-QKV layout supported; packed-QKV raisesUnsupportedArchitectureError). - Runs the eigenspectral calibrator on a
wikitextslice (or the--dataset-name/--dataset-config/--dataset-splityou pass). - Optionally saves / reloads the calibration
.ptartifact under--calibration-dir, keyed deterministically by{model_short}_calib{N}_tok{T}_seed{S}so resumed sweeps share one calibration per (model, seed, n_calib). - Hooks
q_proj/k_proj/v_projof the sampled layers, captures the live Q/K/V tensors during a forward pass on each eval text, and computes per-layer attention-cosine vs FP16 reference for TurboQuant baseline, SpectralQuant v1, and SpectralQuant v2. - Writes the schema-valid JSON via
atomic_write_json(validates beforeos.replace).
-
Calibration artifact resumability. When
--calibration-diris set with--save-calibration, the calibrator artifact is written to disk before the engines fit so a crash mid-fit does not lose the eigendecomposition. Subsequent runs in the same sweep should pass--load-calibrationto skip the re-calibration step.
5. Atomic JSON writes
Half-written JSON files break the schema validator and corrupt downstream plot scripts. All result writes must be atomic:
import json, os, tempfile
def atomic_write_json(path: str, payload: dict) -> None:
os.makedirs(os.path.dirname(path), exist_ok=True)
fd, tmp = tempfile.mkstemp(prefix=".tmp_", dir=os.path.dirname(path))
try:
with os.fdopen(fd, "w") as f:
json.dump(payload, f, indent=2, sort_keys=True)
f.flush()
os.fsync(f.fileno())
os.replace(tmp, path) # atomic on POSIX
finally:
if os.path.exists(tmp):
os.unlink(tmp)
os.replace is atomic on POSIX. Validate the JSON against
schemas/three_way_result.schema.json before the rename — a file that
cannot validate must never appear at the canonical path.
6. Frequent artifact sync
Modal volumes are durable but not infinite, and a Modal app's local FS is ephemeral. Sync artifacts back to durable storage as soon as they exist:
- After every run completes successfully, copy the JSON and stdout log
into the Modal volume mount (e.g.
/results). Do not wait for the whole sweep. - If a Modal volume is not used,
modal nfs put <volume> <local>after each run, orgit pushfrom the runner — but only the JSONs, never the token files. The audit script (scripts/audit_results.py) makes it easy to confirm what made it back. - Locally, after pulling results: re-run
pytest tests/test_result_schema.py -qto confirm every JSON validates.
6a. Git provenance without .git
The Modal image deliberately excludes .git (it is large, often dirty, and
adds nothing to the run). That means a git rev-parse HEAD inside the
container falls through to the placeholder commit 0000000, which silently
poisons the commit field in result JSONs.
To preserve provenance:
- The launcher (
scripts/launch_modal_three_way.py) detects the local repo's HEAD viadetect_local_git_commit(REPO_ROOT)at submit time. - It forwards that SHA into the remote subprocess as the env var
SPECTRALQUANT_GIT_COMMIT. experiments/run_three_way.py::_git_commitreads the env var (and accepts an explicit--git-commit <SHA>override) before falling back togit rev-parse. Only hex SHAs of length ≥ 7 are accepted; bogus values are ignored so the placeholder is preferable to a silently wrong commit.- The launcher's
--dry-runand per-run output prints the SHA it will forward so the operator can confirm provenance before launching.
If detect_local_git_commit returns None (run from outside a git
checkout) the launcher prints a warning and the result JSON records
0000000. Do not silence this warning by patching the placeholder;
fix the working tree instead.
6c. Heartbeat / progress status artifacts
Modal logs are often inaccessible due to control-plane resource limits. To
keep a remote run observable even when streaming logs are gone, the
launcher (scripts/launch_modal_three_way.py) and the harness
(experiments/run_three_way.py) emit small JSON status files at well-
known paths on the Modal volume.
Layout (anchored to the /results Modal volume):
/results/
status/
<run_id>/
status.json # latest snapshot — atomically rewritten on every emit
events.jsonl # append-only history of every emitted event
The <run_id> matches the result-JSON naming so the status directory is
trivially derived from the run config:
<model_short>_b<bits>_calib<N>_eval<M>_seed<S> (prefixed
synthetic_smoke__ for smoke runs). Both expected_output_path and
status_path are printed by main_entry at submission time, e.g.:
[launch_modal_three_way] expected_output_path: /results/three_way/Mistral-7B-v0.3_b3_calib32_eval8_seed42.json
[launch_modal_three_way] status_path: /results/status/Mistral-7B-v0.3_b3_calib32_eval8_seed42/status.json
[launch_modal_three_way] poll status: modal volume get spectralquant-v2-results /results/status/Mistral-7B-v0.3_b3_calib32_eval8_seed42/status.json -
Two layers of status are emitted into the same <status_dir>/<run_id>/
directory:
-
Modal-runner stages — written by
scripts/launch_modal_three_way.py::run_onebefore, around, and after the childrun_three_way.pysubprocess. These give the operator a heartbeat even when the child never starts (e.g. broken image, bad PYTHONPATH, OOM at Popen). Stages emitted in order:modal_run_one_entered— first write afterrun_oneis dispatched on the worker, before any project import. Contains the run_id, expected_output_path, status_path, cwd, python executable- version, and forwarded git commit. If you see no
modal_run_one_enteredartifact, the remote function never ran.
- version, and forwarded git commit. If you see no
subprocess_env_configured— PYTHONPATH and HF cache env vars are set; the child has not yet been spawned.subprocess_starting— about to callsubprocess.Popen.subprocess_started— Popen succeeded, the child has a PID.subprocess_progress— every ~30 s while the child is alive, with bounded sanitized stdout/stderr tails.subprocess_end— child exited 0.failure(terminal) — Popen itself raised, the streaming loop raised, or the child exited non-zero.
-
Benchmark stages — written by
experiments/run_three_way.pyfrom inside the child subprocess. They overwritestatus.jsonas the child progresses past each milestone:start,import_ok,model_load_start,model_load_end,dataset_load_start,dataset_load_end,calibration_start,calibration_end,eval_start,eval_progress(periodic),eval_end, terminalsuccessorfailure.
If status.json is stuck on a Modal-runner stage (modal_run_one_entered
or earlier), the child never started — investigate the image and import
chain. If it is stuck on a benchmark stage, the child started but hung at
that step.
Every payload includes:
run_id,commit,model,avg_bits,n_calib,n_eval,n_layers_sample,host,pid,timestamp(UTC ISO);- For non-terminal stages: optional
message,details, and bounded sanitizedstdout_tail/stderr_tail(≤4000 chars each); - For
failure:error, full sanitizedtraceback, and the latest stdout/stderr tails so the operator can triage without log access.
Sanitization is enforced at write time. experiments/run_status.py:: sanitize_text redacts (a) values of any environment variable whose name
matches the secret patterns TOKEN, SECRET, API_KEY, APIKEY,
PASSWORD, PASSWD, PRIVATE_KEY and (b) token-like literals
(hf_..., ghp_..., github_pat_..., AKIA..., ak-..., as-...).
Empty/short values (<8 chars) are skipped. Status artifacts therefore
carry no token values even if a noisy traceback printed one.
Polling commands:
# Read the latest snapshot (single JSON document):
modal volume get spectralquant-v2-results \
/results/status/<run_id>/status.json -
# Read the full event history:
modal volume get spectralquant-v2-results \
/results/status/<run_id>/events.jsonl -
# Or list the status tree to see all in-flight runs at once:
modal volume ls spectralquant-v2-results /results/status/
Failure rule: if subprocess exits non-zero or raises, the launcher
writes a failure status JSON containing the sanitized stdout/stderr
tails, the exit code, and (for raised exceptions) the traceback. This
artifact is the source of truth for triage when Modal logs are
unavailable.
Smoke-gate rule
No full sweep is launched until the HF smoke writes a success status
JSON. The smoke run is the cheapest way to confirm: the image builds,
HF auth works, dataset loads, calibration runs, the result schema is
satisfied, and the volume is writable. If status.json does not show
stage: success for the smoke, do not pay for a full sweep — fix the
failure mode revealed by the smoke's failure artifact first.
Inline-corpus smoke (harness validation only)
--inline-corpus-smoke is a harness validation gate, not an
evidence-collection mode. It uses the full HF model-load and hook path
but bypasses datasets.load_dataset with a tiny deterministic in-memory
corpus. Use it when:
- the WikiText (or whichever) dataset download is hanging or rate-limited and you need to confirm the model-load → adapter-discovery → hooks → calibration → eval pipeline is intact;
- you want a fast hermetic gate on a small Qwen variant before paying for a full sweep on a 7B-class model.
Inline-corpus runs emit a distinct file (inline_corpus_smoke__…json),
set mode: "inline-corpus-smoke" and paper_valid: false in the result
JSON, and mark both calibration and eval corpora as inline_smoke. They
must not be cited as benchmark evidence; full sweeps require the
WikiText / proper eval corpus path. The status writer emits
dataset_inline_start / dataset_inline_end events instead of
dataset_load_start / dataset_load_end so it is unambiguous from
status alone which corpus path ran.
6d. Persistent HuggingFace cache on the volume
Retries should never re-download the model. The launcher pins the HF
and datasets caches to a persistent subtree of the Modal volume so a
killed-and-resumed run reuses the previous downloads:
/results/
hf_cache/
home/ # HF_HOME (modules, token cache)
hub/ # HUGGINGFACE_HUB_CACHE / TRANSFORMERS_CACHE
datasets/ # HF_DATASETS_CACHE
xdg/ # XDG_CACHE_HOME
Env vars set by experiments/run_status.py::configure_persistent_hf_cache:
HF_HOME, HUGGINGFACE_HUB_CACHE, TRANSFORMERS_CACHE,
HF_DATASETS_CACHE, XDG_CACHE_HOME. The launcher's run_one calls
this function before spawning the subprocess.
Cleanup cautions.
- Never blanket-delete
/results/hf_cache/while a run is in flight — the snapshot lock files underhub/are keyed by partial download sentinels and pulling them mid-fetch will brick the active retry. modal volume rm spectralquant-v2-results /results/hf_cacheis the big-hammer reset; only run it aftermodal app listconfirms there are no in-flight functions, and afterscripts/audit_results.pyconfirms there are no.tmp_*leftovers (see §6b).- The cache shares the volume with result JSONs. Deleting one must not touch the other; always specify the full subpath.
- Because the cache lives on the volume, it does not consume Modal ephemeral disk and will not be wiped between container starts.
6b. Stale .tmp_* files in result directories
atomic_write_json creates a .tmp_<random> file in the destination
directory and renames it on success. If the process is killed between
mkstemp and os.replace (Modal timeout, OOM, Ctrl-C, network blip),
the tempfile is left behind. These files are zero-byte or partially
written, are never schema-valid, and are a strong signal that a run
crashed mid-write.
scripts/audit_results.py scans the known result subdirectories
(results/three_way, results/calibration, etc.) for .tmp_*
leftovers and reports them in both the human table and the --json
payload (stale_tmp_files, summary.stale_tmp_count).
Defaults:
--scan-tmpis on by default.--no-scan-tmpdisables the scan if the operator only wants the manifest summary.--delete-stale-tmpremoves the leftover files. Off by default: inspect first, then delete. The CLI refuses to delete anything whose name does not start with.tmp_or whose resolved path falls outside the repo root.
Workflow when a run crashes:
- Run
python3 scripts/audit_results.py --jsonand inspect thestale_tmp_filesblock to see which run(s) failed mid-write. - Cross-check against the launcher's
git_commitand timestamps in the operator console to identify the responsible run. - Once the crash is triaged (re-run, infrastructure fix, etc.), run
python3 scripts/audit_results.py --delete-stale-tmpto clean up.
7. Timeout strategy
7.0 Detached vs. attached invocation
Long Modal jobs (real-model smoke, full sweep cells) must be launched
in detached mode so the GPU run continues even if the local client
disconnects, hits a tool/wall-clock timeout, or the operator's network
flaps. Attached mode (modal run without -d, or the
python3 scripts/launch_modal_three_way.py … wrapper) is fine for
fast feedback during development but is not safe for paid GPU work
because killing the local client also kills the remote app.
The launcher exposes a top-level Modal App and a main_entry local
entrypoint precisely so modal run -d can submit the job directly with
no Python wrapper sitting between Modal and the operator's terminal.
Concretely:
# ---- Detached real-model smoke (Mistral-7B, 3-bit, n_calib=4, n_eval=2)
# Survives a local Ctrl-C / tool timeout / SSH disconnect.
modal run -d scripts/launch_modal_three_way.py::main_entry -- \
--model mistralai/Mistral-7B-v0.3 \
--avg-bits 3 \
--n-calib 4 --n-eval 2 --n-layers-sample 2 \
--output-dir /results/three_way_smoke \
--status-dir /results/status_hf_smoke \
--smoke
--status-dir is the parent directory; per-run heartbeat artifacts land
at <status-dir>/<run_id>/ (status.json + events.jsonl). Omit the flag to
fall back to the default sibling-of-output-dir layout described below.
Modal prints an app id on submission, e.g. App spectralquant-v2 has started running. View at https://modal.com/apps/<workspace>/<app-id>.
Copy that URL — it is the polling target.
The launcher's main_entry calls run_one.spawn(...) (not .remote())
so the GPU function keeps running after the local modal run -d client
disconnects. The previous failure mode was that .remote() issued
inside a detached app is cancelled when the local caller drops its
connection, with this Modal warning in stderr:
remote() and .map() calls in detached apps may be canceled when the
local caller disconnects. Use .spawn() for detached or background work.
spawn() returns a FunctionCall; the launcher prints its object_id
(the poll handle) and the deterministic artifact path so you can pick up
the result later without needing the streaming logs.
Expected stdout from a detached submission (sanitized):
[launch_modal_three_way] forwarding local commit <sha12> via SPECTRALQUANT_GIT_COMMIT
[launch_modal_three_way] launching (entrypoint): mistralai/Mistral-7B-v0.3 b=3 seed=42 smoke=True
[launch_modal_three_way] spawned remote call: call_id=fc-XXXXXXXXXXXXXXXX
[launch_modal_three_way] expected_output_path: /results/three_way_smoke/synthetic_smoke__Mistral-7B-v0.3_b3_calib4_eval2_seed42.json
[launch_modal_three_way] poll: modal volume ls spectralquant-v2-results /results/three_way_smoke/synthetic_smoke__Mistral-7B-v0.3_b3_calib4_eval2_seed42.json
[launch_modal_three_way] result: python3 -c "import modal; print(modal.FunctionCall.from_id('fc-XXXXXXXXXXXXXXXX').get(timeout=0))"
Copy both call_id=... and expected_output_path: ... — they are the
two things you need to retrieve the run.
7.0a Polling and recovery
After detaching, monitor the run from the same terminal or a fresh one. You have two independent polling channels:
(a) Volume-based polling (fastest, no log access required):
# Watch for the artifact to appear at the printed expected_output_path.
# Use the *exact* path printed by the launcher above.
modal volume ls spectralquant-v2-results /results/three_way_smoke
# Pull the result JSON locally once it exists:
modal volume get spectralquant-v2-results \
/results/three_way_smoke/synthetic_smoke__Mistral-7B-v0.3_b3_calib4_eval2_seed42.json \
./local_results/
(b) FunctionCall-based polling (returns the structured run result):
# Non-blocking peek (timeout=0 raises if not ready, useful for status):
python3 -c "import modal; print(modal.FunctionCall.from_id('<call_id>').get(timeout=0))"
# Block until done (use for an attended check):
python3 -c "import modal; print(modal.FunctionCall.from_id('<call_id>').get())"
(c) Log polling (slower; helpful for triage):
modal app logs <app-id> # tail logs (Ctrl-C does NOT stop the app)
modal app list # running apps + states
modal app stop <app-id> # only if you must force-stop a hung run
Recovery rules:
- The result JSON is written to the Modal volume
spectralquant-v2-resultsunder--output-dir. If the local client died but the run finished, the artifact is still there — confirm withmodal volume ls spectralquant-v2-results /three_way_smoke. The exact filename is theexpected_output_pathprinted at submission time. - A
.tmp_*leftover in the result directory (see §6b) means the run crashed mid-write; pull the logs, triage, and clean withpython3 scripts/audit_results.py --delete-stale-tmponce triage is done. - Never restart a crashed run with
--forceuntil you have confirmed the failure mode. Re-running with the default--skip-if-existssemantics will silently no-op if a stale artifact exists. - If
FunctionCall.from_id(<call_id>).get()raisesmodal.exception.ExecutionErrorit usually means the call was cancelled (e.g. the app was stopped) — checkmodal app logsand the volume for partial state before re-submitting.
7.0b Per-call timeout matrix
Every Modal function call has a hard timeout. Set it conservatively but not infinitely:
| Scope | Timeout | Notes |
|---|---|---|
Smoke run (--smoke, n_calib=4, n_eval=2, 2 layers) |
15 min | If exceeded, something is wrong before model load. |
| Single Mistral b=B run | 90 min | Model load + 8 layers + n_eval=8. |
| Full Mistral b∈{2,3,5} sweep in one function | 4 h | Prefer one function per b — easier to retry. |
| Qwen 3-bit run | 90 min | |
| Accounting audit (no model load) | 5 min | Pure CPU. |
Inside the function, set per-step soft timeouts (signal.alarm or a thread
watchdog) to fail fast if calibration alone takes more than 10 minutes —
that is a sign the wrong tokenizer or wrong dataset path is in use.
8. Retry strategy
- Transient errors (HF rate limit, network blip, transient CUDA OOM
from a previous tenant): retry up to 3 times with exponential backoff
(60s, 180s, 600s). Modal's
retries=argument can do this for the function call as a whole; finer-grained retries belong inside the HTTP/HF client. - Permanent errors (license 403, missing dataset, schema validation
fail): do not retry. Fail loudly, write a
.failure.jsonwith the error message and stack trace, and abort the sweep loop so the operator can fix the root cause before paying for further GPU minutes. - Partial-progress retries: when retrying a run, honor
--skip-if-existsso already-done bits don't re-run.
9. Preflight checks before expensive runs
Run the preflight script locally and on the Modal worker before kicking off a sweep:
python3 scripts/preflight_modal.py --strict
If the preflight reports disk_space low (or you simply expect a large
download), free local cache space with scripts/safe_local_cleanup.py
before the Modal launch. Never use a broad rm -rf over /tmp,
~/.cache, or any directory that contains a working clone — that is exactly
how an active local checkout was lost in a previous incident.
# 1. Always start with a dry run — nothing is deleted without --yes:
python3 scripts/safe_local_cleanup.py \
--delete-hf-cache --delete-playwright-cache --delete-temp-clones
# 2. Apply once you've reviewed the plan:
python3 scripts/safe_local_cleanup.py \
--delete-hf-cache --delete-playwright-cache --delete-temp-clones --yes
# 3. Optional gate: fail the script if the repo's disk has < N GB free:
python3 scripts/safe_local_cleanup.py --min-disk-gb 10
The cleanup script:
- defaults to dry-run; nothing is deleted unless
--yesis also passed; - refuses to delete the active repo root (resolved via
git rev-parse --show-toplevel), any ancestor of it, or anything inside it; - excludes the active repo path exactly when scanning for temp clones under
/tmpand/var/tmp— by both name comparison and resolved-symlink comparison; - only touches an explicit allow-list of caches (
--delete-hf-cache,--delete-playwright-cache,--delete-temp-clones,--delete-torch-cache,--delete-pip-cache); each must be selected individually; - never reads or echoes
HF_TOKENor any other credential.
Any time the local workspace runs low on disk before a Modal launch, this is
the script to use. Broad rm -rf globs are an anti-pattern — see §11.
It must succeed with exit code 0 before the sweep begins. The script verifies (without printing any token value):
- Python version is 3.10–3.12.
- We are inside a git repo, on a known branch, with no uncommitted changes
(or
--allow-dirtyif intentional). - The
HF_TOKENenvironment variable is present, length is reported but value is not. - The Modal CLI is on PATH if Modal is being used (warn if not — the local preflight may be running outside the Modal image).
- Disk space at the result-output directory is sufficient.
- No tracked file path matches the secret patterns in
.gitignore(catches accidentally committed.env,modal_token, etc.). - The expected result directories exist (or can be created).
The preflight script is read-only — it never starts a model run, never downloads a weight, never spends GPU credit.
10. After a run
-
Use
scripts/audit_results.pyto list expected vs. present JSONs:python3 scripts/audit_results.py --expected docs/expected_artifacts.jsonThe audit prints a "present / missing" table and exits non-zero if any expected artifact is missing. It does not interpret the contents — it only confirms file presence.
-
Re-run schema validation:
pytest tests/test_result_schema.py -q -
Update
docs/execution_audit_and_modal_runbook.md§3 with the new results, the commit hash that produced them, and any caveats.
11. Anti-patterns (do not do)
- ❌
os.environ.setdefault("HF_TOKEN", "hf_xxx...")in any committed file. - ❌
subprocess.run(["modal", "token", "set", "--token-id", token_id, ...])in any committed script — token values would appear in process listings. - ❌ Catching exceptions broadly (
except Exception: pass) around a weight-download call. A 403 must surface, not be swallowed. - ❌ Writing partial JSONs to the canonical result path before validation.
- ❌ Skipping the smoke run because "the script worked yesterday." Models and datasets get re-pinned upstream; smoke tests catch that for free.
- ❌ Hard-coding seeds, model names, or output paths in committed scripts — use argparse so the same script runs every cell of the matrix.
- ❌ Running the full sweep on a laptop. Phase 6 is Modal-only; the cutile baseline is not buildable in a generic local Python env.
- ❌
rm -rf /tmp/*,rm -rf ~/.cache/*, or any other broad cleanup glob. An incident occurred where this pattern deleted the active local clone along with the intended cache directories. Usescripts/safe_local_cleanup.pyinstead — it refuses to touch the active repo root and requires explicit per-category flags plus--yes.
9. Engine selection for Phase 6
src/spectralquant/__init__.py exports two engines and benchmark scripts
must record which one they used in the result JSON's software.engine
field (V1-GAP-010 is now closed):
SpectralQuantEngine— canonical pure-Python pipeline. Use this for every quality / accuracy benchmark (perplexity, attention cosine similarity, NIAH, three-way comparisons). It carriesuse_water_fill,wf_min_bits,wf_max_bitsviaEngineConfig, and exposesallocation_metadata()andwaterfill_allocations()for the per-head bit budget that must be embedded in every result JSON.KernelSpectralQuantEngine— cuTile-accelerated subclass. Use only for kernel-path latency benchmarks where the CUDAlaunch_*methods are exercised; it is the same code path as the v1 legacy engine. The kernel engine accepts the same water-fill flags but currently records the metadata only — per-dim execution on the kernel path is a future task. Constructing this class outside Modal raisesRuntimeError; do not silently fall back to the canonical engine if the kernel engine fails to import.
Result JSONs for Phase 6 must record software.engine as either
"SpectralQuantEngine" (canonical) or "KernelSpectralQuantEngine"
(kernel) — these are the two valid values. Anything else is a bug.