Skip to content

Commit 1e44aa8

Browse files
authored
Simplify reusable sweep ingestion with direct source dispatch / 通过直接分发源运行简化复用扫描摄取 (#2244)
* fix: select latest reused sweep artifacts 中文:修复复用扫描产物的选择逻辑,只保留每个逻辑基准测试最新上传的产物。 * refactor: dispatch reusable sweep sources directly Remove the reusable artifact relay and send source and merge run IDs directly to InferenceX-app. Simplify manual recovery to the same two-ID dispatch and add structural workflow tests. 移除可复用工件中转流程,直接将源运行 ID 和合并运行 ID 发送到 InferenceX-app。手动恢复流程也简化为相同的双 ID 分发,并新增工作流结构测试。 * docs: update reusable ingest recovery flow Replace stale relay-job references with the direct source/merge dispatch and app-side preparation and validation steps. 将过时的中转作业引用替换为直接的源运行/合并运行分发,以及应用侧的工件准备和验证步骤。 * docs: simplify reusable ingest guidance Document that InferenceX-app downloads normal source artifacts, substitutes the merge changelog, and relies on existing ingestion behavior for failed rows. Remove obsolete reusable-artifact validation instructions.\n\n中文:说明 InferenceX-app 直接下载常规源运行产物、替换合并运行变更日志,并沿用现有摄取逻辑跳过失败结果。删除过时的复用产物验证说明。
1 parent 63e279c commit 1e44aa8

6 files changed

Lines changed: 74 additions & 318 deletions

File tree

.claude/commands/recover-failed-ingest.md

Lines changed: 22 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,8 @@ argument-hint: <failed-run-or-job-url | pr-number> [source-run-id]
44
---
55

66
Recover the official database ingest for a failed or skipped InferenceX
7-
push-to-main `Run Sweep` workflow by creating a recovery PR that reuses validated
8-
artifacts from an earlier PR sweep. Do not add a one-off recovery workflow.
7+
push-to-main `Run Sweep` workflow by creating a recovery PR that reuses artifacts
8+
from an earlier PR sweep. Do not add a one-off recovery workflow.
99

1010
Inputs from `$ARGUMENTS`:
1111

@@ -29,9 +29,8 @@ Run from a clean InferenceX checkout with authenticated `gh`, `git`, `jq`, and
2929
`.github/workflows/run-sweep.yml` on `main` whose official ingest did not
3030
complete.
3131
- Reuse only a completed `pull_request` run of `run-sweep.yml`. Unpinned reuse
32-
requires success. A specifically pinned failed run is allowed only when
33-
artifact validation proves its available result set is internally consistent;
34-
only completed points are recovered.
32+
requires success. A specifically pinned failed run is allowed; normal
33+
ingestion skips failed benchmark rows, so only completed points are recovered.
3534
- The source run must belong to the original PR being recovered.
3635
- Stop if that PR changed the recovered configuration's execution semantics
3736
after the source SHA: image, model, recipe, runner, launcher, benchmark
@@ -111,10 +110,10 @@ TARGET_RUN_ID=<matching-run-id>
111110
Require event `push`, workflow path `.github/workflows/run-sweep.yml`, and branch
112111
`main`; confirm the target is no longer running before recovering. The
113112
disqualifying state is broader than `failure`/`skipped`: when `/reuse-sweep-run`
114-
was forgotten before merge, `reuse-ingest-artifacts` is skipped, the GPU jobs run
115-
(often `cancelled` to save cost), and because `collect-results`/`collect-evals`
116-
are not skipped, `trigger-ingest` still fires `always()` and lands a *bogus*
117-
ingest under the target's own `run_id`. So a target showing
113+
was forgotten before merge, setup leaves reuse disabled, the GPU jobs run (often
114+
`cancelled` to save cost), and because `collect-results`/`collect-evals` are not
115+
skipped, `trigger-ingest` still fires `always()` and lands a *bogus* ingest under
116+
the target's own `run_id`. So a target showing
118117
`trigger-ingest=success` (and concluding `success` or `cancelled`) can still hold
119118
no valid benchmark data — recovery is required. That bogus row is keyed on the
120119
target `run_id` and is superseded by the recovery ingest under a new `run_id`;
@@ -132,7 +131,7 @@ git diff "$ORIGINAL_BASE_SHA" "$ORIGINAL_MERGE_SHA" -- \
132131
perf-changelog.yaml
133132
```
134133

135-
## 2. Select and validate the source run
134+
## 2. Select the source run
136135

137136
Find candidates from the original PR branch when no run ID was supplied:
138137

@@ -238,7 +237,7 @@ Keep exactly one of `full-sweep-enabled`,
238237
`non-canary-full-sweep-enabled`, `full-sweep-fail-fast`, or
239238
`full-sweep-fail-fast-no-canary`.
240239

241-
## 5. Append the recovery changelog and validate source artifacts
240+
## 5. Append and validate the recovery changelog
242241

243242
Append recovery entries to the end of `perf-changelog.yaml`. Preserve the
244243
original entries' `config-keys`, `description`, `evals-only`, and
@@ -281,61 +280,6 @@ python3 utils/process_changelog.py \
281280
Confirm the generated config contains only the intended recovery scope. Its row
282281
counts may differ from the source run.
283282

284-
Download only the result artifacts needed for local validation. This avoids the
285-
large server-log artifacts retained in the official ingest bundle. Raw per-config
286-
`bmk_<model>_*` artifacts are intentionally not selected — they fall through the
287-
`case` below; the aggregate `results_bmk` is what the validator reads:
288-
289-
```bash
290-
rm -rf /tmp/source-artifacts
291-
ARTIFACT_ARGS=()
292-
while IFS= read -r name; do
293-
case "$name" in
294-
results_bmk|eval_results_all|run-stats|bmk_agentic_*|agentic_*)
295-
ARTIFACT_ARGS+=(-n "$name")
296-
;;
297-
eval_server_logs_*|eval_gpu_metrics_*)
298-
;;
299-
eval_*)
300-
ARTIFACT_ARGS+=(-n "$name")
301-
;;
302-
esac
303-
done < <(
304-
gh api \
305-
"repos/SemiAnalysisAI/InferenceX/actions/runs/$SOURCE_RUN_ID/artifacts?per_page=100" \
306-
--paginate --jq '.artifacts[] | select(.expired == false) | .name' |
307-
sort -u
308-
)
309-
310-
((${#ARTIFACT_ARGS[@]})) || {
311-
echo "No unexpired result artifacts found" >&2
312-
exit 1
313-
}
314-
gh run download "$SOURCE_RUN_ID" \
315-
--repo SemiAnalysisAI/InferenceX \
316-
-D /tmp/source-artifacts \
317-
"${ARTIFACT_ARGS[@]}"
318-
```
319-
320-
Validate the source artifacts:
321-
322-
```bash
323-
python3 utils/validate_reusable_sweep_artifacts.py \
324-
--artifacts-dir /tmp/source-artifacts
325-
```
326-
327-
The validator first collapses reran (flaky) eval duplicates in place — keeping
328-
the latest result per eval identity when a retried eval left duplicate raw dirs
329-
/ aggregate rows — so a legitimate rerun does not fail validation. It only
330-
collapses identities with a clear latest result; genuinely ambiguous duplicates
331-
are still rejected.
332-
333-
The validator does not compare source coverage with
334-
`/tmp/recovery-full-config.json`. It rejects duplicate fixed rows, missing run
335-
stats, inconsistent agentic artifacts, malformed eval metadata, raw/aggregate
336-
eval mismatches, or an empty result set. For a pinned failed batched eval run,
337-
only `completed_eval_concs` are recovered.
338-
339283
## 6. Attach the source SHA without changing the tree
340284

341285
Make the ancestry carrier the final branch commit. `git commit-tree` guarantees
@@ -449,15 +393,15 @@ The push-to-main `Run Sweep` must:
449393
- run `setup` even if the merge message contains `[skip-sweep]`;
450394
- resolve the recovery PR and pinned source run;
451395
- set `reuse-enabled=true`;
452-
- pass `reuse-ingest-artifacts` consistency validation;
453396
- upload recovery changelog metadata;
454-
- run `trigger-ingest`.
397+
- dispatch `source-run-id` and `merge-run-id` from `trigger-ingest`.
455398

456399
Then locate the resulting `repository_dispatch` run in
457400
`SemiAnalysisAI/InferenceX-app`. In the forgotten-`/reuse` case the target's
458401
bogus ingest is also a recent successful `ingest-results` run, so do not pick by
459-
recency — pick the run whose `Download artifacts from InferenceX run` step logs
460-
`RUN_ID: <RECOVERY_RUN_ID>`:
402+
recency — pick the run whose `Prepare artifacts from InferenceX` step logs both
403+
the expected source run and recovery merge run. That app workflow must download
404+
the source artifacts, substitute the merge changelog, and complete ingestion:
461405

462406
```bash
463407
gh run list --repo SemiAnalysisAI/InferenceX-app \
@@ -467,19 +411,22 @@ gh run list --repo SemiAnalysisAI/InferenceX-app \
467411

468412
INGEST_RUN_ID=<candidate-run-id>
469413
gh run view "$INGEST_RUN_ID" --repo SemiAnalysisAI/InferenceX-app --log \
470-
| grep -m1 "RUN_ID: $RECOVERY_RUN_ID" # must match before you trust this run
414+
| grep -m1 "Source run: $SOURCE_RUN_ID" # must match before you trust this run
415+
416+
gh run view "$INGEST_RUN_ID" --repo SemiAnalysisAI/InferenceX-app --log \
417+
| grep -m1 "Merge run: $RECOVERY_RUN_ID" # must also match
471418

472419
gh run watch "$INGEST_RUN_ID" \
473420
--repo SemiAnalysisAI/InferenceX-app --exit-status
474421
```
475422

476-
The ingest's first step is a `sleep 300` "wait for source run to finish", so the
477-
run idles ~5 minutes before doing work — that is normal, not a hang.
423+
Reused ingests skip the normal five-minute wait because the source run is already
424+
complete.
478425

479426
Verify its logs identify `RECOVERY_RUN_ID` as the trigger and `SOURCE_RUN_ID`
480427
plus `SOURCE_RUN_ATTEMPT` as the reused source. Require successful artifact
481-
download, flattening, database ingest, run overrides, database verification,
482-
cache invalidation, and unmapped-entity checks.
428+
download, database ingest, run overrides, database verification, cache
429+
invalidation, and unmapped-entity checks.
483430

484431
Post a final recovery PR comment with the original failed or skipped run/job,
485432
source run/attempt/SHA, recovery merge run, downstream ingest run, recovered

.github/workflows/README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -177,21 +177,21 @@ in the PR. A run ID can pin an eligible successful or failed run:
177177
/reuse-sweep-run <run_id>
178178
```
179179

180-
Failed-run artifacts must still validate. The latest matching comment by an
181-
`OWNER`, `MEMBER`, or `COLLABORATOR` wins. Comments do not trigger or cancel
182-
sweeps; later commits skip a new sweep after changelog/matrix validation.
180+
The latest matching comment by an `OWNER`, `MEMBER`, or `COLLABORATOR` wins.
181+
Comments do not trigger or cancel sweeps; later commits skip a new sweep after
182+
changelog/matrix validation.
183183
Remove and re-add the sweep label to force one.
184184

185185
`utils/merge_with_reuse.sh <pr-number>` is the supported merge path for reuse.
186186
It merges `main`, preserves changelog bytes, fixes an appended `XXX` PR link,
187187
pushes a synchronization commit, waits for checks, then merges.
188188

189-
The main run verifies the source, validates and uploads its ingest artifacts,
190-
then ingests them with merge-run changelog metadata. Source coverage is
189+
The main run passes the selected source run ID and its own merge run ID directly
190+
to InferenceX-app. The app downloads source artifacts, keeps the newest upload
191+
for each exact artifact name, and ingests them with changelog metadata from the
192+
merge run. The normal ingestion code skips failed benchmark rows. Benchmark
193+
rows and public links retain source-run provenance. Source coverage is
191194
authoritative, so later matrix/eval policy changes do not invalidate reuse.
192-
Validation rejects duplicate fixed rows, missing run stats, inconsistent
193-
agentic artifacts, malformed eval metadata, and raw/aggregate eval mismatches.
194-
Batched evals use only `completed_eval_concs`.
195195

196196
Reuse fails closed when authorized but ineligible or invalid; without
197197
authorization, `main` runs the normal full sweep.

.github/workflows/recover-reused-ingest.yml

Lines changed: 4 additions & 146 deletions
Original file line numberDiff line numberDiff line change
@@ -4,158 +4,16 @@ on:
44
workflow_dispatch:
55
inputs:
66
source-run-id:
7-
description: Source PR sweep run ID
8-
required: true
9-
type: string
10-
source-run-attempt:
11-
description: Source PR sweep attempt
12-
required: true
13-
default: "1"
14-
type: string
15-
source-pr-number:
16-
description: Source PR number
17-
required: true
18-
type: string
19-
source-head-sha:
20-
description: Source sweep commit SHA
7+
description: Source PR sweep run ID containing benchmark artifacts
218
required: true
229
type: string
2310
merge-run-id:
24-
description: Original failed merge run ID
11+
description: Main publication run ID containing changelog metadata
2512
required: true
2613
type: string
27-
merge-run-attempt:
28-
description: Original failed merge run attempt
29-
required: true
30-
default: "1"
31-
type: string
3214

3315
jobs:
34-
package-reused-artifacts:
35-
runs-on: blacksmith-4vcpu-ubuntu-2404
36-
permissions:
37-
contents: read
38-
steps:
39-
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
40-
41-
- name: Download source artifacts
42-
env:
43-
GH_TOKEN: ${{ secrets.REPO_PAT || github.token }}
44-
SOURCE_RUN_ID: ${{ inputs.source-run-id }}
45-
ARTIFACTS_PATH: ${{ github.workspace }}/source-artifacts
46-
run: |
47-
mkdir -p "$ARTIFACTS_PATH"
48-
49-
download_artifact() {
50-
local name="$1"
51-
local url="$2"
52-
local zip_path="$RUNNER_TEMP/artifact.zip"
53-
54-
echo "Downloading artifact: $name"
55-
for attempt in 1 2 3; do
56-
if gh api "$url" > "$zip_path"; then
57-
mkdir -p "$ARTIFACTS_PATH/$name"
58-
if unzip -oq "$zip_path" -d "$ARTIFACTS_PATH/$name"; then
59-
rm -f "$zip_path"
60-
return 0
61-
fi
62-
rm -rf "${ARTIFACTS_PATH:?}/$name"
63-
fi
64-
rm -f "$zip_path"
65-
echo " Attempt $attempt/3 failed, retrying in ${attempt}s..."
66-
sleep "$attempt"
67-
done
68-
return 1
69-
}
70-
71-
gh api "repos/${{ github.repository }}/actions/runs/${SOURCE_RUN_ID}/artifacts?per_page=100" --paginate --slurp \
72-
| jq -r '
73-
[.[].artifacts[]
74-
| select(.name != "changelog-metadata")
75-
| . + {logical_name: (.name | sub("_[A-Za-z][A-Za-z0-9.-]*_[0-9]+$"; ""))}
76-
]
77-
| group_by(.logical_name)
78-
| map(sort_by(.created_at) | last)[]
79-
| "\(.name)\t\(.archive_download_url)"' \
80-
| while IFS=$'\t' read -r name url; do
81-
if ! download_artifact "$name" "$url"; then
82-
echo "::error::Failed to download source artifact: $name"
83-
exit 1
84-
fi
85-
done
86-
87-
if [ -z "$(find "$ARTIFACTS_PATH" -mindepth 1 -maxdepth 1 -print -quit)" ]; then
88-
echo "::error::No source artifacts downloaded from run $SOURCE_RUN_ID"
89-
exit 1
90-
fi
91-
92-
- name: Record reuse provenance
93-
env:
94-
ARTIFACTS_PATH: ${{ github.workspace }}/source-artifacts
95-
SOURCE_RUN_ID: ${{ inputs.source-run-id }}
96-
SOURCE_RUN_ATTEMPT: ${{ inputs.source-run-attempt }}
97-
SOURCE_PR_NUMBER: ${{ inputs.source-pr-number }}
98-
SOURCE_HEAD_SHA: ${{ inputs.source-head-sha }}
99-
MERGE_RUN_ID: ${{ inputs.merge-run-id }}
100-
MERGE_RUN_ATTEMPT: ${{ inputs.merge-run-attempt }}
101-
run: |
102-
mkdir -p "$ARTIFACTS_PATH/reused-ingest-metadata"
103-
jq -n \
104-
--arg source_run_id "$SOURCE_RUN_ID" \
105-
--arg source_run_attempt "$SOURCE_RUN_ATTEMPT" \
106-
--arg source_run_url "${{ github.server_url }}/${{ github.repository }}/actions/runs/$SOURCE_RUN_ID" \
107-
--arg source_pr_number "$SOURCE_PR_NUMBER" \
108-
--arg source_head_sha "$SOURCE_HEAD_SHA" \
109-
--arg ingest_run_id "$MERGE_RUN_ID" \
110-
--arg ingest_run_attempt "$MERGE_RUN_ATTEMPT" \
111-
--arg ingest_run_url "${{ github.server_url }}/${{ github.repository }}/actions/runs/$MERGE_RUN_ID" \
112-
'{
113-
source_run_id: $source_run_id,
114-
source_run_attempt: $source_run_attempt,
115-
source_run_url: $source_run_url,
116-
source_pr_number: $source_pr_number,
117-
source_head_sha: $source_head_sha,
118-
ingest_run_id: $ingest_run_id,
119-
ingest_run_attempt: $ingest_run_attempt,
120-
ingest_run_url: $ingest_run_url
121-
}' > "$ARTIFACTS_PATH/reused-ingest-metadata/reuse_source_run.json"
122-
123-
- name: Validate reusable artifacts
124-
run: |
125-
python3 utils/validate_reusable_sweep_artifacts.py \
126-
--artifacts-dir "${{ github.workspace }}/source-artifacts"
127-
128-
- name: Download original merge changelog
129-
env:
130-
GH_TOKEN: ${{ secrets.REPO_PAT || github.token }}
131-
MERGE_RUN_ID: ${{ inputs.merge-run-id }}
132-
CHANGELOG_PATH: ${{ github.workspace }}/merge-changelog
133-
run: |
134-
url=$(gh api "repos/${{ github.repository }}/actions/runs/${MERGE_RUN_ID}/artifacts?per_page=100" --paginate --slurp \
135-
| jq -r '[.[].artifacts[] | select(.name == "changelog-metadata")] | sort_by(.created_at) | last | .archive_download_url // empty')
136-
if [ -z "$url" ]; then
137-
echo "::error::No changelog-metadata artifact found on run $MERGE_RUN_ID"
138-
exit 1
139-
fi
140-
mkdir -p "$CHANGELOG_PATH"
141-
gh api "$url" > "$RUNNER_TEMP/changelog.zip"
142-
unzip -oq "$RUNNER_TEMP/changelog.zip" -d "$CHANGELOG_PATH"
143-
rm -f "$RUNNER_TEMP/changelog.zip"
144-
145-
- name: Upload reusable ingest artifacts
146-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
147-
with:
148-
name: reused-ingest-artifacts
149-
path: source-artifacts/*
150-
151-
- name: Upload merge changelog
152-
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
153-
with:
154-
name: changelog-metadata
155-
path: merge-changelog/*
156-
15716
trigger-agentic-ingest:
158-
needs: package-reused-artifacts
15917
runs-on: ubuntu-latest
16018
steps:
16119
- name: Trigger agentic database ingest
@@ -167,8 +25,8 @@ jobs:
16725
-d '{
16826
"event_type": "ingest-agentic-results",
16927
"client_payload": {
170-
"run-id": "${{ github.run_id }}",
171-
"run-attempt": "${{ github.run_attempt }}",
28+
"source-run-id": "${{ inputs.source-run-id }}",
29+
"merge-run-id": "${{ inputs.merge-run-id }}",
17230
"database-target": "production"
17331
}
17432
}'

0 commit comments

Comments
 (0)