diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..4d761dd --- /dev/null +++ b/.editorconfig @@ -0,0 +1,10 @@ +root = true + +[*] +charset = utf-8 +end_of_line = lf +insert_final_newline = true +trim_trailing_whitespace = true + +[*.md] +trim_trailing_whitespace = false diff --git a/.gitattributes b/.gitattributes index 38d705c..a78c6bd 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,6 +1,14 @@ # Default: let Git normalize line endings * text=auto +# Common text files should stay LF in the repository +*.md text eol=lf +*.html text eol=lf +*.css text eol=lf +*.js text eol=lf +*.toml text eol=lf +*.txt text eol=lf + # Shell scripts must use LF (executed on Linux CI) *.sh text eol=lf diff --git a/.github/workflows/gitlab-manual-ci.yml b/.github/workflows/gitlab-manual-ci.yml new file mode 100644 index 0000000..feb6b76 --- /dev/null +++ b/.github/workflows/gitlab-manual-ci.yml @@ -0,0 +1,176 @@ +name: GitLab Manual CI + +on: + workflow_dispatch: + inputs: + target_ref: + description: "Branch, tag, or SHA in this repository to test" + required: true + default: "develop" + code: + description: "BenchKit code filter, for example: qws,genesis" + required: false + system: + description: "System filter, for example: FugakuLN,MiyabiG" + required: false + app: + description: "BenchPark app filter, for example: osu-micro-benchmarks" + required: false + benchpark: + description: "Run the BenchPark path together with BenchKit" + required: false + type: boolean + default: false + park_only: + description: "Run only the BenchPark path" + required: false + type: boolean + default: false + park_send: + description: "Run the BenchPark send-only path" + required: false + type: boolean + default: false + +concurrency: + group: gitlab-manual-ci-${{ github.run_id }} + cancel-in-progress: false + +permissions: + contents: read + +jobs: + gitlab-manual-ci: + name: Run GitLab CI manually + runs-on: ubuntu-latest + steps: + - name: Check out target ref + uses: actions/checkout@v4 + with: + fetch-depth: 0 + ref: ${{ inputs.target_ref }} + + - name: Push target ref to GitLab test branch + env: + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_REPO: ${{ secrets.GITLAB_REPO }} + TARGET_REF: ${{ inputs.target_ref }} + run: | + set -euo pipefail + + if [ -z "${GITLAB_TOKEN}" ] || [ -z "${GITLAB_REPO}" ]; then + echo "GITLAB_TOKEN and GITLAB_REPO secrets are required." + exit 1 + fi + + branch="github/manual-${GITHUB_RUN_ID}" + echo "GITLAB_TEST_BRANCH=${branch}" >> "${GITHUB_ENV}" + + echo "Testing GitHub ref: ${TARGET_REF}" + echo "Temporary GitLab branch: ${branch}" + + git config user.name "github-bot" + git config user.email "bot@example.com" + git remote add gitlab "https://oauth2:${GITLAB_TOKEN}@${GITLAB_REPO}" + git push -o ci.skip gitlab "HEAD:refs/heads/${branch}" --force + + - name: Trigger and wait for GitLab pipeline + env: + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_REPO: ${{ secrets.GITLAB_REPO }} + GITLAB_TEST_BRANCH: ${{ env.GITLAB_TEST_BRANCH }} + CODE_FILTER: ${{ inputs.code }} + SYSTEM_FILTER: ${{ inputs.system }} + BENCHPARK_APP: ${{ inputs.app }} + BENCHPARK: ${{ inputs.benchpark }} + PARK_ONLY: ${{ inputs.park_only }} + PARK_SEND: ${{ inputs.park_send }} + run: | + set -euo pipefail + + repo="${GITLAB_REPO#https://}" + repo="${repo#http://}" + host="${repo%%/*}" + project_path="${repo#*/}" + project_path="${project_path%.git}" + + project_encoded="$(PROJECT_PATH="${project_path}" python3 -c 'import os, urllib.parse; print(urllib.parse.quote(os.environ["PROJECT_PATH"], safe=""))')" + api="https://${host}/api/v4/projects/${project_encoded}/pipeline" + + curl_args=( + --fail + --silent + --request POST + --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" + --form "ref=${GITLAB_TEST_BRANCH}" + ) + + var_index=0 + add_variable() { + local key="$1" + local value="$2" + if [ -n "${value}" ]; then + echo "GitLab variable: ${key}=${value}" + curl_args+=(--form "variables[${var_index}][key]=${key}") + curl_args+=(--form "variables[${var_index}][value]=${value}") + var_index=$((var_index + 1)) + fi + } + + add_variable "code" "${CODE_FILTER}" + add_variable "system" "${SYSTEM_FILTER}" + add_variable "app" "${BENCHPARK_APP}" + + if [ "${BENCHPARK}" = "true" ]; then + add_variable "benchpark" "true" + fi + if [ "${PARK_ONLY}" = "true" ]; then + add_variable "park_only" "true" + fi + if [ "${PARK_SEND}" = "true" ]; then + add_variable "park_send" "true" + fi + + response="$(curl "${curl_args[@]}" "${api}")" + pipeline_id="$(PIPELINE="${response}" python3 -c 'import json, os; print(json.loads(os.environ["PIPELINE"])["id"])')" + pipeline_url="$(PIPELINE="${response}" python3 -c 'import json, os; print(json.loads(os.environ["PIPELINE"]).get("web_url", ""))')" + + echo "GitLab pipeline: ${pipeline_url}" + pipeline_api="https://${host}/api/v4/projects/${project_encoded}/pipelines/${pipeline_id}" + + for _ in $(seq 1 180); do + response="$(curl --fail --silent --header "PRIVATE-TOKEN: ${GITLAB_TOKEN}" "${pipeline_api}")" + status="$(PIPELINE="${response}" python3 -c 'import json, os; print(json.loads(os.environ["PIPELINE"])["status"])')" + + echo "GitLab pipeline status: ${status}" + case "${status}" in + success) + exit 0 + ;; + failed|canceled|skipped) + exit 1 + ;; + manual) + echo "GitLab pipeline is waiting for manual action." + exit 1 + ;; + esac + + sleep 20 + done + + echo "Timed out waiting for GitLab pipeline ${pipeline_id}." + exit 1 + + - name: Delete GitLab test branch + if: always() + env: + GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} + GITLAB_REPO: ${{ secrets.GITLAB_REPO }} + GITLAB_TEST_BRANCH: ${{ env.GITLAB_TEST_BRANCH }} + run: | + set -euo pipefail + if [ -n "${GITLAB_TEST_BRANCH:-}" ]; then + git remote add gitlab-cleanup "https://oauth2:${GITLAB_TOKEN}@${GITLAB_REPO}" || true + git push gitlab-cleanup --delete "${GITLAB_TEST_BRANCH}" || true + fi diff --git a/.github/workflows/guard-main-pr-source.yml b/.github/workflows/guard-main-pr-source.yml new file mode 100644 index 0000000..d85a41d --- /dev/null +++ b/.github/workflows/guard-main-pr-source.yml @@ -0,0 +1,23 @@ +name: Guard main PR source + +on: + pull_request: + branches: + - main + +jobs: + guard-main-pr-source: + name: Require upstream develop for main PRs + runs-on: ubuntu-latest + steps: + - name: Allow only upstream develop to target main + run: | + if [ "${{ github.head_ref }}" != "develop" ] || \ + [ "${{ github.event.pull_request.head.repo.full_name }}" != "${{ github.repository }}" ]; then + echo "Pull requests to main are only allowed from the upstream develop branch." + echo "Head repository: ${{ github.event.pull_request.head.repo.full_name }}" + echo "Head branch: ${{ github.head_ref }}" + exit 1 + fi + + echo "Pull request source is upstream develop." diff --git a/.github/workflows/result-server-tests.yml b/.github/workflows/result-server-tests.yml new file mode 100644 index 0000000..099dd61 --- /dev/null +++ b/.github/workflows/result-server-tests.yml @@ -0,0 +1,53 @@ +name: Result Server Tests + +on: + pull_request: + paths: + - "result_server/**" + - "scripts/test_result_server.py" + - "scripts/validate_result_quality.py" + - "scripts/result_server/send_results.sh" + - "config/result_quality_policy.json" + - "requirements-result-server.txt" + - ".github/workflows/result-server-tests.yml" + push: + branches: + - "**" + paths: + - "result_server/**" + - "scripts/test_result_server.py" + - "scripts/validate_result_quality.py" + - "scripts/result_server/send_results.sh" + - "config/result_quality_policy.json" + - "requirements-result-server.txt" + - ".github/workflows/result-server-tests.yml" + workflow_dispatch: + +jobs: + pytest: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: + - "3.9" + - "3.12" + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + cache: "pip" + cache-dependency-path: requirements-result-server.txt + + - name: Install result_server test dependencies + run: | + python -m pip install --upgrade pip + python -m pip install -r requirements-result-server.txt + + - name: Run result_server pytest suite + run: python scripts/test_result_server.py diff --git a/.github/workflows/sync-to-gitlab.yml b/.github/workflows/sync-to-gitlab.yml index 51e7c9c..ca21233 100644 --- a/.github/workflows/sync-to-gitlab.yml +++ b/.github/workflows/sync-to-gitlab.yml @@ -1,9 +1,10 @@ -name: Sync all branches to GitLab +name: Sync protected branches to GitLab on: push: branches: - - '**' # 全ブランチを対象にする + - develop + - main workflow_dispatch: jobs: @@ -12,16 +13,18 @@ jobs: steps: - uses: actions/checkout@v3 with: - fetch-depth: 0 # 全ブランチとタグを取得 + fetch-depth: 0 - - name: Push all branches and tags to GitLab + - name: Push protected branches and tags to GitLab env: GITLAB_TOKEN: ${{ secrets.GITLAB_TOKEN }} GITLAB_REPO: ${{ secrets.GITLAB_REPO }} run: | + set -euo pipefail + git config user.name "github-bot" git config user.email "bot@example.com" git remote add gitlab https://oauth2:${GITLAB_TOKEN}@${GITLAB_REPO} - # 全ブランチ・タグを強制同期 - git push gitlab --all --force - git push gitlab --tags --force \ No newline at end of file + git fetch origin +refs/heads/develop:refs/remotes/origin/develop +refs/heads/main:refs/remotes/origin/main + git push -o ci.skip gitlab refs/remotes/origin/develop:refs/heads/develop refs/remotes/origin/main:refs/heads/main --force + git push -o ci.skip gitlab --tags --force diff --git a/.gitignore b/.gitignore index 98d9a71..2002883 100644 --- a/.gitignore +++ b/.gitignore @@ -26,7 +26,10 @@ result_server/config/allowed_emails.json # Flask session files result_server/flask_session/ +# Local audit workspace +audit/ + # editor backup files and caches *~ *.bak -*.pyc \ No newline at end of file +*.pyc diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 0f8380a..d4f98ce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -8,9 +8,7 @@ # # Files that automatically skip CI: # - README.md, ADD_APP.md (documentation) -# - result_server/templates/*.html (web templates) -# - .kiro/**/* (Kiro configuration) -# - .vscode/**/* (VSCode settings) +# - result_server/**/* (portal/server code and templates) # # Important Notes: # - trigger_child_pipeline depends on generate_matrix, so we use needs: optional: true @@ -96,9 +94,7 @@ generate_matrix: when: always - changes: - "*.md" - - "result_server/templates/*.html" - - ".kiro/**/*" - - ".vscode/**/*" + - "result_server/**/*" when: never - when: always @@ -133,9 +129,7 @@ trigger_child_pipeline: when: always - changes: - "*.md" - - "result_server/templates/*.html" - - ".kiro/**/*" - - ".vscode/**/*" + - "result_server/**/*" when: never - when: always # BenchPark Monitor Jobs diff --git a/.kiro/specs/api-restructure/.config.kiro b/.kiro/specs/api-restructure/.config.kiro deleted file mode 100644 index 227f6df..0000000 --- a/.kiro/specs/api-restructure/.config.kiro +++ /dev/null @@ -1 +0,0 @@ -{"specId": "54b3654d-256e-4405-a14c-01ceeb68ee4c", "workflowType": "requirements-first", "specType": "feature"} \ No newline at end of file diff --git a/.kiro/specs/api-restructure/design.md b/.kiro/specs/api-restructure/design.md deleted file mode 100644 index 6b18c0e..0000000 --- a/.kiro/specs/api-restructure/design.md +++ /dev/null @@ -1,283 +0,0 @@ -# 設計ドキュメント: API整理・リファクタリング - -## Overview - -BenchKit結果サーバ(result_server)のAPIルート構造を整理し、一貫性のある設計に改善するリファクタリング。主な変更は以下の通り: - -1. データ受信APIを `/api/ingest/` プレフィックス配下に統一 -2. `receive_bp` + `upload_bp` を `api_bp` に統合(`routes/api.py`) -3. 旧パスの後方互換ルートを deprecatedログ付きで維持 -4. SAVE_DIR/ESTIMATED_DIR を `current_app.config` から取得する方式に統一 -5. 推定結果ページのURLを `/estimated_results/` → `/estimated/` に短縮 -6. 旧ルートファイル(`receive.py`, `upload_tgz.py`)の削除 -7. CI/CDクライアント(`send_results.sh`)の新パス対応 - -### 現在のルート構造 - -``` -POST /write-api → receive_bp (routes/receive.py) 結果JSON受信 -POST /write-est → receive_bp (routes/receive.py) 推定結果JSON受信 -POST /upload-tgz → upload_bp (routes/upload_tgz.py) PA Data受信 -GET /results/ → results_bp (routes/results.py) 結果一覧 -GET /estimated_results/ → estimated_bp (routes/estimated.py) 推定結果一覧 -``` - -### リファクタリング後のルート構造 - -``` -POST /api/ingest/result → api_bp (routes/api.py) 結果JSON受信 -POST /api/ingest/estimate → api_bp (routes/api.py) 推定結果JSON受信 -POST /api/ingest/padata → api_bp (routes/api.py) PA Data受信 -POST /write-api → api_bp (互換ルート, deprecatedログ) -POST /write-est → api_bp (互換ルート, deprecatedログ) -POST /upload-tgz → api_bp (互換ルート, deprecatedログ) -GET /results/ → results_bp (変更なし) -GET /estimated/ → estimated_bp (プレフィックス短縮) -``` - -## Architecture - -### Blueprint構成の変更 - -```mermaid -graph TD - subgraph "現在" - A1[receive_bp
routes/receive.py] -->|"/write-api"| H1[結果JSON受信] - A1 -->|"/write-est"| H2[推定結果JSON受信] - A2[upload_bp
routes/upload_tgz.py] -->|"/upload-tgz"| H3[PA Data受信] - end - - subgraph "リファクタリング後" - B1[api_bp
routes/api.py] -->|"/api/ingest/result"| H4[結果JSON受信] - B1 -->|"/api/ingest/estimate"| H5[推定結果JSON受信] - B1 -->|"/api/ingest/padata"| H6[PA Data受信] - B1 -->|"/write-api"| H4 - B1 -->|"/write-est"| H5 - B1 -->|"/upload-tgz"| H6 - end -``` - -### SAVE_DIR管理の変更 - -```mermaid -graph LR - subgraph "現在" - M1[receive.py
SAVE_DIR='received'] - M2[upload_tgz.py
SAVE_DIR='received'] - M3[results.py
SAVE_DIR='received'] - M4[estimated.py
ESTIMATE_DIR='estimated_results'] - M5[results_loader.py
SAVE_DIR='received'
ESTIMATED_DIR='estimated_results'] - M6[app_dev.py
loader.SAVE_DIR = ...
results_route.SAVE_DIR = ...] - end - - subgraph "リファクタリング後" - C1[app.py
app.config RECEIVED_DIR
app.config ESTIMATED_DIR] --> C2[全ルート/ユーティリティ
current_app.config から取得] - end -``` - -### 設計判断 - -1. **Blueprint統合**: `receive_bp`と`upload_bp`は同じ「データ受信」責務を持つため、`api_bp`に統合。APIキー認証ロジックも共通化。 -2. **互換ルート方式**: 旧パスは同一Blueprint内の別ルートとして定義し、同じハンドラ関数を呼び出す。`app.logger.warning()`でdeprecatedログを出力。 -3. **`/api/ingest/` プレフィックス**: Blueprint自体の`url_prefix`を`/api/ingest`に設定し、各ルートは`/result`, `/estimate`, `/padata`とする。互換ルートはBlueprintの外(`app.py`側)で登録するか、Blueprint内で絶対パスとして定義する。 -4. **互換ルートの実装方法**: `api_bp`のurl_prefixを空にし、新旧両方のルートを同一Blueprint内で定義する。これにより、`app.py`側の変更を最小限に抑える。 - -## Components and Interfaces - -### 1. `routes/api.py` (新規) - -統合されたデータ受信Blueprint。 - -```python -# api_bp = Blueprint("api", __name__) -# url_prefix="" で登録(app.py側) - -# 新パス -@api_bp.route("/api/ingest/result", methods=["POST"]) -def ingest_result(): ... - -@api_bp.route("/api/ingest/estimate", methods=["POST"]) -def ingest_estimate(): ... - -@api_bp.route("/api/ingest/padata", methods=["POST"]) -def ingest_padata(): ... - -# 互換ルート(deprecatedログ付き) -@api_bp.route("/write-api", methods=["POST"]) -def compat_write_api(): - current_app.logger.warning("Deprecated: /write-api → /api/ingest/result") - return ingest_result() - -@api_bp.route("/write-est", methods=["POST"]) -def compat_write_est(): - current_app.logger.warning("Deprecated: /write-est → /api/ingest/estimate") - return ingest_estimate() - -@api_bp.route("/upload-tgz", methods=["POST"]) -def compat_upload_tgz(): - current_app.logger.warning("Deprecated: /upload-tgz → /api/ingest/padata") - return ingest_padata() -``` - -共通関数: -- `require_api_key()`: APIキー認証(`EXPECTED_API_KEY`環境変数と比較) -- `save_json_file(data, prefix, out_dir, given_uuid=None)`: JSON保存(既存ロジック移植) - -ディレクトリパスの取得: -- `current_app.config["RECEIVED_DIR"]` で受信ディレクトリを取得 -- `current_app.config["ESTIMATED_DIR"]` で推定結果ディレクトリを取得 - -### 2. `routes/results.py` (変更) - -変更点: -- モジュールレベル変数 `SAVE_DIR = "received"` を削除 -- 各ハンドラで `current_app.config["RECEIVED_DIR"]` を使用 -- `check_file_permission()` と `result_compare()` の `SAVE_DIR` 参照を変更 - -### 3. `routes/estimated.py` (変更) - -変更点: -- モジュールレベル変数 `ESTIMATE_DIR = "estimated_results"` を削除 -- 各ハンドラで `current_app.config["ESTIMATED_DIR"]` を使用 -- Blueprint登録時のurl_prefixを `/estimated` に変更 - -### 4. `utils/results_loader.py` (変更) - -変更点: -- モジュールレベル変数 `SAVE_DIR`, `ESTIMATED_DIR` を削除 -- `load_results_table()`, `load_estimated_results_table()` に `directory` 引数を追加(必須) -- `load_single_result()`, `load_multiple_results()` の `save_dir` 引数をデフォルトなしの必須引数に変更 -- 呼び出し元(routes)が `current_app.config` から取得したパスを渡す - -### 5. `app.py` (変更) - -変更点: -- `receive_bp`, `upload_bp` のimportを `api_bp` に変更 -- Blueprint登録: - ```python - app.register_blueprint(api_bp, url_prefix=prefix) - app.register_blueprint(results_bp, url_prefix=f"{prefix}/results") - app.register_blueprint(estimated_bp, url_prefix=f"{prefix}/estimated") # 短縮 - ``` - -### 6. `app_dev.py` (変更) - -変更点: -- モジュール変数の直接書き換え(`loader.SAVE_DIR = ...`, `results_route.SAVE_DIR = ...`)を削除 -- `app.config["RECEIVED_DIR"]` と `app.config["ESTIMATED_DIR"]` の設定のみで動作 - -### 7. `templates/_navigation.html` (変更) - -変更点: -- `/estimated_results` → `/estimated` にリンク更新 - -### 8. `scripts/send_results.sh` (変更) - -変更点: -- `/write-api` → `/api/ingest/result` -- `/upload-tgz` → `/api/ingest/padata` - -## Data Models - -データモデル自体に変更はない。APIのリクエスト/レスポンス形式は完全に維持される。 - -### 結果JSON受信 (`/api/ingest/result`) -- Request: `POST` with raw JSON body, `X-API-Key` header -- Response: `{"status": "ok", "id": "", "timestamp": "", "json_file": ""}` - -### 推定結果JSON受信 (`/api/ingest/estimate`) -- Request: `POST` with raw JSON body, `X-API-Key` header, optional `X-UUID` header -- Response: `{"status": "ok", "id": "", "timestamp": "", "json_file": ""}` - -### PA Data受信 (`/api/ingest/padata`) -- Request: `POST` multipart form with `id`, `timestamp`, `file` fields, `X-API-Key` header -- Response: `{"status": "uploaded", "id": "", "timestamp": "", "file": "", "replaced": }` - -### 設定値 (`app.config`) -- `RECEIVED_DIR`: 結果JSON/PA Dataの保存先ディレクトリ(絶対パス) -- `ESTIMATED_DIR`: 推定結果JSONの保存先ディレクトリ(絶対パス) - - -## Correctness Properties - -*プロパティとは、システムの全ての有効な実行において成り立つべき特性や振る舞いのことです。人間が読める仕様と機械的に検証可能な正しさの保証を橋渡しする、形式的な記述です。* - -### Property 1: APIプレフィックスの一貫性 - -*For any* データ受信エンドポイント(ingest系)のルール、そのURLパスは `/api/ingest/` プレフィックスで始まること(互換ルートを除く)。 - -**Validates: Requirements 1.1** - -### Property 2: レスポンス形式の保全 - -*For any* 有効なJSON入力データ、`/api/ingest/result` へのPOSTレスポンスは必ず `status`, `id`, `timestamp`, `json_file` キーを含み、`/api/ingest/padata` へのPOSTレスポンスは必ず `status`, `id`, `timestamp`, `file`, `replaced` キーを含むこと。 - -**Validates: Requirements 1.5** - -### Property 3: APIキー認証の統一性 - -*For any* ingestエンドポイント(`/api/ingest/result`, `/api/ingest/estimate`, `/api/ingest/padata`)と *for any* リクエスト、不正なAPIキーまたはAPIキーなしの場合は401ステータスが返り、正しいAPIキーの場合は認証が通過すること。 - -**Validates: Requirements 2.4** - -### Property 4: 旧パスと新パスのレスポンス等価性 - -*For any* 有効なリクエストボディ、旧パス(`/write-api`)と新パス(`/api/ingest/result`)に同一のリクエストを送信した場合、レスポンスのJSON構造(キーセット)が同一であること。同様に `/write-est` と `/api/ingest/estimate`、`/upload-tgz` と `/api/ingest/padata` についても成り立つこと。 - -**Validates: Requirements 3.1, 3.2** - -### Property 5: ディレクトリ設定の伝播 - -*For any* `app.config["RECEIVED_DIR"]` に設定されたディレクトリパス、結果JSON受信時にそのパスにファイルが保存されること。同様に `app.config["ESTIMATED_DIR"]` に設定されたパスに推定結果が保存されること。 - -**Validates: Requirements 4.1, 4.3, 4.4** - -## Error Handling - -### APIキー認証エラー -- 不正なAPIキーまたはキーなし → HTTP 401 `Invalid API Key` -- 既存の動作を維持 - -### PA Dataアップロードエラー -- UUIDなしまたは不正 → HTTP 400 `Invalid or missing UUID` -- タイムスタンプなし → HTTP 400 `Missing Timestamp` -- ファイルなし → HTTP 400 `No file uploaded` -- 既存の動作を維持 - -### ディレクトリ不在 -- `app.config["RECEIVED_DIR"]` / `app.config["ESTIMATED_DIR"]` は `create_app()` 内で `os.makedirs(exist_ok=True)` により自動作成(既存動作維持) - -### 互換ルートのログ -- 旧パスへのアクセス時に `current_app.logger.warning()` でdeprecated警告を出力 -- リクエスト処理自体は正常に実行(エラーにはしない) - -## Testing Strategy - -### テストフレームワーク -- **pytest**: ユニットテスト・統合テスト -- **hypothesis**: プロパティベーステスト(Python用PBTライブラリ) -- Flask `test_client()` を使用したHTTPレベルのテスト - -### ユニットテスト(example-based) - -1. **新パスの動作確認**: `/api/ingest/result`, `/api/ingest/estimate`, `/api/ingest/padata` への正常リクエスト -2. **互換ルートの動作確認**: `/write-api`, `/write-est`, `/upload-tgz` への正常リクエスト -3. **deprecatedログの出力確認**: 互換ルートアクセス時のログメッセージ検証 -4. **Blueprint統合の確認**: `api_bp` が登録されていること -5. **URLプレフィックス変更の確認**: `/estimated/` でアクセス可能なこと -6. **既存テスト25件の通過**: リファクタリング後も全テストがパス - -### プロパティベーステスト - -各プロパティテストは最低100回のイテレーションで実行する。 -各テストにはコメントで設計ドキュメントのプロパティ番号を参照する。 - -タグ形式: **Feature: api-restructure, Property {number}: {property_text}** - -- **Property 1テスト**: Flaskアプリのルールを列挙し、ingest系(互換ルート除く)が全て `/api/ingest/` で始まることを検証 -- **Property 2テスト**: hypothesisで任意のJSON辞書を生成し、`/api/ingest/result` へPOSTした際のレスポンスキーセットを検証 -- **Property 3テスト**: hypothesisで任意のAPIキー文字列を生成し、正しいキーなら認証通過、不正なキーなら401が返ることを全エンドポイントで検証 -- **Property 4テスト**: hypothesisで任意のJSON辞書を生成し、旧パスと新パスに同一リクエストを送信してレスポンスのキーセットが一致することを検証 -- **Property 5テスト**: hypothesisで任意のディレクトリ名を生成し、`app.config["RECEIVED_DIR"]` に設定後、結果受信でそのディレクトリにファイルが作成されることを検証 - -各プロパティは1つのプロパティベーステストで実装する。 diff --git a/.kiro/specs/api-restructure/requirements.md b/.kiro/specs/api-restructure/requirements.md deleted file mode 100644 index 23f048b..0000000 --- a/.kiro/specs/api-restructure/requirements.md +++ /dev/null @@ -1,122 +0,0 @@ -# 要件ドキュメント - -## はじめに - -BenchKit結果サーバ(result_server)のAPIルート名称を統一し、パス構造を合理的に再配置するリファクタリング。現在のAPIは名称の不統一(`write-api`, `write-est`, `upload-tgz`)、データ受信用Blueprintの不要な分離、APIエンドポイントとページルートの区別の欠如、SAVE_DIRのモジュールレベル変数管理の煩雑さといった問題を抱えている。本リファクタリングでは、これらを解消し、一貫性のあるAPI設計に改善する。 - -## 本番環境の構成 - -- systemdで起動: `gunicorn -w 2 -b 127.0.0.1:8800 benchkit.result_server.app:app` -- `WorkingDirectory=/home/nakamura/fugakunext/main` -- `BASE_PATH=/home/nakamura/fugakunext` -- ディレクトリ構成: `main/` 配下に `benchkit/`, `config/`, `estimated_results/`, `flask_session/`, `received/` -- `config/allowed_emails.json` が1ファイルのみ存在 - -## APIクライアント - -- `scripts/send_results.sh`: CI/CDから `/write-api` と `/upload-tgz` を呼び出し -- BenchPark CI (`benchpark-bridge/scripts/ci_generator.sh`) も同じ `send_results.sh` を使用 -- `/write-est` を呼び出すクライアントスクリプトはリポジトリ内に存在しない(外部利用の可能性あり) - -## 用語集 - -- **Result_Server**: BenchKit結果サーバ。Flask Blueprintで構成されたWebアプリケーション -- **Ingest_API**: データ受信用APIエンドポイント群(結果JSON、推定結果JSON、PA Dataの受信) -- **Results_View**: 結果表示用ページルート群(一覧、詳細、比較、機密結果) -- **Estimated_View**: 推定結果表示用ページルート群(一覧、個別表示) -- **Blueprint**: Flaskのルートグループ化機構 -- **SAVE_DIR**: 受信した結果JSONおよびPA Dataの保存ディレクトリ(`received/`) -- **ESTIMATED_DIR**: 受信した推定結果JSONの保存ディレクトリ(`estimated_results/`) -- **CI_Client**: CI/CDパイプラインからAPIを呼び出すスクリプト(`scripts/send_results.sh`) - -## 要件 - -### 要件1: APIエンドポイントの命名統一 - -**ユーザーストーリー:** 開発者として、APIエンドポイント名から機能が一目で分かるようにしたい。APIの管理・ドキュメント化が容易になるため。 - -#### 受け入れ基準 - -1. THE Ingest_API SHALL 全てのデータ取り込みエンドポイントを `/api/ingest/` プレフィックス配下に配置する -2. WHEN 結果JSONを受信する場合、THE Ingest_API SHALL `POST /api/ingest/result` でリクエストを受け付ける(旧: `/write-api`) -3. WHEN 推定結果JSONを受信する場合、THE Ingest_API SHALL `POST /api/ingest/estimate` でリクエストを受け付ける(旧: `/write-est`) -4. WHEN PA Data(tgz)をアップロードする場合、THE Ingest_API SHALL `POST /api/ingest/padata` でリクエストを受け付ける(旧: `/upload-tgz`) -5. THE Ingest_API SHALL 各エンドポイントのレスポンス形式を変更せず維持する - -### 要件2: データ受信Blueprintの統合 - -**ユーザーストーリー:** 開発者として、データ受信に関するルートを1つのBlueprintにまとめたい。コードの見通しが良くなり保守性が向上するため。 - -#### 受け入れ基準 - -1. THE Result_Server SHALL `receive_bp`と`upload_bp`を1つのBlueprint(`api_bp`)に統合する -2. THE Result_Server SHALL 統合後のBlueprintを `routes/api.py` に配置する -3. THE Result_Server SHALL 統合後も全てのデータ受信機能(結果JSON受信、推定結果JSON受信、PA Dataアップロード)を維持する -4. THE Result_Server SHALL APIキー認証ロジックを統合Blueprint内で共通化する - -### 要件3: 後方互換性の維持 - -**ユーザーストーリー:** 運用担当者として、APIリファクタリング後も既存のCI/CDパイプラインが正常に動作し続けることを保証したい。 - -#### 受け入れ基準 - -1. THE Result_Server SHALL 旧パス(`/write-api`, `/write-est`, `/upload-tgz`)へのリクエストを新パス(`/api/ingest/result`, `/api/ingest/estimate`, `/api/ingest/padata`)と同一の処理で受け付ける互換ルートとして維持する -2. WHEN CI_Clientが旧パスにリクエストを送信した場合、THE Result_Server SHALL 正常にリクエストを処理し同一のレスポンスを返す -3. THE Result_Server SHALL 旧パスの互換ルートにアクセスがあった場合、非推奨(deprecated)であることをログ出力する -4. THE CI_Client(`scripts/send_results.sh`)SHALL 新しいAPIパスを使用するよう更新する - -### 要件4: SAVE_DIR管理の改善 - -**ユーザーストーリー:** 開発者として、保存ディレクトリの設定をFlask app.configで一元管理したい。開発環境でのオーバーライドが簡潔になるため。 - -#### 受け入れ基準 - -1. THE Result_Server SHALL SAVE_DIRとESTIMATED_DIRをFlaskの`app.config`(`RECEIVED_DIR`, `ESTIMATED_DIR`)から取得する -2. THE Result_Server SHALL 各ルートモジュールでモジュールレベル変数としてSAVE_DIRを定義しない -3. WHEN ルートハンドラがディレクトリパスを必要とする場合、THE Result_Server SHALL `current_app.config`から取得する -4. THE Result_Server SHALL `utils/results_loader.py`のSAVE_DIR・ESTIMATED_DIRもapp.configから取得する方式に変更する -5. THE Result_Server SHALL 開発用起動スクリプト(`app_dev.py`)でのモジュール変数直接書き換えを不要にする - -### 要件5: ページルートのパス整理 - -**ユーザーストーリー:** ユーザーとして、URLの構造が直感的で一貫性があることを期待する。ページ間のナビゲーションが分かりやすくなるため。 - -#### 受け入れ基準 - -1. THE Results_View SHALL `/results/` 配下のパス構造を維持する(`/results/`, `/results/confidential`, `/results/compare`, `/results/detail/`, `/results/`) -2. THE Estimated_View SHALL `/estimated/` をURLプレフィックスとして使用する(`/estimated_results/` から短縮) -3. THE Navigation SHALL 新しいURLパスに合わせてリンクを更新する -4. THE Result_Server SHALL `app.py`のBlueprint登録で新しいプレフィックスを使用する - -### 要件6: 不要ファイルの整理 - -**ユーザーストーリー:** 開発者として、リファクタリング後に不要になった旧ルートファイルを削除したい。コードベースの整理のため。 - -#### 受け入れ基準 - -1. WHEN Blueprint統合が完了した場合、THE Result_Server SHALL `routes/receive.py`と`routes/upload_tgz.py`を削除する -2. THE Result_Server SHALL 新しい`routes/api.py`が全てのデータ受信機能を含むことを確認する - -### 要件7: テストの更新 - -**ユーザーストーリー:** 開発者として、リファクタリング後も全てのテストが通ることを保証したい。 - -#### 受け入れ基準 - -1. THE Result_Server SHALL 既存テスト(25テスト)がリファクタリング後も全て通過する -2. WHEN 新しいAPIパスが追加された場合、THE Result_Server SHALL 新パスに対するテストを追加する -3. WHEN 旧パスの互換ルートが存在する場合、THE Result_Server SHALL 旧パスへのリクエストが正常に処理されることをテストする - -## 正当性プロパティ - -### P1: APIパス一貫性 -全てのデータ受信エンドポイントは `/api/` プレフィックスで始まること。 - -### P2: 後方互換性 -旧パス(`/write-api`, `/write-est`, `/upload-tgz`)へのリクエストは、新パスと同一のレスポンスを返すこと。 - -### P3: ディレクトリ設定の一元性 -全てのルートモジュールとユーティリティモジュールが `current_app.config` からディレクトリパスを取得し、モジュールレベル変数に依存しないこと。 - -### P4: 機能保全 -リファクタリング前後で、全てのAPI機能(結果受信、推定結果受信、PA Dataアップロード、結果表示、比較、詳細表示)が同一の動作をすること。 diff --git a/.kiro/specs/api-restructure/tasks.md b/.kiro/specs/api-restructure/tasks.md deleted file mode 100644 index b05e87b..0000000 --- a/.kiro/specs/api-restructure/tasks.md +++ /dev/null @@ -1,139 +0,0 @@ -# Implementation Plan: API整理・リファクタリング - -## Overview - -BenchKit結果サーバのAPIルート構造を整理し、データ受信Blueprintの統合、パス命名の統一、SAVE_DIR管理の改善を段階的に実施する。既存テスト25件の通過を維持しながら、新パス・互換ルート・プロパティベーステストを追加する。 - -## Tasks - -- [x] 1. `utils/results_loader.py` のSAVE_DIR/ESTIMATED_DIR引数必須化 - - [x] 1.1 `load_results_table()` に `directory` 引数を追加し、モジュール変数 `SAVE_DIR` の参照を除去する - - `load_results_table(directory, public_only=True, ...)` の形に変更 - - 関数内の `SAVE_DIR` 参照を全て `directory` 引数に置換 - - _Requirements: 4.1, 4.4_ - - [x] 1.2 `load_estimated_results_table()` に `directory` 引数を追加し、モジュール変数 `ESTIMATED_DIR` の参照を除去する - - `load_estimated_results_table(directory, public_only=True, ...)` の形に変更 - - 関数内の `ESTIMATED_DIR` 参照を全て `directory` 引数に置換 - - _Requirements: 4.1, 4.4_ - - [x] 1.3 `load_single_result()` と `load_multiple_results()` の `save_dir` 引数をデフォルトなしの必須引数に変更する - - `save_dir=None` → `save_dir`(必須)に変更 - - 関数内の `if save_dir is None: save_dir = SAVE_DIR` フォールバックを削除 - - _Requirements: 4.4_ - - [x] 1.4 モジュールレベル変数 `SAVE_DIR` と `ESTIMATED_DIR` を `results_loader.py` から削除する - - _Requirements: 4.2_ - -- [x] 2. `routes/results.py` の `current_app.config` 対応 - - [x] 2.1 モジュールレベル変数 `SAVE_DIR = "received"` を削除し、各ハンドラで `current_app.config["RECEIVED_DIR"]` を使用する - - `results()`, `result_compare()`, `result_detail()`, `show_result()` の各ハンドラを更新 - - `check_file_permission()` と `serve_confidential_file()` の呼び出し箇所も更新 - - `load_results_table()`, `load_single_result()`, `load_multiple_results()` の呼び出しに `directory` / `save_dir` 引数を追加 - - _Requirements: 4.1, 4.3_ - -- [x] 3. `routes/estimated.py` の `current_app.config` 対応 + URLプレフィックス変更 - - [x] 3.1 モジュールレベル変数 `ESTIMATE_DIR = "estimated_results"` を削除し、各ハンドラで `current_app.config["ESTIMATED_DIR"]` を使用する - - `estimated_results()`, `show_estimated_result()` の各ハンドラを更新 - - `load_estimated_results_table()` の呼び出しに `directory` 引数を追加 - - _Requirements: 4.1, 4.3, 5.2_ - -- [x] 4. Checkpoint - 既存テスト通過確認 - - Ensure all tests pass, ask the user if questions arise. - - 既存テスト25件が `results_loader.py` と `routes/` の変更後も通過することを確認 - - テスト内の `loader.SAVE_DIR` 参照がある場合は修正する - -- [x] 5. `routes/api.py` の新規作成(api_bp統合、新パス + 互換ルート) - - [x] 5.1 `routes/api.py` を新規作成し、`api_bp` Blueprintを定義する - - `receive.py` の `require_api_key()`, `save_json_file()` を移植 - - `upload_tgz.py` の `is_valid_uuid()`, PA Dataアップロードロジックを移植 - - 全ハンドラで `current_app.config["RECEIVED_DIR"]` / `current_app.config["ESTIMATED_DIR"]` を使用 - - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 2.1, 2.2, 2.3, 2.4_ - - [x] 5.2 新パスのエンドポイントを実装する - - `POST /api/ingest/result` → `ingest_result()` - - `POST /api/ingest/estimate` → `ingest_estimate()` - - `POST /api/ingest/padata` → `ingest_padata()` - - _Requirements: 1.1, 1.2, 1.3, 1.4_ - - [x] 5.3 互換ルート(deprecatedログ付き)を実装する - - `POST /write-api` → `compat_write_api()` が `ingest_result()` を呼び出し - - `POST /write-est` → `compat_write_est()` が `ingest_estimate()` を呼び出し - - `POST /upload-tgz` → `compat_upload_tgz()` が `ingest_padata()` を呼び出し - - 各互換ルートで `current_app.logger.warning()` によるdeprecatedログを出力 - - _Requirements: 3.1, 3.2, 3.3_ - -- [x] 6. `app.py` のBlueprint登録変更 - - [x] 6.1 `receive_bp` と `upload_bp` のimportを `api_bp` に変更し、Blueprint登録を更新する - - `from routes.api import api_bp` に変更 - - `app.register_blueprint(api_bp, url_prefix=prefix)` で登録 - - `estimated_bp` のurl_prefixを `f"{prefix}/estimated"` に変更 - - `receive_bp`, `upload_bp` のimportと登録を削除 - - _Requirements: 2.1, 2.2, 5.2, 5.4_ - -- [x] 7. `app_dev.py` のモジュール変数書き換え削除 - - [x] 7.1 `create_dev_app()` から `loader.SAVE_DIR`, `loader.ESTIMATED_DIR`, `results_route.SAVE_DIR`, `estimated_route.ESTIMATE_DIR` の直接書き換えを削除する - - `app.config["RECEIVED_DIR"]` と `app.config["ESTIMATED_DIR"]` の設定のみで動作するようにする - - `estimated_bp` のurl_prefixを `/estimated` に変更 - - _Requirements: 4.5_ - -- [x] 8. `templates/_navigation.html` のURL更新と `scripts/send_results.sh` の新パス対応 - - [x] 8.1 `_navigation.html` の推定結果リンクを `/estimated_results` → `/estimated` に変更する - - _Requirements: 5.3_ - - [x] 8.2 `scripts/send_results.sh` のAPIパスを新パスに更新する - - `/write-api` → `/api/ingest/result` - - `/upload-tgz` → `/api/ingest/padata` - - _Requirements: 3.4_ - -- [x] 9. 旧ファイル(`receive.py`, `upload_tgz.py`)の削除 - - [x] 9.1 `routes/receive.py` と `routes/upload_tgz.py` を削除する - - `app.py` や他のファイルからの参照が残っていないことを確認 - - _Requirements: 6.1, 6.2_ - -- [x] 10. Checkpoint - 既存テスト25件の通過確認 - - Ensure all tests pass, ask the user if questions arise. - - 全リファクタリング完了後に既存テスト25件が通過することを確認 - - テスト内の旧Blueprint参照(`receive_bp`, `upload_bp`)やモジュール変数参照を修正 - - _Requirements: 7.1_ - -- [x] 11. 新パス・互換ルートのテスト追加 - - [x] 11.1 `result_server/tests/test_api_routes.py` を新規作成し、新パスと互換ルートのユニットテストを実装する - - Flask `test_client()` を使用 - - `/api/ingest/result`, `/api/ingest/estimate`, `/api/ingest/padata` への正常リクエストテスト - - `/write-api`, `/write-est`, `/upload-tgz` への正常リクエストテスト(互換ルート) - - APIキー認証エラーテスト(401レスポンス) - - deprecatedログ出力の確認テスト - - `/estimated/` でのアクセス確認テスト - - _Requirements: 7.2, 7.3_ - - - [ ]* 11.2 Property 1のプロパティベーステストを実装する - - **Property 1: APIプレフィックスの一貫性** - - Flaskアプリのルールを列挙し、ingest系エンドポイント(互換ルート除く)が全て `/api/ingest/` で始まることを検証 - - **Validates: Requirements 1.1** - - - [ ]* 11.3 Property 2のプロパティベーステストを実装する - - **Property 2: レスポンス形式の保全** - - hypothesisで任意のJSON辞書を生成し、`/api/ingest/result` へPOSTした際のレスポンスキーセットが `{status, id, timestamp, json_file}` であることを検証 - - **Validates: Requirements 1.5** - - - [ ]* 11.4 Property 3のプロパティベーステストを実装する - - **Property 3: APIキー認証の統一性** - - hypothesisで任意のAPIキー文字列を生成し、正しいキーなら認証通過、不正なキーなら401が返ることを全ingestエンドポイントで検証 - - **Validates: Requirements 2.4** - - - [ ]* 11.5 Property 4のプロパティベーステストを実装する - - **Property 4: 旧パスと新パスのレスポンス等価性** - - hypothesisで任意のJSON辞書を生成し、旧パスと新パスに同一リクエストを送信してレスポンスのキーセットが一致することを検証 - - **Validates: Requirements 3.1, 3.2** - - - [ ]* 11.6 Property 5のプロパティベーステストを実装する - - **Property 5: ディレクトリ設定の伝播** - - hypothesisで任意のディレクトリ名を生成し、`app.config["RECEIVED_DIR"]` に設定後、結果受信でそのディレクトリにファイルが作成されることを検証 - - **Validates: Requirements 4.1, 4.3, 4.4** - -- [x] 12. Final checkpoint - 全テスト通過確認 - - Ensure all tests pass, ask the user if questions arise. - - 既存テスト25件 + 新規テストが全て通過することを確認 - -## Notes - -- タスク1〜3でSAVE_DIR管理を先に改善し、タスク4で既存テストの通過を確認してからBlueprint統合に進む -- タスク `*` 付きはオプション(プロパティベーステスト)でスキップ可能 -- 各タスクは設計ドキュメントの対応コンポーネントを参照 -- チェックポイントで段階的に動作を検証 -- プロパティベーステストはhypothesisライブラリを使用 diff --git a/.kiro/specs/benchest-integration/.config.kiro b/.kiro/specs/benchest-integration/.config.kiro deleted file mode 100644 index 54f9f25..0000000 --- a/.kiro/specs/benchest-integration/.config.kiro +++ /dev/null @@ -1 +0,0 @@ -{"specId": "054020f6-5261-44dd-855e-8a86ce2e3f12", "workflowType": "requirements-first", "specType": "feature"} \ No newline at end of file diff --git a/.kiro/specs/benchest-integration/design.md b/.kiro/specs/benchest-integration/design.md deleted file mode 100644 index 24e8ef1..0000000 --- a/.kiro/specs/benchest-integration/design.md +++ /dev/null @@ -1,698 +0,0 @@ -# 設計書: 性能推定機能のBenchKit統合 - -## 概要 - -BenchKitのCIパイプラインに性能推定(BenchEst)機能を統合する。既存の `build → run → send_results` パイプラインに `estimate → send_estimate` ステージを追加し、推定スクリプトが存在するアプリケーション×推定対象システム(MiyabiG, RC_GH200)の組み合わせでのみ推定ジョブを自動生成する。 - -### 設計方針 - -1. **既存パターンの踏襲**: `send_results.sh` / `emit_send_results_job` のパターンを踏襲し、推定版を追加する -2. **YAML生成ルール準拠**: scriptセクションはシンプルに、複雑なロジックは `scripts/run_estimate.sh` に分離 -3. **差し替え可能性**: `estimate.sh` 内の推定ロジック部分のみを変更すれば実推定ツールに差し替え可能 -4. **推定対象システムのハードコード**: `ESTIMATE_SYSTEMS` 変数として `job_functions.sh` に定義。設定ファイル化は将来の拡張時に検討 - -## アーキテクチャ - -### パイプライン全体像 - -```mermaid -graph LR - subgraph "既存パイプライン" - B[build] --> R[run] - R --> SR[send_results] - end - subgraph "追加パイプライン(条件付き)" - SR --> E[estimate] - E --> SE[send_estimate] - end - - style E fill:#f9f,stroke:#333 - style SE fill:#f9f,stroke:#333 -``` - -### 推定ジョブ生成の判定フロー - -```mermaid -flowchart TD - A[list.csv の各行を処理] --> B{estimate.sh が存在?} - B -- No --> SKIP[ジョブ生成しない] - B -- Yes --> C{システムが ESTIMATE_SYSTEMS に含まれる?} - C -- No --> SKIP - C -- Yes --> D[estimate ジョブ生成] - D --> E[send_estimate ジョブ生成] -``` - -### ファイル構成 - -``` -scripts/ -├── job_functions.sh # 既存 + emit_estimate_job, emit_send_estimate_job, ESTIMATE_SYSTEMS 追加 -├── matrix_generate.sh # 既存 + estimate/send_estimate ステージ追加 -├── run_estimate.sh # 新規: 推定実行ラッパー -├── send_estimate.sh # 新規: 推定結果送信 -├── estimate_common.sh # 新規: 推定共通関数ライブラリ -├── send_results.sh # 既存(変更なし) -└── result.sh # 既存(変更なし) - -programs/ -└── qws/ - ├── build.sh # 既存 - ├── run.sh # 既存 - ├── list.csv # 既存 - └── estimate.sh # 新規: ダミー推定スクリプト - -.gitlab-ci.yml # estimate_uuid, code 変数追加、推定モードrules追加 -``` - -### 推定トリガーモード(UUID指定) - -```mermaid -sequenceDiagram - participant User as ユーザー - participant CI as GitLab CI - participant RS as Result Server - - User->>CI: estimate_uuid=xxx, code=qws でトリガー - CI->>CI: build/run スキップ(rules) - CI->>CI: generate_estimate_matrix 実行 - CI->>RS: UUID指定で Result_JSON 取得 - CI->>CI: estimate.sh 実行 - CI->>RS: 推定結果送信 -``` - -## コンポーネントとインターフェース - -### 1. `scripts/estimate_common.sh` — 推定共通関数ライブラリ - -benchest_ref/common_funcs.sh を参考に、result_server互換のJSON出力を行う共通関数群。 - -#### グローバル変数 - -```bash -# read_values で設定される変数 -est_code="" -est_exp="" -est_fom="" -est_system="" -est_node_count="" - -# estimate.sh 側で設定する変数(print_json が参照) -est_benchmark_system="" -est_benchmark_fom="" -est_benchmark_nodes="" -est_current_system="" -est_current_fom="" -est_current_nodes="" -est_current_method="" -est_future_system="" -est_future_fom="" -est_future_nodes="" -est_future_method="" -``` - -#### 関数 - -| 関数名 | 引数 | 説明 | -|--------|------|------| -| `read_values` | `$1`: Result_JSONファイルパス | JSONからcode, exp, FOM, system, node_countを読み取り、グローバル変数に設定。ファイル不在やFOM欠落時はエラー終了 | -| `print_json` | なし | グローバル変数からresult_server互換のEstimate_JSONを標準出力に出力 | - -#### `read_values` の実装方針 - -- `jq` コマンドでJSONフィールドを抽出 -- ファイル不在時: `echo "ERROR: ..." >&2; exit 1` -- FOMフィールド欠落時: `echo "ERROR: ..." >&2; exit 1` - -#### `print_json` の出力フォーマット - -```json -{ - "code": "$est_code", - "exp": "$est_exp", - "benchmark_system": "$est_benchmark_system", - "benchmark_fom": , - "benchmark_nodes": "$est_benchmark_nodes", - "current_system": { - "system": "$est_current_system", - "fom": , - "nodes": "$est_current_nodes", - "method": "$est_current_method" - }, - "future_system": { - "system": "$est_future_system", - "fom": , - "nodes": "$est_future_nodes", - "method": "$est_future_method" - }, - "performance_ratio": -} -``` - -`performance_ratio` は `print_json` 内で `current_system.fom / future_system.fom` として計算する(`awk` 使用)。 - -### 2. `programs//estimate.sh` — アプリケーション別推定スクリプト - -#### インターフェース - -```bash -#!/bin/bash -# Usage: bash programs//estimate.sh -# Output: results/estimate*.json -``` - -- 第1引数: Result_JSONファイルパス(例: `results/result0.json`) -- 出力先: `results/estimate__.json`(estimate.sh内で決定) -- `source scripts/estimate_common.sh` で共通関数を読み込む - -#### qwsダミー推定の実装例 - -```bash -#!/bin/bash -source scripts/estimate_common.sh - -read_values "$1" - -# --- Dummy estimation model (scale-mock) --- -est_benchmark_system="$est_system" -est_benchmark_fom="$est_fom" -est_benchmark_nodes="$est_node_count" - -est_current_system="Fugaku" -est_current_fom=$(awk -v fom="$est_fom" 'BEGIN {printf "%.3f", fom * 10}') -est_current_nodes="$est_node_count" -est_current_method="scale-mock" - -est_future_system="FugakuNEXT" -est_future_fom=$(awk -v fom="$est_fom" 'BEGIN {printf "%.3f", fom * 2}') -est_future_nodes="$est_node_count" -est_future_method="scale-mock" - -# --- Output --- -output_file="results/estimate_${est_code}_0.json" -print_json > "$output_file" -echo "Estimate written to $output_file" -``` - -### 3. `scripts/run_estimate.sh` — 推定実行ラッパー - -CIジョブのscriptセクションから `bash scripts/run_estimate.sh ` で呼び出される。 - -#### 処理フロー - -1. 第1引数からプログラムコード名を取得 -2. `programs//estimate.sh` の存在確認 -3. `results/` ディレクトリ内の `result*.json` を検出 -4. 各 Result_JSON に対して `estimate.sh` を実行 -5. 推定結果ファイルの存在確認 - -```bash -#!/bin/bash -set -euo pipefail - -code="$1" -estimate_script="programs/${code}/estimate.sh" - -if [[ ! -f "$estimate_script" ]]; then - echo "WARNING: $estimate_script not found, skipping estimation" - exit 0 -fi - -found=0 -for json_file in results/result*.json; do - [[ ! -f "$json_file" ]] && continue - found=1 - echo "Running estimation: $estimate_script $json_file" - bash "$estimate_script" "$json_file" -done - -if [[ "$found" -eq 0 ]]; then - echo "WARNING: No result*.json found in results/, skipping estimation" - exit 0 -fi - -echo "Estimation complete. Estimate files:" -ls results/estimate*.json 2>/dev/null || echo "No estimate files generated" -``` - -### 4. `scripts/send_estimate.sh` — 推定結果送信 - -`send_results.sh` のパターンを踏襲。 - -#### 処理フロー - -1. `results/` 内の `estimate*.json` を検出 -2. 各ファイルを `/api/ingest/estimate` にPOST -3. ファイルが存在しない場合は警告のみで正常終了 - -```bash -#!/bin/bash -set -euo pipefail - -echo "Sending estimate results to server" - -found=0 -for json_file in results/estimate*.json; do - [[ ! -f "$json_file" ]] && continue - found=1 - echo "Posting $json_file to ${RESULT_SERVER}/api/ingest/estimate" - curl --fail -sS -X POST "${RESULT_SERVER}/api/ingest/estimate" \ - -H "X-API-Key: ${RESULT_SERVER_KEY}" \ - -H "Content-Type: application/json" \ - --data-binary @"$json_file" - echo "Sent: $json_file" -done - -if [[ "$found" -eq 0 ]]; then - echo "WARNING: No estimate*.json files found in results/" -fi - -echo "All estimate results sent." -``` - -### 5. `scripts/job_functions.sh` への追加 - -#### 追加定数 - -```bash -# Estimate target systems (comma-separated) -ESTIMATE_SYSTEMS="MiyabiG,RC_GH200" -``` - -#### 追加関数 - -| 関数名 | 引数 | 説明 | -|--------|------|------| -| `is_estimate_target` | `$1`: system名 | システムが推定対象か判定。`ESTIMATE_SYSTEMS` に含まれれば return 0 | -| `has_estimate_script` | `$1`: program_dir | `$1/estimate.sh` が存在すれば return 0 | -| `emit_estimate_job` | `$1`: job_prefix, `$2`: depends_on, `$3`: code, `$4`: output_file | estimate ジョブのYAMLブロックを出力 | -| `emit_send_estimate_job` | `$1`: job_prefix, `$2`: depends_on, `$3`: output_file | send_estimate ジョブのYAMLブロックを出力 | - -#### `emit_estimate_job` の出力YAML - -```yaml -${job_prefix}_estimate: - stage: estimate - needs: ["${depends_on}"] - tags: ["general"] - script: - - echo "Running estimation for ${code}" - - bash scripts/run_estimate.sh ${code} - artifacts: - paths: - - results/ - expire_in: 1 week -``` - -#### `emit_send_estimate_job` の出力YAML - -```yaml -${job_prefix}_send_estimate: - stage: send_estimate - needs: ["${depends_on}"] - tags: [fncx-curl-jq] - environment: - name: $CI_COMMIT_BRANCH - script: - - bash scripts/send_estimate.sh -``` - -### 6. `scripts/matrix_generate.sh` への変更 - -#### ステージ追加 - -```bash -stages: - - build - - run - - send_results - - estimate # 追加 - - send_estimate # 追加 -``` - -#### 推定ジョブ生成ロジック - -既存の `emit_send_results_job` 呼び出しの直後に、条件付きで推定ジョブを生成する: - -```bash -# After emit_send_results_job call: -if has_estimate_script "$program_path" && is_estimate_target "$system"; then - emit_estimate_job "$job_prefix" "${job_prefix}_send_results" "$program" "$OUTPUT_FILE" - emit_send_estimate_job "$job_prefix" "${job_prefix}_estimate" "$OUTPUT_FILE" -fi -``` - -推定ジョブは `send_results` ジョブの後に実行される(`needs` で依存)。これにより、ベンチマーク結果の送信が完了してから推定が開始される。 - -### 7. `.gitlab-ci.yml` への変更 - -#### 変数追加 - -```yaml -variables: - code: "" - system: "" - app: "" - benchpark: "false" - park_only: "false" - park_send: "false" - estimate_uuid: "" # 追加: UUID指定による再推定トリガー -``` - -#### 推定モード用ジョブ追加 - -`estimate_uuid` が指定された場合、通常のbuild/runをスキップし、推定専用パイプラインを実行する。 - -```yaml -generate_estimate_matrix: - stage: generate - script: - - bash scripts/generate_estimate_from_uuid.sh - tags: - - general - artifacts: - paths: - - .gitlab-ci.estimate.yml - expire_in: 1 hour - rules: - - if: '$estimate_uuid != ""' - when: always - - when: never - -trigger_estimate_pipeline: - stage: trigger - trigger: - include: - - artifact: .gitlab-ci.estimate.yml - job: generate_estimate_matrix - strategy: depend - needs: - - job: generate_estimate_matrix - optional: true - rules: - - if: '$estimate_uuid != ""' - when: always - - when: never -``` - -#### 既存ジョブのrules修正 - -`generate_matrix` と `trigger_child_pipeline` に推定モード除外ルールを追加: - -```yaml -rules: - - if: '$estimate_uuid != ""' - when: never # 推定モードでは通常パイプラインを無効化 - # ... 既存のrules -``` - -### 8. `scripts/generate_estimate_from_uuid.sh` — UUID指定推定用YAMLジェネレータ - -`estimate_uuid` と `code` が指定された場合に、推定専用の子パイプラインYAMLを生成する。 - -#### 処理フロー - -1. `estimate_uuid` と `code` の両方が指定されていることを確認 -2. Result_ServerからUUID指定でResult_JSONを取得 -3. 推定実行 → 結果送信のYAMLを生成 - -```yaml -# 生成されるYAML -stages: - - fetch - - estimate - - send_estimate - -fetch_result: - stage: fetch - tags: [fncx-curl-jq] - script: - - mkdir -p results - - bash scripts/fetch_result_by_uuid.sh - artifacts: - paths: - - results/ - expire_in: 1 week - -estimate_${code}: - stage: estimate - needs: ["fetch_result"] - tags: ["general"] - script: - - bash scripts/run_estimate.sh ${code} - artifacts: - paths: - - results/ - expire_in: 1 week - -send_estimate_${code}: - stage: send_estimate - needs: ["estimate_${code}"] - tags: [fncx-curl-jq] - environment: - name: $CI_COMMIT_BRANCH - script: - - bash scripts/send_estimate.sh -``` - -### 9. `scripts/fetch_result_by_uuid.sh` — UUID指定結果取得 - -```bash -#!/bin/bash -set -euo pipefail - -if [[ -z "${estimate_uuid:-}" || -z "${code:-}" ]]; then - echo "ERROR: Both estimate_uuid and code must be specified" - exit 1 -fi - -echo "Fetching result for UUID: $estimate_uuid" -curl --fail -sS -o "results/result0.json" \ - "${RESULT_SERVER}/api/result/${estimate_uuid}" -echo "Fetched result to results/result0.json" -``` - -## データモデル - -### Estimate_JSON フォーマット(result_server互換) - -```json -{ - "code": "qws", - "exp": "strong_scaling_48", - "benchmark_system": "MiyabiG", - "benchmark_fom": 123.456, - "benchmark_nodes": "48", - "current_system": { - "system": "Fugaku", - "fom": 1234.56, - "nodes": "48", - "method": "measured" - }, - "future_system": { - "system": "FugakuNEXT", - "fom": 246.912, - "nodes": "48", - "method": "scale-mock" - }, - "performance_ratio": 5.0 -} -``` - -### フィールド定義 - -| フィールド | 型 | 説明 | -|-----------|-----|------| -| `code` | string | アプリケーションコード名 | -| `exp` | string | 実験名(Exp) | -| `benchmark_system` | string | ベンチマーク実行システム名 | -| `benchmark_fom` | number | ベンチマーク実行時のFOM値 | -| `benchmark_nodes` | string | ベンチマーク実行時のノード数 | -| `current_system.system` | string | 現行システム名(例: "Fugaku") | -| `current_system.fom` | number | 現行システムでのFOM値 | -| `current_system.nodes` | string | 現行システムでのノード数 | -| `current_system.method` | string | 推定手法("measured", "scale-mock" 等) | -| `future_system.system` | string | 将来システム名(例: "FugakuNEXT") | -| `future_system.fom` | number | 将来システムでの推定FOM値 | -| `future_system.nodes` | string | 将来システムでのノード数 | -| `future_system.method` | string | 推定手法 | -| `performance_ratio` | number | current_system.fom / future_system.fom | - -### ESTIMATED_FIELD_MAP との互換性 - -result_server の `results_loader.py` で定義されている: - -```python -ESTIMATED_FIELD_MAP = {"system": "benchmark_system", "code": "code", "exp": "exp"} -``` - -`load_estimated_results_table` は以下のフィールドを参照する: -- `data.get("code")` → `code` -- `data.get("exp")` → `exp` -- `data.get("benchmark_system")` → `benchmark_system` -- `data.get("benchmark_fom")` → `benchmark_fom` -- `data.get("benchmark_nodes")` → `benchmark_nodes` -- `data.get("current_system", {})` → current_system オブジェクト -- `data.get("future_system", {})` → future_system オブジェクト -- `data.get("performance_ratio")` → `performance_ratio` - -上記のEstimate_JSONフォーマットはこれらすべてと互換性がある。 - -### 推定対象システム定義 - -```bash -ESTIMATE_SYSTEMS="MiyabiG,RC_GH200" -``` - -`job_functions.sh` にハードコードする。将来的にシステム数が増えた場合は `config/estimate_systems.csv` 等に外出しを検討する。 - - -## 正当性プロパティ(Correctness Properties) - -*プロパティとは、システムのすべての有効な実行において成り立つべき特性や振る舞いのことである。人間が読める仕様と機械的に検証可能な正当性保証の橋渡しとなる形式的な記述である。* - -### Property 1: read_values ラウンドトリップ - -*For any* 有効なResult_JSON(code, exp, FOM, system, node_countフィールドを含む)に対して、`read_values` を実行した後のグローバル変数(est_code, est_exp, est_fom, est_system, est_node_count)は、元のJSONの対応するフィールド値と一致する。 - -**Validates: Requirements 1.1** - -### Property 2: print_json フォーマット完全性 - -*For any* 有効なグローバル変数の組み合わせ(est_code, est_exp, est_benchmark_system, est_benchmark_fom, est_benchmark_nodes, est_current_*, est_future_*)に対して、`print_json` の出力は有効なJSONであり、以下のすべてを満たす: -- トップレベルに code, exp, benchmark_system, benchmark_fom, benchmark_nodes, performance_ratio フィールドが存在する -- current_system オブジェクトに system, fom, nodes, method フィールドが存在する -- future_system オブジェクトに system, fom, nodes, method フィールドが存在する -- 各フィールドの値がグローバル変数の値と一致する - -**Validates: Requirements 1.2, 1.3, 1.4, 5.1, 5.2, 5.3** - -### Property 3: performance_ratio 計算の正確性 - -*For any* 正の数値ペア(current_fom, future_fom)に対して、`print_json` が出力するJSONの `performance_ratio` は `current_fom / future_fom` と等しい(浮動小数点精度の範囲内)。 - -**Validates: Requirements 5.4** - -### Property 4: Estimate_JSON と result_server の互換性 - -*For any* 有効なEstimate_JSON に対して、そのJSONファイルを `load_estimated_results_table` で読み込んだ結果の行(row)は、code, exp, benchmark_system, benchmark_fom, benchmark_nodes, systemA_fom, systemA_system, systemA_nodes, systemA_method, systemB_fom, systemB_system, systemB_nodes, systemB_method, performance_ratio の各フィールドが元のJSONの対応する値と一致する。 - -**Validates: Requirements 5.5** - -### Property 5: 推定ジョブ生成の条件判定 - -*For any* プログラム×システムの組み合わせに対して、推定ジョブ(estimate, send_estimate)が生成されるのは、`programs//estimate.sh` が存在し、かつシステムが `ESTIMATE_SYSTEMS`(MiyabiG, RC_GH200)に含まれる場合に限る。それ以外の組み合わせでは推定ジョブは一切生成されない。 - -**Validates: Requirements 2.7, 4.4, 4.5** - -### Property 6: 生成YAMLの構造正当性 - -*For any* 推定対象のプログラム×システムの組み合わせに対して、生成されたYAMLは以下を満たす: -- `estimate` ステージと `send_estimate` ステージが存在する -- estimate ジョブの `needs` が対応する send_results ジョブを参照する -- send_estimate ジョブの `needs` が対応する estimate ジョブを参照する - -**Validates: Requirements 4.1, 4.2, 4.3** - -### Property 7: ダミー推定モデルのスケーリング - -*For any* 正のFOM値に対して、qwsのダミー推定モデルは current_system.fom = FOM × 10、future_system.fom = FOM × 2 を出力し、両方の method フィールドが "scale-mock" である。 - -**Validates: Requirements 7.2, 7.3** - -### Property 8: send_estimate.sh の全ファイル送信 - -*For any* `results/` ディレクトリ内の `estimate*.json` ファイル集合に対して、`send_estimate.sh` はすべてのファイルを `/api/ingest/estimate` エンドポイントにPOSTする。送信されたファイル数は元のファイル数と一致する。 - -**Validates: Requirements 3.1, 3.2** - -### Property 9: run_estimate.sh の全結果処理 - -*For any* `results/` ディレクトリ内の `result*.json` ファイル集合に対して、`run_estimate.sh` は各ファイルに対して `programs//estimate.sh` を1回ずつ呼び出す。 - -**Validates: Requirements 9.2** - -## エラーハンドリング - -### estimate_common.sh - -| エラー条件 | 処理 | -|-----------|------| -| `read_values`: ファイルが存在しない | stderr にエラーメッセージ出力、`exit 1` | -| `read_values`: FOMフィールドが欠落 | stderr にエラーメッセージ出力、`exit 1` | -| `read_values`: jqコマンドが利用不可 | stderr にエラーメッセージ出力、`exit 1` | -| `print_json`: future_fom が 0 | performance_ratio を 0 として出力(ゼロ除算回避) | - -### run_estimate.sh - -| エラー条件 | 処理 | -|-----------|------| -| estimate.sh が存在しない | 警告メッセージ出力、`exit 0`(正常終了) | -| result*.json が存在しない | 警告メッセージ出力、`exit 0`(正常終了) | -| estimate.sh が非ゼロで終了 | `set -euo pipefail` により即座にエラー終了 | - -### send_estimate.sh - -| エラー条件 | 処理 | -|-----------|------| -| estimate*.json が存在しない | 警告メッセージ出力、`exit 0`(正常終了) | -| HTTP POST失敗(curl --fail) | エラーメッセージ出力、非ゼロ終了 | -| RESULT_SERVER 未設定 | `set -euo pipefail` により即座にエラー終了 | -| RESULT_SERVER_KEY 未設定 | `set -euo pipefail` により即座にエラー終了 | - -### fetch_result_by_uuid.sh - -| エラー条件 | 処理 | -|-----------|------| -| estimate_uuid 未設定 | エラーメッセージ出力、`exit 1` | -| code 未設定 | エラーメッセージ出力、`exit 1` | -| Result_Server からの取得失敗 | `curl --fail` によりエラー終了 | - -### matrix_generate.sh(推定関連) - -| エラー条件 | 処理 | -|-----------|------| -| estimate.sh が存在しない | 推定ジョブを生成しない(正常動作) | -| システムが推定対象外 | 推定ジョブを生成しない(正常動作) | - -## テスト戦略 - -### テストアプローチ - -ユニットテストとプロパティベーステストの二本立てで検証する。 - -- **ユニットテスト**: 具体的な入出力例、エッジケース、エラー条件の検証 -- **プロパティベーステスト**: ランダム生成された入力に対する普遍的な性質の検証 - -### プロパティベーステスト - -**ライブラリ**: Python の `hypothesis` を使用(result_server のテストで既に使用実績あり) - -シェルスクリプトのテストは、Python から `subprocess` でシェルスクリプトを呼び出す形式で実施する。YAML生成のテストは生成されたYAMLをパースして検証する。 - -**設定**: 各プロパティテストは最低100イテレーション実行する。 - -**タグ付け**: 各テストにコメントでプロパティ参照を記載する。 -- フォーマット: `Feature: benchest-integration, Property {number}: {property_text}` - -**各プロパティは1つのプロパティベーステストで実装する。** - -### テスト対象と手法 - -| コンポーネント | プロパティテスト | ユニットテスト | -|--------------|----------------|--------------| -| `estimate_common.sh` (read_values) | Property 1: ラウンドトリップ | エッジケース: ファイル不在、FOM欠落 | -| `estimate_common.sh` (print_json) | Property 2: フォーマット完全性, Property 3: ratio計算 | — | -| Estimate_JSON ↔ result_server | Property 4: 互換性 | — | -| matrix_generate.sh(推定ジョブ生成) | Property 5: 条件判定, Property 6: YAML構造 | 具体的なプログラム×システム組み合わせの例 | -| qws/estimate.sh(ダミーモデル) | Property 7: スケーリング | — | -| send_estimate.sh | Property 8: 全ファイル送信 | エッジケース: ファイル不在時の正常終了 | -| run_estimate.sh | Property 9: 全結果処理 | エッジケース: result不在時の正常終了 | - -### ユニットテスト - -- `read_values` に存在しないファイルを渡した場合のエラー終了確認 -- `read_values` にFOM欠落JSONを渡した場合のエラー終了確認 -- `send_estimate.sh` でestimate*.jsonが存在しない場合の正常終了確認 -- `run_estimate.sh` でresult*.jsonが存在しない場合の正常終了確認 -- `fetch_result_by_uuid.sh` でestimate_uuid/code未設定時のエラー確認 -- `.gitlab-ci.yml` のrules: estimate_uuid指定時に通常パイプラインがスキップされることの確認 diff --git a/.kiro/specs/benchest-integration/requirements.md b/.kiro/specs/benchest-integration/requirements.md deleted file mode 100644 index e01466b..0000000 --- a/.kiro/specs/benchest-integration/requirements.md +++ /dev/null @@ -1,147 +0,0 @@ -# 要件定義書: 性能推定機能のBenchKit統合 - -## はじめに - -BenchKitのCIパイプラインに性能推定(BenchEst)機能を統合する。ベンチマーク実行後に、推定スクリプトが用意されているアプリケーションに対してのみ自動で性能推定を行い、推定結果をresult_serverに送信する仕組みを構築する。性能推定モデルは現時点ではダミー実装とし、将来的に非公開リポジトリの実推定ツールに差し替え可能な設計とする。 - -### 性能推定の概要 - -性能推定は、ベンチマーク実行結果(演算区間・通信区間の時間データ)を基に、将来システムでの性能を推定する処理である。 - -- **推定対象システム**: 現状MiyabiGとRC_GH200のみに限定する。GH200が将来機に最もハードウェア的に近いため、これらのシステムでのみ有意な推定が可能。estimate.shが存在しても、対象外システムでは推定ジョブを生成しない -- **current_system(現行システム)**: Fugakuでの実測値を使用する。result_serverに蓄積された実測結果から、アプリごとに指定されたExpの最新結果を取得する -- **future_system(将来システム)**: 推定ツールにより算出する。演算区間はパフォーマンスカウンター生データを解析して将来機での性能を推定し、通信区間はスケーリングモデルで推定する -- **推定手法**: 複数の演算区間と通信区間の時間をそれぞれスケーリングした合計値から、通信と演算がオーバーラップしている時間を減じた時間が推定実行時間となる -- **演算区間の推定**: 現行機のパフォーマンスカウンター生データを解析して将来機での性能を推定するツール(非公開)で処理される。この処理がすべての演算区間に対して行われる - -## 用語集 - -- **BenchKit**: ベンチマークの自動実行・結果収集を行うCI/CDフレームワーク -- **BenchEst**: 性能推定基盤。ベンチマーク結果に基づきターゲットシステムの性能を推定する -- **Estimate_Script**: `programs//estimate.sh` に配置されるアプリケーション別の推定スクリプト。存在するアプリかつ推定対象システム(MiyabiG, RC_GH200)でのみ推定が実行される -- **Estimate_Systems**: 推定対象システムのリスト。現状は `MiyabiG,RC_GH200` に固定。将来的に拡張可能 -- **Estimate_Common**: `scripts/estimate_common.sh` に配置される推定処理の共通関数ライブラリ -- **Result_Server**: ベンチマーク結果および推定結果を受信・保存・表示するFlask Webサーバ -- **Send_Estimate_Script**: `scripts/send_estimate.sh` に配置される推定結果送信スクリプト -- **Matrix_Generator**: `scripts/matrix_generate.sh` に配置されるCI YAMLジェネレータ -- **Result_JSON**: ベンチマーク実行結果のJSONファイル(`results/result*.json`) -- **Estimate_JSON**: 推定結果のJSONファイル(`results/estimate*.json`) -- **FOM**: Figure of Merit。ベンチマークの性能指標値 -- **PA_Data**: Performance Analysis Data。パフォーマンスカウンター生データを含むアーカイブ(`.tgz`) -- **CI_Pipeline**: GitLab CIの generate → trigger → build → run → send_results の実行パイプライン - -## 要件 - -### 要件1: 推定共通関数ライブラリ - -**ユーザーストーリー:** アプリケーション開発者として、推定スクリプトで共通的に使う関数群を利用したい。推定結果のJSON生成やベンチマーク結果の読み取りを毎回書かなくて済むようにしたい。 - -#### 受け入れ基準 - -1. THE Estimate_Common SHALL ベンチマーク Result_JSON からcode、exp、FOM、system、node_countの値を読み取る `read_values` 関数を提供する -2. THE Estimate_Common SHALL result_serverが期待するフォーマットに準拠した Estimate_JSON を生成する `print_json` 関数を提供する -3. THE Estimate_Common SHALL current_system(現行システム実測)とfuture_system(将来システム推定)の2つのターゲットシステムの結果を出力する -4. THE Estimate_Common SHALL benchmark_system、benchmark_fom、benchmark_nodes、code、exp、performance_ratioの各フィールドを Estimate_JSON に含める -5. WHEN `read_values` 関数に存在しないファイルパスが渡された場合、THEN THE Estimate_Common SHALL エラーメッセージを表示して非ゼロの終了コードを返す -6. WHEN `read_values` 関数にFOMフィールドを含まないJSONが渡された場合、THEN THE Estimate_Common SHALL エラーメッセージを表示して非ゼロの終了コードを返す - - -### 要件2: アプリケーション別推定スクリプト - -**ユーザーストーリー:** アプリケーション開発者として、自分のアプリケーション固有の推定ロジックを自由に記述したい。推定モデルの中身はアプリごとに異なるため、フレームワーク化せず自由記述を許容する必要がある。 - -#### 受け入れ基準 - -1. THE Estimate_Script SHALL `programs//estimate.sh` のパスに配置される -2. WHEN Estimate_Script が実行される場合、THE Estimate_Script SHALL 第1引数として Result_JSON のファイルパスを受け取る -3. THE Estimate_Script SHALL Estimate_Common の `source` による読み込みと関数呼び出しにより推定結果を生成する -4. THE Estimate_Script SHALL 推定結果を `results/` ディレクトリ配下に `estimate*.json` として出力する -5. THE Estimate_Script SHALL アプリケーション開発者が推定ロジックを自由に記述できる構造を維持する(フレームワーク化しない) -6. WHEN 将来の実推定ツールに差し替える場合、THE Estimate_Script SHALL スクリプト内部の推定ロジック部分のみを変更することで対応可能とする -7. THE Estimate_Script SHALL `programs//estimate.sh` が存在しないアプリケーション、または推定対象システム以外では推定処理が実行されない(推定はオプショナル) - -### 要件3: 推定結果送信スクリプト - -**ユーザーストーリー:** CI/CDパイプラインの一部として、推定結果をresult_serverに自動送信したい。既存のsend_results.shと同様の仕組みで推定結果を送信する。 - -#### 受け入れ基準 - -1. THE Send_Estimate_Script SHALL `results/` ディレクトリ内の全ての `estimate*.json` ファイルを検出して送信する -2. THE Send_Estimate_Script SHALL Result_Server の `/api/ingest/estimate` エンドポイントにHTTP POSTで Estimate_JSON を送信する -3. THE Send_Estimate_Script SHALL 環境変数 `RESULT_SERVER` からサーバURLを取得する -4. THE Send_Estimate_Script SHALL 環境変数 `RESULT_SERVER_KEY` からAPIキーを取得し、`X-API-Key` ヘッダーに設定する -5. WHEN 送信対象の `estimate*.json` ファイルが存在しない場合、THE Send_Estimate_Script SHALL 警告メッセージを表示して正常終了する(エラーにしない) -6. IF HTTP POSTが失敗した場合、THEN THE Send_Estimate_Script SHALL エラーメッセージを表示して非ゼロの終了コードを返す - -### 要件4: CIパイプラインへの推定ステージ統合 - -**ユーザーストーリー:** ベンチマーク実行後に、推定スクリプトが用意されているアプリケーションに対してのみ自動で性能推定が実行され、結果がresult_serverに送信されるようにしたい。 - -#### 受け入れ基準 - -1. THE Matrix_Generator SHALL 既存の `build → run → send_results` パイプラインに `estimate` ステージと `send_estimate` ステージを追加する -2. THE Matrix_Generator SHALL `estimate` ジョブを `send_results` ジョブの後に実行する(`needs` で依存関係を指定) -3. THE Matrix_Generator SHALL `send_estimate` ジョブを `estimate` ジョブの後に実行する -4. WHEN `programs//estimate.sh` が存在し、かつ対象システムが推定対象システム(MiyabiG, RC_GH200)である場合のみ、THE Matrix_Generator SHALL そのプログラム×システムの組み合わせに対して推定ジョブを生成する -5. WHEN `programs//estimate.sh` が存在しない場合、または対象システムが推定対象システムでない場合、THE Matrix_Generator SHALL そのプログラム×システムの組み合わせに対して推定ジョブおよび送信ジョブを一切生成しない(スキップする) -6. THE Matrix_Generator SHALL 推定ジョブのscriptセクションで `bash scripts/run_estimate.sh ` を呼び出す(複雑なロジックは別スクリプトに分離) -7. THE Matrix_Generator SHALL 推定ジョブが `results/` ディレクトリのartifactsを前段ジョブから引き継ぐ -8. THE Matrix_Generator SHALL YAML生成ルール(scriptセクションはシンプルに、複雑なロジックは別スクリプトに分離)に準拠する - -### 要件5: 推定結果JSONフォーマット - -**ユーザーストーリー:** result_serverの既存の推定結果表示機能と互換性のあるJSONフォーマットで推定結果を出力したい。既存のestimated_results.htmlテンプレートとresults_loader.pyが正しく動作する必要がある。 - -#### 受け入れ基準 - -1. THE Estimate_JSON SHALL 以下のトップレベルフィールドを含む: code, exp, benchmark_system, benchmark_fom, benchmark_nodes, current_system, future_system, performance_ratio -2. THE Estimate_JSON SHALL current_system オブジェクトに system, fom, nodes, method の各フィールドを含める -3. THE Estimate_JSON SHALL future_system オブジェクトに system, fom, nodes, method の各フィールドを含める -4. THE Estimate_JSON SHALL performance_ratio を current_system.fom と future_system.fom の比率として計算する -5. THE Estimate_JSON SHALL Result_Server の `load_estimated_results_table` 関数および `ESTIMATED_FIELD_MAP` と互換性のあるフォーマットとする - -### 要件6: 推定ツール差し替え可能性 - -**ユーザーストーリー:** 将来、ダミーの推定モデルを非公開リポジトリの実推定ツールに差し替えたい。ハードウェア機密情報を含む実ツールはBenchKitの公開リポジトリには置かない。 - -#### 受け入れ基準 - -1. THE Estimate_Script SHALL 推定ロジック部分と共通処理部分(入力読み取り・出力生成)を分離した構造とする -2. THE Estimate_Common SHALL 推定モデルの実装に依存しない汎用的なインターフェースを提供する -3. WHEN 実推定ツールに差し替える場合、THE Estimate_Script SHALL estimate.sh内の推定ロジック部分のみの変更で対応可能とする -4. THE Estimate_Script SHALL 外部ツールの呼び出し(コマンド実行やスクリプト呼び出し)を推定ロジック部分に記述可能とする -5. THE 実推定ツール SHALL 複数の演算区間に対してパフォーマンスカウンター生データを解析し、将来機での性能を推定する処理を行う想定とする -6. THE Estimate_Script SHALL 演算区間と通信区間のスケーリング結果を合算し、オーバーラップ時間を減じる処理を記述可能な構造とする - -### 要件7: ダミー推定モデルの提供 - -**ユーザーストーリー:** 統合テストやCI動作確認のために、実推定ツールがなくても動作するダミー推定モデルを提供したい。 - -#### 受け入れ基準 - -1. THE Estimate_Script SHALL デフォルトでダミー推定モデルを含む実装を提供する(qwsアプリ用) -2. THE Estimate_Script SHALL ダミーモデルとして、元のFOMに固定倍率を掛けた値をcurrent_systemおよびfuture_systemの推定FOMとする -3. THE Estimate_Script SHALL ダミーモデルの推定手法(method)を "scale-mock" と記録する - -### 要件8: 推定トリガーモード(UUID指定による再推定) - -**ユーザーストーリー:** 推定モデルを更新した際に、過去のベンチマーク結果に対して再推定を実行したい。UUID指定で特定の結果を再推定できるようにする。 - -#### 受け入れ基準 - -1. WHEN CI変数 `estimate_uuid` が指定された場合、THE CI_Pipeline SHALL 通常のbuild/runステージをスキップし、推定ステージのみを実行する -2. WHEN `estimate_uuid` が指定された場合、THE CI_Pipeline SHALL Result_Server から指定UUIDの Result_JSON を取得する -3. WHEN `estimate_uuid` が指定された場合、THE CI_Pipeline SHALL CI変数 `code` で指定されたプログラムの Estimate_Script を実行する -4. WHEN `estimate_uuid` が指定された場合、THE CI_Pipeline SHALL 推定結果を Result_Server に送信する -5. WHEN `estimate_uuid` と `code` の両方が指定されていない場合、THE CI_Pipeline SHALL エラーメッセージを表示する - -### 要件9: 推定用ランナースクリプト - -**ユーザーストーリー:** CIパイプラインの推定ジョブから呼び出される推定実行スクリプトを提供したい。YAML生成ルールに従い、複雑なロジックを別スクリプトに分離する。 - -#### 受け入れ基準 - -1. THE `scripts/run_estimate.sh` SHALL 第1引数としてプログラムコード名を受け取る -2. THE `scripts/run_estimate.sh` SHALL `results/` ディレクトリ内の Result_JSON を検出し、対応する `programs//estimate.sh` を実行する -3. THE `scripts/run_estimate.sh` SHALL 推定結果ファイル(estimate*.json)が `results/` ディレクトリに出力されることを確認する -4. WHEN Result_JSON が存在しない場合、THE `scripts/run_estimate.sh` SHALL 警告メッセージを表示して正常終了する diff --git a/.kiro/specs/benchest-integration/tasks.md b/.kiro/specs/benchest-integration/tasks.md deleted file mode 100644 index 9184c33..0000000 --- a/.kiro/specs/benchest-integration/tasks.md +++ /dev/null @@ -1,156 +0,0 @@ -# 実装計画: 性能推定機能のBenchKit統合 - -## 概要 - -既存の `build → run → send_results` パイプラインに `estimate → send_estimate` ステージを追加する。共通基盤から順にボトムアップで実装し、最後にCIパイプラインに統合する。テストはPython hypothesis + subprocess でシェルスクリプトを検証する。 - -## タスク - -- [x] 1. 推定共通関数ライブラリの作成 - - [x] 1.1 `scripts/estimate_common.sh` を作成する - - グローバル変数(est_code, est_exp, est_fom, est_system, est_node_count, est_benchmark_*, est_current_*, est_future_*)を定義 - - `read_values` 関数: jqでResult_JSONからcode, exp, FOM, system, node_countを読み取りグローバル変数に設定 - - ファイル不在時・FOMフィールド欠落時はstderrにエラーメッセージを出力して `exit 1` - - `print_json` 関数: グローバル変数からresult_server互換のEstimate_JSONを標準出力に出力 - - `performance_ratio` は `est_current_fom / est_future_fom` をawkで計算(future_fomが0の場合は0を出力) - - _Requirements: 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 5.1, 5.2, 5.3, 5.4_ - - - [ ]* 1.2 Property 1: read_values ラウンドトリップテスト - - **Property 1: read_values ラウンドトリップ** - - hypothesisでランダムなcode, exp, FOM, system, node_countを生成し、一時Result_JSONを作成 - - subprocess経由で `read_values` を実行し、グローバル変数の値が元のJSON値と一致することを検証 - - **Validates: Requirements 1.1** - - - [ ]* 1.3 Property 2: print_json フォーマット完全性テスト - - **Property 2: print_json フォーマット完全性** - - hypothesisでランダムなグローバル変数値を生成し、subprocess経由で `print_json` を実行 - - 出力が有効なJSONであり、全必須フィールド(code, exp, benchmark_system, benchmark_fom, benchmark_nodes, current_system, future_system, performance_ratio)が存在し、値が一致することを検証 - - **Validates: Requirements 1.2, 1.3, 1.4, 5.1, 5.2, 5.3** - - - [ ]* 1.4 Property 3: performance_ratio 計算の正確性テスト - - **Property 3: performance_ratio 計算の正確性** - - hypothesisで正の数値ペア(current_fom, future_fom)を生成し、`print_json` 出力のperformance_ratioが `current_fom / future_fom` と浮動小数点精度の範囲内で一致することを検証 - - **Validates: Requirements 5.4** - - - [ ]* 1.5 read_values エラーケースのユニットテスト - - 存在しないファイルパスを渡した場合の非ゼロ終了コード確認 - - FOMフィールドを含まないJSONを渡した場合の非ゼロ終了コード確認 - - _Requirements: 1.5, 1.6_ - -- [x] 2. アプリケーション別推定スクリプトの作成 - - [x] 2.1 `programs/qws/estimate.sh` を作成する - - `source scripts/estimate_common.sh` で共通関数を読み込み - - 第1引数としてResult_JSONファイルパスを受け取る - - `read_values` でベンチマーク結果を読み取り - - ダミー推定モデル: current_fom = FOM × 10, future_fom = FOM × 2, method = "scale-mock" - - `print_json` で `results/estimate__0.json` に出力 - - _Requirements: 2.1, 2.2, 2.3, 2.4, 2.5, 2.6, 6.1, 7.1, 7.2, 7.3_ - - - [ ]* 2.2 Property 7: ダミー推定モデルのスケーリングテスト - - **Property 7: ダミー推定モデルのスケーリング** - - hypothesisで正のFOM値を生成し、一時Result_JSONを作成 - - subprocess経由で `programs/qws/estimate.sh` を実行 - - 出力JSONの current_system.fom = FOM × 10, future_system.fom = FOM × 2, method = "scale-mock" を検証 - - **Validates: Requirements 7.2, 7.3** - -- [x] 3. チェックポイント - 共通基盤とアプリ別スクリプトの動作確認 - - すべてのテストが通ることを確認し、不明点があればユーザーに質問してください。 - -- [x] 4. CI実行用ラッパーと送信スクリプトの作成 - - [x] 4.1 `scripts/run_estimate.sh` を作成する - - 第1引数としてプログラムコード名を受け取る - - `programs//estimate.sh` の存在確認(不在時は警告メッセージ出力して正常終了) - - `results/result*.json` を検出し、各ファイルに対して `estimate.sh` を実行 - - result*.jsonが存在しない場合は警告メッセージ出力して正常終了 - - 推定結果ファイルの存在確認メッセージ出力 - - _Requirements: 9.1, 9.2, 9.3, 9.4_ - - - [ ]* 4.2 Property 9: run_estimate.sh の全結果処理テスト - - **Property 9: run_estimate.sh の全結果処理** - - hypothesisでresult*.jsonファイル数(1〜5)を生成し、一時ディレクトリにダミーResult_JSONを配置 - - subprocess経由で `run_estimate.sh` を実行し、各result*.jsonに対してestimate.shが1回ずつ呼び出されることを検証 - - **Validates: Requirements 9.2** - - - [x] 4.3 `scripts/send_estimate.sh` を作成する - - `results/` 内の `estimate*.json` を検出し、各ファイルを `/api/ingest/estimate` にPOST - - 環境変数 `RESULT_SERVER` からサーバURL、`RESULT_SERVER_KEY` からAPIキーを取得 - - ファイルが存在しない場合は警告メッセージ出力して正常終了 - - HTTP POST失敗時は非ゼロ終了 - - _Requirements: 3.1, 3.2, 3.3, 3.4, 3.5, 3.6_ - - - [ ]* 4.4 Property 8: send_estimate.sh の全ファイル送信テスト - - **Property 8: send_estimate.sh の全ファイル送信** - - hypothesisでestimate*.jsonファイル数(1〜5)を生成し、モックサーバ(またはcurlモック)で送信を検証 - - 送信されたファイル数が元のファイル数と一致することを検証 - - **Validates: Requirements 3.1, 3.2** - -- [x] 5. チェックポイント - ラッパーと送信スクリプトの動作確認 - - すべてのテストが通ることを確認し、不明点があればユーザーに質問してください。 - -- [x] 6. job_functions.sh への推定ジョブ生成関数追加 - - [x] 6.1 `scripts/job_functions.sh` に推定関連の定数と関数を追加する - - `ESTIMATE_SYSTEMS="MiyabiG,RC_GH200"` 定数を追加 - - `is_estimate_target` 関数: システムが `ESTIMATE_SYSTEMS` に含まれるか判定 - - `has_estimate_script` 関数: `$1/estimate.sh` が存在するか判定 - - `emit_estimate_job` 関数: estimate ジョブのYAMLブロックを出力(tags: general, needs: send_results, script: bash scripts/run_estimate.sh) - - `emit_send_estimate_job` 関数: send_estimate ジョブのYAMLブロックを出力(tags: fncx-curl-jq, needs: estimate, script: bash scripts/send_estimate.sh) - - YAML生成ルール準拠: scriptセクションはシンプルに - - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5, 4.6, 4.7, 4.8_ - -- [x] 7. matrix_generate.sh への推定ステージ統合 - - [x] 7.1 `scripts/matrix_generate.sh` にestimate/send_estimateステージと推定ジョブ生成ロジックを追加する - - stagesに `estimate` と `send_estimate` を追加 - - 既存の `emit_send_results_job` 呼び出し直後に、`has_estimate_script` && `is_estimate_target` の条件で `emit_estimate_job` と `emit_send_estimate_job` を呼び出す - - cross/native両モードで同様の処理を追加 - - _Requirements: 4.1, 4.2, 4.3, 4.4, 4.5_ - - - [ ]* 7.2 Property 5: 推定ジョブ生成の条件判定テスト - - **Property 5: 推定ジョブ生成の条件判定** - - hypothesisでプログラム名とシステム名の組み合わせを生成し、estimate.shの有無とシステムがESTIMATE_SYSTEMSに含まれるかの条件で、生成YAMLに推定ジョブが含まれる/含まれないことを検証 - - **Validates: Requirements 2.7, 4.4, 4.5** - - - [ ]* 7.3 Property 6: 生成YAMLの構造正当性テスト - - **Property 6: 生成YAMLの構造正当性** - - 推定対象の組み合わせで生成されたYAMLをパースし、estimateステージとsend_estimateステージの存在、needsの依存関係が正しいことを検証 - - **Validates: Requirements 4.1, 4.2, 4.3** - -- [x] 8. チェックポイント - YAML生成の動作確認 - - すべてのテストが通ることを確認し、不明点があればユーザーに質問してください。 - -- [x] 9. .gitlab-ci.yml の変更とUUID推定モード - - [x] 9.1 `.gitlab-ci.yml` に `estimate_uuid` 変数と推定モード用ジョブを追加する - - variables に `estimate_uuid: ""` を追加 - - `generate_estimate_matrix` ジョブ: `estimate_uuid != ""` の場合のみ実行、`bash scripts/generate_estimate_from_uuid.sh` を呼び出し - - `trigger_estimate_pipeline` ジョブ: 生成されたYAMLで子パイプラインをトリガー - - 既存の `generate_matrix` と `trigger_child_pipeline` のrulesに `estimate_uuid != ""` → `when: never` を追加 - - _Requirements: 8.1, 8.2, 8.3, 8.4_ - - - [x] 9.2 `scripts/generate_estimate_from_uuid.sh` を作成する - - `estimate_uuid` と `code` の両方が指定されていることを確認(未指定時はエラー終了) - - fetch → estimate → send_estimate の3ステージYAMLを `.gitlab-ci.estimate.yml` に生成 - - YAML生成ルール準拠: scriptセクションはシンプルに - - _Requirements: 8.1, 8.2, 8.3, 8.4, 8.5_ - - - [x] 9.3 `scripts/fetch_result_by_uuid.sh` を作成する - - `estimate_uuid` と `code` の両方が設定されていることを確認(未設定時はエラー終了) - - Result_Serverから指定UUIDのResult_JSONを取得し `results/result0.json` に保存 - - _Requirements: 8.2, 8.5_ - -- [x] 10. Estimate_JSON と result_server の互換性テスト - - [ ]* 10.1 Property 4: Estimate_JSON ↔ result_server 互換性テスト - - **Property 4: Estimate_JSON と result_server の互換性** - - hypothesisで有効なEstimate_JSONを生成し、一時ファイルに書き出し - - `load_estimated_results_table` で読み込んだ結果の行が、元のJSONの各フィールド(code, exp, benchmark_system, benchmark_fom, benchmark_nodes, systemA_*, systemB_*, performance_ratio)と一致することを検証 - - Python直接テスト(subprocess不要) - - **Validates: Requirements 5.5** - -- [x] 11. 最終チェックポイント - 全テスト通過確認 - - すべてのテストが通ることを確認し、不明点があればユーザーに質問してください。 - -## 備考 - -- `*` マーク付きタスクはオプショナルで、MVP実装時にはスキップ可能 -- 各タスクは対応する要件番号を参照しており、トレーサビリティを確保 -- プロパティテストは対応する実装タスクの直後に配置し、早期にバグを検出 -- シェルスクリプトのテストはPython hypothesis + subprocess で実施 -- Property 4(result_server互換性)のみPython直接テスト diff --git a/.kiro/specs/ci-pipeline-refactor/.config.kiro b/.kiro/specs/ci-pipeline-refactor/.config.kiro deleted file mode 100644 index 2e719c3..0000000 --- a/.kiro/specs/ci-pipeline-refactor/.config.kiro +++ /dev/null @@ -1 +0,0 @@ -{"specId": "c5f152a7-fd4c-4209-83f5-451d7f08d790", "workflowType": "requirements-first", "specType": "feature"} \ No newline at end of file diff --git a/.kiro/specs/ci-pipeline-refactor/design.md b/.kiro/specs/ci-pipeline-refactor/design.md deleted file mode 100644 index 71a3441..0000000 --- a/.kiro/specs/ci-pipeline-refactor/design.md +++ /dev/null @@ -1,491 +0,0 @@ -# 設計文書: CI基盤リファクタリングと堅牢化 - -## 概要 (Overview) - -本設計は、CIパイプライン基盤のリファクタリングを行い、以下の5つの主要変更を実現する: - -1. **List_CSVの簡素化**: `system,enable,nodes,numproc_node,nthreads,elapse` の6カラム形式に変更。mode/queue_groupをList_CSVから削除し、enableカラムを追加する -2. **System_CSVの拡張**: mode/queue_groupカラムを追加し、システム設定を一元管理する -3. **Run_Scriptの引数統一**: genesis/genesis-nonbonded-kernelsのrun.shからList_CSV読み込みを廃止し、4引数(system, nodes, numproc_node, nthreads)ベースに統一する -4. **BK_Functions共通関数**: `scripts/bk_functions.sh` にFOM/SECTION/OVERLAP出力の標準化関数を作成する -5. **Matrix_Generator/Test_Submit対応**: 新CSV形式に対応するよう既存スクリプトを更新する - -### 設計判断の根拠 - -- **mode/queue_groupのSystem_CSV集約**: 同一システムのmode/queue_groupは全プログラムで共通であるため、各list.csvに重複定義する必要がない。System_CSVに集約することで単一の真実の源(Single Source of Truth)を実現する -- **enableカラム**: `#`コメントアウトは機械的なパースが困難で、意図が不明確。明示的なyes/noフラグにより、プログラム的なフィルタリングが容易になる -- **引数ベースRun_Script**: Run_ScriptがList_CSVを直接読む現在の設計は、呼び出し元(Matrix_Generator)との責任分離が不明確。引数ベースにすることで、Run_Scriptは純粋な実行ロジックに集中できる -- **bk_functions.sh**: 各Run_Scriptで独自にFOM行を構築している現状は、フォーマットの不整合リスクがある。共通関数で標準化することで、Result_Parser(result.sh)との互換性を保証する - -## アーキテクチャ (Architecture) - -### 現在のデータフロー - -```mermaid -graph TD - A[programs/*/list.csv
7カラム: system,mode,queue_group,...] --> B[matrix_generate.sh] - C[config/system.csv
4カラム: system,tag,roles,queue] --> B - D[config/queue.csv] --> B - B --> E[.gitlab-ci.generated.yml] - E --> F[GitLab CI Runner] - F --> G[programs/*/run.sh] - G -->|FOM行を直接echo| H[results/result] - H --> I[scripts/result.sh] - I --> J[result*.json] -``` - -### 変更後のデータフロー - -```mermaid -graph TD - A[programs/*/list.csv
6カラム: system,enable,nodes,...] --> B[matrix_generate.sh] - C[config/system.csv
6カラム: system,mode,tag_build,tag_run,queue,queue_group] --> B - D[config/queue.csv] --> B - B --> E[.gitlab-ci.generated.yml] - E --> F[GitLab CI Runner] - F --> G[programs/*/run.sh
引数: system nodes numproc_node nthreads] - G -->|source| H[scripts/bk_functions.sh] - H -->|bk_emit_result/section/overlap| I[results/result] - I --> J[scripts/result.sh] - J --> K[result*.json] -``` - -### 変更影響範囲 - -| ファイル | 変更種別 | 概要 | -|---------|---------|------| -| `config/system.csv` | 修正 | 1システム1行形式に再設計(mode, tag_build, tag_run, queue, queue_group) | -| `programs/*/list.csv` (5ファイル) | 修正 | 6カラム形式に変更 | -| `scripts/bk_functions.sh` | 新規 | 共通関数ライブラリ | -| `scripts/job_functions.sh` | 修正 | parse_list_csv_line を6カラム対応に変更、get_system_info関数追加(1行からmode/tag_build/tag_run/queue/queue_group取得) | -| `scripts/matrix_generate.sh` | 修正 | 新CSV形式対応、enableフィルタリング、System_CSVからmode/queue_group取得 | -| `scripts/test_submit.sh` | 修正 | 新CSV形式対応 | -| `programs/genesis/run.sh` | 修正 | List_CSV読み込み削除、引数ベースに変更 | -| `programs/genesis-nonbonded-kernels/run.sh` | 修正 | List_CSV読み込み削除、引数ベースに変更 | - -## コンポーネントとインターフェース (Components and Interfaces) - -### 1. List_CSV(新形式) - -``` -system,enable,nodes,numproc_node,nthreads,elapse -Fugaku,yes,1,4,12,0:10:00 -FugakuLN,no,1,1,1,0:10:00 -``` - -- 6カラム固定 -- enableは `yes` または `no` のみ有効 -- `#`コメント行は使用しない - -### 2. System_CSV(新形式: 1システム1行) - -``` -system,mode,tag_build,tag_run,queue,queue_group -Fugaku,cross,fugaku_login1,fugaku_jacamar,FJ,small -FugakuLN,native,,fugaku_login1,none,small -FugakuCN,native,,fugaku_jacamar,FJ,small -RC_GH200,native,,cloud_jacamar,SLURM_RC_GH200,dummy -RC_DGXS,native,,cloud_jacamar,SLURM_RC_DGXS,dummy -RC_GENOA,native,,cloud_jacamar,SLURM_RC_GENOA,dummy -MiyabiG,cross,miyabi_g_login,miyabi_g_jacamar,PBS_Miyabi,debug-g -MiyabiC,cross,miyabi_c_login,miyabi_c_jacamar,PBS_Miyabi,debug-c -``` - -- 1システム1行(旧形式の複数行を統合) -- `mode=cross`: `tag_build`でビルドジョブ、`tag_run`でランジョブを生成 -- `mode=native`: `tag_run`のみ使用(build_runジョブ)、`tag_build`は空 -- 旧`roles`カラムは廃止(modeから自動判定) -- Matrix_Generatorはシステム名で検索し、modeに応じてタグを使い分ける - -### 3. bk_functions.sh - -`source scripts/bk_functions.sh` でRun_Scriptから読み込む。 - -#### bk_emit_result - -```bash -# 名前付き引数 -bk_emit_result --fom 12.345 --fom-version DDSolverJacobi --exp CASE0 \ - --nodes 1 --numproc-node 4 --nthreads 12 -# 出力: FOM:12.345 FOM_version:DDSolverJacobi Exp:CASE0 node_count:1 numproc_node:4 nthreads:12 - -# --fomのみ(最小) -bk_emit_result --fom 12.345 -# 出力: FOM:12.345 - -# --confidentialオプション -bk_emit_result --fom 12.345 --confidential TeamA -# 出力: FOM:12.345 confidential:TeamA -``` - -- `--fom` は必須。未指定時はstderrにエラーを出力し、exit code 1を返す -- `--fom` に非数値が渡された場合もstderrにエラーを出力し、exit code 1を返す -- その他の引数は省略可能。省略時は対応するkey:valueペアを出力しない -- POSIX互換(jq不要) - -#### bk_emit_section - -```bash -# 位置引数2つ -bk_emit_section compute_kernel 0.30 -# 出力: SECTION:compute_kernel time:0.30 -``` - -- 引数不足時はstderrにエラーを出力し、exit code 1を返す -- time値が非数値の場合もstderrにエラーを出力し、exit code 1を返す - -#### bk_emit_overlap - -```bash -# 位置引数2つ(セクション名はカンマ区切り) -bk_emit_overlap compute_kernel,communication 0.05 -# 出力: OVERLAP:compute_kernel,communication time:0.05 -``` - -- 引数不足時はstderrにエラーを出力し、exit code 1を返す -- time値が非数値の場合もstderrにエラーを出力し、exit code 1を返す - -### 4. job_functions.sh の変更 - -#### parse_list_csv_line(変更) - -```bash -# 変更前: 7引数(system, mode, queue_group, nodes, numproc_node, nthreads, elapse) -# 変更後: 6引数(system, enable, nodes, numproc_node, nthreads, elapse) -parse_list_csv_line "$system" "$enable" "$nodes" "$numproc_node" "$nthreads" "$elapse" -# エクスポート変数: csv_system, csv_enable, csv_nodes, csv_numproc_node, csv_nthreads, csv_elapse -``` - -- ヘッダ行(`system`で始まる行)はスキップ(return 1) -- enable値が `yes` でも `no` でもない場合、stderrに警告を出力しスキップ(return 1) -- enable値が `no` の場合もスキップ(return 1) - -#### get_system_mode(新規) - -```bash -# System_CSVからmodeを取得(1システム1行なので直接取得) -get_system_mode "Fugaku" -# 出力: cross -``` - -- システム名で検索し、2カラム目のmode値を返す - -#### get_system_queue_group(新規) - -```bash -# System_CSVからqueue_groupを取得 -get_system_queue_group "Fugaku" -# 出力: small -``` - -- システム名で検索し、6カラム目のqueue_group値を返す - -#### get_system_tag_build(新規) - -```bash -# System_CSVからビルド用タグを取得 -get_system_tag_build "Fugaku" -# 出力: fugaku_login1 -``` - -- mode=crossの場合に使用。mode=nativeの場合は空文字を返す - -#### get_system_tag_run(新規) - -```bash -# System_CSVから実行用タグを取得 -get_system_tag_run "Fugaku" -# 出力: fugaku_jacamar -``` - -- cross/native両方で使用。nativeの場合はbuild_runジョブのタグとして使用 - -### 5. matrix_generate.sh の変更 - -主な変更点: -- `parse_list_csv_line` の呼び出しを6カラム対応に変更 -- mode/queue_groupを `get_system_mode` / `get_system_queue_group` で取得 -- tag_build/tag_runを `get_system_tag_build` / `get_system_tag_run` で取得(旧awkベースのタグ検索を置き換え) -- enableフィルタリングは `parse_list_csv_line` 内で処理(`no`の行はスキップ) -- `get_queue_template` はqueue値を使うので変更不要(System_CSVのqueue列から取得) - -### 6. Run_Script の変更 - -#### genesis/run.sh - -```bash -# 変更前: $1=system のみ、list.csvからnodes等を読み込み -# 変更後: $1=system, $2=nodes, $3=numproc_node, $4=nthreads -system="$1" -nodes="$2" -numproc_node="$3" -nthreads="$4" -numproc=$(( numproc_node * nodes )) -totalcores=$(( numproc * nthreads )) -``` - -#### genesis-nonbonded-kernels/run.sh - -```bash -# 変更前: $1=system のみ、list.csvからnodes等を読み込み -# 変更後: $1=system, $2=nodes, $3=numproc_node, $4=nthreads -system="$1" -nodes="$2" -numproc_node="$3" -nthreads="$4" -numproc=$(( numproc_node * nodes )) -``` - -### 7. test_submit.sh の変更 - -- 新しい6カラムList_CSV形式に対応 -- mode/queue_group/tag情報をSystem_CSVから取得する関数を使用 -- 旧rolesベースのタグ検索を廃止 - -## データモデル (Data Models) - -### List_CSV スキーマ - -| カラム | 型 | 必須 | 説明 | -|-------|-----|------|------| -| system | string | ○ | システム名(Fugaku, FugakuLN等) | -| enable | enum(yes,no) | ○ | ジョブ有効/無効 | -| nodes | integer | ○ | ノード数 | -| numproc_node | integer | ○ | ノードあたりプロセス数 | -| nthreads | integer | ○ | スレッド数 | -| elapse | time_string | ○ | 実行時間制限(H:MM:SS形式) | - -### System_CSV スキーマ - -| カラム | 型 | 必須 | 説明 | -|-------|-----|------|------| -| system | string | ○ | システム名(1システム1行) | -| mode | enum(cross,native) | ○ | 実行モード | -| tag_build | string | △ | ビルド用GitLab Runnerタグ(cross時は必須、native時は空) | -| tag_run | string | ○ | 実行用GitLab Runnerタグ(native時はbuild_run用) | -| queue | string | ○ | キュー名(queue.csvのキーに対応) | -| queue_group | string | ○ | キューグループ名 | - -### bk_emit_result 出力フォーマット - -``` -FOM:<数値> [FOM_version:<文字列>] [Exp:<文字列>] [node_count:<整数>] [numproc_node:<整数>] [nthreads:<整数>] [confidential:<文字列>] -``` - -- スペース区切りのkey:valueペア -- FOMは必須、他はオプション -- Result_Parser(result.sh)の既存パースロジック(`grep -Eo 'KEY:[ ]*VALUE'`)と互換 - -### bk_emit_section 出力フォーマット - -``` -SECTION:<セクション名> time:<数値> -``` - -### bk_emit_overlap 出力フォーマット - -``` -OVERLAP:<カンマ区切りセクション名> time:<数値> -``` - -### System_CSV マッピング(具体値) - -| system | mode | tag_build | tag_run | queue | queue_group | -|--------|------|-----------|---------|-------|-------------| -| Fugaku | cross | fugaku_login1 | fugaku_jacamar | FJ | small | -| FugakuLN | native | | fugaku_login1 | none | small | -| FugakuCN | native | | fugaku_jacamar | FJ | small | -| RC_GH200 | native | | cloud_jacamar | SLURM_RC_GH200 | dummy | -| RC_DGXS | native | | cloud_jacamar | SLURM_RC_DGXS | dummy | -| RC_GENOA | native | | cloud_jacamar | SLURM_RC_GENOA | dummy | -| MiyabiG | cross | miyabi_g_login | miyabi_g_jacamar | PBS_Miyabi | debug-g | -| MiyabiC | cross | miyabi_c_login | miyabi_c_jacamar | PBS_Miyabi | debug-c | - - -## 正当性プロパティ (Correctness Properties) - -*プロパティとは、システムの全ての有効な実行において真であるべき特性や振る舞いのことである。プロパティは、人間が読める仕様と機械的に検証可能な正当性保証の橋渡しをする。* - -以下のプロパティは、prework分析に基づいて冗長性を排除し統合したものである。 - -### Property 1: parse_list_csv_lineの6カラムパース正当性 - -*For any* 有効な6カラムCSV行(system, enable, nodes, numproc_node, nthreads, elapse)に対して、`parse_list_csv_line` を呼び出した場合、エクスポートされる変数 `csv_system`, `csv_enable`, `csv_nodes`, `csv_numproc_node`, `csv_nthreads`, `csv_elapse` は入力値と一致すること(前後空白のトリムを除く)。 - -**Validates: Requirements 1.3, 8.1, 8.9** - -### Property 2: enableフィルタリング - -*For any* CSV行に対して、`parse_list_csv_line` が成功(return 0)するのは enable 値が `yes` の場合のみであり、enable 値が `no` の場合はスキップ(return 1)されること。 - -**Validates: Requirements 2.2, 2.3, 8.2** - -### Property 3: 不正なenable値の拒否 - -*For any* `yes` でも `no` でもない文字列が enable カラムに指定された場合、`parse_list_csv_line` はスキップ(return 1)し、stderrに警告メッセージを出力すること。 - -**Validates: Requirements 2.4** - -### Property 4: numproc/totalcoresの算術導出 - -*For any* 正の整数 nodes, numproc_node, nthreads に対して、numproc は `numproc_node × nodes` と等しく、totalcores は `numproc × nthreads` と等しいこと。 - -**Validates: Requirements 3.3, 3.6** - -### Property 5: bk_emit_resultの出力正当性 - -*For any* 有効な引数の組み合わせ(--fom は必須の数値、他はオプション)に対して、`bk_emit_result` の出力は提供された引数に対応するkey:valueペアのみを含み、`FOM:<値>` が先頭に来ること。省略された引数に対応するkey:valueペアは出力に含まれないこと。 - -**Validates: Requirements 4.2, 4.6, 4.7** - -### Property 6: bk_functions数値バリデーション - -*For any* 数値が期待される引数(bk_emit_resultの--fom、bk_emit_sectionのtime、bk_emit_overlapのtime)に非数値文字列が渡された場合、関数はstderrにエラーメッセージを出力し、exit code 1を返すこと。 - -**Validates: Requirements 4.5, 5.5, 6.5** - -### Property 7: bk_emit出力とResult_Parserの往復互換性 - -*For any* 有効なFOM値、セクション名、時間値に対して、`bk_emit_result` / `bk_emit_section` / `bk_emit_overlap` が出力した行を `result.sh` のパースロジックで処理した場合、元の値が正しく抽出されること。 - -**Validates: Requirements 4.9, 5.7, 6.7** - -### Property 8: bk_emit_sectionの出力フォーマット - -*For any* 有効なセクション名と数値の時間値に対して、`bk_emit_section` の出力は `SECTION:<名前> time:<時間>` の形式であること。 - -**Validates: Requirements 5.2, 5.6** - -### Property 9: bk_emit_overlapの出力フォーマット - -*For any* 有効なカンマ区切りセクション名と数値の時間値に対して、`bk_emit_overlap` の出力は `OVERLAP:<セクション名> time:<時間>` の形式であること。 - -**Validates: Requirements 6.2, 6.6** - -### Property 10: System_CSVのモード・タグ整合性 - -*For any* System_CSVの行に対して、modeが `cross` の場合はtag_buildとtag_runが両方非空であり、modeが `native` の場合はtag_buildが空でtag_runが非空であること。 - -**Validates: Requirements 7.2, 7.3** - -### Property 11: System_CSVからのmode/tag/queue_group検索 - -*For any* システム名に対して、`get_system_mode`、`get_system_tag_build`、`get_system_tag_run`、`get_system_queue_group` は、System_CSVの該当行から正しい値を返すこと。 - -**Validates: Requirements 7.5, 7.6, 8.3, 8.4** - -### Property 12: YAML生成のモード別ジョブ構造 - -*For any* 有効な構成(system, nodes, numproc_node, nthreads)に対して、modeが `cross` の場合は分離されたbuildジョブとrunジョブが生成され、modeが `native` の場合はbuild_runジョブが生成されること。 - -**Validates: Requirements 8.5, 8.6** - -## エラーハンドリング (Error Handling) - -### bk_functions.sh - -| エラー条件 | 対応 | 終了コード | -|-----------|------|-----------| -| `bk_emit_result` で `--fom` 未指定 | stderrにエラーメッセージ出力 | 1 | -| `bk_emit_result` で `--fom` に非数値 | stderrにエラーメッセージ出力 | 1 | -| `bk_emit_section` で引数不足 | stderrにエラーメッセージ出力 | 1 | -| `bk_emit_section` でtime値が非数値 | stderrにエラーメッセージ出力 | 1 | -| `bk_emit_overlap` で引数不足 | stderrにエラーメッセージ出力 | 1 | -| `bk_emit_overlap` でtime値が非数値 | stderrにエラーメッセージ出力 | 1 | -| 不明な名前付き引数 | 無視(将来の拡張性のため) | 0 | - -### parse_list_csv_line - -| エラー条件 | 対応 | 終了コード | -|-----------|------|-----------| -| ヘッダ行(`system`で始まる) | スキップ | 1 | -| enable値が `yes` でも `no` でもない | stderrに警告出力、スキップ | 1 | -| enable値が `no` | スキップ | 1 | - -### get_system_mode / get_system_tag_build / get_system_tag_run / get_system_queue_group - -| エラー条件 | 対応 | 終了コード | -|-----------|------|-----------| -| 指定システムがSystem_CSVに存在しない | 空文字を返す | 0 | - -### matrix_generate.sh - -| エラー条件 | 対応 | -|-----------|------| -| mode/queue_groupが空(System_CSVに未定義) | 警告を出力しその構成をスキップ | -| テンプレートが見つからない | 警告を出力しその構成をスキップ | - -## テスト戦略 (Testing Strategy) - -### テストアプローチ - -本機能では、ユニットテストとプロパティベーステストの二重アプローチを採用する。 - -- **ユニットテスト**: 具体的な例、エッジケース、エラー条件の検証 -- **プロパティベーステスト**: 全入力に対する普遍的プロパティの検証 - -### プロパティベーステスト設定 - -- **ライブラリ**: [Hypothesis](https://hypothesis.readthedocs.io/) (Python) を使用 - - シェルスクリプトの関数テストは、Pythonから `subprocess` でシェル関数を呼び出す形式で実施 - - CSVパースやSystem_CSV検索のロジックはPythonでも再実装可能だが、実際のシェル関数をテストすることで実装との乖離を防ぐ -- **反復回数**: 各プロパティテストは最低100回実行 -- **タグ形式**: `Feature: ci-pipeline-refactor, Property {number}: {property_text}` -- **各正当性プロパティは1つのプロパティベーステストで実装する** - -### ユニットテスト - -以下のケースをユニットテストでカバーする: - -1. **List_CSVファイル構造検証** (Requirements 1.1, 1.2, 1.4, 2.1, 2.5) - - 各プログラムのlist.csvが正しい6カラムヘッダを持つこと - - mode/queue_groupカラムが存在しないこと - - `#`コメント行が存在しないこと - -2. **マイグレーション値保存検証** (Requirements 1.5, 2.6, 2.7) - - 既存の構成値(nodes, numproc_node, nthreads, elapse)が保存されていること - - 旧コメント行がenable=noに変換されていること - - 旧アクティブ行がenable=yesに変換されていること - -3. **System_CSVファイル構造検証** (Requirements 7.1, 7.7) - - 正しい6カラムヘッダを持つこと - - mode/queue_group値が旧List_CSVの値と一致すること - -4. **Run_Script構造検証** (Requirements 3.1, 3.2, 3.4, 3.5) - - genesis/run.shがlist.csvを読み込まないこと - - genesis-nonbonded-kernels/run.shがlist.csvを読み込まないこと - -5. **bk_functions.sh存在検証** (Requirements 4.1, 4.8, 5.1, 6.1) - - ファイルが存在すること - - jqへの依存がないこと - - 3つの関数が定義されていること - -6. **bk_emit_result エッジケース** (Requirements 4.3, 4.4) - - `--fom` 未指定時のエラー出力とexit code 1 - - `--confidential` オプションの動作 - -7. **bk_emit_section/overlap エッジケース** (Requirements 5.3, 5.4, 6.3, 6.4) - - 引数不足時のエラー出力とexit code 1 - -8. **YAML生成の機能等価性検証** (Requirements 8.8) - - 同一の有効構成に対して、新旧のYAML出力が機能的に等価であること - -9. **test_submit.sh対応検証** (Requirements 8.10) - - 新しい6カラム形式を正しくパースすること - -### プロパティベーステスト - -| Property | テスト内容 | 生成戦略 | -|----------|-----------|---------| -| P1 | parse_list_csv_lineの6カラムパース | ランダムなシステム名、数値、時間文字列を生成 | -| P2 | enableフィルタリング | enable=yes/noをランダムに生成し、戻り値を検証 | -| P3 | 不正enable値の拒否 | yes/no以外のランダム文字列を生成 | -| P4 | numproc/totalcores算術 | ランダムな正の整数を生成し、計算結果を検証 | -| P5 | bk_emit_result出力 | ランダムな数値と文字列の組み合わせを生成 | -| P6 | 数値バリデーション | 非数値文字列をランダム生成し、拒否を検証 | -| P7 | emit/parse往復互換性 | ランダムなFOM値・セクション名・時間値を生成し、emit→parseの往復を検証 | -| P8 | bk_emit_section出力 | ランダムなセクション名と時間値を生成 | -| P9 | bk_emit_overlap出力 | ランダムなカンマ区切り名と時間値を生成 | -| P10 | System_CSVロール整合性 | ランダムなSystem_CSV行を生成し、ロールとmode/queue_groupの整合性を検証 | -| P11 | mode/queue_group検索 | ランダムなSystem_CSVデータを生成し、検索結果を検証 | -| P12 | YAML生成モード別構造 | ランダムな構成とモードを生成し、生成されたYAMLのジョブ構造を検証 | diff --git a/.kiro/specs/ci-pipeline-refactor/requirements.md b/.kiro/specs/ci-pipeline-refactor/requirements.md deleted file mode 100644 index f57c5f6..0000000 --- a/.kiro/specs/ci-pipeline-refactor/requirements.md +++ /dev/null @@ -1,148 +0,0 @@ -# 要件文書: CI基盤リファクタリングと堅牢化 - -## はじめに - -本文書は、CIパイプライン基盤のリファクタリングと堅牢化に関する要件を定義する。主な目的は以下の通り: - -1. list.csvからmode/queue_groupカラムを廃止し、システム設定(system.csv)に責任を集約する -2. enableカラムの導入によるジョブ有効/無効の明示的管理 -3. run.sh内のlist.csv読み込みを廃止し、引数ベースのインターフェースに統一する -4. FOM/SECTION/OVERLAP出力を標準化する共通関数群(bk_functions.sh)を作成する -5. system.csvおよびmatrix_generate.shを新しいCSV形式に対応させる - -## 用語集 - -- **List_CSV**: 各プログラムディレクトリ(`programs/*/list.csv`)に配置されるCSVファイル。ベンチマーク実行構成(システム名、ノード数、プロセス数等)を定義する -- **System_CSV**: `config/system.csv` に配置されるCSVファイル。システムごとのタグ、ロール、キュー、実行モード等を定義する -- **Queue_CSV**: `config/queue.csv` に配置されるCSVファイル。キュー名とジョブ投入テンプレートを定義する -- **Matrix_Generator**: `scripts/matrix_generate.sh`。List_CSVとSystem_CSVを読み込み、GitLab CI用の動的YAMLを生成するスクリプト -- **Run_Script**: 各プログラムの `programs/*/run.sh`。ベンチマーク実行を行うスクリプト -- **BK_Functions**: `scripts/bk_functions.sh` に配置される共通関数ライブラリ。FOM出力等の標準化関数を提供する -- **Job_Functions**: `scripts/job_functions.sh` に配置される既存の共通関数ライブラリ。CSVパース等の関数を提供する -- **Result_Parser**: `scripts/result.sh`。Run_Scriptの出力(FOM行、SECTION行、OVERLAP行)をパースしてJSON形式に変換するスクリプト -- **Test_Submit**: `scripts/test_submit.sh`。手動テスト投入用スクリプト -- **FOM**: Figure of Merit。ベンチマーク性能指標値 -- **SECTION**: FOMの内訳を構成する時間区間(例: compute_kernel, communication) -- **OVERLAP**: 複数SECTIONが重複する時間区間 -- **mode**: ビルドと実行の方式を示す値。`cross`(ビルドと実行が別ノード)または `native`(同一ノードでビルドと実行) -- **queue_group**: ジョブスケジューラに投入する際のキューグループ名(例: small, debug-g) -- **enable**: ジョブ構成の有効/無効を示すカラム値。`yes` または `no` - -## 要件 - -### 要件1: List_CSVからmode/queue_groupカラムを廃止する - -**ユーザーストーリー:** 開発者として、List_CSVからmode/queue_groupカラムを削除したい。これにより、実行モードとキューグループの管理をSystem_CSVに一元化し、各プログラムのCSV管理を簡素化できる。 - -#### 受入基準 - -1. THE List_CSV SHALL have the header format `system,enable,nodes,numproc_node,nthreads,elapse` (6 columns) -2. THE List_CSV SHALL NOT contain `mode` or `queue_group` columns -3. WHEN a List_CSV file is read, THE Job_Functions SHALL parse 6 columns: system, enable, nodes, numproc_node, nthreads, elapse -4. THE List_CSV for each program (genesis, genesis-nonbonded-kernels, qws, LQCD_dw_solver, scale-letkf) SHALL be updated to the new 6-column format -5. WHEN the List_CSV is migrated, THE existing configuration values for nodes, numproc_node, nthreads, elapse SHALL be preserved unchanged - -### 要件2: enableカラムの追加 - -**ユーザーストーリー:** 開発者として、List_CSVにenableカラムを追加したい。これにより、`#`コメントアウトに代わる明示的なジョブ有効/無効管理が可能になる。 - -#### 受入基準 - -1. THE List_CSV SHALL contain an `enable` column as the second column with values `yes` or `no` -2. WHEN a List_CSV line has enable value `yes`, THE Matrix_Generator SHALL include the line in job generation -3. WHEN a List_CSV line has enable value `no`, THE Matrix_Generator SHALL skip the line -4. WHEN a List_CSV line has an enable value other than `yes` or `no`, THE Job_Functions SHALL treat the line as invalid and skip the line with a warning message to stderr -5. THE List_CSV SHALL NOT use `#` comment-out lines for disabling job configurations -6. WHEN migrating existing List_CSV files, THE previously commented-out lines (prefixed with `#`) SHALL be converted to lines with enable value `no` -7. WHEN migrating existing List_CSV files, THE previously active lines SHALL be converted to lines with enable value `yes` - -### 要件3: Run_Script内のList_CSV読み込みの廃止 - -**ユーザーストーリー:** 開発者として、genesis/run.shとgenesis-nonbonded-kernels/run.shからList_CSV読み込みロジックを削除したい。これにより、Run_Scriptのインターフェースが引数ベースに統一され、保守性が向上する。 - -#### 受入基準 - -1. THE genesis Run_Script SHALL accept 4 positional arguments: system, nodes, numproc_node, nthreads -2. THE genesis Run_Script SHALL NOT read List_CSV files -3. THE genesis Run_Script SHALL derive numproc and totalcores from the positional arguments (numproc = numproc_node × nodes, totalcores = numproc × nthreads) -4. THE genesis-nonbonded-kernels Run_Script SHALL accept 4 positional arguments: system, nodes, numproc_node, nthreads -5. THE genesis-nonbonded-kernels Run_Script SHALL NOT read List_CSV files -6. THE genesis-nonbonded-kernels Run_Script SHALL derive numproc from the positional arguments (numproc = numproc_node × nodes) -7. WHEN the genesis Run_Script is invoked with 4 arguments, THE Run_Script SHALL produce the same FOM output as the current implementation -8. WHEN the genesis-nonbonded-kernels Run_Script is invoked with 4 arguments, THE Run_Script SHALL produce the same FOM output as the current implementation - - -### 要件4: bk_emit_result関数の作成 - -**ユーザーストーリー:** 開発者として、FOM出力を標準化するbk_emit_result関数を作成したい。これにより、各Run_ScriptでのFOM出力形式のばらつきを解消し、Result_Parserとの整合性を保証できる。 - -#### 受入基準 - -1. THE BK_Functions SHALL provide a `bk_emit_result` function in `scripts/bk_functions.sh` -2. THE `bk_emit_result` function SHALL accept named arguments: `--fom`, `--fom-version`, `--exp`, `--nodes`, `--numproc-node`, `--nthreads` -3. THE `bk_emit_result` function SHALL require the `--fom` argument as mandatory -4. WHEN `--fom` is not provided, THE `bk_emit_result` function SHALL print an error message to stderr and return exit code 1 -5. WHEN `--fom` is provided with a non-numeric value, THE `bk_emit_result` function SHALL print an error message to stderr and return exit code 1 -6. WHEN all required arguments are valid, THE `bk_emit_result` function SHALL output a single line in the format: `FOM: FOM_version: Exp: node_count: numproc_node: nthreads:` -7. WHEN optional arguments are omitted, THE `bk_emit_result` function SHALL omit the corresponding key-value pairs from the output line -8. THE BK_Functions SHALL NOT depend on jq or other non-POSIX tools -9. THE `bk_emit_result` output format SHALL be compatible with the existing Result_Parser (`scripts/result.sh`) parsing logic - -### 要件5: bk_emit_section関数の作成 - -**ユーザーストーリー:** 開発者として、SECTION出力を標準化するbk_emit_section関数を作成したい。これにより、FOM内訳の出力形式を統一し、Result_Parserとの整合性を保証できる。 - -#### 受入基準 - -1. THE BK_Functions SHALL provide a `bk_emit_section` function in `scripts/bk_functions.sh` -2. THE `bk_emit_section` function SHALL accept 2 positional arguments: section name and time value -3. WHEN the section name argument is missing, THE `bk_emit_section` function SHALL print an error message to stderr and return exit code 1 -4. WHEN the time value argument is missing, THE `bk_emit_section` function SHALL print an error message to stderr and return exit code 1 -5. WHEN the time value is not a valid number, THE `bk_emit_section` function SHALL print an error message to stderr and return exit code 1 -6. WHEN both arguments are valid, THE `bk_emit_section` function SHALL output a single line in the format: `SECTION: time: