Commit ·
78463b7
1
Parent(s): 918007b
feat: add northflank smoke workflow
Browse files
README.md
CHANGED
|
@@ -34,6 +34,7 @@ Implementation status:
|
|
| 34 |
- [x] Add a post-terminal guard so `step()` is a no-op after `done=True`
|
| 35 |
- [x] Re-run the baseline comparison on the `constellaration`-backed branch state
|
| 36 |
- [x] Replace the synthetic evaluator with `constellaration`
|
|
|
|
| 37 |
- [ ] Add tracked `P1` fixtures under `server/data/p1/`
|
| 38 |
- [ ] Run manual playtesting and record the first reward pathology
|
| 39 |
- [ ] Refresh the heuristic baseline for the real verifier path
|
|
|
|
| 34 |
- [x] Add a post-terminal guard so `step()` is a no-op after `done=True`
|
| 35 |
- [x] Re-run the baseline comparison on the `constellaration`-backed branch state
|
| 36 |
- [x] Replace the synthetic evaluator with `constellaration`
|
| 37 |
+
- [x] Add a runnable Northflank smoke workflow and note
|
| 38 |
- [ ] Add tracked `P1` fixtures under `server/data/p1/`
|
| 39 |
- [ ] Run manual playtesting and record the first reward pathology
|
| 40 |
- [ ] Refresh the heuristic baseline for the real verifier path
|
docs/FUSION_DELIVERABLES_MAP.md
CHANGED
|
@@ -11,6 +11,7 @@ Use this map to sequence execution, not to reopen already-locked task choices.
|
|
| 11 |
- [x] `P1` contract is frozen in code
|
| 12 |
- [x] official `constellaration` verifier loop is wired
|
| 13 |
- [x] baseline comparison has been rerun on the real verifier path
|
|
|
|
| 14 |
- [ ] tracked fixtures are checked in
|
| 15 |
- [ ] manual playtest evidence exists
|
| 16 |
- [ ] heuristic baseline has been refreshed for the real verifier path
|
|
|
|
| 11 |
- [x] `P1` contract is frozen in code
|
| 12 |
- [x] official `constellaration` verifier loop is wired
|
| 13 |
- [x] baseline comparison has been rerun on the real verifier path
|
| 14 |
+
- [x] Northflank smoke workflow and note are committed
|
| 15 |
- [ ] tracked fixtures are checked in
|
| 16 |
- [ ] manual playtest evidence exists
|
| 17 |
- [ ] heuristic baseline has been refreshed for the real verifier path
|
docs/FUSION_DESIGN_LAB_PLAN_V2.md
CHANGED
|
@@ -12,6 +12,7 @@
|
|
| 12 |
- [x] low-fidelity `run` plus high-fidelity `submit` split is documented
|
| 13 |
- [x] post-terminal `step()` guard is in place
|
| 14 |
- [x] baseline comparison has been rerun on the real verifier path
|
|
|
|
| 15 |
- [ ] tracked `P1` fixtures are added
|
| 16 |
- [ ] manual playtest evidence is recorded
|
| 17 |
- [ ] heuristic baseline is refreshed for the real verifier path
|
|
|
|
| 12 |
- [x] low-fidelity `run` plus high-fidelity `submit` split is documented
|
| 13 |
- [x] post-terminal `step()` guard is in place
|
| 14 |
- [x] baseline comparison has been rerun on the real verifier path
|
| 15 |
+
- [x] Northflank smoke workflow and note are committed
|
| 16 |
- [ ] tracked `P1` fixtures are added
|
| 17 |
- [ ] manual playtest evidence is recorded
|
| 18 |
- [ ] heuristic baseline is refreshed for the real verifier path
|
docs/FUSION_NEXT_12_HOURS_CHECKLIST.md
CHANGED
|
@@ -14,6 +14,7 @@ Do not expand scope beyond one stable task. Training is supporting evidence, not
|
|
| 14 |
- [x] add a post-terminal guard in `step()`
|
| 15 |
- [x] replace the synthetic evaluator with `constellaration`
|
| 16 |
- [x] re-run baselines on the real verifier path
|
|
|
|
| 17 |
- [ ] add tracked fixtures and manual playtest evidence
|
| 18 |
- [ ] refresh the heuristic baseline after the real-verifier rerun
|
| 19 |
|
|
|
|
| 14 |
- [x] add a post-terminal guard in `step()`
|
| 15 |
- [x] replace the synthetic evaluator with `constellaration`
|
| 16 |
- [x] re-run baselines on the real verifier path
|
| 17 |
+
- [x] commit the Northflank smoke workflow and note
|
| 18 |
- [ ] add tracked fixtures and manual playtest evidence
|
| 19 |
- [ ] refresh the heuristic baseline after the real-verifier rerun
|
| 20 |
|
training/notebooks/NORTHFLANK_SMOKE_NOTE.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Northflank Smoke Note
|
| 2 |
+
|
| 3 |
+
This is the concrete smoke path for the repo's Northflank compute gate.
|
| 4 |
+
|
| 5 |
+
## Goal
|
| 6 |
+
|
| 7 |
+
Prove all four required conditions in the Northflank Jupyter workspace:
|
| 8 |
+
|
| 9 |
+
1. `constellaration` imports
|
| 10 |
+
2. one rotating-ellipse boundary is generated
|
| 11 |
+
3. one low-fidelity verifier call succeeds
|
| 12 |
+
4. one JSON artifact is written to persistent storage
|
| 13 |
+
|
| 14 |
+
## Repo Entry Point
|
| 15 |
+
|
| 16 |
+
Use [northflank_smoke.py](/Users/suhjungdae/code/fusion-design-lab/training/notebooks/northflank_smoke.py).
|
| 17 |
+
|
| 18 |
+
It uses the repo SSOT values from:
|
| 19 |
+
|
| 20 |
+
- [server/environment.py](/Users/suhjungdae/code/fusion-design-lab/server/environment.py)
|
| 21 |
+
- [server/physics.py](/Users/suhjungdae/code/fusion-design-lab/server/physics.py)
|
| 22 |
+
|
| 23 |
+
## Northflank Run
|
| 24 |
+
|
| 25 |
+
Run from the repo root after the notebook container is up and the persistent volume is mounted:
|
| 26 |
+
|
| 27 |
+
```bash
|
| 28 |
+
uv sync --extra notebooks
|
| 29 |
+
uv run python training/notebooks/northflank_smoke.py \
|
| 30 |
+
--output-dir /path/to/northflank/persistent-storage/fusion-design-lab/smoke
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
Replace `/path/to/northflank/persistent-storage/...` with the real mounted path in the workspace.
|
| 34 |
+
|
| 35 |
+
## Expected Output
|
| 36 |
+
|
| 37 |
+
The command prints the artifact path it wrote, for example:
|
| 38 |
+
|
| 39 |
+
```text
|
| 40 |
+
/path/to/northflank/persistent-storage/fusion-design-lab/smoke/northflank_smoke_20260308T000000Z.json
|
| 41 |
+
```
|
| 42 |
+
|
| 43 |
+
The JSON artifact records:
|
| 44 |
+
|
| 45 |
+
- UTC timestamp
|
| 46 |
+
- `constellaration` version
|
| 47 |
+
- generated boundary type
|
| 48 |
+
- rotating-ellipse baseline parameters
|
| 49 |
+
- low-fidelity verifier metrics
|
| 50 |
+
|
| 51 |
+
## Local Dry Run
|
| 52 |
+
|
| 53 |
+
The same script can be exercised locally with:
|
| 54 |
+
|
| 55 |
+
```bash
|
| 56 |
+
uv run python training/notebooks/northflank_smoke.py
|
| 57 |
+
```
|
| 58 |
+
|
| 59 |
+
That writes a small artifact under `training/notebooks/artifacts/`.
|
training/notebooks/README.md
CHANGED
|
@@ -14,7 +14,8 @@ Recommended split:
|
|
| 14 |
|
| 15 |
## Status
|
| 16 |
|
| 17 |
-
- [
|
|
|
|
| 18 |
- [ ] manual-playtest notebook or trace notebook saved
|
| 19 |
- [ ] thin public Colab notebook saved
|
| 20 |
|
|
@@ -32,4 +33,9 @@ Northflank smoke gate:
|
|
| 32 |
- run one low-fidelity verifier call
|
| 33 |
- write one artifact to persistent storage
|
| 34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 35 |
The notebooks are supporting evidence for the environment, not the primary product.
|
|
|
|
| 14 |
|
| 15 |
## Status
|
| 16 |
|
| 17 |
+
- [x] Northflank smoke notebook note saved
|
| 18 |
+
- [x] runnable Northflank smoke script saved
|
| 19 |
- [ ] manual-playtest notebook or trace notebook saved
|
| 20 |
- [ ] thin public Colab notebook saved
|
| 21 |
|
|
|
|
| 33 |
- run one low-fidelity verifier call
|
| 34 |
- write one artifact to persistent storage
|
| 35 |
|
| 36 |
+
Runnable repo path:
|
| 37 |
+
|
| 38 |
+
- `uv run python training/notebooks/northflank_smoke.py --output-dir <mounted-persistent-storage-path>`
|
| 39 |
+
- note: `training/notebooks/NORTHFLANK_SMOKE_NOTE.md`
|
| 40 |
+
|
| 41 |
The notebooks are supporting evidence for the environment, not the primary product.
|
training/notebooks/northflank_smoke.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
|
| 3 |
+
import argparse
|
| 4 |
+
import json
|
| 5 |
+
from dataclasses import asdict, dataclass
|
| 6 |
+
from datetime import UTC, datetime
|
| 7 |
+
from importlib.metadata import version
|
| 8 |
+
from pathlib import Path
|
| 9 |
+
|
| 10 |
+
from constellaration.initial_guess import generate_rotating_ellipse
|
| 11 |
+
|
| 12 |
+
from server.environment import BASELINE_PARAMS, N_FIELD_PERIODS
|
| 13 |
+
from server.physics import EvaluationMetrics, evaluate_params
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
DEFAULT_OUTPUT_DIR = Path("training/notebooks/artifacts")
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
@dataclass(frozen=True)
|
| 20 |
+
class SmokeArtifact:
|
| 21 |
+
created_at_utc: str
|
| 22 |
+
constellaration_version: str
|
| 23 |
+
boundary_type: str
|
| 24 |
+
n_field_periods: int
|
| 25 |
+
params: dict[str, float]
|
| 26 |
+
metrics: dict[str, float | bool]
|
| 27 |
+
|
| 28 |
+
|
| 29 |
+
def parse_args() -> argparse.Namespace:
|
| 30 |
+
parser = argparse.ArgumentParser(
|
| 31 |
+
description=(
|
| 32 |
+
"Run the Fusion Design Lab Northflank smoke check: generate one "
|
| 33 |
+
"rotating-ellipse boundary, run one low-fidelity verifier call, "
|
| 34 |
+
"and write a JSON artifact."
|
| 35 |
+
)
|
| 36 |
+
)
|
| 37 |
+
parser.add_argument(
|
| 38 |
+
"--output-dir",
|
| 39 |
+
type=Path,
|
| 40 |
+
default=DEFAULT_OUTPUT_DIR,
|
| 41 |
+
help=(
|
| 42 |
+
"Directory where the smoke artifact should be written. On Northflank, "
|
| 43 |
+
"point this at the mounted persistent storage path."
|
| 44 |
+
),
|
| 45 |
+
)
|
| 46 |
+
return parser.parse_args()
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def build_artifact() -> SmokeArtifact:
|
| 50 |
+
boundary = generate_rotating_ellipse(
|
| 51 |
+
aspect_ratio=BASELINE_PARAMS.aspect_ratio,
|
| 52 |
+
elongation=BASELINE_PARAMS.elongation,
|
| 53 |
+
rotational_transform=BASELINE_PARAMS.rotational_transform,
|
| 54 |
+
n_field_periods=N_FIELD_PERIODS,
|
| 55 |
+
)
|
| 56 |
+
metrics = evaluate_params(
|
| 57 |
+
BASELINE_PARAMS,
|
| 58 |
+
n_field_periods=N_FIELD_PERIODS,
|
| 59 |
+
fidelity="low",
|
| 60 |
+
)
|
| 61 |
+
return SmokeArtifact(
|
| 62 |
+
created_at_utc=datetime.now(UTC).isoformat(),
|
| 63 |
+
constellaration_version=version("constellaration"),
|
| 64 |
+
boundary_type=type(boundary).__name__,
|
| 65 |
+
n_field_periods=N_FIELD_PERIODS,
|
| 66 |
+
params=BASELINE_PARAMS.model_dump(),
|
| 67 |
+
metrics=_metrics_payload(metrics),
|
| 68 |
+
)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def write_artifact(output_dir: Path, artifact: SmokeArtifact) -> Path:
|
| 72 |
+
output_dir.mkdir(parents=True, exist_ok=True)
|
| 73 |
+
timestamp = datetime.now(UTC).strftime("%Y%m%dT%H%M%SZ")
|
| 74 |
+
output_path = output_dir / f"northflank_smoke_{timestamp}.json"
|
| 75 |
+
output_path.write_text(json.dumps(asdict(artifact), indent=2, sort_keys=True) + "\n")
|
| 76 |
+
return output_path
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def _metrics_payload(metrics: EvaluationMetrics) -> dict[str, float | bool]:
|
| 80 |
+
return {
|
| 81 |
+
"max_elongation": metrics.max_elongation,
|
| 82 |
+
"aspect_ratio": metrics.aspect_ratio,
|
| 83 |
+
"average_triangularity": metrics.average_triangularity,
|
| 84 |
+
"edge_iota_over_nfp": metrics.edge_iota_over_nfp,
|
| 85 |
+
"p1_score": metrics.p1_score,
|
| 86 |
+
"p1_feasibility": metrics.p1_feasibility,
|
| 87 |
+
"constraints_satisfied": metrics.constraints_satisfied,
|
| 88 |
+
"vacuum_well": metrics.vacuum_well,
|
| 89 |
+
}
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def main() -> None:
|
| 93 |
+
args = parse_args()
|
| 94 |
+
artifact = build_artifact()
|
| 95 |
+
output_path = write_artifact(args.output_dir, artifact)
|
| 96 |
+
print(output_path)
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
if __name__ == "__main__":
|
| 100 |
+
main()
|