Repository navigation
feat(recovery): one-command recovery PR and metadata-less reused ingest / feat(recovery):一条命令创建恢复 PR,并支持无元数据的复用入库 - #4502
Conversation
Add recover_failed_ingest create-pr (dry run by default) and let recover-reused-ingest.yml regenerate changelog metadata from original-merge-sha. 新增 recover_failed_ingest create-pr(默认仅预演),并允许 recover-reused-ingest.yml 通过 original-merge-sha 重新生成 changelog 元数据。
|
Generally most InferenceX submissions don't need to add changes to the infx package. On the off chance they do, generally the diff is small. Please prompt your agent to clean your PR to keep your diff to only necessary & minimal changes too. We have an increased code quality bar for changes to the infx package. 中文大多数 InferenceX 提交通常不需要修改 infx 包。即使确实需要,改动通常也很小。请让你的 agent 清理 PR,使改动只包含必要且最小的内容。infx 包的改动有更高的代码质量要求。 |
| runs-on: ubuntu-latest | ||
| permissions: | ||
| contents: read | ||
| steps: | ||
| - name: Trigger agentic database ingest | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| - name: Validate inputs | ||
| env: | ||
| SOURCE_RUN_ID: ${{ inputs.source-run-id }} | ||
| MERGE_RUN_ID: ${{ inputs.merge-run-id }} | ||
| ORIGINAL_MERGE_SHA: ${{ inputs.original-merge-sha }} | ||
| run: | | ||
| [[ "$SOURCE_RUN_ID" =~ ^[0-9]+$ ]] || { echo "::error::source-run-id must be numeric"; exit 1; } | ||
| if [[ -n "$MERGE_RUN_ID" && -n "$ORIGINAL_MERGE_SHA" ]] || [[ -z "$MERGE_RUN_ID" && -z "$ORIGINAL_MERGE_SHA" ]]; then | ||
| echo "::error::Set exactly one of merge-run-id or original-merge-sha" | ||
| exit 1 | ||
| fi | ||
| if [[ -n "$MERGE_RUN_ID" && ! "$MERGE_RUN_ID" =~ ^[0-9]+$ ]]; then | ||
| echo "::error::merge-run-id must be numeric" | ||
| exit 1 | ||
| fi | ||
| if [[ -n "$ORIGINAL_MERGE_SHA" && ! "$ORIGINAL_MERGE_SHA" =~ ^[0-9a-f]{40}$ ]]; then | ||
| echo "::error::original-merge-sha must be a full 40-character commit SHA" | ||
| exit 1 | ||
| fi | ||
|
|
||
| - name: Checkout original merge | ||
| if: inputs.original-merge-sha != '' | ||
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | ||
| with: | ||
| ref: ${{ inputs.original-merge-sha }} | ||
| fetch-depth: 0 | ||
| persist-credentials: false | ||
|
|
||
| - uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1 | ||
| if: inputs.original-merge-sha != '' | ||
|
|
||
| # Same metadata merge-ingest.yml's "Extract changelog metadata" step uploads, for the | ||
| # push that merged original-merge-sha (a squash merge's push before is its parent). | ||
| - name: Extract changelog metadata | ||
| id: plan | ||
| if: inputs.original-merge-sha != '' | ||
| env: | ||
| ORIGINAL_MERGE_SHA: ${{ inputs.original-merge-sha }} | ||
| PYTHONPATH: ${{ github.workspace }}/inferencex-e2e | ||
| run: | | ||
| git merge-base --is-ancestor "$ORIGINAL_MERGE_SHA" origin/main || { | ||
| echo "::error::$ORIGINAL_MERGE_SHA is not on main" | ||
| exit 1 | ||
| } | ||
| cd "$GITHUB_WORKSPACE/inferencex-e2e" | ||
| uv run --no-project --exclude-newer PT12H --python 3.12 --with pydantic --with pyyaml \ | ||
| python -m infx.matrix.plan \ | ||
| --changelog-file perf-changelog.yaml \ | ||
| --base-ref "${ORIGINAL_MERGE_SHA}^" \ | ||
| --head-ref "$ORIGINAL_MERGE_SHA" > "$RUNNER_TEMP/plan.json" | ||
| jq -c '.changelog_metadata' "$RUNNER_TEMP/plan.json" > "$GITHUB_WORKSPACE/changelog_metadata.json" | ||
| jq -e '(.entries // []) | length > 0' "$GITHUB_WORKSPACE/changelog_metadata.json" >/dev/null || { | ||
| echo "::error::$ORIGINAL_MERGE_SHA appended no changelog entries" | ||
| exit 1 | ||
| } | ||
| agentic=$(jq '((.single_node.agentic // []) + (.multi_node.agentic // [])) | length > 0' "$RUNNER_TEMP/plan.json") | ||
| echo "agentic=$agentic" >> "$GITHUB_OUTPUT" | ||
|
|
||
| - name: Upload changelog artifact | ||
| if: inputs.original-merge-sha != '' | ||
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | ||
| with: | ||
| name: changelog-metadata | ||
| path: changelog_metadata.json | ||
| if-no-files-found: error | ||
|
|
||
| - name: Trigger database ingest | ||
| uses: actions/github-script@3a2844b7e9c422d3c10d287c895573f7108da1b3 # v9.0.0 | ||
| env: | ||
| SOURCE_RUN_ID: ${{ inputs.source-run-id }} | ||
| # Without merge-run-id this run carries the regenerated changelog metadata. | ||
| MERGE_RUN_ID: ${{ inputs.merge-run-id || github.run_id }} | ||
| # A supplied merge run keeps the original agentic-only behavior. | ||
| AGENTIC: ${{ inputs.merge-run-id != '' && 'true' || steps.plan.outputs.agentic }} | ||
| with: | ||
| github-token: ${{ secrets.FRONTEND_PAT }} # zizmor: ignore[secrets-outside-env] | ||
| script: | | ||
| const agentic = process.env.AGENTIC === 'true'; | ||
| const client_payload = { | ||
| "source-run-id": process.env.SOURCE_RUN_ID, | ||
| "merge-run-id": process.env.MERGE_RUN_ID, | ||
| }; | ||
| if (agentic) client_payload["database-target"] = "production"; | ||
| await github.rest.repos.createDispatchEvent({ | ||
| owner: "SemiAnalysisAI", | ||
| repo: "InferenceX-app", | ||
| event_type: "ingest-agentic-results", | ||
| client_payload: { | ||
| "source-run-id": process.env.SOURCE_RUN_ID, | ||
| "merge-run-id": process.env.MERGE_RUN_ID, | ||
| "database-target": "production" | ||
| } | ||
| event_type: agentic ? "ingest-agentic-results" : "ingest-results", | ||
| client_payload, | ||
| }); |
There was a problem hiding this comment.
🟡 (optional) Operators using the new original-merge-sha path can push any run's results into the production database, with none of the reuse checks merge-ingest enforces. The "Validate inputs" step only checks that inputs are numeric or 40-hex. The dispatch then forwards source-run-id unchecked. Nothing confirms the run belongs to the merged PR, has unexpired results, avoids evals-only/agentx-fast, or that merge-ingest has not already succeeded for that SHA. Fix: before upload and dispatch, run the checks create-pr already does (resolve_reusable_run, check_source_belongs_to_pr, check_merge_ingest_runs), with the read permissions they need.
Why this was flagged
The skill doc (.claude/commands/recover-failed-ingest.md:59-64) tells operators to pass original-merge-sha whenever merge-ingest failed before it uploaded changelog-metadata. In merge-ingest.yml that covers runs stopped at "Resolve reused sweep"/"Require sweep reuse" (lines 39-55), i.e. merges with no valid /use authorization or an ineligible source. On the base branch, recover-reused-ingest could only dispatch with a merge-run-id whose changelog-metadata existed, and merge-ingest uploads that (line 87) only after the reuse gate passes. Now the new path regenerates metadata for any commit on main (recover-reused-ingest.yml:66-85). It then dispatches ingest-results/ingest-agentic-results with inputs.source-run-id (line 101) that nothing has validated. A wrong run ID, a source from another PR, an evals-only source, or a second dispatch for a merge whose ingest already succeeded all get written to production. The job only has contents: read (line 30), so it cannot run any of these checks today.
Verification: Trigger: an operator dispatches recover-reused-ingest.yml with original-merge-sha and a source-run-id that is wrong. The "Validate inputs" step only checks formats. "Trigger database ingest" then forwards SOURCE_RUN_ID: ${{ inputs.source-run-id }}. The base workflow also forwarded source-run-id unchecked. merge-ingest.yml checks reuse at lines 39-55.
Recovering a failed merge ingest (#4456 → #4499, #4442 → #4466) took a long manual procedure. This PR turns most of it into one command and covers merges whose ingest failed before uploading changelog metadata.
recover_failed_ingest create-pr --pr-number N --source-run-id IDis a dry run by default. It checks that the PR merged into main, that nomerge-ingest.ymlpush run for its merge commit is pending or succeeded, and that the source run passes the pinned/usereuse rules (resolve_reusable_run: completed, unexpired result artifacts, noevals-only/agentx-fast, trusted external dispatches allowed). The source must also have tested the original PR. The tool then extracts the merge commit's appended changelog bytes and prints the plan. With--executeit works in a temporary worktree: it createschore/recover-N-ingestwith an empty bootstrap commit, opens a bilingual PR, and appends the entries verbatim exceptpr-link. It then commits, runsvalidate_perf_changelogandmatrix.planagainstorigin/main, checks that only the changelog changed, and pushes.--post-usealso comments/use ID. The tool stops when anything is ambiguous: a non-prefix history rewrite, a link it cannot replace line for line, an entry already repeated on main, or an existing branch.recover-reused-ingest.ymlnow takes an optionaloriginal-merge-shaas an alternative tomerge-run-id. It checks out that commit and requires it to be on main. It then regenerateschangelog-metadatawithinfx.matrix.planfor<sha>^..<sha>, as merge-ingest's "Extract changelog metadata" step does, uploads it, and dispatchesingest-resultsoringest-agentic-resultswith this run asmerge-run-id. Dispatches that passmerge-run-idbehave as before.AI model disclosure
Prepared with Claude Opus 5.5 (
claude-opus-5-5), which wrote the code, tests, docs, and this description.中文
恢复失败的合并入库(#4456 → #4499、#4442 → #4466)原本需要一长串手动步骤。本 PR 把大部分步骤合并为一条命令,并支持入库在上传 changelog 元数据之前就失败的合并。
recover_failed_ingest create-pr --pr-number N --source-run-id ID默认只做预演。它检查 PR 已合并到 main、该合并提交没有进行中或已成功的merge-ingest.ymlpush 运行,并确认源运行符合固定/use的复用规则(resolve_reusable_run:已完成、结果制品未过期、无evals-only/agentx-fast,允许受信任的外部 dispatch)。源运行还必须测试过原 PR。随后工具提取合并提交追加的 changelog 字节并打印计划。加上--execute后,它在临时 worktree 中执行:创建带空引导提交的chore/recover-N-ingest,开启双语 PR,并逐字追加条目,仅替换pr-link。然后提交、针对origin/main运行validate_perf_changelog和matrix.plan、确认只改动了 changelog,再推送。--post-use还会发表/use ID评论。遇到任何歧义时工具会停止,例如历史并非前缀追加、链接无法逐行替换、main 上已有重复条目或分支已存在。recover-reused-ingest.yml新增可选的original-merge-sha,可代替merge-run-id。它检出该提交并要求其位于 main 上,然后像 merge-ingest 的 "Extract changelog metadata" 步骤一样,用infx.matrix.plan为<sha>^..<sha>重新生成changelog-metadata,上传后以本运行作为merge-run-id分派ingest-results或ingest-agentic-results。传入merge-run-id的分派行为不变。