Skip to content

Commit 39e3757

Browse files
committed
Document checkpoint and GPU broker contracts
1 parent 9a5e539 commit 39e3757

6 files changed

Lines changed: 218 additions & 3 deletions

File tree

docs/miner/README.md

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,11 +61,21 @@ def compute_loss(model, batch, ctx):
6161
6262
def train_step(model, batch, optimizer, ctx):
6363
...
64+
65+
def save_checkpoint(model, checkpoint_dir, ctx):
66+
...
67+
68+
def load_checkpoint(model, checkpoint_dir, ctx):
69+
...
6470
```
6571

6672
`configure_optimizer` gives full optimizer and LR control. Use it for custom optimizers, parameter groups, schedulers, or learning rates that should not be reduced to evaluator fallback choices. Without that hook, the fallback optimizer may apply safe evaluator defaults/caps, including learning-rate caps.
6773

68-
`train_step` can implement a fully custom update step. Use it when you need a training loop other than the evaluator default. It must return a loss tensor and stay within sandbox and resource limits.
74+
`train_step` can implement a fully custom update step. Use it when you need a training loop other than the evaluator default. It must return a loss tensor and stay within sandbox and resource limits. PRISM launches 1-8 GPU container runs with single-node torchrun, including `torchrun --standalone --nnodes=1 --nproc-per-node=1` for a 1 GPU run. When PRISM wraps default multi-process training with DDP, a custom `train_step` that bypasses the default loop must be DDP-safe and rank-aware.
75+
76+
Use `save_checkpoint(model, checkpoint_dir, ctx)` and `load_checkpoint(model, checkpoint_dir, ctx)` only for model state inside the evaluator-provided checkpoint workspace. The checkpoint fields on `ctx` are `checkpoint_dir`, `resume_checkpoint_dir`, `checkpoint_api_version`, `attempt`, `is_resume`, `rank`, `local_rank`, `world_size`, `distributed_backend`, `device`, and `checkpoint_metadata`. `save_checkpoint` may return `None`, a checkpoint-dir-relative `str`, or the exact shape `{"path": str, "metadata": dict[str, object]}`. Return `None` only when no checkpoint artifact should be recorded; return a checkpoint-dir-relative `str` or the exact dict shape when PRISM should accept and record a produced checkpoint artifact. PRISM records accepted checkpoint artifacts through manifest paths under the run artifact root. External checkpoint paths and miner-selected resume sources are not supported. The workspace cap is decimal 10G, exactly `10_000_000_000` bytes.
77+
78+
Evaluator resume in v1 is retry-only after eligible infrastructure or eviction failures. It does not resume sandbox failures, miner code failures, scoring failures, or policy failures.
6979

7080
## Artifact Manifest
7181

docs/operators.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ PRISM_PLATFORM_EVAL_PIDS_LIMIT=512
5555
PRISM_DOCKER_NETWORK=none
5656
```
5757

58+
For broker-backed runs, PRISM sends only the official Platform SDK GPU field `gpu_count`. `gpu_count=None` or an omitted field is CPU-only; a positive integer is a GPU request. Platform owns `gpu_resource_name`, defaulting to `nvidia.com/gpu`, and Kubernetes broker mode maps positive counts to `resources.limits['nvidia.com/gpu']` unless Platform is configured with a different resource name. PRISM GPU device IDs are metadata for observability, not Kubernetes placement semantics. Single-GPU runs use `torchrun --standalone --nnodes=1 --nproc-per-node=1`; PRISM does not claim multi-node support.
59+
5860
## Review Configuration
5961

6062
Recommended production review settings:

docs/scaling.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,20 @@ Official score-eligible GPU runs use a fixed runtime profile. The default policy
4444

4545
Official scoring uses the fixed profile from SQL runtime config or its defaults. Autosplit is allowed only for non-scoring or development paths such as smoke runs. It is not an official scoring shortcut.
4646

47+
### Broker GPU Contract
48+
49+
PRISM passes the actual lease size to the Platform broker as `gpu_count`. `gpu_count=None` or an omitted `gpu_count` means CPU-only execution and must not create a Kubernetes GPU resource limit. A positive integer requests that many GPUs. Invalid values such as `0`, negatives, booleans, strings, or floats fail validation before placement.
50+
51+
Platform owns `gpu_resource_name`; PRISM does not pass it. In Kubernetes broker mode, Platform maps a positive `gpu_count` to `resources.limits['nvidia.com/gpu']` by default, or to the configured Platform-owned resource name. Prism GPU environment variables, labels, payload metadata, and device IDs are observability and backward-compatibility metadata only. Device IDs are not Kubernetes placement semantics, and this contract is not an arbitrary TPU, AMD, or custom accelerator abstraction.
52+
53+
## Single-Node Torchrun And DDP
54+
55+
PRISM's distributed v1 scope is single-node only. Runs with 1-8 GPUs use single-node torchrun with one process per GPU, including `torchrun --standalone --nnodes=1 --nproc-per-node=1` for a 1 GPU run. Requests above 8 GPUs are rejected. This documents command and environment support, not proof that every submission succeeds on 8 GPUs.
56+
57+
PRISM DDP-wraps default training before running the default loop. Rank 0 writes shared checkpoint and manifest artifacts, including `prism_run_manifest.v1.json`; other ranks participate in training and synchronization without writing those shared artifacts. Custom `train_step` implementations that bypass the default loop must be DDP-safe and rank-aware.
58+
59+
PRISM does not support multi-node distributed training in v1.
60+
4761
## Bad Scaling Predictors
4862

4963
The following signals are weak predictors of frontier-scale performance when used alone:

docs/security.md

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,15 @@ Container limits include:
104104
* read-only runtime option
105105
* optional GPU type and count
106106

107-
Official score-eligible GPU jobs use the fixed official GPU profile from SQL runtime config or defaults, with a maximum of 8 GPUs. Autosplit is for non-scoring or development paths only.
107+
Official score-eligible GPU jobs use the fixed official GPU profile from SQL runtime config or defaults, with a maximum of 8 GPUs. Autosplit is for non-scoring or development paths only. PRISM sends the actual lease size as `gpu_count`: `None` or omitted means CPU-only, while a positive integer requests GPUs. Platform owns `gpu_resource_name` and maps the count to Kubernetes `resources.limits['nvidia.com/gpu']` by default. PRISM does not pass `gpu_resource_name`; device IDs remain metadata for logs and manifests, not Kubernetes placement semantics. Network isolation depends on the cluster CNI and the NetworkPolicy enforcement configured by Platform and the operator.
108+
109+
## Checkpoint Workspace Security
110+
111+
Checkpoint hooks write only inside an evaluator-owned checkpoint workspace. PRISM rejects absolute paths, `..` traversal, symlinks, and any checkpoint path outside that workspace. The checkpoint workspace cap is decimal 10G, exactly `10_000_000_000` bytes.
112+
113+
Evaluator resume in v1 is retry-only after eligible infrastructure or eviction failures, with validated same submission, code, architecture, and recipe lineage. It is not used for sandbox failures, miner code failures, scoring failures, or policy failures. Miners cannot select arbitrary external checkpoint paths or resume sources, and PRISM does not support object-store or cloud checkpoint upload in this contract.
114+
115+
Distributed execution is single-node only in v1. PRISM launches 1-8 GPU container runs with single-node torchrun, including `torchrun --standalone --nnodes=1 --nproc-per-node=1` for a 1 GPU run. PRISM does not support multi-node distributed training.
108116

109117
## Scientific Security References
110118

docs/submissions.md

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ def compute_loss(model, batch, ctx):
8787
8888
def train_step(model, batch, optimizer, ctx):
8989
...
90+
91+
def save_checkpoint(model, checkpoint_dir, ctx):
92+
...
93+
94+
def load_checkpoint(model, checkpoint_dir, ctx):
95+
...
9096
```
9197

9298
| Hook | Purpose | Attribution |
@@ -96,13 +102,53 @@ def train_step(model, batch, optimizer, ctx):
96102
| `infer` | Fallback inference path when `inference_logits` is absent. | Training or inference owner |
97103
| `compute_loss` | Custom loss, auxiliary losses, regularization. | Training owner |
98104
| `train_step` | Fully custom update step. | Training owner |
105+
| `save_checkpoint` | Save model state into the evaluator-provided checkpoint workspace. | Training owner |
106+
| `load_checkpoint` | Load model state from an evaluator-approved retry checkpoint workspace. | Training owner |
99107

100108
Use `configure_optimizer` when you need complete optimizer and LR control, including parameter groups, custom optimizer classes, scheduler setup, clipping wrappers, or learning rates outside evaluator defaults. If `configure_optimizer` is absent, the fallback optimizer may apply safe evaluator defaults/caps, including learning-rate caps, while still reading recipe metadata where allowed.
101109

102-
Use `train_step` when the default `zero_grad`, `loss.backward`, gradient clipping, and `optimizer.step` loop is not enough. `train_step` can implement a fully custom update step, as long as it returns a valid loss tensor and stays within the sandbox and resource limits.
110+
Use `train_step` when the default `zero_grad`, `loss.backward`, gradient clipping, and `optimizer.step` loop is not enough. `train_step` can implement a fully custom update step, as long as it returns a valid loss tensor and stays within the sandbox and resource limits. PRISM launches 1-8 GPU container runs with single-node torchrun, including `torchrun --standalone --nnodes=1 --nproc-per-node=1` for a 1 GPU run. When PRISM launches multi-process torchrun/DDP, PRISM wraps the model, but custom `train_step` implementations are responsible for DDP-safe and rank-aware behavior if they bypass the default loop. PRISM does not support multi-node distributed training.
103111

104112
If both `inference_logits` and `infer` exist, `inference_logits` takes precedence.
105113

114+
## Checkpoint Hooks
115+
116+
Submissions may define these exact optional hook signatures:
117+
118+
```python
119+
def save_checkpoint(model, checkpoint_dir, ctx):
120+
...
121+
122+
def load_checkpoint(model, checkpoint_dir, ctx):
123+
...
124+
```
125+
126+
`checkpoint_dir` is an evaluator-owned directory. Save into that directory or a child path under it. Do not choose an external checkpoint path. `load_checkpoint` is called only when PRISM approves a retry resume source for the same submission, code, architecture, and recipe lineage. v1 resume is retry-only after eligible infrastructure or eviction failures, not sandbox failures, miner code failures, scoring failures, or policy failures. PRISM does not support arbitrary external checkpoint resume.
127+
128+
Checkpoint and distributed fields on `ctx` are:
129+
130+
* `checkpoint_dir`
131+
* `resume_checkpoint_dir`
132+
* `checkpoint_api_version`
133+
* `attempt`
134+
* `is_resume`
135+
* `rank`
136+
* `local_rank`
137+
* `world_size`
138+
* `distributed_backend`
139+
* `device`
140+
* `checkpoint_metadata`
141+
142+
Accepted `save_checkpoint` return schemas are:
143+
144+
* `None`, when no checkpoint artifact was produced for PRISM to record.
145+
* A checkpoint-dir-relative `str`, when the main checkpoint file or directory is below `checkpoint_dir` and should be accepted and recorded.
146+
* The exact shape `{"path": str, "metadata": dict[str, object]}`. `path` is checkpoint-dir-relative, and `metadata` contains JSON-compatible checkpoint metadata for an accepted and recorded checkpoint artifact.
147+
148+
Writing files under `checkpoint_dir` is not enough for PRISM to record a produced checkpoint. Return a checkpoint-dir-relative `str` or the exact dict shape above when the hook creates a checkpoint artifact that should be accepted for manifest recording and retry resume.
149+
150+
Absolute paths, `..` traversal, symlinks, and paths outside `checkpoint_dir` are rejected. PRISM records accepted checkpoint artifacts in `prism_run_manifest.v1.json` using artifact-root-relative manifest paths, not host paths. The checkpoint workspace cap is exactly decimal 10G, `10_000_000_000` bytes.
151+
106152
## Artifact Manifest
107153

108154
Official and smoke evaluators write `prism_run_manifest.v1.json`. The manifest is the scoring contract for artifacts and metrics, not a free-form log. Submitted metrics are not free-form claims. They must be derived from artifacts, evaluator logs, and manifest fields that validators can check.

tests/test_docs_contract.py

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,12 +152,147 @@ def test_submission_docs_clarify_custom_training_boundaries() -> None:
152152
"configure_optimizer` gives full optimizer and LR control",
153153
"fallback optimizer may apply safe evaluator defaults/caps",
154154
"train_step` can implement a fully custom update step",
155+
"custom `train_step` implementations are responsible for DDP-safe and rank-aware behavior",
155156
"training_for_arch` submission cannot silently change architecture family",
156157
"Submitted metrics are not free-form claims",
157158
):
158159
assert expected in combined
159160

160161

162+
163+
def test_checkpoint_hook_contract_docs_are_exact() -> None:
164+
submissions_doc = read_doc("docs/submissions.md")
165+
miner_doc = read_doc("docs/miner/README.md")
166+
security_doc = read_doc("docs/security.md")
167+
combined = f"{submissions_doc}\n{miner_doc}\n{security_doc}"
168+
169+
for expected in (
170+
"save_checkpoint(model, checkpoint_dir, ctx)",
171+
"load_checkpoint(model, checkpoint_dir, ctx)",
172+
"`checkpoint_dir`",
173+
"`resume_checkpoint_dir`",
174+
"`checkpoint_api_version`",
175+
"`attempt`",
176+
"`is_resume`",
177+
"`rank`",
178+
"`local_rank`",
179+
"`world_size`",
180+
"`distributed_backend`",
181+
"`device`",
182+
"`checkpoint_metadata`",
183+
"`None`, when no checkpoint artifact was produced for PRISM to record",
184+
"A checkpoint-dir-relative `str`",
185+
"should be accepted and recorded",
186+
'The exact shape `{"path": str, "metadata": dict[str, object]}`',
187+
"Writing files under `checkpoint_dir` is not enough for PRISM to record",
188+
"Return `None` only when no checkpoint artifact should be recorded",
189+
"return a checkpoint-dir-relative `str` or the exact dict shape",
190+
"artifact-root-relative manifest paths",
191+
"decimal 10G, `10_000_000_000` bytes",
192+
"decimal 10G, exactly `10_000_000_000` bytes",
193+
"retry-only after eligible infrastructure or eviction failures",
194+
"same submission, code, architecture, and recipe lineage",
195+
):
196+
assert expected in combined
197+
198+
forbidden_checkpoint_semantics = (
199+
"`None`, when the hook writes its checkpoint files directly under `checkpoint_dir`",
200+
"None, when the hook writes its checkpoint files directly under checkpoint_dir",
201+
)
202+
for phrase in forbidden_checkpoint_semantics:
203+
assert phrase not in combined
204+
205+
206+
def test_distributed_docs_define_v1_single_node_scope() -> None:
207+
submissions_doc = read_doc("docs/submissions.md")
208+
miner_doc = read_doc("docs/miner/README.md")
209+
scaling_doc = read_doc("docs/scaling.md")
210+
security_doc = read_doc("docs/security.md")
211+
combined = f"{submissions_doc}\n{miner_doc}\n{scaling_doc}\n{security_doc}"
212+
213+
for expected in (
214+
"single-node only",
215+
"Runs with 1-8 GPUs use single-node torchrun",
216+
"torchrun --standalone --nnodes=1 --nproc-per-node=1",
217+
"Requests above 8 GPUs are rejected",
218+
"Rank 0 writes shared checkpoint and manifest artifacts",
219+
"PRISM DDP-wraps default training",
220+
"custom `train_step` implementations are responsible for DDP-safe and rank-aware behavior",
221+
(
222+
"Custom `train_step` implementations that bypass the default loop "
223+
"must be DDP-safe and rank-aware"
224+
),
225+
"does not support multi-node distributed training",
226+
"command and environment support, not proof that every submission succeeds on 8 GPUs",
227+
):
228+
assert expected in combined
229+
230+
231+
def test_public_docs_define_platform_gpu_broker_contract() -> None:
232+
scaling_doc = read_doc("docs/scaling.md")
233+
security_doc = read_doc("docs/security.md")
234+
operators_doc = read_doc("docs/operators.md")
235+
combined = f"{scaling_doc}\n{security_doc}\n{operators_doc}"
236+
237+
for expected in (
238+
"`gpu_count=None` or an omitted `gpu_count` means CPU-only",
239+
"A positive integer requests that many GPUs",
240+
"Platform owns `gpu_resource_name`",
241+
"PRISM does not pass `gpu_resource_name`",
242+
"resources.limits['nvidia.com/gpu']",
243+
"Device IDs are not Kubernetes placement semantics",
244+
"device IDs remain metadata",
245+
"torchrun --standalone --nnodes=1 --nproc-per-node=1",
246+
"does not claim multi-node support",
247+
"Network isolation depends on the cluster CNI",
248+
):
249+
assert expected in combined
250+
251+
forbidden_claims = (
252+
"device IDs are Kubernetes placement semantics",
253+
"multi-node distributed training is supported",
254+
"supports arbitrary TPU",
255+
"supports AMD accelerator abstraction",
256+
)
257+
combined_lower = combined.lower()
258+
for phrase in forbidden_claims:
259+
assert phrase.lower() not in combined_lower
260+
261+
262+
def test_checkpoint_docs_do_not_overpromise_unsupported_scope() -> None:
263+
public_docs = [
264+
"README.md",
265+
"docs/submissions.md",
266+
"docs/miner/README.md",
267+
"docs/scaling.md",
268+
"docs/security.md",
269+
]
270+
combined = "\n".join(read_doc(path) for path in public_docs)
271+
combined_lower = combined.lower()
272+
273+
for expected in (
274+
"PRISM does not support arbitrary external checkpoint resume",
275+
"External checkpoint paths and miner-selected resume sources are not supported",
276+
"Miners cannot select arbitrary external checkpoint paths or resume sources",
277+
"does not support object-store or cloud checkpoint upload",
278+
"does not support multi-node distributed training",
279+
):
280+
assert expected in combined
281+
282+
forbidden_phrases = (
283+
".omo",
284+
"Prometheus",
285+
"Metis",
286+
"workflow artifacts",
287+
"arbitrary external checkpoint resume is supported",
288+
"miner-selected external checkpoint paths are supported",
289+
"object-store checkpoint upload is supported",
290+
"cloud checkpoint upload is supported",
291+
"multi-node distributed training is supported",
292+
)
293+
for phrase in forbidden_phrases:
294+
assert phrase.lower() not in combined_lower
295+
161296
def test_security_doc_clarifies_metric_review_limits() -> None:
162297
security_doc = read_doc("docs/security.md")
163298

0 commit comments

Comments
 (0)