Skip to content

feat(recovery): one-command recovery PR and metadata-less reused ingest / feat(recovery):一条命令创建恢复 PR,并支持无元数据的复用入库 - #4502

Merged
Oseltamivir merged 2 commits into
mainfrom
feat/recover-ingest-create-pr
Oct 25, 2026
Merged

Oseltamivir merged 2 commits into
mainfrom
feat/recover-ingest-create-pr

Conversation

@Oseltamivir

Copy link
Copy Markdown
Collaborator

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 ID is a dry run by default. It checks that the PR merged into main, that no merge-ingest.yml push run for its merge commit is pending or succeeded, and that the source run passes the pinned /use reuse rules (resolve_reusable_run: completed, unexpired result artifacts, no evals-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 --execute it works in a temporary worktree: it creates chore/recover-N-ingest with an empty bootstrap commit, opens a bilingual PR, and appends the entries verbatim except pr-link. It then commits, runs validate_perf_changelog and matrix.plan against origin/main, checks that only the changelog changed, and pushes. --post-use also 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.yml now takes an optional original-merge-sha as an alternative to merge-run-id. It checks out that commit and requires it to be on main. It then regenerates changelog-metadata with infx.matrix.plan for <sha>^..<sha>, as merge-ingest's "Extract changelog metadata" step does, uploads it, and dispatches ingest-results or ingest-agentic-results with this run as merge-run-id. Dispatches that pass merge-run-id behave as before.
  • The recover-failed-ingest skill documents both paths.
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.yml push 运行,并确认源运行符合固定 /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 的分派行为不变。
  • recover-failed-ingest 技能文档已说明这两种路径。

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 元数据。
@github-actions

Copy link
Copy Markdown
Contributor

inferencex-e2e/infx diff: +510 -0

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 包的改动有更高的代码质量要求。

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nothing blocking. The comments below are optional suggestions. There is no need to push a fix for them before merging.

Comment on lines 28 to 120
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,
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 (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.

@Oseltamivir
Oseltamivir merged commit 31c6ed5 into main Oct 25, 2026
4 checks passed
@Oseltamivir
Oseltamivir deleted the feat/recover-ingest-create-pr branch October 25, 2026 10:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

1 participant