runops 2.3.0__tar.gz → 2.4.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {runops-2.3.0 → runops-2.4.0}/PKG-INFO +2 -1
- {runops-2.3.0 → runops-2.4.0}/README.md +1 -0
- {runops-2.3.0 → runops-2.4.0}/SPEC.md +22 -7
- {runops-2.3.0 → runops-2.4.0}/docs/adapter.md +4 -1
- {runops-2.3.0 → runops-2.4.0}/docs/guide.md +8 -5
- {runops-2.3.0 → runops-2.4.0}/docs/reference.md +8 -8
- {runops-2.3.0 → runops-2.4.0}/pyproject.toml +1 -1
- {runops-2.3.0 → runops-2.4.0}/src/runops/__init__.py +1 -1
- {runops-2.3.0 → runops-2.4.0}/src/runops/cli.py +47 -18
- {runops-2.3.0 → runops-2.4.0}/src/runops/harness.py +10 -6
- {runops-2.3.0 → runops-2.4.0}/src/runops/manifest.py +18 -2
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/create.py +110 -10
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/setup.py +4 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/skills.py +42 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/tidy.py +143 -56
- {runops-2.3.0 → runops-2.4.0}/src/runops/skills/runops-create-run/SKILL.md +7 -4
- {runops-2.3.0 → runops-2.4.0}/src/runops/skills/runops-maintain/SKILL.md +2 -2
- {runops-2.3.0 → runops-2.4.0}/src/runops/skills/runops-reference/SKILL.md +5 -5
- {runops-2.3.0 → runops-2.4.0}/src/runops/skills/runops-tidy/SKILL.md +5 -4
- {runops-2.3.0 → runops-2.4.0}/tests/test_adapters.py +28 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_archive_series.py +56 -1
- {runops-2.3.0 → runops-2.4.0}/tests/test_mv_naming.py +40 -0
- runops-2.4.0/tests/test_run_job.py +70 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_skills.py +30 -0
- {runops-2.3.0 → runops-2.4.0}/uv.lock +1 -1
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/check/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/improve-harness/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/agents/openai.yaml +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/examples/refactor-request.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/references/anti-patterns.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/references/api-compatibility.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/references/packaging-modernization.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/references/refactor-playbook.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/references/verification.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/scripts/api_surface_snapshot.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/scripts/import_smoke.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/scripts/inspect_python_package.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/python-package-refactor/scripts/refactor_quality_gate.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/release/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/simulator-adapter/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/spec-reviewer/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.agents/skills/sync-docs/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/agents/spec-reviewer.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/rules/architecture.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/rules/dev-workflow.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/rules/gotchas.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/settings.json +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/skills/check/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/skills/improve-harness/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/skills/python-package-refactor/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/skills/release/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/skills/simulator-adapter/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/skills/spec-reviewer/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.claude/skills/sync-docs/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.codex/README.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/.codex/agents/spec-reviewer.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/.codex/config.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/.codex/rules/runops.rules +0 -0
- {runops-2.3.0 → runops-2.4.0}/.dockerignore +0 -0
- {runops-2.3.0 → runops-2.4.0}/.gitattributes +0 -0
- {runops-2.3.0 → runops-2.4.0}/.github/assets/conversation-dark.svg +0 -0
- {runops-2.3.0 → runops-2.4.0}/.github/assets/conversation-light.svg +0 -0
- {runops-2.3.0 → runops-2.4.0}/.github/assets/hero-dark.svg +0 -0
- {runops-2.3.0 → runops-2.4.0}/.github/assets/hero-light.svg +0 -0
- {runops-2.3.0 → runops-2.4.0}/.github/assets/make_readme_art.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/.github/workflows/ci.yml +0 -0
- {runops-2.3.0 → runops-2.4.0}/.github/workflows/publish.yml +0 -0
- {runops-2.3.0 → runops-2.4.0}/.gitignore +0 -0
- {runops-2.3.0 → runops-2.4.0}/.harness/manifest.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/.pre-commit-config.yaml +0 -0
- {runops-2.3.0 → runops-2.4.0}/.vscode/settings.json +0 -0
- {runops-2.3.0 → runops-2.4.0}/AGENTS.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/CLAUDE.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/LICENSE +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/adapters/__init__.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/adapters/base.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/adapters/generic.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/adapters/registry.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/case.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/config.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/errors.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/jobgen.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/launchers.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/legacy_adapters/__init__.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/legacy_adapters/beach.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/legacy_adapters/emses.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/__init__.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/common.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/listing.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/migrate.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/notes.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/submit.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/ops/sync.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/project.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/runtime.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/skills/runops-check-status/SKILL.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/slurm.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/src/runops/util.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/__init__.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/conftest.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/.codex/rules/a.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/.runops/test-runs/T20260701-0001/receipt.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/.runops/work/beach-x-abc/bin/beach +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/AGENTS.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/CLAUDE.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/cases/beach/plane/beach.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/cases/beach/plane/case.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/cases/old/beach.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/cases/old/case.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/launchers.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/research/CURRENT.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runops.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runs/AGENTS.md +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runs/_archive/plane/R20260601-0001/manifest.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/input/beach.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/manifest.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runs/plane/R20260702-0001/manifest.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runs/scan/R20260701-0002/manifest.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/runs/scan/survey.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/simulators.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/fixtures/v1-project/site.toml +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/golden/mpi_standard.sh +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/golden/openmp_rsc.sh +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_build.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_dev_harness.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_jobgen.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_legacy_adapters.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_lifecycle.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_manifest_config.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_migrate.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_project_scope.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_safety.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_scratch_tidy.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_setup_misc.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_sweep.py +0 -0
- {runops-2.3.0 → runops-2.4.0}/tests/test_sync_evidence.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: runops
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4.0
|
|
4
4
|
Summary: Run ledger and Slurm job submission for HPC simulation projects
|
|
5
5
|
Project-URL: Homepage, https://github.com/Nkzono99/runops
|
|
6
6
|
Project-URL: Documentation, https://github.com/Nkzono99/runops/blob/main/docs/reference.md
|
|
@@ -73,6 +73,7 @@ runo init
|
|
|
73
73
|
| 試してから本計算 | 100 step だけ smoke で試して、通ったら wc を 3 通りに振った候補を見せて |
|
|
74
74
|
| 様子を見る | 先週投げた run の状況を見て。失敗していたら原因をノートに残して |
|
|
75
75
|
| 続きを流す | R20261015-0003 の続きを 2 倍の step で |
|
|
76
|
+
| 資源を変えて作り直す | R20261015-0003 を 256 並列・72 時間で作り直して(case はそのまま) |
|
|
76
77
|
| 片付ける | 古い run を整理して。消すものと archive するものを先に見せて |
|
|
77
78
|
| 作り直した旧系列を片付ける | 旧系列は新系列で置き換えたので、対応を記録して archive して |
|
|
78
79
|
| simulator を足す | パッチを当てた ~/BEACH-dev をビルドして、別の simulator として登録して |
|
|
@@ -42,6 +42,7 @@ runo init
|
|
|
42
42
|
| 試してから本計算 | 100 step だけ smoke で試して、通ったら wc を 3 通りに振った候補を見せて |
|
|
43
43
|
| 様子を見る | 先週投げた run の状況を見て。失敗していたら原因をノートに残して |
|
|
44
44
|
| 続きを流す | R20261015-0003 の続きを 2 倍の step で |
|
|
45
|
+
| 資源を変えて作り直す | R20261015-0003 を 256 並列・72 時間で作り直して(case はそのまま) |
|
|
45
46
|
| 片付ける | 古い run を整理して。消すものと archive するものを先に見せて |
|
|
46
47
|
| 作り直した旧系列を片付ける | 旧系列は新系列で置き換えたので、対応を記録して archive して |
|
|
47
48
|
| simulator を足す | パッチを当てた ~/BEACH-dev をビルドして、別の simulator として登録して |
|
|
@@ -283,6 +283,12 @@ ntasks = 800
|
|
|
283
283
|
adapter の `render_inputs()` が `[params]` を適用して上書きする。
|
|
284
284
|
- `runo new` / `runo sweep` の `--set k=v` は `[params]` の上書きである。
|
|
285
285
|
既存 key でなくても受け付ける(adapter の `validate_params()` が warning を出せる)。
|
|
286
|
+
warning には判定した adapter と package version を付け、executable が package 由来でない
|
|
287
|
+
(`local_source` / `local_executable`)ときは「schema が古いだけかもしれない」と添える。
|
|
288
|
+
- run の `[job]` は、case から作る run では case の `[job]`、`new --from RUN`(`--continue` 含む)
|
|
289
|
+
では **元 run の `[job]`** を引き継ぐ(元 run に無い key は case から)。`--job k=v` で個別に上書きし(`[job]` の key のみ)、
|
|
290
|
+
`--job-from-case` で case の値に戻す。資源の変更に case の編集を要しない。作成時に、元 run
|
|
291
|
+
(または case)に対する params と資源の差分を表示する。
|
|
286
292
|
- **variation は case ではなく run で表現する。** 「同じ物理設定で 1 つの値だけ変える」
|
|
287
293
|
ために新しい case を作らない。`new CASE --set`、`new --from RUN --set`、`sweep` を使う。
|
|
288
294
|
`case new --from CASE` は、base 入力ファイルの構造そのものが変わるときだけ使う。
|
|
@@ -388,6 +394,7 @@ cancel_reason = "" # cancel --why の理由。retry はこれが
|
|
|
388
394
|
|
|
389
395
|
[variation]
|
|
390
396
|
changed_keys = ["plasma.wc", "plasma.phiz"] # case の params との差分 key
|
|
397
|
+
job_changed_keys = ["walltime"] # case の [job] との差分 key(--job / 元 run から)
|
|
391
398
|
|
|
392
399
|
[identity]
|
|
393
400
|
input_hash = "sha256:..." # input/ 配下全ファイルの content hash
|
|
@@ -419,7 +426,7 @@ created ──submit──▶ submitted ──(slurm)──▶ running ──▶
|
|
|
419
426
|
│ └───cancel──────────────┴──────▶ cancelled
|
|
420
427
|
│
|
|
421
428
|
└────────────── archive ─────────────────────────▶ archived
|
|
422
|
-
(completed / failed / cancelled から)
|
|
429
|
+
(created / completed / failed / cancelled から)
|
|
423
430
|
```
|
|
424
431
|
|
|
425
432
|
- `retry` は `failed` / `cancelled` を `created` に戻し `job.attempt` を +1 する。
|
|
@@ -494,9 +501,9 @@ created ──submit──▶ submitted ──(slurm)──▶ running ──▶
|
|
|
494
501
|
| `runo context [--json]` | agent 向け現在地。project 名、simulator、active run 数、直近 run 5 件、scratch 期限切れ数、`LOG.md` 末尾 20 行。配置した runops skill が install 済みの runops より古ければ `runo update --skills-only` を促す warning |
|
|
495
502
|
| `runo case new NAME --simulator SIM [--from CASE]` | case をスキャフォールド。adapter の `case_template()` を展開 |
|
|
496
503
|
| `runo case list` | case 一覧 |
|
|
497
|
-
| `runo new CASE [--why TEXT] [--label L] [--set k=v]... [--tag T]... [--scratch] [--dest DIR] [--submit] [--created-by WHO] [--json]` | 単発 run を作る |
|
|
498
|
-
| `runo new --from RUN [--why] [--set k=v]... [--continue] [--submit]` | 既存 run の params から派生 run
|
|
499
|
-
| `runo sweep CASE --set k=v1,v2,... [--set ...] [--names KEY=n1,n2,...]... [--label L] [--dest DIR] [--why TEXT] [--apply] [--submit] [--limit N] [--json]` | 直積の候補を表示する。`--apply` で生成。`--submit` は `--apply` と同時のみ有効 |
|
|
504
|
+
| `runo new CASE [--why TEXT] [--label L] [--set k=v]... [--job k=v]... [--tag T]... [--scratch] [--dest DIR] [--submit] [--created-by WHO] [--json]` | 単発 run を作る |
|
|
505
|
+
| `runo new --from RUN [--why] [--set k=v]... [--job k=v]... [--job-from-case] [--continue] [--submit]` | 既存 run の params と `[job]` から派生 run を作る(§5)。`--continue` は adapter が `extend_inputs()` を実装する場合のみ有効(§8.1) |
|
|
506
|
+
| `runo sweep CASE --set k=v1,v2,... [--set ...] [--names KEY=n1,n2,...]... [--job k=v]... [--label L] [--dest DIR] [--why TEXT] [--apply] [--submit] [--limit N] [--json]` | 直積の候補を表示する。`--apply` で生成。`--submit` は `--apply` と同時のみ有効 |
|
|
500
507
|
| `runo submit RUN... [--partition P] [--qos Q] [--afterok JOBID] [-y]` | job.sh を生成して sbatch。`created` 以外は拒否 |
|
|
501
508
|
| `runo submit --all [DIR] [-y]` | DIR 配下の `created` run をすべて投入。件数と概算 core-hours を表示して確認 |
|
|
502
509
|
| `runo cancel RUN... [--why TEXT] [-y]` | scancel して sync。`--why` は `cancelled: ...` の note として残す |
|
|
@@ -509,7 +516,7 @@ created ──submit──▶ submitted ──(slurm)──▶ running ──▶
|
|
|
509
516
|
| `runo pin RUN [--off] [--reason TEXT]` | `run.pinned` を切り替える。pinned は `list` に常に出て、`tidy` / `delete` の対象外 |
|
|
510
517
|
| `runo summarize RUN... [--force]` | adapter の `summarize()` を呼び `analysis/summary.json` を書く。既存があれば `--force` なしではスキップ |
|
|
511
518
|
| `runo mv RUN... [--dest DIR] [--label L] [--why TEXT]` | run を `runs/` 配下で改名・移動する。id、入力、履歴は変えない(§7.7) |
|
|
512
|
-
| `runo archive RUN\|DIR... [--superseded-by RUN\|DIR --why TEXT] [-y]` / `runo archive --older-than 90d [-y]` | `runs/_archive/<元の相対 path>` に移動して `archived`
|
|
519
|
+
| `runo archive RUN\|DIR... [--superseded-by RUN\|DIR --why TEXT] [-y]` / `runo archive --older-than 90d [-y]` | `runs/_archive/<元の相対 path>` に移動して `archived` にする。対象は created と終端の run(created は job を持たないため)。移動前に全対象を検査し、指定した RUN が対象外なら何も動かさない。DIR は配下の run(系列)を指し、submitted / running と pinned を除いて表示する。created は `--superseded-by` があるときだけ含める。`--superseded-by` は各 run に置き換え先(`parent_run` がその run の置き換え先 run、無ければ指定した全 run)を `run.superseded_by` に記録し、`--why` を note に残す |
|
|
513
520
|
| `runo restore RUN` | archive 前の場所に戻す。既に何かあれば拒否 |
|
|
514
521
|
| `runo delete RUN... [-y]` | directory を削除。`created`/`failed`/`cancelled`/scratch のみ。`pinned` は拒否 |
|
|
515
522
|
| `runo tidy [--apply] [--only KIND] [--older-than AGE] [--purge-work RUN...] [--json]` | 整理候補を表示する(§7.5)。`--apply` で実行 |
|
|
@@ -534,7 +541,7 @@ created ──submit──▶ submitted ──(slurm)──▶ running ──▶
|
|
|
534
541
|
|
|
535
542
|
| kind | 条件 | 動作 |
|
|
536
543
|
|---|---|---|
|
|
537
|
-
| `expired-scratch` | `scratch = true` かつ `expires_at` を過ぎ、`pinned = false
|
|
544
|
+
| `expired-scratch` | `scratch = true` かつ `expires_at` を過ぎ、`pinned = false`、archived でない | delete |
|
|
538
545
|
| `stale-failed` | `failed`/`cancelled` で `defaults.tidy_failed_days`(既定 30)日より古く、note も `why` も空 | delete |
|
|
539
546
|
| `old-completed` | `completed` で `defaults.tidy_archive_days`(既定 90)日より古く、`pinned = false` | archive |
|
|
540
547
|
| `orphan-job` | `submitted`/`running` だが squeue/sacct に job が無い | sync(state 確定) |
|
|
@@ -573,6 +580,9 @@ created ──submit──▶ submitted ──(slurm)──▶ running ──▶
|
|
|
573
580
|
- 対象は `created`、`completed`、`failed`、`cancelled`。`submitted` / `running` は拒否する
|
|
574
581
|
(Slurm が run directory の絶対 path を持つ)。`archived` は `restore` を使う。
|
|
575
582
|
- 既存 path は上書きしない(`move_noreplace`)。`created` の run は job.sh を作り直す。
|
|
583
|
+
- 複数 run は `run.lock` を 1 回取り、全対象の状態と移動先を検査してから移す。1 件でも拒否されれば
|
|
584
|
+
どれも動かさない。移動中に filesystem の error が起きたら止まり、移し終えた run id を error に示す
|
|
585
|
+
(exit code 1)。`archive RUN...` も同じ手順で移す。
|
|
576
586
|
- 手で `mv` しても run は id で見つかるが、`run.label` と `[path].run_dir` が古いまま残る。
|
|
577
587
|
|
|
578
588
|
---
|
|
@@ -665,12 +675,15 @@ runops は `init` と `update` で次を行う。
|
|
|
665
675
|
(例: `mpiemses3d-input-review`)。`runops-` で始まる名前は予約されており展開しない。
|
|
666
676
|
2. `references/` を `.agents/skills/<adapter>-references/` にコピーし、
|
|
667
677
|
`SKILL.md` が無ければ reference 一覧を列挙した `SKILL.md` を生成する。
|
|
678
|
+
展開した skill の `../../references/`(package 内の配置での参照)は
|
|
679
|
+
`../<adapter>-references/` に書き換える。
|
|
668
680
|
3. 展開先には `.managed-by-runops` marker を置く。marker のある directory は
|
|
669
681
|
`update` 時に **無条件で上書き** する。marker の無い同名 directory は触らず warning。
|
|
670
682
|
4. `.runops/skills.lock` に adapter 名、package version、展開した directory、content hash を記録する。
|
|
671
683
|
|
|
672
684
|
`doctor` は lock の package version と現在 install されている version を比較し、
|
|
673
|
-
差があれば `runo update --skills-only`
|
|
685
|
+
差があれば `runo update --skills-only` を提案する。展開済み skill の相対リンクで
|
|
686
|
+
存在しない file を指すものも報告する。
|
|
674
687
|
|
|
675
688
|
既存の simulator repo(MPIEMSES3D、emout、BEACH)は `plugins/<name>-context/` に
|
|
676
689
|
`skills/` と `references/` を既に持つ。各 package 側で必要な変更は
|
|
@@ -967,4 +980,6 @@ v2 の文書は次の 4 ファイルだけにする。
|
|
|
967
980
|
| 操作範囲は起動 project(2026-10-21) | AGENTS.md block と skill に「対象は起動した project の run と job id だけ。アカウント全体の squeue / sacct は診断に見るだけ。自分で別 directory に移らず、生の `sbatch` / `scancel` を使わない」を書く。CLI は別 project の path を拒否する | 範囲を言わない「再投入して」で、エージェントが別 project や runops 管理外のジョブまで再投入した。cwd で project を決める既存の model に、path 引数の抜け道(`submit --all /other/runs` など)があった。他 session が修正のために止めた run を見分けられるよう、`cancel --why` を note に残し、`retry` はその理由を示して確認を取る(#112) |
|
|
968
981
|
| 置き換えた系列の整理(2026-10-21) | `archive` が DIR(系列)を受け、`--superseded-by` で置き換え先を manifest に記録する。新しい command と tidy の種別は足さない。skill は新系列を作ったら旧系列の要否を確かめ、不要なら archive して現用系列・archive 先・件数を報告する | 修正版を作った後に旧系列が作業領域に残り、毎回ユーザーが整理を頼んでいた。`parent_run` だけでは比較用の派生と置き換えを区別できないので、置き換えは明示操作で記録する。旧 `archive` は 1 件ずつ検査していて、途中の running run で一部だけ移動していた(#113) |
|
|
969
982
|
| 生成ハーネスの書き方(2026-10-21) | AGENTS.md の Rules を「作業の進め方」5 項目に絞り、各項目を「何をするか」と短い理由で書く。skill は手順に専念し、AGENTS.md の方針(manifest を手で編集しない等)を繰り返さない | #112・#113 で規則が増え、禁止形(never / do not)が並んでいた。現行の Claude の prompting 指針は、しないことより何をするかを、理由と共に書くことを勧め、強い禁止語は過剰反応を招くとしている |
|
|
983
|
+
| 複数 run の mv / archive(2026-10-22) | 1 回の `run.lock` の下で全対象を読み直して検査し、通ってから移す。途中の filesystem error では移し終えた run id を示して止まる | `mv A B C --dest X` は 1 件ずつ検査して移していたので、途中の submitted run で一部だけ移動していた。archive は表示の後で lock を取り直していて、その間に再投入された run で同じことが起き得た |
|
|
984
|
+
| FEEDBACK 棚卸し 2.4.0(2026-10-23) | `--job k=v` と `new --from` の `[job]` 継承、created の archive / restore、archived scratch を期限削除しない、展開 skill の references 参照の書き換えと `doctor` のリンク検査、adapter warning への版の併記、生成 AGENTS.md に解析環境の置き場所 | hub の FEEDBACK で、資源変更のための case 編集(3 回)、置換済み draft を archive できず helper で迂回(2 回)、保管目的の archive が期限削除される、skill から reference に辿れない、新版の正規キーが誤記に見える、解析依存の置き場所が project ごとに違う、が記録された。いずれも既存 command の拡張で、command 数は増やさない |
|
|
970
985
|
| 使い方ガイド | `docs/guide.md` を追加し、文書を 4 ファイルにする(§13.3) | README の依頼例の表と command 一覧の間を埋める、場面ごとの流れ(case と run の分け方、smoke から本計算、状態の決まり方、整理)の置き場所が無かった |
|
|
@@ -81,7 +81,10 @@ project の `adapters/mysim.py` に `Adapter` という名前で置けば使え
|
|
|
81
81
|
`runo init` と `runo update` は `skills/*` を project の `.agents/skills/` と
|
|
82
82
|
`.claude/skills/` に同名でコピーし、`references/` を `<adapter>-references` として置きます。
|
|
83
83
|
skill 名は package 側で名前空間を付けてください(例: `mysim-input-review`)。
|
|
84
|
-
`runops-`
|
|
84
|
+
`runops-` で始まる名前は予約されています。skill から reference へは
|
|
85
|
+
`../<adapter>-references/X.md` と書いてください。package 内の配置どおりの
|
|
86
|
+
`../../references/X.md` は展開時にこの形へ書き換えます。`runo doctor` は展開済み skill の
|
|
87
|
+
相対リンク切れを報告します。展開した directory には
|
|
85
88
|
`.managed-by-runops` が置かれ、更新時に上書きされます。利用者が作った同名 directory は
|
|
86
89
|
上書きしません。
|
|
87
90
|
|
|
@@ -105,8 +105,11 @@ run id、入力、sweep の帰属、ノートはそのままで、移動元は
|
|
|
105
105
|
投入、削除、整理、移行の前には、エージェントが対象の run、partition、資源量、core-hours を示して
|
|
106
106
|
確認を取ります。`runo` 自体も、複数 run の投入や削除の前に確認を求めます。
|
|
107
107
|
|
|
108
|
-
資源量(partition、walltime、並列数)は case の `[job]` に書きます。
|
|
109
|
-
|
|
108
|
+
資源量(partition、walltime、並列数)は case の `[job]` に書きます。run ごとに変えるときは
|
|
109
|
+
「R… を 256 並列・72 時間で作り直して」と頼めば `runo new --from RUN --job processes=256 --job walltime=72:00:00`
|
|
110
|
+
になり、case は変えません。`--from` で作る run は元 run の資源を引き継ぎ、作成時に元 run との
|
|
111
|
+
params と資源の差分を表示します。1 回だけ partition を変えるなら「今回は gr20001b で投げて」と頼めば
|
|
112
|
+
`runo submit RUN --partition gr20001b` になります。
|
|
110
113
|
|
|
111
114
|
walltime は、同じ case・同じ資源で終わった run の実時間を元に決めます。`runo list --json` と
|
|
112
115
|
`runo status` は、各 run の `elapsed`(最新の実行の実時間)、`walltime`、使った割合を示します。
|
|
@@ -209,7 +212,7 @@ run は消さなくても `runo list` が古いものを隠しますが、direct
|
|
|
209
212
|
|
|
210
213
|
| 種類 | 条件 | 処理 |
|
|
211
214
|
|---|---|---|
|
|
212
|
-
| `expired-scratch` | 期限切れの scratch run | 削除 |
|
|
215
|
+
| `expired-scratch` | 期限切れの scratch run(archive したものは除く) | 削除 |
|
|
213
216
|
| `stale-failed` | 30 日より古い failed / cancelled で、`why` もノートも無い | 削除 |
|
|
214
217
|
| `old-completed` | 90 日より古い completed | archive |
|
|
215
218
|
| `orphan-job` | 投入済みなのに Slurm が知らない | `sync` で状態を確定 |
|
|
@@ -225,8 +228,8 @@ run id で引き続き見つかり、`runo restore` で戻せます。
|
|
|
225
228
|
> 旧系列は不要。新系列で置き換えたと記録して archive して。
|
|
226
229
|
|
|
227
230
|
で `runo archive runs/<case>/<旧系列> --superseded-by runs/<case>/<新系列> --why "..."` になります。
|
|
228
|
-
系列の directory を渡すと、対象の run
|
|
229
|
-
|
|
231
|
+
系列の directory を渡すと、対象の run を先に表示し、投入中・実行中の run と pin した run は
|
|
232
|
+
除きます。投入前の run(created)も `--superseded-by` を付けたときは archive でき、`runo restore` で戻せます。各旧 run の `superseded_by` には、その run から `--from` で作った新 run(無ければ新系列の全 run)が
|
|
230
233
|
記録され、理由はノートに残ります。比較用に派生しただけの run は置き換えとは扱わないので、
|
|
231
234
|
旧系列が不要かどうかは毎回確かめます。
|
|
232
235
|
|
|
@@ -112,7 +112,7 @@ runops だけが書きます。手で編集しないでください。主な fie
|
|
|
112
112
|
| `[simulator]` | `name`、`adapter`、`adapter_source`、`adapter_version`、`executable`、`resolver_mode`、`package_version`、`exe_hash`、`git_commit`、`git_dirty`、`build_dir` |
|
|
113
113
|
| `[job]` | case の `[job]` に加え `job_id`、`attempt`、`submitted_at`、`finished_at`、`last_slurm_state`、`exit_code`、`[[job.attempts]]`(attempt ごとの `job_id`、`started_at`、`exit_code`、`exe_hash`、`elapsed`、`cancel --why` の `cancel_reason`) |
|
|
114
114
|
| `[params]` | case の params に `--set` を適用した最終値 |
|
|
115
|
-
| `[variation]` | `changed_keys` |
|
|
115
|
+
| `[variation]` | `changed_keys`、`job_changed_keys`(case の `[job]` と違う key) |
|
|
116
116
|
| `[identity]` | `input_hash` |
|
|
117
117
|
| `[storage]` | `work_purged_at` |
|
|
118
118
|
| `[[notes]]` | `at`、`by`、`text` |
|
|
@@ -148,22 +148,22 @@ run id は削除後も再利用しません。`--dest` に既存 run の中は
|
|
|
148
148
|
| command | 動作 |
|
|
149
149
|
|---|---|
|
|
150
150
|
| `runo init [SIM...] [--site kudpc\|standard] [--no-install] [-y]` | project を作る。`SIM` は `emses` や `name=package-spec`。端末から引数なしで実行すると site と simulator を尋ねる。`--site` の既定はホスト名から推定(KUDPC のログインノードなら `kudpc`、`camphor` も同義) |
|
|
151
|
-
| `runo doctor [--json]` | 設定、adapter の解決経路、executable、sbatch、skill
|
|
151
|
+
| `runo doctor [--json]` | 設定、adapter の解決経路、executable、sbatch、skill の鮮度とリンク切れを点検 |
|
|
152
152
|
| `runo update [--skills-only] [-y]` | simulator package を更新し skill と AGENTS.md の管理 block を再生成 |
|
|
153
153
|
| `runo build SIM [--force]` | `local_source` の simulator を build |
|
|
154
154
|
| `runo context [--json]` | agent 向けの現在地 |
|
|
155
155
|
| `runo case new NAME [-s SIM] [--from CASE]` / `runo case list` | case の作成と一覧 |
|
|
156
|
-
| `runo new CASE [--why] [--label] [--set k=v]... [--tag] [--scratch] [--dest DIR] [--submit] [--json]` | run を 1
|
|
157
|
-
| `runo new --from RUN [--set k=v]... [--continue]` | 既存 run の params
|
|
158
|
-
| `runo sweep CASE --set k=v1,v2 ... [--names k=n1,n2...] [--label] [--dest DIR] [--limit N] [--apply] [--submit] [--json]` | 直積の候補表示。`--apply` で作成。`--names` は各値の短い label、`--dest` は sweep directory の親 |
|
|
156
|
+
| `runo new CASE [--why] [--label] [--set k=v]... [--job k=v]... [--tag] [--scratch] [--dest DIR] [--submit] [--json]` | run を 1 つ作る。`--job` はこの run の `[job]` の値 |
|
|
157
|
+
| `runo new --from RUN [--set k=v]... [--job k=v]... [--job-from-case] [--continue]` | 既存 run の params と `[job]` から派生(`--job-from-case` で `[job]` は case から)。`--continue` は adapter の restart 対応が必要 |
|
|
158
|
+
| `runo sweep CASE --set k=v1,v2 ... [--names k=n1,n2...] [--job k=v]... [--label] [--dest DIR] [--limit N] [--apply] [--submit] [--json]` | 直積の候補表示。`--apply` で作成。`--names` は各値の短い label、`--dest` は sweep directory の親 |
|
|
159
159
|
| `runo submit RUN... [-p P] [--qos Q] [--afterok JOB]` / `runo submit --all [DIR] [-y]` | job.sh を生成して sbatch |
|
|
160
160
|
| `runo cancel RUN... [--why TEXT] [-y]` / `runo retry RUN [--submit] [-y]` | scancel(`--why` は note に残る)/ failed・cancelled を created に戻す(理由付きで cancel された run は確認を取る) |
|
|
161
161
|
| `runo status [RUN...]` / `runo sync [RUN...]` / `runo log RUN [--err] [-n N] [-f]` | 状態表示 / Slurm から更新 / log 表示 |
|
|
162
162
|
| `runo list [PATH] [--all] [--since 30d] [--case] [--sweep] [--status] [--tag] [--scratch] [--archived] [--json]` | run 一覧 |
|
|
163
163
|
| `runo note RUN [TEXT]` / `runo pin RUN [--off] [--reason]` | ノート追記・表示 / pin |
|
|
164
164
|
| `runo summarize RUN... [--force]` | adapter の要約を `analysis/summary.json` に書く |
|
|
165
|
-
| `runo mv RUN... [--dest DIR] [--label L] [--why TEXT]` | `runs/` 配下で改名・移動。id、入力、sweep、ノートは保つ。submitted / running と archived
|
|
166
|
-
| `runo archive RUN\|DIR... [--superseded-by RUN\|DIR --why TEXT]` / `runo archive --older-than 90d` / `runo restore RUN` | `runs/_archive/` へ移動 /
|
|
165
|
+
| `runo mv RUN... [--dest DIR] [--label L] [--why TEXT]` | `runs/` 配下で改名・移動。id、入力、sweep、ノートは保つ。submitted / running と archived は不可。全対象を検査してから移し、1 件でも不可なら何も動かさない |
|
|
166
|
+
| `runo archive RUN\|DIR... [--superseded-by RUN\|DIR --why TEXT]` / `runo archive --older-than 90d` / `runo restore RUN` | `runs/_archive/` へ移動 / 戻す。対象は created と終了した run。移動前に全対象を検査し、対象外の RUN があれば何も動かさない。DIR は配下の run(系列)で、submitted / running と pinned を除き、created は `--superseded-by` のときだけ含めて表示する。`--superseded-by` は置き換え先(その run から `--from` で派生した run、無ければ指定した全 run)を `superseded_by` に記録し、`--why` をノートに残す |
|
|
167
167
|
| `runo delete RUN... [-y]` | created・failed・cancelled・scratch の run を削除 |
|
|
168
168
|
| `runo tidy [--apply] [--only KIND] [--older-than AGE] [--purge-work RUN...]` | 整理候補の表示と実行 |
|
|
169
169
|
| `runo migrate-v1 [--dry-run] [--apply-delete-test-runs] [-y]` | v1 project の変換 |
|
|
@@ -175,7 +175,7 @@ scratch と archived は表示しません。
|
|
|
175
175
|
|
|
176
176
|
| kind | 条件 | 動作 |
|
|
177
177
|
|---|---|---|
|
|
178
|
-
| `expired-scratch` | 期限切れの scratch run | delete |
|
|
178
|
+
| `expired-scratch` | 期限切れの scratch run(archived は除く) | delete |
|
|
179
179
|
| `stale-failed` | `tidy_failed_days`(既定 30)日より古い failed / cancelled で、why も note も無い | delete |
|
|
180
180
|
| `old-completed` | `tidy_archive_days`(既定 90)日より古い completed | archive |
|
|
181
181
|
| `orphan-job` | Slurm が知らない submitted / running | sync |
|
|
@@ -312,9 +312,13 @@ def case_list(as_json: bool = typer.Option(False, "--json")) -> None:
|
|
|
312
312
|
# ---------------------------------------------------------------------------
|
|
313
313
|
|
|
314
314
|
|
|
315
|
-
def _print_created(
|
|
315
|
+
def _print_created(
|
|
316
|
+
project: Project, manifests: list[Manifest], changes: list[str] | None = None
|
|
317
|
+
) -> None:
|
|
316
318
|
for m in manifests:
|
|
317
319
|
out.print(f"created {m.id} {project.rel(m.run_dir)}")
|
|
320
|
+
for line in changes or []:
|
|
321
|
+
out.print(f" {line}", markup=False)
|
|
318
322
|
|
|
319
323
|
|
|
320
324
|
def _submit_each(project: Project, manifests: list[Manifest], *, quiet: bool = False) -> None:
|
|
@@ -341,16 +345,24 @@ def new(
|
|
|
341
345
|
continue_run: bool = typer.Option(
|
|
342
346
|
False, "--continue", help="Continue from --from RUN (adapter support needed)."
|
|
343
347
|
),
|
|
348
|
+
job: Optional[list[str]] = typer.Option(
|
|
349
|
+
None, "--job", help="key=value for this run's [job] (walltime, processes, ...)."
|
|
350
|
+
),
|
|
351
|
+
job_from_case: bool = typer.Option(
|
|
352
|
+
False, "--job-from-case", help="With --from: take [job] from the case, not the run."
|
|
353
|
+
),
|
|
344
354
|
submit: bool = typer.Option(False, "--submit"),
|
|
345
355
|
created_by: str = typer.Option("", "--created-by"),
|
|
346
356
|
as_json: bool = typer.Option(False, "--json", help="Print the created run as JSON."),
|
|
347
357
|
) -> None:
|
|
348
358
|
"""Create one run."""
|
|
349
|
-
from runops.ops.create import CreateRequest, create_run, parse_sets
|
|
359
|
+
from runops.ops.create import CreateRequest, create_run, parse_job, parse_sets
|
|
350
360
|
|
|
351
361
|
project = _project()
|
|
352
362
|
if case and from_run:
|
|
353
363
|
raise UsageError("give either CASE or --from RUN, not both")
|
|
364
|
+
if job_from_case and not from_run:
|
|
365
|
+
raise UsageError("--job-from-case needs --from RUN")
|
|
354
366
|
request = CreateRequest(
|
|
355
367
|
case=case or "",
|
|
356
368
|
sets=parse_sets(sets or []),
|
|
@@ -362,13 +374,15 @@ def new(
|
|
|
362
374
|
created_by=created_by or _actor(),
|
|
363
375
|
from_run=from_run,
|
|
364
376
|
continue_run=continue_run,
|
|
377
|
+
job=parse_job(job or []),
|
|
378
|
+
job_from_case=job_from_case,
|
|
365
379
|
)
|
|
366
380
|
result = create_run(project, request)
|
|
367
381
|
_warn(result.warnings)
|
|
368
382
|
if not why:
|
|
369
383
|
_warn(["no --why given; consider `runo note RUN` to record the purpose"])
|
|
370
384
|
if not as_json:
|
|
371
|
-
_print_created(project, result.manifests)
|
|
385
|
+
_print_created(project, result.manifests, result.changes)
|
|
372
386
|
if submit:
|
|
373
387
|
_submit_each(project, result.manifests, quiet=as_json)
|
|
374
388
|
if as_json:
|
|
@@ -388,6 +402,9 @@ def sweep(
|
|
|
388
402
|
dest: str = typer.Option(
|
|
389
403
|
"", "--dest", help="Parent of the sweep directory (default runs/<case>)."
|
|
390
404
|
),
|
|
405
|
+
job: Optional[list[str]] = typer.Option(
|
|
406
|
+
None, "--job", help="key=value for the [job] of every point (walltime, processes, ...)."
|
|
407
|
+
),
|
|
391
408
|
why: str = typer.Option("", "--why"),
|
|
392
409
|
apply: bool = typer.Option(False, "--apply", help="Create the runs."),
|
|
393
410
|
submit: bool = typer.Option(False, "--submit", help="Submit after --apply."),
|
|
@@ -401,6 +418,7 @@ def sweep(
|
|
|
401
418
|
from runops.ops.create import (
|
|
402
419
|
CreateRequest,
|
|
403
420
|
apply_sweep,
|
|
421
|
+
parse_job,
|
|
404
422
|
parse_sweep_names,
|
|
405
423
|
parse_sweep_sets,
|
|
406
424
|
plan_sweep,
|
|
@@ -418,6 +436,7 @@ def sweep(
|
|
|
418
436
|
limit=limit,
|
|
419
437
|
dest=dest,
|
|
420
438
|
names=parse_sweep_names(names or [], axes),
|
|
439
|
+
job=parse_job(job or []),
|
|
421
440
|
)
|
|
422
441
|
if as_json and not apply:
|
|
423
442
|
_print_json(
|
|
@@ -429,6 +448,7 @@ def sweep(
|
|
|
429
448
|
{"ref": p.ref, "label": p.label, "values": p.values} for p in plan.points
|
|
430
449
|
],
|
|
431
450
|
"core_hours": plan.core_hours,
|
|
451
|
+
"job": plan.job,
|
|
432
452
|
}
|
|
433
453
|
)
|
|
434
454
|
return
|
|
@@ -446,6 +466,15 @@ def sweep(
|
|
|
446
466
|
f"{len(plan.points)} runs -> {project.rel(plan.parent)}/ "
|
|
447
467
|
f"(~{plan.core_hours:.1f} core-hours)"
|
|
448
468
|
)
|
|
469
|
+
overridden = {k: v for k, v in plan.job.items() if plan.case.job.get(k) != v}
|
|
470
|
+
if overridden:
|
|
471
|
+
out.print(
|
|
472
|
+
" job vs case: "
|
|
473
|
+
+ ", ".join(
|
|
474
|
+
f"{k} {plan.case.job.get(k, '-')} -> {v}" for k, v in overridden.items()
|
|
475
|
+
),
|
|
476
|
+
markup=False,
|
|
477
|
+
)
|
|
449
478
|
if not apply:
|
|
450
479
|
out.print("preview only; add --apply to create")
|
|
451
480
|
return
|
|
@@ -458,7 +487,7 @@ def sweep(
|
|
|
458
487
|
result = apply_sweep(project, plan, request)
|
|
459
488
|
_warn(result.warnings)
|
|
460
489
|
if not as_json:
|
|
461
|
-
_print_created(project, result.manifests)
|
|
490
|
+
_print_created(project, result.manifests, result.changes)
|
|
462
491
|
if submit:
|
|
463
492
|
_submit_each(project, result.manifests, quiet=as_json)
|
|
464
493
|
if as_json:
|
|
@@ -586,6 +615,8 @@ def _status_table(project: Project, manifests: list[Manifest]) -> Table:
|
|
|
586
615
|
status += " [dim](scratch)[/]"
|
|
587
616
|
if s["pinned"]:
|
|
588
617
|
status += " [dim](pinned)[/]"
|
|
618
|
+
if s["superseded_by"]:
|
|
619
|
+
status += f" [dim](-> {', '.join(s['superseded_by'])})[/]"
|
|
589
620
|
table.add_row(
|
|
590
621
|
s["id"],
|
|
591
622
|
status,
|
|
@@ -793,10 +824,10 @@ def archive(
|
|
|
793
824
|
why: str = typer.Option("", "--why", help="Reason, recorded in each run's notes."),
|
|
794
825
|
yes: bool = typer.Option(False, "-y", "--yes"),
|
|
795
826
|
) -> None:
|
|
796
|
-
"""Move
|
|
827
|
+
"""Move created or finished runs to runs/_archive/ (same relative path)."""
|
|
797
828
|
from datetime import datetime
|
|
798
829
|
|
|
799
|
-
from runops.ops.tidy import ArchivePlan,
|
|
830
|
+
from runops.ops.tidy import ArchivePlan, archive_runs, plan_archive
|
|
800
831
|
|
|
801
832
|
project = _project()
|
|
802
833
|
if superseded_by and not why:
|
|
@@ -827,11 +858,11 @@ def archive(
|
|
|
827
858
|
line += f" -> {', '.join(plan.superseded_by[m.id])}"
|
|
828
859
|
out.print(line)
|
|
829
860
|
_confirm(f"Archive {len(plan.targets)} runs?", yes)
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
out.print(f"archived {
|
|
861
|
+
archived = archive_runs(
|
|
862
|
+
project, plan.targets, superseded_by=plan.superseded_by, why=why, by=_actor()
|
|
863
|
+
)
|
|
864
|
+
for run_id, dst in archived:
|
|
865
|
+
out.print(f"archived {run_id} -> {project.rel(dst)}")
|
|
835
866
|
|
|
836
867
|
|
|
837
868
|
@app.command("mv")
|
|
@@ -844,7 +875,7 @@ def mv_cmd(
|
|
|
844
875
|
why: str = typer.Option("", "--why", help="Reason, recorded in the run's notes."),
|
|
845
876
|
) -> None:
|
|
846
877
|
"""Rename or move runs under runs/, keeping their ids, inputs and history."""
|
|
847
|
-
from runops.ops.tidy import
|
|
878
|
+
from runops.ops.tidy import move_runs
|
|
848
879
|
|
|
849
880
|
if not dest and label is None:
|
|
850
881
|
raise UsageError("give --dest and/or --label")
|
|
@@ -852,12 +883,10 @@ def mv_cmd(
|
|
|
852
883
|
targets = _resolve(project, runs)
|
|
853
884
|
if label is not None and len(targets) > 1:
|
|
854
885
|
raise UsageError("--label applies to one RUN; use --dest to move several")
|
|
855
|
-
|
|
856
|
-
for
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
out.print(f"moved {m.id}: {project.rel(result.src)} -> {project.rel(result.dst)}")
|
|
860
|
-
_warn(warnings)
|
|
886
|
+
results = move_runs(project, targets, dest=dest, label=label, why=why, by=_actor())
|
|
887
|
+
for r in results:
|
|
888
|
+
out.print(f"moved {r.run_id}: {project.rel(r.src)} -> {project.rel(r.dst)}")
|
|
889
|
+
_warn([w for r in results for w in r.warnings])
|
|
861
890
|
|
|
862
891
|
|
|
863
892
|
@app.command()
|
|
@@ -14,8 +14,8 @@ COMMANDS = """\
|
|
|
14
14
|
|---|---|
|
|
15
15
|
| `runo context --json` | where the project stands: active runs, recent runs, LOG.md tail |
|
|
16
16
|
| `runo case list` / `runo case new NAME --simulator SIM [--from CASE]` | list / scaffold cases |
|
|
17
|
-
| `runo new CASE --why TEXT [--set k=v]... [--scratch] [--submit]` | create one run |
|
|
18
|
-
| `runo new --from RUN --why TEXT [--set k=v]... [--continue]` | derive a run
|
|
17
|
+
| `runo new CASE --why TEXT [--set k=v]... [--job k=v]... [--scratch] [--submit]` | create one run |
|
|
18
|
+
| `runo new --from RUN --why TEXT [--set k=v]... [--job k=v]... [--continue]` | derive a run (keeps its params and [job]) |
|
|
19
19
|
| `runo sweep CASE --set k=v1,v2 [--names k=n1,n2] [--apply] [--submit]` | preview / create a parameter product |
|
|
20
20
|
| `runo submit RUN... [--partition P]` / `runo submit --all DIR` | sbatch created runs |
|
|
21
21
|
| `runo sync` / `runo status RUN` / `runo log RUN [--err]` | refresh state from Slurm / show / read logs |
|
|
@@ -25,7 +25,7 @@ COMMANDS = """\
|
|
|
25
25
|
| `runo summarize RUN` | adapter summary into analysis/summary.json |
|
|
26
26
|
| `runo tidy` / `runo tidy --apply` | list / apply cleanup (expired scratch, stale failures, old runs) |
|
|
27
27
|
| `runo mv RUN... [--dest DIR] [--label L] --why TEXT` | rename / move runs, keeping ids and inputs |
|
|
28
|
-
| `runo archive RUN\\|DIR... [--superseded-by RUN\\|DIR --why T]` | move finished runs (or a series) to runs/_archive |
|
|
28
|
+
| `runo archive RUN\\|DIR... [--superseded-by RUN\\|DIR --why T]` | move created or finished runs (or a series) to runs/_archive |
|
|
29
29
|
| `runo restore RUN` / `runo delete RUN` | move back / remove |
|
|
30
30
|
| `runo build SIM` | build a local_source simulator into .runops/build |
|
|
31
31
|
| `runo doctor` / `runo update` | check environment / upgrade simulator packages and skills |"""
|
|
@@ -70,9 +70,9 @@ the project found from the current directory.
|
|
|
70
70
|
sessions, so act on them only when the user names them. If nothing here matches, say so.
|
|
71
71
|
- **Change runs through `runo`.** `runo` writes each run's manifest and `input/`, so
|
|
72
72
|
they stay an exact record of what ran.
|
|
73
|
-
Express a change as a new run (`runo new --from RUN --set k=v`,
|
|
74
|
-
rename or regroup with `runo mv`. Variations are
|
|
75
|
-
structure of the base input.
|
|
73
|
+
Express a change as a new run (`runo new --from RUN --set k=v`, `--job walltime=...`
|
|
74
|
+
for resources, `runo sweep`) and rename or regroup with `runo mv`. Variations are
|
|
75
|
+
runs; a new case is for a different structure of the base input.
|
|
76
76
|
- **Leave the reason behind.** Give each new run `--why`. When you cancel runs to fix or
|
|
77
77
|
replace them, add `runo cancel --why`, so later sessions and `runo retry` leave them
|
|
78
78
|
stopped. When a new series replaces an old one, ask whether the old one is still
|
|
@@ -86,6 +86,10 @@ Notes live in three places, where `runo context` and the next session find them:
|
|
|
86
86
|
- reusable knowledge (formulas, methods, pitfalls): `notes/<topic>.md`, extending the
|
|
87
87
|
existing file for a topic.
|
|
88
88
|
|
|
89
|
+
Analysis tools the simulator package does not bring go in `.runops/analysis-env/`
|
|
90
|
+
(`uv venv .runops/analysis-env`), so `.venv` stays as `runo update` manages it. Run heavy
|
|
91
|
+
reading and plotting where the site allows compute jobs.
|
|
92
|
+
|
|
89
93
|
## Skills
|
|
90
94
|
|
|
91
95
|
{skill_lines}
|
|
@@ -27,7 +27,7 @@ ACTIVE_STATES = frozenset({RunState.SUBMITTED, RunState.RUNNING})
|
|
|
27
27
|
TERMINAL_STATES = frozenset({RunState.COMPLETED, RunState.FAILED, RunState.CANCELLED})
|
|
28
28
|
|
|
29
29
|
_TRANSITIONS: dict[RunState, frozenset[RunState]] = {
|
|
30
|
-
RunState.CREATED: frozenset({RunState.SUBMITTED}),
|
|
30
|
+
RunState.CREATED: frozenset({RunState.SUBMITTED, RunState.ARCHIVED}),
|
|
31
31
|
RunState.SUBMITTED: frozenset(
|
|
32
32
|
{RunState.RUNNING, RunState.COMPLETED, RunState.FAILED, RunState.CANCELLED}
|
|
33
33
|
),
|
|
@@ -37,10 +37,24 @@ _TRANSITIONS: dict[RunState, frozenset[RunState]] = {
|
|
|
37
37
|
RunState.COMPLETED: frozenset({RunState.ARCHIVED}),
|
|
38
38
|
RunState.FAILED: frozenset({RunState.CREATED, RunState.ARCHIVED}),
|
|
39
39
|
RunState.CANCELLED: frozenset({RunState.CREATED, RunState.ARCHIVED}),
|
|
40
|
-
RunState.ARCHIVED: frozenset(TERMINAL_STATES),
|
|
40
|
+
RunState.ARCHIVED: frozenset({*TERMINAL_STATES, RunState.CREATED}),
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
|
|
44
|
+
# Shown with each run so resource differences are visible next to parameter differences.
|
|
45
|
+
_RESOURCE_KEYS = (
|
|
46
|
+
"partition",
|
|
47
|
+
"qos",
|
|
48
|
+
"walltime",
|
|
49
|
+
"nodes",
|
|
50
|
+
"ntasks",
|
|
51
|
+
"cpus_per_task",
|
|
52
|
+
"processes",
|
|
53
|
+
"threads",
|
|
54
|
+
"cores",
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
44
58
|
class Manifest:
|
|
45
59
|
"""A run manifest backed by a plain dict so unknown fields survive."""
|
|
46
60
|
|
|
@@ -198,6 +212,8 @@ class Manifest:
|
|
|
198
212
|
# Enough for an agent to compare runs without another command.
|
|
199
213
|
"params": self.params,
|
|
200
214
|
"changed_keys": list(self.section("variation").get("changed_keys", [])),
|
|
215
|
+
"job_changed_keys": list(self.section("variation").get("job_changed_keys", [])),
|
|
216
|
+
"resources": {k: self.job[k] for k in _RESOURCE_KEYS if k in self.job},
|
|
201
217
|
"parent_run": self.parent_run,
|
|
202
218
|
"superseded_by": self.superseded_by,
|
|
203
219
|
"exe_hash": str(self.section("simulator").get("exe_hash", "")),
|