runops 2.5.0__tar.gz → 2.7.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.
Files changed (142) hide show
  1. {runops-2.5.0 → runops-2.7.0}/PKG-INFO +6 -1
  2. {runops-2.5.0 → runops-2.7.0}/README.md +5 -0
  3. {runops-2.5.0 → runops-2.7.0}/SPEC.md +59 -14
  4. {runops-2.5.0 → runops-2.7.0}/docs/guide.md +46 -4
  5. {runops-2.5.0 → runops-2.7.0}/docs/reference.md +19 -6
  6. {runops-2.5.0 → runops-2.7.0}/pyproject.toml +1 -1
  7. {runops-2.5.0 → runops-2.7.0}/src/runops/__init__.py +1 -1
  8. {runops-2.5.0 → runops-2.7.0}/src/runops/cli.py +51 -30
  9. {runops-2.5.0 → runops-2.7.0}/src/runops/harness.py +16 -4
  10. {runops-2.5.0 → runops-2.7.0}/src/runops/manifest.py +11 -3
  11. runops-2.7.0/src/runops/ops/analysis.py +113 -0
  12. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/common.py +2 -0
  13. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/create.py +1 -3
  14. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/notes.py +8 -6
  15. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/progress.py +16 -0
  16. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/setup.py +3 -0
  17. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/tidy.py +80 -3
  18. {runops-2.5.0 → runops-2.7.0}/src/runops/project.py +9 -3
  19. runops-2.7.0/src/runops/skills/runops-analyze/SKILL.md +40 -0
  20. {runops-2.5.0 → runops-2.7.0}/src/runops/skills/runops-check-status/SKILL.md +4 -0
  21. {runops-2.5.0 → runops-2.7.0}/src/runops/skills/runops-reference/SKILL.md +6 -4
  22. {runops-2.5.0 → runops-2.7.0}/src/runops/skills/runops-tidy/SKILL.md +4 -0
  23. runops-2.7.0/tests/test_analysis_layout.py +311 -0
  24. {runops-2.5.0 → runops-2.7.0}/tests/test_progress.py +50 -0
  25. {runops-2.5.0 → runops-2.7.0}/tests/test_skills.py +1 -0
  26. {runops-2.5.0 → runops-2.7.0}/uv.lock +1 -1
  27. {runops-2.5.0 → runops-2.7.0}/.agents/skills/check/SKILL.md +0 -0
  28. {runops-2.5.0 → runops-2.7.0}/.agents/skills/improve-harness/SKILL.md +0 -0
  29. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/SKILL.md +0 -0
  30. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/agents/openai.yaml +0 -0
  31. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/examples/refactor-request.md +0 -0
  32. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/references/anti-patterns.md +0 -0
  33. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/references/api-compatibility.md +0 -0
  34. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/references/packaging-modernization.md +0 -0
  35. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/references/refactor-playbook.md +0 -0
  36. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/references/verification.md +0 -0
  37. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/scripts/api_surface_snapshot.py +0 -0
  38. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/scripts/import_smoke.py +0 -0
  39. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/scripts/inspect_python_package.py +0 -0
  40. {runops-2.5.0 → runops-2.7.0}/.agents/skills/python-package-refactor/scripts/refactor_quality_gate.py +0 -0
  41. {runops-2.5.0 → runops-2.7.0}/.agents/skills/release/SKILL.md +0 -0
  42. {runops-2.5.0 → runops-2.7.0}/.agents/skills/simulator-adapter/SKILL.md +0 -0
  43. {runops-2.5.0 → runops-2.7.0}/.agents/skills/spec-reviewer/SKILL.md +0 -0
  44. {runops-2.5.0 → runops-2.7.0}/.agents/skills/sync-docs/SKILL.md +0 -0
  45. {runops-2.5.0 → runops-2.7.0}/.claude/agents/spec-reviewer.md +0 -0
  46. {runops-2.5.0 → runops-2.7.0}/.claude/rules/architecture.md +0 -0
  47. {runops-2.5.0 → runops-2.7.0}/.claude/rules/dev-workflow.md +0 -0
  48. {runops-2.5.0 → runops-2.7.0}/.claude/rules/gotchas.md +0 -0
  49. {runops-2.5.0 → runops-2.7.0}/.claude/settings.json +0 -0
  50. {runops-2.5.0 → runops-2.7.0}/.claude/skills/check/SKILL.md +0 -0
  51. {runops-2.5.0 → runops-2.7.0}/.claude/skills/improve-harness/SKILL.md +0 -0
  52. {runops-2.5.0 → runops-2.7.0}/.claude/skills/python-package-refactor/SKILL.md +0 -0
  53. {runops-2.5.0 → runops-2.7.0}/.claude/skills/release/SKILL.md +0 -0
  54. {runops-2.5.0 → runops-2.7.0}/.claude/skills/simulator-adapter/SKILL.md +0 -0
  55. {runops-2.5.0 → runops-2.7.0}/.claude/skills/spec-reviewer/SKILL.md +0 -0
  56. {runops-2.5.0 → runops-2.7.0}/.claude/skills/sync-docs/SKILL.md +0 -0
  57. {runops-2.5.0 → runops-2.7.0}/.codex/README.md +0 -0
  58. {runops-2.5.0 → runops-2.7.0}/.codex/agents/spec-reviewer.toml +0 -0
  59. {runops-2.5.0 → runops-2.7.0}/.codex/config.toml +0 -0
  60. {runops-2.5.0 → runops-2.7.0}/.codex/rules/runops.rules +0 -0
  61. {runops-2.5.0 → runops-2.7.0}/.dockerignore +0 -0
  62. {runops-2.5.0 → runops-2.7.0}/.gitattributes +0 -0
  63. {runops-2.5.0 → runops-2.7.0}/.github/assets/conversation-dark.svg +0 -0
  64. {runops-2.5.0 → runops-2.7.0}/.github/assets/conversation-light.svg +0 -0
  65. {runops-2.5.0 → runops-2.7.0}/.github/assets/hero-dark.svg +0 -0
  66. {runops-2.5.0 → runops-2.7.0}/.github/assets/hero-light.svg +0 -0
  67. {runops-2.5.0 → runops-2.7.0}/.github/assets/make_readme_art.py +0 -0
  68. {runops-2.5.0 → runops-2.7.0}/.github/workflows/ci.yml +0 -0
  69. {runops-2.5.0 → runops-2.7.0}/.github/workflows/publish.yml +0 -0
  70. {runops-2.5.0 → runops-2.7.0}/.gitignore +0 -0
  71. {runops-2.5.0 → runops-2.7.0}/.harness/manifest.toml +0 -0
  72. {runops-2.5.0 → runops-2.7.0}/.pre-commit-config.yaml +0 -0
  73. {runops-2.5.0 → runops-2.7.0}/.vscode/settings.json +0 -0
  74. {runops-2.5.0 → runops-2.7.0}/AGENTS.md +0 -0
  75. {runops-2.5.0 → runops-2.7.0}/CLAUDE.md +0 -0
  76. {runops-2.5.0 → runops-2.7.0}/LICENSE +0 -0
  77. {runops-2.5.0 → runops-2.7.0}/docs/adapter.md +0 -0
  78. {runops-2.5.0 → runops-2.7.0}/src/runops/adapters/__init__.py +0 -0
  79. {runops-2.5.0 → runops-2.7.0}/src/runops/adapters/base.py +0 -0
  80. {runops-2.5.0 → runops-2.7.0}/src/runops/adapters/generic.py +0 -0
  81. {runops-2.5.0 → runops-2.7.0}/src/runops/adapters/registry.py +0 -0
  82. {runops-2.5.0 → runops-2.7.0}/src/runops/case.py +0 -0
  83. {runops-2.5.0 → runops-2.7.0}/src/runops/config.py +0 -0
  84. {runops-2.5.0 → runops-2.7.0}/src/runops/errors.py +0 -0
  85. {runops-2.5.0 → runops-2.7.0}/src/runops/jobgen.py +0 -0
  86. {runops-2.5.0 → runops-2.7.0}/src/runops/launchers.py +0 -0
  87. {runops-2.5.0 → runops-2.7.0}/src/runops/legacy_adapters/__init__.py +0 -0
  88. {runops-2.5.0 → runops-2.7.0}/src/runops/legacy_adapters/beach.py +0 -0
  89. {runops-2.5.0 → runops-2.7.0}/src/runops/legacy_adapters/emses.py +0 -0
  90. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/__init__.py +0 -0
  91. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/listing.py +0 -0
  92. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/migrate.py +0 -0
  93. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/skills.py +0 -0
  94. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/submit.py +0 -0
  95. {runops-2.5.0 → runops-2.7.0}/src/runops/ops/sync.py +0 -0
  96. {runops-2.5.0 → runops-2.7.0}/src/runops/runtime.py +0 -0
  97. {runops-2.5.0 → runops-2.7.0}/src/runops/skills/runops-create-run/SKILL.md +0 -0
  98. {runops-2.5.0 → runops-2.7.0}/src/runops/skills/runops-maintain/SKILL.md +0 -0
  99. {runops-2.5.0 → runops-2.7.0}/src/runops/slurm.py +0 -0
  100. {runops-2.5.0 → runops-2.7.0}/src/runops/util.py +0 -0
  101. {runops-2.5.0 → runops-2.7.0}/tests/__init__.py +0 -0
  102. {runops-2.5.0 → runops-2.7.0}/tests/conftest.py +0 -0
  103. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/.codex/rules/a.md +0 -0
  104. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/.runops/test-runs/T20260701-0001/receipt.toml +0 -0
  105. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/.runops/work/beach-x-abc/bin/beach +0 -0
  106. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/AGENTS.md +0 -0
  107. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/CLAUDE.md +0 -0
  108. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/cases/beach/plane/beach.toml +0 -0
  109. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/cases/beach/plane/case.toml +0 -0
  110. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/cases/old/beach.toml +0 -0
  111. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/cases/old/case.toml +0 -0
  112. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/launchers.toml +0 -0
  113. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/research/CURRENT.md +0 -0
  114. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runops.toml +0 -0
  115. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runs/AGENTS.md +0 -0
  116. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runs/_archive/plane/R20260601-0001/manifest.toml +0 -0
  117. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/input/beach.toml +0 -0
  118. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runs/plane/R20260701-0001--a/manifest.toml +0 -0
  119. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runs/plane/R20260702-0001/manifest.toml +0 -0
  120. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runs/scan/R20260701-0002/manifest.toml +0 -0
  121. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/runs/scan/survey.toml +0 -0
  122. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/simulators.toml +0 -0
  123. {runops-2.5.0 → runops-2.7.0}/tests/fixtures/v1-project/site.toml +0 -0
  124. {runops-2.5.0 → runops-2.7.0}/tests/golden/mpi_standard.sh +0 -0
  125. {runops-2.5.0 → runops-2.7.0}/tests/golden/openmp_rsc.sh +0 -0
  126. {runops-2.5.0 → runops-2.7.0}/tests/test_adapters.py +0 -0
  127. {runops-2.5.0 → runops-2.7.0}/tests/test_archive_series.py +0 -0
  128. {runops-2.5.0 → runops-2.7.0}/tests/test_build.py +0 -0
  129. {runops-2.5.0 → runops-2.7.0}/tests/test_dev_harness.py +0 -0
  130. {runops-2.5.0 → runops-2.7.0}/tests/test_jobgen.py +0 -0
  131. {runops-2.5.0 → runops-2.7.0}/tests/test_legacy_adapters.py +0 -0
  132. {runops-2.5.0 → runops-2.7.0}/tests/test_lifecycle.py +0 -0
  133. {runops-2.5.0 → runops-2.7.0}/tests/test_manifest_config.py +0 -0
  134. {runops-2.5.0 → runops-2.7.0}/tests/test_migrate.py +0 -0
  135. {runops-2.5.0 → runops-2.7.0}/tests/test_mv_naming.py +0 -0
  136. {runops-2.5.0 → runops-2.7.0}/tests/test_project_scope.py +0 -0
  137. {runops-2.5.0 → runops-2.7.0}/tests/test_run_job.py +0 -0
  138. {runops-2.5.0 → runops-2.7.0}/tests/test_safety.py +0 -0
  139. {runops-2.5.0 → runops-2.7.0}/tests/test_scratch_tidy.py +0 -0
  140. {runops-2.5.0 → runops-2.7.0}/tests/test_setup_misc.py +0 -0
  141. {runops-2.5.0 → runops-2.7.0}/tests/test_sweep.py +0 -0
  142. {runops-2.5.0 → runops-2.7.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.5.0
3
+ Version: 2.7.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
@@ -76,6 +76,7 @@ runo init
76
76
  | 資源を変えて作り直す | R20261015-0003 を 256 並列・72 時間で作り直して(case はそのまま) |
77
77
  | 片付ける | 古い run を整理して。消すものと archive するものを先に見せて |
78
78
  | 作り直した旧系列を片付ける | 旧系列は新系列で置き換えたので、対応を記録して archive して |
79
+ | 解析する | この系列の速度を比較して。図とスクリプト、調査メモを系列の `_analysis/` にまとめて |
79
80
  | simulator を足す | パッチを当てた ~/BEACH-dev をビルドして、別の simulator として登録して |
80
81
  | 更新する | runops と simulator を最新にして |
81
82
  | v1 から移る | この project を runops v2 に移行して(`runo migrate-v1` を dry-run で確認してから) |
@@ -100,15 +101,19 @@ lunar-sheath/
100
101
  │ │ │ ├── work/ Slurm のログと出力
101
102
  │ │ │ └── analysis/
102
103
  │ │ └── wc-phiz-scan/ sweep はまとめて 1 階層
104
+ │ │ └── _analysis/ 系列・sweep の比較調査(topic ごと)
103
105
  │ ├── _scratch/ smoke / debug(期限付き)
104
106
  │ └── _archive/ archive した run
105
107
  ├── LOG.md run をまたぐ結論と次の判断
106
108
  ├── notes/ 再利用する知見
109
+ ├── analysis/ 複数系列にまたがる調査。共通コードは lib/
107
110
  └── AGENTS.md, .agents/skills/, .claude/skills/
108
111
  ```
109
112
 
110
113
  run は ID が変わらないので、directory を移動してもエージェントは見つけられます。
111
114
  `work/` は Git の対象外で、`manifest.toml`、`input/`、`job.sh` は Git で追えます。
115
+ 解析は依存する run の範囲で置き場所を選び、調査ごとに `README.md`、`scripts/`、`out/`、
116
+ `logs/` をまとめます。判断は `LOG.md` からリンクし、古い解析 job の log は整理候補になります。
112
117
 
113
118
  ## 対応 simulator
114
119
 
@@ -45,6 +45,7 @@ runo init
45
45
  | 資源を変えて作り直す | R20261015-0003 を 256 並列・72 時間で作り直して(case はそのまま) |
46
46
  | 片付ける | 古い run を整理して。消すものと archive するものを先に見せて |
47
47
  | 作り直した旧系列を片付ける | 旧系列は新系列で置き換えたので、対応を記録して archive して |
48
+ | 解析する | この系列の速度を比較して。図とスクリプト、調査メモを系列の `_analysis/` にまとめて |
48
49
  | simulator を足す | パッチを当てた ~/BEACH-dev をビルドして、別の simulator として登録して |
49
50
  | 更新する | runops と simulator を最新にして |
50
51
  | v1 から移る | この project を runops v2 に移行して(`runo migrate-v1` を dry-run で確認してから) |
@@ -69,15 +70,19 @@ lunar-sheath/
69
70
  │ │ │ ├── work/ Slurm のログと出力
70
71
  │ │ │ └── analysis/
71
72
  │ │ └── wc-phiz-scan/ sweep はまとめて 1 階層
73
+ │ │ └── _analysis/ 系列・sweep の比較調査(topic ごと)
72
74
  │ ├── _scratch/ smoke / debug(期限付き)
73
75
  │ └── _archive/ archive した run
74
76
  ├── LOG.md run をまたぐ結論と次の判断
75
77
  ├── notes/ 再利用する知見
78
+ ├── analysis/ 複数系列にまたがる調査。共通コードは lib/
76
79
  └── AGENTS.md, .agents/skills/, .claude/skills/
77
80
  ```
78
81
 
79
82
  run は ID が変わらないので、directory を移動してもエージェントは見つけられます。
80
83
  `work/` は Git の対象外で、`manifest.toml`、`input/`、`job.sh` は Git で追えます。
84
+ 解析は依存する run の範囲で置き場所を選び、調査ごとに `README.md`、`scripts/`、`out/`、
85
+ `logs/` をまとめます。判断は `LOG.md` からリンクし、古い解析 job の log は整理候補になります。
81
86
 
82
87
  ## 対応 simulator
83
88
 
@@ -119,12 +119,14 @@ v1 で運用した 2 つの project(`project_dust_release`: BEACH、run 120、
119
119
  <run_id>--<label>/ # 単発 run
120
120
  <sweep_label>/
121
121
  <run_id>--<label>/ # sweep で生成された run
122
+ _analysis/<topic>/ # この group に依存する調査(§9.1)
122
123
  _scratch/
123
124
  <run_id>--<label>/ # scratch run
124
125
  _archive/
125
126
  <case_name>/... # archive された run(元の相対 path を保つ)
126
127
  adapters/ # 任意。project ローカル adapter `<name>.py`(§8.1)
127
128
  notes/ # 永続的な知見。1 topic 1 file。runops は管理しない(§9)
129
+ analysis/<topic>/ # 複数 group にまたがる調査。共通コードは analysis/lib/(§9.1)
128
130
  LOG.md # project 全体の追記専用ログ(§9)
129
131
  AGENTS.md # agent 向け入口(§10)
130
132
  CLAUDE.md # `@AGENTS.md` のみ
@@ -147,7 +149,8 @@ v1 で運用した 2 つの project(`project_dust_release`: BEACH、run 120、
147
149
  `runs/` 配下の任意の場所に移動してよい。runops は manifest の `run.id` で run を
148
150
  見つける(§7.2)。
149
151
  - 既定の配置先は `runs/<case_name>/`。`--dest` で上書きできる。`_` で始まる
150
- directory(`_scratch`、`_archive`)は runops が予約する。
152
+ directory(`_scratch`、`_archive`、`_analysis`)は runops が予約する。
153
+ `_analysis` は group の調査用で、run の発見・作成先から除く。
151
154
  - run directory の中(自身と子孫)には run を置けない。`--dest` が既存 run の中なら拒否し、
152
155
  `delete` は中に別の run があれば拒否する(§7.2 の走査は manifest のある directory で止まる)。
153
156
  - run id は `R<YYYYMMDD>-<NNNN>`。その日の既存 run と `.runops/last_run_id` の大きい方に 1 を足す。
@@ -160,7 +163,8 @@ v1 で運用した 2 つの project(`project_dust_release`: BEACH、run 120、
160
163
  - `materials/`、`refs/`、`scripts/` などユーザーが置く directory は runops の関知外。
161
164
  runops は `runs/`、`cases/`、`.runops/`、`.agents/skills/`、`.claude/skills/`、
162
165
  `AGENTS.md`、`CLAUDE.md`、`runops.toml` 以外を作成・変更しない(`LOG.md`、`notes/`
163
- は `init` の雛形生成のみ)。
166
+ は `init` の雛形生成のみ)。例外として `tidy --apply --only analysis-logs` は
167
+ §7.5 の解析 log を削除できる。解析のスクリプト・結果・README は agent が書く。
164
168
 
165
169
  ---
166
170
 
@@ -471,8 +475,12 @@ adapter が `progress(run_dir)` を持つとき、`sync` は running の run と
471
475
  上限 1000 件程度)。queue を離れたときの観測は終了時刻で記録し、終了後の待ち時間を速度に含めない。
472
476
  - 要約を `[[job.attempts]].progress` に置く: `done`、`total`、`unit`、`start`、`fraction`、
473
477
  `rate_recent_per_hour`(直近 10 分)、`rate_attempt_per_hour`(program 開始から。起点は
474
- `start`)、`eta_hours`(running のみ)。program 開始時刻は job.sh が stdout に出す
478
+ `start`)、`eta_hours`(running のみ)、`last_change_at`(`done` が最後に増えた観測時刻)、
479
+ `stalled_hours`(最新観測が `last_change_at` から 1 時間以上進んでいないとき、その長さ。
480
+ 終了後の観測には付けない)。program 開始時刻は job.sh が stdout に出す
475
481
  `runops: start` 行から取り、`started_at` に入れる。
482
+ - `status` / `list` は `stalled_hours` があれば `stalled 6.7h` と表示する。観測は `sync` のときだけ
483
+ なので、停止の検出は `sync` の間隔より細かくならない。
476
484
  - `status` / `list` は要約だけを読む(file を開かない)。read-only command は観測を足さない。
477
485
  - 解析は adapter が行い、runops は保存と表示だけを持つ(§1.2 #4)。
478
486
 
@@ -493,7 +501,8 @@ adapter が `progress(run_dir)` を持つとき、`sync` は running の run と
493
501
  `--json` は作成した run の同じ summary を返す。summary には `params`、`changed_keys`、
494
502
  `parent_run`、`exe_hash`、`input_hash` を含め、run 同士の比較に別 command を要らなくする。
495
503
  walltime の根拠として `walltime`、最新 attempt の `elapsed`、`walltime_used`(elapsed / walltime)、
496
- `slurm_state`(`TIMEOUT` など)も含める。
504
+ `slurm_state`(`TIMEOUT` など)も含める。`list` / `status` の `--brief` は summary から
505
+ `params`、`why`、`last_note` を外す(多数の run を横断して渡すとき用。表では note / why 列を省く)。
497
506
  - 破壊的 command(`cancel`、`delete`、`tidy --apply`、`archive`)は対象を表示して
498
507
  確認を取る。`-y/--yes` で省略。
499
508
  - exit code: 0 成功、1 対象 run の実行時エラー(例: sbatch 失敗)、2 使用方法・設定エラー。
@@ -503,7 +512,7 @@ adapter が `progress(run_dir)` を持つとき、`sync` は running の run と
503
512
  - `runs/` 配下(`_archive/` を含む)を再帰的に走査し `manifest.toml` を集める。
504
513
  `.runops/index.toml` に `run_id -> run_dir` の cache を持ち、manifest が cache の
505
514
  path に無ければ再走査して cache を更新する。cache は常に再生成可能で、
506
- 壊れていても動作に影響しない。
515
+ 壊れていても動作に影響しない。`_analysis/` は走査せず、そこにある manifest は run と扱わない。
507
516
  - 走査対象は `runs/` のみ。project 外や `runs/` 外の run は扱わない。
508
517
 
509
518
  ### 7.3 command 一覧
@@ -514,7 +523,7 @@ adapter が `progress(run_dir)` を持つとき、`sync` は running の run と
514
523
  | `runo doctor [--json]` | `runops.toml` の妥当性、adapter の import 可否、executable の存在、`sbatch` の有無、skill の鮮度を検査 |
515
524
  | `runo update [--skills-only] [-y]` | adapter package を upgrade し、skill を再展開する |
516
525
  | `runo build SIM [--force]` | `local_source` simulator を `.runops/build/` に build する(§4.1) |
517
- | `runo context [--json]` | agent 向け現在地。project 名、simulator、active run 数、直近 run 5 件、scratch 期限切れ数、`LOG.md` 末尾 20 行。配置した runops skill が install 済みの runops より古ければ `runo update --skills-only` を促す warning |
526
+ | `runo context [--json]` | agent 向け現在地。project 名、simulator、active run 数と全 active run(`--brief` と同じ summary、`active`)、直近 run 5 件、scratch 期限切れ数、`LOG.md` 末尾 20 行、現用 group の解析 topic 名(§9.1)。配置した runops skill が install 済みの runops より古ければ `runo update --skills-only` を促す warning |
518
527
  | `runo case new NAME --simulator SIM [--from CASE]` | case をスキャフォールド。adapter の `case_template()` を展開 |
519
528
  | `runo case list` | case 一覧 |
520
529
  | `runo new CASE [--why TEXT] [--label L] [--set k=v]... [--job k=v]... [--tag T]... [--scratch] [--dest DIR] [--submit] [--created-by WHO] [--json]` | 単発 run を作る |
@@ -524,10 +533,10 @@ adapter が `progress(run_dir)` を持つとき、`sync` は running の run と
524
533
  | `runo submit --all [DIR] [-y]` | DIR 配下の `created` run をすべて投入。件数と概算 core-hours を表示して確認 |
525
534
  | `runo cancel RUN... [--why TEXT] [-y]` | scancel して sync。`--why` は `cancelled: ...` の note として残す |
526
535
  | `runo retry RUN [--submit] [-y]` | `failed`/`cancelled` を `created` に戻す。直近の cancel に `--why` があれば理由を示して確認を取る |
527
- | `runo status [RUN...] [--json]` | manifest の状態を表示。Slurm を呼ばない |
536
+ | `runo status [RUN...] [--brief] [--json]` | manifest の状態を表示。Slurm を呼ばない。`--brief` は §7.1 |
528
537
  | `runo sync [RUN...] [--json]` | squeue/sacct で状態を更新。引数なしは `submitted`/`running` 全件 |
529
538
  | `runo log RUN [--err] [-n N] [-f]` | 最新 job の stdout/stderr を表示。`-f` は tail -f |
530
- | `runo list [PATH] [--all] [--since 30d] [--case C] [--sweep L] [--status S]... [--tag T]... [--scratch] [--archived] [--json]` | run 一覧(§7.4) |
539
+ | `runo list [PATH] [--all] [--since 30d] [--case C] [--sweep L] [--status S]... [--tag T]... [--scratch] [--archived] [--brief] [--json]` | run 一覧(§7.4) |
531
540
  | `runo note RUN [TEXT] [--by WHO]` | TEXT があれば `[[notes]]` に追記、なければ notes と `notes.md` を表示 |
532
541
  | `runo pin RUN [--off] [--reason TEXT]` | `run.pinned` を切り替える。pinned は `list` に常に出て、`tidy` / `delete` の対象外 |
533
542
  | `runo summarize RUN... [--force]` | adapter の `summarize()` を呼び `analysis/summary.json` を書く。既存があれば `--force` なしではスキップ |
@@ -546,13 +555,14 @@ adapter が `progress(run_dir)` を持つとき、`sync` は running の run と
546
555
  - 既定で表示しない: scratch run、archived run、上記以外の古い terminal run。
547
556
  - `--all` は scratch と archived 以外をすべて表示。`--scratch`、`--archived` は
548
557
  それぞれを **追加** する。
549
- - 表示列: id、status、label、case、sweep、created(相対時刻)、最新 note の先頭 60 文字、`why`。
558
+ - 表示列: id、status、label、case、sweep、created(相対時刻)、elapsed、progress、最新 note の先頭 60 文字、`why`。
559
+ `--brief` は note と `why` の列を省く。
550
560
  - sort は `created_at` 降順。
551
561
  - `PATH` を与えた場合はその directory 配下に限定する。
552
562
 
553
563
  ### 7.5 `tidy` の規則
554
564
 
555
- `tidy` は候補を 4 種に分けて表示する。`--apply` はすべてを実行し、
565
+ `tidy` は候補を 6 種に分けて表示する。`--apply` はすべてを実行し、
556
566
  `--apply --only KIND` で絞れる。
557
567
 
558
568
  | kind | 条件 | 動作 |
@@ -562,8 +572,14 @@ adapter が `progress(run_dir)` を持つとき、`sync` は running の run と
562
572
  | `old-completed` | `completed` で `defaults.tidy_archive_days`(既定 90)日より古く、`pinned = false` | archive |
563
573
  | `orphan-job` | `submitted`/`running` だが squeue/sacct に job が無い | sync(state 確定) |
564
574
  | `unused-build` | `.runops/build/*` のうち、非 archived run の manifest から参照されないもの | delete |
575
+ | `analysis-logs` | §9.1 の解析 directory 配下の `logs/` にある `*.<jobid>.out/err`。組の最終更新が `tidy_failed_days` より古く、同じ log directory・job 名の最新の組ではない | log file のみ delete |
565
576
 
566
577
  - `--older-than AGE`(例 `14d`)はその回だけ、上の 2 つの日数をどちらもこの値で置き換える。
578
+ - `analysis-logs` は project の `analysis/`、group の `_analysis/`、run の `analysis/`
579
+ (topic の下も含む)を対象にする。stdout/stderr は同じ job id の組として更新時刻を比較し、
580
+ 最新の組は常に残す。同時刻なら job id が大きい組を最新とする。片方だけの log も同じ扱い。
581
+ archived / pinned / submitted / running run の解析と、symlink は対象外。
582
+ 適用直前にも対象範囲・更新時刻・最新組を再確認し、条件が変われば削除せず止まる。
567
583
  - `--purge-work RUN...` は指定 run の `work/` の中身を削除し `work_purged_at` を記録する。
568
584
  adapter が `keep_patterns()` を返す場合、一致するファイルは残す。`completed` 以外は拒否。
569
585
  adapter を読み込めない(残すファイルを判定できない)ときは何も消さずにエラーにする。
@@ -710,7 +726,7 @@ runops は `init` と `update` で次を行う。
710
726
 
711
727
  ### 8.3 runops 自身の skill
712
728
 
713
- runops は次の 5 skill だけを同梱し、simulator skill と同じ仕組みで展開する。
729
+ runops は次の 6 skill だけを同梱し、simulator skill と同じ仕組みで展開する。
714
730
  利用者が覚えるコマンドは `runo init` だけとし、それ以外(simulator の追加、更新、v1 移行を含む)は
715
731
  エージェントへの依頼で行う。
716
732
  各 SKILL.md は 60 行以内に収める。
@@ -720,6 +736,7 @@ runops は次の 5 skill だけを同梱し、simulator skill と同じ仕組み
720
736
  | `runops-create-run` | `new` / `sweep` の使い方。`--why` を必ず書く、scratch の使い分け |
721
737
  | `runops-check-status` | `sync` → `list` → `log` の順。失敗時の `retry` と `tidy --purge-work` |
722
738
  | `runops-tidy` | `tidy` を定期的に実行する判断基準。pin の使い方 |
739
+ | `runops-analyze` | 依存する run の範囲で調査の置き場所を決め、script・結果・log・README をまとめる |
723
740
  | `runops-reference` | command 一覧(§7.3 の表をそのまま載せる) |
724
741
  | `runops-maintain` | simulator の追加、runops と simulator package の更新、`migrate-v1`、`doctor` の読み方 |
725
742
 
@@ -748,7 +765,7 @@ v1 の `emses` / `beach` adapter は次の順で runops 本体から出す。
748
765
  見つかると使われなくなる(解決順 1 が優先)ので、ユーザーが消せばよい。
749
766
  ---
750
767
 
751
- ## 9. ノート
768
+ ## 9. ノートと解析
752
769
 
753
770
  置き場所は 3 つに固定し、runops はどれも「管理」しない(schema、budget、rotation、
754
771
  seal、index を持たない)。
@@ -766,10 +783,33 @@ seal、index を持たない)。
766
783
  - 複数 run にまたがる結論と次の判断は `LOG.md` に日付付きで追記する
767
784
  - 再利用する式・手法・環境の落とし穴は `notes/<topic>.md` に書く。既存 file があれば追記し、
768
785
  似た topic の file を増やさない
769
- - それ以外の場所(`research/`、`analysis/*.md`、`runs/` 直下など)に note file を作らない
786
+ - 調査の詳細は §9.1 の README にまとめ、LOG.md からリンクする
770
787
  - v1 の `research/results/`、`.runops/insights/`、`research/archive/legacy/` は移行時に
771
788
  `notes/legacy/` 配下へ **移動せず** そのまま残し、`notes/README.md` から path を案内する(§11)。
772
789
 
790
+ ### 9.1 解析の置き場所
791
+
792
+ | 結果が依存する範囲 | 置き場所 |
793
+ |---|---|
794
+ | 1 run | `runs/.../<RUN>/analysis/`(必要なら `<topic>/`) |
795
+ | 1 group(同じ directory に並ぶ系列・sweep) | `runs/<group>/_analysis/<topic>/` |
796
+ | 複数 group・case または project 全体 | `analysis/<topic>/` |
797
+ | 共通コード | `analysis/lib/` |
798
+
799
+ - group は directory であり、新しい entity / ID / ledger を持たない。
800
+ - 調査 directory は `README.md`(問い・対象 run id・方法・結果・制約)、`scripts/`
801
+ (Python、sbatch)、`out/`(図・JSON・CSV)、`logs/`(job の stdout/stderr)からなる。
802
+ topic は内容で名付け、同じ問いは README に日付付きで追記する。
803
+ - `init` の `.gitignore` は `**/logs/`、`__pycache__/`、`.runops/analysis-env/` を含む。
804
+ 既存 project のファイルは自動で再配置しない。
805
+ - `context` は現用 group の `_analysis/*/README.md` がある topic 名を表示し、
806
+ `--json` では `group_analysis` に project 相対の group path → topic 名一覧を返す。
807
+ README 本文や `out/`、simulator の `work/` は読まない。
808
+ - `archive DIR` が group 配下の全 run を移す場合、その group と配下の group の
809
+ `_analysis/` も同じ相対 path の `runs/_archive/` に移す。run が残る group の解析は残す。
810
+ 確認表示に解析の移動先を含め、移動前に run と解析の全移動先を検査し、既存なら上書きしない。
811
+ `archive RUN...` / `--older-than` は group の解析を動かさず、`restore RUN` は run のみを戻す。
812
+
773
813
  ---
774
814
 
775
815
  ## 10. harness 生成
@@ -916,7 +956,7 @@ src/runops/
916
956
  | 項目 | 目標 |
917
957
  |---|---|
918
958
  | `src/runops/` の行数(skill の md を除く) | 8,000 行以下 |
919
- | CLI command 数(`case new` / `case list` を含む。同じ command の option 違いは数えない) | 25 以下。§7.3 の表の distinct command は 24 |
959
+ | CLI command 数(`case new` / `case list` を含む。同じ command の option 違いは数えない) | 25 以下。§7.3 の表の distinct command は 25 |
920
960
  | ユーザーが編集する TOML | `runops.toml`、`case.toml` の 2 種 |
921
961
  | runops が書く TOML | `manifest.toml`、`.runops/skills.lock`、`.runops/index.toml` の 3 種 |
922
962
  | 依存 | `typer`、`rich`、`tomli-w`、`tomlkit`、`jinja2`、`packaging`(`mcp` を外す) |
@@ -954,6 +994,9 @@ src/runops/
954
994
  9b. 同名 adapter が entry point と `adapters/X.py` の両方にあるとき entry point が優先される。
955
995
  どちらも無く `generic` でもなければ exit code 2 で案内が出る。
956
996
  10. `manifest.toml` の書き込み途中で例外が起きても元のファイルが壊れない。
997
+ 11. 解析 log の tidy は最新組・新しい組・対象外ファイルを残し、確認後の更新も削除しない。
998
+ group の archive は全 run が対象のときだけ解析も移し、衝突時は何も動かさない。
999
+ context は調査の名前だけを読み、解析内の manifest を run と誤認しない。
957
1000
 
958
1001
  ### 13.3 ドキュメント
959
1002
 
@@ -974,6 +1017,7 @@ v2 の文書は次の 4 ファイルだけにする。
974
1017
 
975
1018
  | 項目 | 決定 | 理由 |
976
1019
  |---|---|---|
1020
+ | 解析の配置 2.6.0(2026-10-23) | 依存する run の範囲に応じて run 内 `analysis/`、group の `_analysis/<topic>/`、project の `analysis/<topic>/` にまとめる。skill、log の tidy、group archive、context を対応させる(§9.1) | 報告・図・script・job log が分散・混在していた。group は既存の directory として扱い、新しい entity や command は足さない。既存ファイルの整理は移動一覧を提示してから行う |
977
1021
  | job の cwd | run_dir(v1 と同じ)。Slurm log は `work/` | v1 adapter の path 規約と実 project の job.sh をそのまま使える |
978
1022
  | adapter の追加 hook | `configure(sim_config)`、`job_prelude(run_dir)` | generic の `command` 取得と、EMSES の `rm -f *_0000.h5` など v1 の setup command を表す |
979
1023
  | launcher `kind = "none"` | MPI launcher を使わず program を直接実行 | OpenMP のみの BEACH run 用 |
@@ -1000,5 +1044,6 @@ v2 の文書は次の 4 ファイルだけにする。
1000
1044
  | 生成ハーネスの書き方(2026-10-21) | AGENTS.md の Rules を「作業の進め方」5 項目に絞り、各項目を「何をするか」と短い理由で書く。skill は手順に専念し、AGENTS.md の方針(manifest を手で編集しない等)を繰り返さない | #112・#113 で規則が増え、禁止形(never / do not)が並んでいた。現行の Claude の prompting 指針は、しないことより何をするかを、理由と共に書くことを勧め、強い禁止語は過剰反応を招くとしている |
1001
1045
  | 複数 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 で同じことが起き得た |
1002
1046
  | 進捗と系列配置 2.5.0(2026-10-23) | adapter に任意の `progress()`、`sync` が時刻付き観測を保存し速度・残り時間を要約(§6.4)。`new --from` の既定置き場所を元 run と同じ directory に。agent context の `docs/` も展開 | 速度比較のために agent が時刻付き batch 観測を手で保存し、再開点や終了後の待ちを毎回補正していた(4 回)。継続世代が case 直下に並び、同じ計算の系列を見分けにくかった。skill が package の `docs/` を参照していたが展開されていなかった。進捗の解析は simulator 固有なので adapter が持ち、runops は保存と表示だけにする |
1047
+ | 横断の状態確認 2.7.0(2026-10-30) | `list` / `status` に `--brief`、`context` に全 active run の `active`、progress 要約に `last_change_at` と `stalled_hours`(§6.4、§7.1、§7.3) | hub で 4 project の active run を集めるのに `recent_runs` 5 件では足りず、全 `params` 付きの `list --json` は shell 引数長を超えた(3 回)。進捗が 6 時間止まった run を 49% と報告した。新しい command は足さず、既存の summary を小さくし、観測から止まりを出す |
1003
1048
  | 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 数は増やさない |
1004
1049
  | 使い方ガイド | `docs/guide.md` を追加し、文書を 4 ファイルにする(§13.3) | README の依頼例の表と command 一覧の間を埋める、場面ごとの流れ(case と run の分け方、smoke から本計算、状態の決まり方、整理)の置き場所が無かった |
@@ -134,7 +134,10 @@ walltime は、同じ case・同じ資源で終わった run の実時間を元
134
134
  adapter が進捗に対応していれば(BEACH の batch、MPIEMSES3D の step)、`sync` のたびに時刻付きの
135
135
  観測を残し、`runo list` / `status` に `62% 0.198 s/batch eta 3.1h` のように進み具合、直近 10 分の
136
136
  速度、残り時間を出します。resume した run は再開点から数え、終わった run は終了時刻までで速度を出すので、
137
- 版を切り替えた前後の比較にそのまま使えます(`--json` の `progress`)。
137
+ 版を切り替えた前後の比較にそのまま使えます(`--json` の `progress`)。`sync` を重ねても進みが 1 時間以上
138
+ 変わらない run には `stalled 6.7h` が付くので、Slurm では RUNNING でも止まっている run を見分けられます。
139
+ 全 project を横断して見るときは `runo context --json` の `active`(全 active run)か `runo list --brief --json`
140
+ (`params` や note を省いた小さい JSON)を使います。
138
141
 
139
142
  ### 状態の決まり方
140
143
 
@@ -192,7 +195,7 @@ project の run だけが対象です。同じアカウントの別 project の
192
195
  前の job が終わるのを待って投入したいときは、「R20261016-0005 が成功したら投げて」と頼めば
193
196
  `runo submit RUN --afterok <job_id>` になります。
194
197
 
195
- ## 6. 記録を残す
198
+ ## 6. 解析して記録を残す
196
199
 
197
200
  記録の置き場所は 3 つに決まっています。
198
201
 
@@ -202,10 +205,40 @@ project の run だけが対象です。同じアカウントの別 project の
202
205
  | 複数の run をまたぐ結論と次の判断 | `LOG.md` に日付見出しで追記 | 今日の scan の結論を LOG にまとめて |
203
206
  | 再利用する式、手法、落とし穴 | `notes/<topic>.md`(1 topic 1 file) | Debye 長の見積もり方を notes に残して |
204
207
 
205
- エージェントはこれ以外の場所(`research/`、`analysis/*.md`、`runs/` の下など)にノートを作りません。
206
208
  `notes/` に似た topic の file があれば、新しく作らずに追記します。
207
209
 
208
- `runo context` は `LOG.md` の末尾と `notes/` の file 名を表示します。エージェントは作業の始めに
210
+ 調査の詳細と図・スクリプトは、結果が依存する run の範囲に合わせてまとめます。
211
+
212
+ > この系列の速度を比較して。図とスクリプト、調査メモを系列の `_analysis/` にまとめて。
213
+
214
+ | 依存する範囲 | 置き場所 |
215
+ |---|---|
216
+ | 1 run | `<run_dir>/analysis/`(必要なら topic を切る) |
217
+ | 1 系列・sweep | `runs/<group>/_analysis/<topic>/` |
218
+ | 複数系列・case、project 全体 | `analysis/<topic>/` |
219
+ | 共通コードとそのテスト | `analysis/lib/` |
220
+
221
+ group は run が並ぶ directory です。登録や新しい ID は要りません。
222
+ topic は `speed-by-version` のように問いの内容で名付け、同じ問いをやり直すときは
223
+ 同じ directory の結果を更新し、README に日付付きで追記します。
224
+
225
+ ```text
226
+ <topic>/
227
+ README.md 問い・対象 run id と path・方法・結果・制約
228
+ scripts/ Python と sbatch
229
+ out/ 図、JSON、CSV、HTML
230
+ logs/ 解析 job の stdout/stderr(Git 管理外)
231
+ ```
232
+
233
+ エージェントは `runops-analyze` skill を使い、調査の README と、そこへのリンクを含む
234
+ `LOG.md` の判断を更新します。`notes/` は run に依存しない知見に使います。
235
+ 解析の追加依存は `.runops/analysis-env/` に置き、重い読み込みや作図はサイトの規則に従って
236
+ 計算ノードで実行します。sbatch は調査 directory から投入し、事前に `logs/` を作って
237
+ `#SBATCH -o logs/%x.%j.out` と `#SBATCH -e logs/%x.%j.err` を指定します。
238
+ 既存 project のファイルは自動で移動しません。整理を頼むと、移動前に一覧を提示します。
239
+
240
+ `runo context` は `LOG.md` の末尾、`notes/` の file 名、現用 group の
241
+ `_analysis/*/README.md` がある topic 名を表示します。エージェントは作業の始めに
209
242
  これを読むので、前回の続きから話を始められます。
210
243
 
211
244
  ## 7. 整理する
@@ -221,6 +254,7 @@ run は消さなくても `runo list` が古いものを隠しますが、direct
221
254
  | `expired-scratch` | 期限切れの scratch run(archive したものは除く) | 削除 |
222
255
  | `stale-failed` | 30 日より古い failed / cancelled で、`why` もノートも無い | 削除 |
223
256
  | `old-completed` | 90 日より古い completed | archive |
257
+ | `analysis-logs` | 30 日より古い解析 job の log(各 job 名の最新組は保持) | log のみ削除 |
224
258
  | `orphan-job` | 投入済みなのに Slurm が知らない | `sync` で状態を確定 |
225
259
  | `unused-build` | どの run も使っていない build cache | 削除 |
226
260
 
@@ -239,6 +273,14 @@ run id で引き続き見つかり、`runo restore` で戻せます。
239
273
  記録され、理由はノートに残ります。比較用に派生しただけの run は置き換えとは扱わないので、
240
274
  旧系列が不要かどうかは毎回確かめます。
241
275
 
276
+ directory 指定で group の全 run を archive すると、その `_analysis/` も同じ相対 path で
277
+ 移動します。run が残る group の解析は元の場所に残り、移動先に既存の解析があれば、run を
278
+ 動かす前に停止します。`archive RUN...` と `restore RUN` は run だけを移動します。
279
+
280
+ 解析 log だけの整理は「古い解析 log を整理して」で `runo tidy --only analysis-logs` に
281
+ なります。各 `logs/` の `*.<jobid>.out/err` を組で比較し、job 名ごとに最新の組を残します。
282
+ 片方が新しければ両方とも残し、README、script、結果は削除しません。
283
+
242
284
  論文の図に使う run や基準にする run は pin しておくと、`list` に常に表示され、`tidy` や `delete`
243
285
  の対象になりません。
244
286
 
@@ -6,7 +6,7 @@
6
6
  ## runops.toml
7
7
 
8
8
  project root にあり、`runo` はここを上方向に探して project を特定します。
9
- command はこの project の `runs/` 配下だけを扱い、別 project の run directory や
9
+ run を対象にする command はこの project の `runs/` 配下だけを扱い、別 project の run directory や
10
10
  directory を path で渡すとエラーになります。run id は project ごとなので、同じ id が
11
11
  別 project にあっても cwd の project の run を指します。
12
12
 
@@ -141,7 +141,8 @@ run id は削除後も再利用しません。`--dest` に既存 run の中は
141
141
  `list --json` と `status --json` は、walltime の根拠として `walltime`、最新 attempt の `elapsed`、
142
142
  `walltime_used`(elapsed / walltime)、`slurm_state` も返します。adapter が `progress()` を持てば
143
143
  `progress`(`done`、`total`、`unit`、`fraction`、`rate_recent_per_hour`、`rate_attempt_per_hour`、
144
- `eta_hours`)も返し、表の `progress` 列に `62% 0.198 s/batch eta 3.1h` と出します。表では `TIMEOUT` などを
144
+ `eta_hours`、`last_change_at`、`stalled_hours`)も返し、表の `progress` 列に `62% 0.198 s/batch eta 3.1h` と出します。
145
+ `sync` の観測で `done` が 1 時間以上増えていなければ `stalled 6.7h` が付きます(観測は `sync` のときだけ)。表では `TIMEOUT` などを
145
146
  `failed (TIMEOUT)` と示します。
146
147
 
147
148
  全 command は `--help` で option を確認できます。`RUN` には run id、run directory、
@@ -153,15 +154,15 @@ run id は削除後も再利用しません。`--dest` に既存 run の中は
153
154
  | `runo doctor [--json]` | 設定、adapter の解決経路、executable、sbatch、skill の鮮度とリンク切れを点検 |
154
155
  | `runo update [--skills-only] [-y]` | simulator package を更新し skill と AGENTS.md の管理 block を再生成 |
155
156
  | `runo build SIM [--force]` | `local_source` の simulator を build |
156
- | `runo context [--json]` | agent 向けの現在地 |
157
+ | `runo context [--json]` | agent 向けの現在地。全 active run(`active`、`--brief` と同じ形)、直近 5 run、現用 group の解析 topic 名(JSON の `group_analysis` は group path → topic 名一覧) |
157
158
  | `runo case new NAME [-s SIM] [--from CASE]` / `runo case list` | case の作成と一覧 |
158
159
  | `runo new CASE [--why] [--label] [--set k=v]... [--job k=v]... [--tag] [--scratch] [--dest DIR] [--submit] [--json]` | run を 1 つ作る。`--job` はこの run の `[job]` の値 |
159
160
  | `runo new --from RUN [--set k=v]... [--job k=v]... [--job-from-case] [--continue]` | 既存 run の params と `[job]` から派生(`--job-from-case` で `[job]` は case から)。置き場所の既定は元 run と同じ directory。`--continue` は adapter の restart 対応が必要 |
160
161
  | `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 の親 |
161
162
  | `runo submit RUN... [-p P] [--qos Q] [--afterok JOB]` / `runo submit --all [DIR] [-y]` | job.sh を生成して sbatch |
162
163
  | `runo cancel RUN... [--why TEXT] [-y]` / `runo retry RUN [--submit] [-y]` | scancel(`--why` は note に残る)/ failed・cancelled を created に戻す(理由付きで cancel された run は確認を取る) |
163
- | `runo status [RUN...]` / `runo sync [RUN...]` / `runo log RUN [--err] [-n N] [-f]` | 状態表示 / Slurm から更新 / log 表示 |
164
- | `runo list [PATH] [--all] [--since 30d] [--case] [--sweep] [--status] [--tag] [--scratch] [--archived] [--json]` | run 一覧 |
164
+ | `runo status [RUN...] [--brief]` / `runo sync [RUN...]` / `runo log RUN [--err] [-n N] [-f]` | 状態表示 / Slurm から更新 / log 表示 |
165
+ | `runo list [PATH] [--all] [--since 30d] [--case] [--sweep] [--status] [--tag] [--scratch] [--archived] [--brief] [--json]` | run 一覧。`--brief` は `params`、`why`、note を省く(表では note / why 列) |
165
166
  | `runo note RUN [TEXT]` / `runo pin RUN [--off] [--reason]` | ノート追記・表示 / pin |
166
167
  | `runo summarize RUN... [--force]` | adapter の要約を `analysis/summary.json` に書く |
167
168
  | `runo mv RUN... [--dest DIR] [--label L] [--why TEXT]` | `runs/` 配下で改名・移動。id、入力、sweep、ノートは保つ。submitted / running と archived は不可。全対象を検査してから移し、1 件でも不可なら何も動かさない |
@@ -173,7 +174,11 @@ run id は削除後も再利用しません。`--dest` に既存 run の中は
173
174
  `list` は既定で active、created、直近 `list_since_days` 日、pinned の run を表示し、
174
175
  scratch と archived は表示しません。
175
176
 
176
- `tidy` の候補は次の 5 種類です。
177
+ `archive DIR` が group 配下の全 run を移すときは、group の `_analysis/` も同じ相対 path で
178
+ 移動します。run が残る group の解析は残し、移動先が存在すれば全 run の移動前に拒否します。
179
+ `archive RUN...` / `--older-than` と `restore RUN` は group の解析を移動しません。
180
+
181
+ `tidy` の候補は次の 6 種類です。
177
182
 
178
183
  | kind | 条件 | 動作 |
179
184
  |---|---|---|
@@ -182,9 +187,17 @@ scratch と archived は表示しません。
182
187
  | `old-completed` | `tidy_archive_days`(既定 90)日より古い completed | archive |
183
188
  | `orphan-job` | Slurm が知らない submitted / running | sync |
184
189
  | `unused-build` | 非 archived run が参照しない `.runops/build/*` | delete |
190
+ | `analysis-logs` | `tidy_failed_days` より古い解析 log。各 log directory・job 名の最新組は保持 | log のみ delete |
185
191
 
186
192
  日数は `runops.toml` の `[defaults]` で変えられます。`--older-than 14d` のように指定すると、
187
193
  その回だけ両方の日数をその値で置き換えます。pinned の run は `tidy` と `delete` の対象になりません。
188
194
  `--purge-work` は adapter を読み込めないと何も消さずにエラーにします。
189
195
 
196
+ `analysis-logs` は project の `analysis/`、group の `_analysis/`、run の `analysis/` 配下の
197
+ `logs/*.<jobid>.out/err`(topic の下も含む)に限定します。組の新しい方の更新時刻で年齢を判定し、
198
+ 最新組を残します。同時刻なら job id が大きい組を最新とします。片方だけの log も同じ扱いです。
199
+ archived / pinned / submitted / running run の解析と symlink は対象外で、適用直前にも条件を
200
+ 確認します。`--json` の解析 log 候補は `files`(削除する project 相対 path)と `cutoff`
201
+ (ISO 日時)も持ちます。確認後に条件が変われば削除せず、再度 tidy を実行するよう案内します。
202
+
190
203
  exit code は 0 が成功、1 が実行時エラー(sbatch 失敗など)、2 が使い方や設定のエラーです。
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "runops"
7
- version = "2.5.0"
7
+ version = "2.7.0"
8
8
  description = "Run ledger and Slurm job submission for HPC simulation projects"
9
9
  readme = "README.md"
10
10
  license = "Apache-2.0"
@@ -1,3 +1,3 @@
1
1
  """runops: run ledger and Slurm job submission for HPC simulation projects."""
2
2
 
3
- __version__ = "2.5.0"
3
+ __version__ = "2.7.0"
@@ -257,12 +257,24 @@ def context(as_json: bool = typer.Option(False, "--json")) -> None:
257
257
  f"runs: {counts or 'none'}; active: {data['active_runs']}; "
258
258
  f"expired scratch: {data['expired_scratch']}"
259
259
  )
260
+ if data["active"]:
261
+ from runops.ops.progress import format_progress
262
+
263
+ out.print("active:")
264
+ for run in data["active"]:
265
+ out.print(
266
+ f" {run['id']} {run['status']:9} {run['case']} {run['label']} "
267
+ f"{format_progress(run['progress'])}"
268
+ )
269
+ out.print("recent:")
260
270
  for run in data["recent_runs"]:
261
271
  out.print(
262
272
  f" {run['id']} {run['status']:9} {run['case']} {run['label']} {run['why'][:60]}"
263
273
  )
264
274
  if data["notes_files"]:
265
275
  out.print("notes/: " + ", ".join(data["notes_files"]))
276
+ for group, topics in data["group_analysis"].items():
277
+ out.print(f"{group}/_analysis/: {', '.join(topics)}", markup=False)
266
278
  if data["log_tail"]:
267
279
  out.print("--- LOG.md (tail) ---")
268
280
  out.print(data["log_tail"], markup=False)
@@ -590,23 +602,14 @@ def retry(
590
602
  _submit_each(project, [m])
591
603
 
592
604
 
593
- def _status_table(project: Project, manifests: list[Manifest]) -> Table:
605
+ def _status_table(project: Project, manifests: list[Manifest], *, brief: bool = False) -> Table:
594
606
  from runops.ops.listing import relative_age
595
607
  from runops.ops.progress import format_progress
596
608
 
597
- table = Table(
598
- "id",
599
- "status",
600
- "label",
601
- "case",
602
- "sweep",
603
- "created",
604
- "elapsed",
605
- "progress",
606
- "note",
607
- "why",
608
- box=None,
609
- )
609
+ columns = ["id", "status", "label", "case", "sweep", "created", "elapsed", "progress"]
610
+ if not brief:
611
+ columns += ["note", "why"]
612
+ table = Table(*columns, box=None)
610
613
  for m in manifests:
611
614
  s = m.to_summary(project.root)
612
615
  color = {
@@ -628,7 +631,7 @@ def _status_table(project: Project, manifests: list[Manifest]) -> Table:
628
631
  status += " [dim](pinned)[/]"
629
632
  if s["superseded_by"]:
630
633
  status += f" [dim](-> {', '.join(s['superseded_by'])})[/]"
631
- table.add_row(
634
+ row = [
632
635
  s["id"],
633
636
  status,
634
637
  s["label"],
@@ -637,15 +640,19 @@ def _status_table(project: Project, manifests: list[Manifest]) -> Table:
637
640
  relative_age(s["created_at"]),
638
641
  elapsed,
639
642
  format_progress(s["progress"]),
640
- s["last_note"][:60],
641
- s["why"][:60],
642
- )
643
+ ]
644
+ if not brief:
645
+ row += [s["last_note"][:60], s["why"][:60]]
646
+ table.add_row(*row)
643
647
  return table
644
648
 
645
649
 
646
650
  @app.command()
647
651
  def status(
648
652
  runs: Optional[list[str]] = typer.Argument(None),
653
+ brief: bool = typer.Option(
654
+ False, "--brief", help="Leave out params, why, notes and the note/why columns."
655
+ ),
649
656
  as_json: bool = typer.Option(False, "--json"),
650
657
  ) -> None:
651
658
  """Show manifest state (no Slurm query). Without RUN: active runs."""
@@ -660,18 +667,19 @@ def status(
660
667
  if as_json:
661
668
  data = []
662
669
  for m in targets:
663
- s = m.to_summary(project.root)
664
- s.update(
665
- {
666
- "params": m.params,
667
- "job": {k: v for k, v in m.job.items() if k != "attempts"},
668
- "notes": m.notes,
669
- }
670
- )
670
+ s = m.to_summary(project.root, brief=brief)
671
+ if not brief:
672
+ s.update(
673
+ {
674
+ "params": m.params,
675
+ "job": {k: v for k, v in m.job.items() if k != "attempts"},
676
+ "notes": m.notes,
677
+ }
678
+ )
671
679
  data.append(s)
672
680
  _print_json(data)
673
681
  return
674
- out.print(_status_table(project, targets))
682
+ out.print(_status_table(project, targets, brief=brief))
675
683
 
676
684
 
677
685
  @app.command("sync")
@@ -728,6 +736,9 @@ def list_cmd(
728
736
  tags: Optional[list[str]] = typer.Option(None, "--tag"),
729
737
  scratch: bool = typer.Option(False, "--scratch", help="Also show scratch runs."),
730
738
  archived: bool = typer.Option(False, "--archived", help="Also show archived runs."),
739
+ brief: bool = typer.Option(
740
+ False, "--brief", help="Leave out params, why and notes (small JSON, short table)."
741
+ ),
731
742
  as_json: bool = typer.Option(False, "--json"),
732
743
  ) -> None:
733
744
  """List active and recent runs."""
@@ -750,12 +761,12 @@ def list_cmd(
750
761
  )
751
762
  runs = select_runs(project, flt)
752
763
  if as_json:
753
- _print_json([m.to_summary(project.root) for m in runs])
764
+ _print_json([m.to_summary(project.root, brief=brief) for m in runs])
754
765
  return
755
766
  if not runs:
756
767
  out.print("no runs (try --all, --scratch, --archived)")
757
768
  return
758
- out.print(_status_table(project, runs))
769
+ out.print(_status_table(project, runs, brief=brief))
759
770
 
760
771
 
761
772
  @app.command()
@@ -869,12 +880,22 @@ def archive(
869
880
  if m.id in plan.superseded_by:
870
881
  line += f" -> {', '.join(plan.superseded_by[m.id])}"
871
882
  out.print(line)
883
+ for directory in plan.analysis_dirs:
884
+ dest = project.runs_dir / "_archive" / directory.relative_to(project.runs_dir)
885
+ out.print(f" analysis {project.rel(directory)} -> {project.rel(dest)}", markup=False)
872
886
  _confirm(f"Archive {len(plan.targets)} runs?", yes)
873
887
  archived = archive_runs(
874
- project, plan.targets, superseded_by=plan.superseded_by, why=why, by=_actor()
888
+ project,
889
+ plan.targets,
890
+ superseded_by=plan.superseded_by,
891
+ why=why,
892
+ by=_actor(),
893
+ analysis_dirs=plan.analysis_dirs,
875
894
  )
876
895
  for run_id, dst in archived:
877
896
  out.print(f"archived {run_id} -> {project.rel(dst)}")
897
+ for directory in plan.analysis_dirs:
898
+ out.print(f"archived analysis {project.rel(directory)}", markup=False)
878
899
 
879
900
 
880
901
  @app.command("mv")