CreativeEngineer commited on
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
- - [ ] Northflank smoke notebook note saved
 
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()