Skip to content

ci: add isolated DSW SWE-bench release pipeline#7656

Draft
DennisYu07 wants to merge 8 commits into
mainfrom
agent/dsw-swe-release-pipeline
Draft

ci: add isolated DSW SWE-bench release pipeline#7656
DennisYu07 wants to merge 8 commits into
mainfrom
agent/dsw-swe-release-pipeline

Conversation

@DennisYu07

@DennisYu07 DennisYu07 commented Jul 24, 2026

Copy link
Copy Markdown
Collaborator

What this PR does

This PR adds an isolated, asynchronous release benchmark pipeline:

GitHub Release -> short DSW dispatch job -> PostgreSQL queue -> persistent Coordinator + 10 Harbor executors -> SWE grader -> OSS -> persistent Publisher -> originating Release

For every release.published event, the self-hosted DSW runner resolves the immutable Qwen Code tag/commit, freezes all 500 SWE-bench Verified instances, inserts the run plus Release callback metadata into PostgreSQL, records the pool run_id, and exits. The GitHub Actions job does not remain active for the benchmark duration.

The persistent DSW services then own execution and publication:

  • Coordinator: leases, heartbeats, expired-lease recovery, one infrastructure retry, counters, circuit breaker, and manifest completion.
  • Ten Executors: atomically claim one case each and run one isolated Harbor/Docker testbed at a time.
  • Publisher: watches terminal runs and actively updates the triggering Release plus its public JSON asset.

Harbor live trial directories use local NVMe because OSS FUSE does not support the POSIX chmod operations Harbor performs during trial initialization. Each completed attempt is copied to the dedicated OSS prefix. A score is published only when all 500 frozen instances have a unique terminal result and the run is SUCCEEDED; a quarantined run reports status/counts without a score.

This implementation is isolated from #7584 through a new workflow, runner label qwen-benchmark-dsw, suite dsw_release_swe_verified_v1, PostgreSQL database qwen_benchmark_dsw_release_v1, runtime root /mnt/workspace/qwen-benchmark-dsw-release-v1, OSS prefix /mnt/data/qwen-benchmark/dsw-release-v1, and dedicated Release markers.

Why it's needed

Qwen Code releases need a public, reproducible score and a reliable chain from release publication to execution and presentation. Keeping Actions alive for a 12–16 hour benchmark couples orchestration to a temporary CI process and temporary github.token. The asynchronous design lets GitHub perform only authenticated dispatch while the DSW pool owns long-running scheduling, recovery, validation, and final writeback.

Reviewer Test Plan

How to verify

  1. Confirm no changed path overlaps feat(benchmark): add release evaluation pipeline #7584 and all state/data identifiers use the DSW isolation namespace.
  2. Run shell syntax, Python parsing, workflow YAML parsing, git diff --check, and a secret-pattern scan.
  3. Confirm the DSW runner is online with self-hosted, Linux, X64, and qwen-benchmark-dsw.
  4. Confirm the persistent pool contains one Coordinator, ten Executors, and one Publisher.
  5. Publish an isolated prerelease targeting this branch. Confirm release.published automatically starts the branch workflow without manual DSW dispatch.
  6. Confirm Actions ends after resolving the Release and inserting exactly 500 PostgreSQL tasks with executor_count=10.
  7. Monitor leases, heartbeats, Docker resources, disk, attempts, grader results, and OSS artifacts until all 500 cases are terminal.
  8. Confirm infrastructure errors retry at most once; model/test failures do not retry; the circuit breaker stops new claims after an unaccepted terminal infra/execution failure.
  9. Confirm a SUCCEEDED run publishes version/commit, dataset revision, 500/500 completion, resolved/unresolved/error counts, score, run ID, and a JSON asset. Confirm a QUARANTINED run publishes no score.

Current observed validation:

  • Static workflow/script checks pass and no credential is stored in the branch.
  • Synthetic pool test: 500/500 terminal with ten executors, zero execution errors, and zero infrastructure failures.
  • Earlier real Harbor test: sympy__sympy-20590 completed SUCCEEDED/RESOLVED, reward 1.0, 40 trajectory steps, expected nightly Qwen Code version, and complete verifier output.
  • Branch-targeted prereleases were observed to trigger this branch's release.published workflow automatically.
  • The first full event run exposed OSS FUSE chmod incompatibility before container start. The run was stopped and quarantined; live Harbor work has been moved to local NVMe with per-attempt OSS archival.
  • A post-fix real-case preflight is running under the persistent DSW services before the next full 500-case event run.
  • The final full-suite Action, pool run, OSS evidence, and Release writeback links will be added after the clean rerun completes.

Evidence (Before & After)

N/A — CI and benchmark infrastructure only; no TUI behavior changes.

Tested on

OS Status
🍏 macOS ✅ static validation and Git workflow
🪟 Windows N/A
🐧 Linux ✅ DSW scheduler, Harbor/Docker, PostgreSQL, and OSS

Environment (optional)

Alibaba Cloud PAI DSW, 16 vCPU / 64 GiB, local NVMe Docker storage, OSS mounted at /mnt/data, PostgreSQL 16, Harbor Docker testbeds, Qwen Code v0.20.0-nightly.20260722.b98306b7e, model qwen3.7-max.

Risk & Scope

  • Main risk or tradeoff: full Verified runs depend on model capacity, npm/network availability, Docker image preparation, disk pressure, and repository-specific compilation. Publication therefore depends on PostgreSQL manifest reconciliation rather than CI process exit.
  • External prerequisite: the DSW pool services and scoped GitHub write credential are host configuration, not repository secrets.
  • Out of scope: PAI-Sandbox, Kubernetes, multi-node scaling, RDS migration, and public raw trajectories.
  • Breaking changes / migration notes: none. The workflow activates on published Releases only after the workflow exists at the Release target commit.

Linked Issues

Related to #7584, with intentionally separate code, state, data, and experiments.

中文说明

本 PR 做了什么

本 PR 新增一条相互隔离的异步 Release 跑分链路:

GitHub Release -> DSW 短派发任务 -> PostgreSQL 队列 -> 常驻 Coordinator + 10 个 Harbor Executor -> SWE grader -> OSS -> 常驻 Publisher -> 原 Release

release.published 触发后,DSW self-hosted runner 只负责解析不可变的 Qwen Code tag/commit、冻结 500 条 SWE-bench Verified manifest、把 run 和 Release 回调信息写入 PostgreSQL,并记录 run_id 后结束。GitHub Actions 不等待 12–16 小时的完整跑分。

后续由 DSW 常驻服务负责:

  • Coordinator:lease、heartbeat、过期接管、基础设施错误的一次重试、计数、熔断和整次 run 完成校验。
  • 10 个 Executor:从 PostgreSQL 原子领取 case,每个 Executor 同时运行一个独立 Harbor/Docker testbed。
  • Publisher:监听终态 run,主动更新触发它的 Release 和公开 JSON asset。

Harbor 实时工作目录使用本地 NVMe,因为 OSS FUSE 不支持 Harbor 初始化 trial 时执行的 POSIX chmod。每个 attempt 完成后逐文件归档到独立 OSS 前缀。只有 500 个冻结实例全部具有唯一终态且 run 为 SUCCEEDED 才公布分数;QUARANTINED 只公布状态和数量,不公布分数。

本实现通过新的 workflow、qwen-benchmark-dsw runner 标签、suite dsw_release_swe_verified_v1、数据库 qwen_benchmark_dsw_release_v1、runtime /mnt/workspace/qwen-benchmark-dsw-release-v1、OSS /mnt/data/qwen-benchmark/dsw-release-v1 和独立 Release 标记,与 #7584 完全隔离。

为什么需要

Release 跑分需要从发布、调度、执行、校验到公开展示的完整链路。让 GitHub Actions 持续等待长任务,会把运行生命周期绑定到临时 CI 进程和临时 github.token。异步方案让 GitHub 只做鉴权派发,DSW 资源池独立负责长任务调度、恢复、发布门禁和最终主动回写。

Reviewer 测试计划

如何验证

  1. 确认变更路径与 feat(benchmark): add release evaluation pipeline #7584 不重叠,状态和数据均使用 DSW 专用命名空间。
  2. 执行 shell、Python、YAML、空白和疑似密钥检查。
  3. 确认 DSW runner 在线且包含 qwen-benchmark-dsw 标签。
  4. 确认常驻服务包含 1 个 Coordinator、10 个 Executor 和 1 个 Publisher。
  5. 发布指向本分支的独立测试 prerelease,确认 release.published 自动触发,不人工 dispatch DSW。
  6. 确认 Actions 在写入 500 条 PostgreSQL task 和 Release 回调信息后立即结束。
  7. 持续监控 lease、heartbeat、Docker、磁盘、attempt、grader 和 OSS,直到 500 条全部终态。
  8. 确认 infra 最多重试一次,模型/测试失败不重试,出现不可接受的终态 infra/execution failure 后熔断停止领取新任务。
  9. 确认成功 run 回写版本、commit、数据集 revision、500/500、各类数量、分数、run ID 和 JSON asset;隔离 run 不公布分数。

当前验证:

  • workflow/script 静态检查通过,分支未存储任何凭证。
  • 合成调度测试使用 10 个 Executor 完成 500/500,0 execution error,0 infra failure。
  • 之前真实 Harbor sympy__sympy-20590SUCCEEDED/RESOLVED,reward 1.0,40 个 trajectory step,Qwen Code nightly 版本正确,verifier 产物完整。
  • 已实际观察到指向本分支的 prerelease 自动触发本分支 release.published workflow。
  • 第一次全量事件运行暴露了 OSS FUSE chmod 不兼容,发生在容器启动前;该运行已停止并隔离。Harbor 实时目录现已迁移到本地 NVMe,并在 attempt 结束后归档 OSS。
  • 修复后的真实单 case 正在常驻 DSW 服务中预检;通过后将重新启动干净的 500-case 全量事件运行。
  • 全量 Action、pool run、OSS 证据和 Release 回写链接会在干净重跑完成后补充。

风险与范围

  • 主要风险:模型容量、npm/网络、镜像准备、磁盘压力和跨 repo 编译长尾;因此发布以 PostgreSQL manifest 状态收敛为准,不以 CI 进程退出为准。
  • 外部前提:DSW 常驻 pool 和受控 GitHub 写凭证属于主机配置,不进入仓库。
  • 范围外:PAI-Sandbox、Kubernetes、多节点扩容、RDS 迁移和公开 raw trajectory。
  • Breaking change:无。只有 Release 目标 commit 中存在该 workflow 时才会响应发布事件。

@DennisYu07
DennisYu07 had a problem deploying to production-release July 24, 2026 06:43 — with GitHub Actions Failure
@DennisYu07

Copy link
Copy Markdown
Collaborator Author

Full 500-case DSW release-chain run completed and was written back to the test prerelease.

Public JSON validation passed: 500 records, 500 unique instance IDs, no duplicate IDs, and state totals match the Release summary. DSW SSH is currently unreachable, so final PostgreSQL-to-OSS alignment and unified root-cause analysis of the 56 execution errors / 5 infrastructure failures remain pending host access restoration. This was a non-production prerelease validation; do not merge based on the quarantined score.

@wenshao

wenshao commented Jul 25, 2026

Copy link
Copy Markdown
Collaborator

Review: ci: add isolated DSW SWE-bench release pipeline

Thanks for this — the async design (Actions dispatches, the persistent pool owns the 12–16h run) is the right call, and the isolation namespacing vs #7584 is thorough and consistent. The shell and Python are clean and defensive.

However, I found three blocking issues, the first of which would cause significant unintended cost the moment this lands. Details and reproductions below.


🔴 Blocking

1. The release.published trigger is unfiltered, and every release queues a full 500-instance run

INSTANCE_LIMIT is hardcoded to '500' for all release events, and there is no tag-family or prerelease guard:

on:
  release:
    types: [published]
...
INSTANCE_LIMIT: ${{ github.event_name == 'release' && '500' || inputs.instance_limit }}

This repo publishes 58 releases in the last 30 days (~2/day), across many tag families that have nothing to do with Qwen Code versions:

2026-07-24  dsw-swe-full-poc-20260724-046c1e5      <- your own POC prereleases (7 on this day alone)
2026-07-24  v0.21.0
2026-07-24  v0.20.1-nightly.20260724.7d17c44a3     <- nightly, every single day
2026-07-21  cua-driver-rs-v0.7.3                   <- Rust driver, not Qwen Code
2026-07-14  sdk-typescript-v0.1.8
2026-07-13  desktop-v0.0.5
2026-07-22  v0.20.0-preview.0

Two things make this concrete rather than theoretical:

  • release.yml creates the nightly prerelease with secrets.CI_BOT_PAT, carrying the explicit comment "CI_BOT_PAT required: GITHUB_TOKEN events cannot trigger downstream release-event workflows." These releases are deliberately built to fire release-event workflows.
  • Your own PR body confirms the prerelease path fires: "Branch-targeted prereleases were observed to trigger this branch's release.published workflow automatically."

So after merge, arrival rate (~2 runs/day, 9 on 2026-07-24) far exceeds drain rate (one 10-executor pool, 12–16h per 500-case run), and cancel-in-progress: false means nothing is ever dropped. On 2026-07-24 alone this would have queued 9 × 500 = 4,500 instances. A cua-driver-rs-v0.7.3 release would resolve fine through git fetch/git rev-list and dispatch a 500-case benchmark against a Rust driver tag.

cd-cua-driver.yml already establishes the fix pattern in this repo — scope to your own tag family:

    if: >-
      github.event_name != 'release' ||
      (github.event.release.prerelease == false &&
       startsWith(github.event.release.tag_name, 'v') &&
       !contains(github.event.release.tag_name, '-'))

Adjust to taste (you may want previews in, POC tags out), but the job must not run for every published release.

2. yamllint CI gate will fail — 40 errors

.yamllint.yml sets quoted-strings: {quote-type: single, required: true}, enforced in CI via node scripts/lint.js --yamllint over all tracked YAML. Baseline is clean; this file is not:

$ yamllint -c .yamllint.yml .github/workflows/ci.yml .github/workflows/release.yml .github/workflows/qwen-triage.yml
$ echo $?
0

$ yamllint -c .yamllint.yml .github/workflows/dsw-swe-verified-release.yml
  1:7   error  string value is not quoted with single quotes  (quoted-strings)
  5:13  error  string value is not quoted with single quotes  (quoted-strings)
  ... 40 errors total

Every scalar needs single quotes — name: 'DSW SWE-bench Verified Release', types: ['published'], runs-on: ['self-hosted', 'Linux', 'X64', 'qwen-benchmark-dsw'], etc. Compare sync-release-to-oss.yml.

3. workflow_dispatch with instance_limit of 5 or 500 always fails

instance_id has a non-empty default, and is forwarded whenever non-empty:

      instance_id:
        default: sympy__sympy-20590     # non-empty default
...
BENCHMARK_INSTANCE_ID: ${{ github.event_name == 'release' && '' || inputs.instance_id }}
if [[ -n "${BENCHMARK_INSTANCE_ID:-}" ]]; then
  manifest_args+=(--instance-id "${BENCHMARK_INSTANCE_ID}")
fi

But make-manifest.py hard-rejects that combination. Reproduced against a synthetic 500-instance dataset root:

$ python3 make-manifest.py --dataset-root ds500 --limit 500 --instance-id repo__proj-1 --output outA.json
--instance-id requires --limit 1
exit=1

So selecting instance_limit: 5 or 500 in the UI fails at manifest generation unless the operator remembers to manually clear a pre-filled text box. Given the run only becomes useful at 500, this breaks the primary manual path. Suggested fix:

BENCHMARK_INSTANCE_ID: ${{ (github.event_name != 'release' && inputs.instance_limit == '1') && inputs.instance_id || '' }}

🟡 Medium

  • permissions: contents: write is over-privileged. The job only reads a release via the REST API and fetches a tag — nothing writes using github.token, since the PR states the Publisher uses a separate host-configured credential. The closest analogue, sync-release-to-oss.yml, uses contents: 'read'. On a persistent self-hosted runner this is worth minimizing. Recommend contents: read.

  • The executor_count input is inert. It's a required choice (1/4/10), written into dispatch-receipt.json, and reported in the step summary — but it is never passed to pool submit (check the arg list; there's no --executor-count). Real concurrency is whatever the persistent pool runs. Either wire it through or drop it; as-is it's a control that silently does nothing, and the PR body's "inserting exactly 500 PostgreSQL tasks with executor_count=10" overstates it.

  • Idempotency key is CI-run-scoped, not release-scoped. BENCHMARK_IDEMPOTENCY_KEY: dsw-release-v1-${{ github.run_id }} correctly dedupes re-runs of the same run, but a workflow_dispatch targeting an already-benchmarked release gets a different key and creates a second run for the same release_id. Two terminal runs then race to write the same Release — and since manual dispatch defaults to instance_limit: '1', a one-case diagnostic can overwrite a published 500-case score. Consider keying on release id + instance count, or having the Publisher refuse to downgrade a full-suite result.

  • No repository guard. sync-release-to-oss.yml uses if: github.repository == 'QwenLM/qwen-code'. Without it, a fork that publishes a release queues a job that hangs until the 15-minute timeout waiting for a qwen-benchmark-dsw runner that doesn't exist. Cheap to add.

  • No tests, and this is the first Python in .github/scripts/. Every non-trivial script in that tree is .mjs with a colocated .test.mjsclassify-release-notes, pr-safety-precheck, resolve-sandbox-image, serve-ab-diff, web-shell-visuals-compose, web-shell-visuals-publish. This adds a new language with no test runner wired up and zero coverage. One test over the --instance-id/--limit interaction would have caught blocking issue 如何自定义密钥文件 .env可能与其他文件冲突 #3.


🔵 Minor

  • --infra-failure-threshold 0 means a single flaky Docker pull or network blip quarantines the entire 12–16h, 500-case run (with --max-attempts 2 allowing one retry first). Fail-closed is defensible for score integrity, but over a run this long it's likely to be hit — worth confirming it's intentional and that requeueing is cheap.
  • docs/design/dsw-swe-verified-release-pipeline.md documents the host credential path and its ownership/mode (/mnt/workspace/.../config/model.key, root:github-runner, 0640) in a public repo. No secret is exposed, but it's needless recon detail.
  • The draft guard test "$(jq -r .draft release.json)" = "false" fails with no diagnostic under set -e, and GET /releases/tags/{tag} doesn't resolve drafts anyway — so it's close to dead code.
  • make-manifest.py counts any directory whose name contains __, so a stray __pycache__ in dataset_root makes the count 501 and hard-fails the exact-500 gate. Fail-closed, but a marker-file check would be more robust.
  • The default DSN postgresql://qwen_benchmark@127.0.0.1:55432/... pins host topology in source. Not a secret, but config-shaped.

✅ Verified clean

Things I actively checked that hold up:

  • shellcheck -x on the dispatch script: clean. python3 -m py_compile: clean. actionlint: clean apart from the expected unknown-custom-runner-label warning.
  • The Benchmark-Qwen-Ref: override handles CRLF correctly. GitHub API release bodies come back with \r\n, and I expected the [[:space:]]*$ anchor to be a bug — it isn't. Byte-level check confirms the \r is consumed:
    $ printf 'Benchmark-Qwen-Ref: v0.20.0-nightly.20260722.b98306b7e\r\n' | sed -nE 's/^Benchmark-Qwen-Ref:...$/\1/p' | od -c
    0000000  v 0 . 2 0 . 0 - n i g h t l y . 2 0 2 6 0 7 2 2 . b 9 8 3 0 6 b 7 e \n
    
  • The override regex is anchored and admits no shell metacharacters, so the release-body → git fetch path isn't injectable (and release bodies are maintainer-controlled regardless). Correctly restricted to prereleases only.
  • actions/checkout is SHA-pinned with a version comment, matching repo convention.
  • The steps.releasesteps.dispatch env override chain is correct: RELEASE_ID/QWEN_REF/QWEN_COMMIT are all non-empty at the point the ${VAR:?} guards run.

Process note: overlap with #7584

#7584 ("feat(benchmark): add release evaluation pipeline", same author, 27 files, opened 2026-07-23) is still open and targets the same objective. Both hook release.published. The namespacing here is genuinely thorough, but if both land, every published release fires two independent full-suite benchmark pipelines against the same DSW host — doubling the cost problem in blocking issue #1 and leaving two pipelines to maintain. I'd suggest an explicit decision on which one is the intended production path before either merges, rather than landing an isolated duplicate.


中文说明

感谢这个 PR — 异步设计(Actions 只负责派发,常驻 pool 负责 12–16 小时的跑分)方向是对的,与 #7584 的隔离命名也做得很彻底一致,shell 和 Python 代码本身干净且有防御性。

但发现 3 个阻塞问题,其中第一个在合入后会立刻造成大量非预期开销。

🔴 阻塞

  1. release.published 没有任何过滤,每个 release 都会排入完整 500 实例运行。 INSTANCE_LIMIT 对所有 release 事件硬编码为 '500',且没有 tag 前缀或 prerelease 判断。本仓库最近 30 天发布了 58 个 release(约每天 2 个),涵盖 cua-driver-rs-v*desktop-v*sdk-typescript-v*,以及你自己的 dsw-swe-full-poc-*(仅 7-24 一天就有 7 个)。两点佐证这不是理论问题:release.ymlsecrets.CI_BOT_PAT 创建 nightly,并明确注释 "GITHUB_TOKEN events cannot trigger downstream release-event workflows",即这些 release 就是为了触发下游 release 事件而设计的;你的 PR 描述本身也确认 prerelease 会自动触发。到达速率(约 2 次/天,7-24 当天 9 次)远超消费速率(单个 10-executor pool,每次 12–16 小时),且 cancel-in-progress: false 不会丢弃任何一次 —— 仅 7-24 一天就会排入 9 × 500 = 4500 个实例。仓库里 cd-cua-driver.yml 已有现成写法:用 startsWith(github.event.release.tag_name, 'v') 之类的条件把 job 限定到自己的 tag 家族。

  2. yamllint CI 会失败,共 40 处错误。 .yamllint.yml 要求 quoted-strings: quote-type: single, required: true,CI 通过 node scripts/lint.js --yamllint 对所有 YAML 强制执行。现有 workflow 基线为 0 错误,本文件 40 个错误。所有标量都需要单引号,可参考 sync-release-to-oss.yml

  3. workflow_dispatch 选 5 或 500 时必然失败。 instance_id 有非空默认值 sympy__sympy-20590,只要非空就会传入,而 make-manifest.py 会硬性拒绝该组合。已用合成的 500 实例数据集本地复现:退出码 1,报错 --instance-id requires --limit 1。建议改为 ${{ (github.event_name != 'release' && inputs.instance_limit == '1') && inputs.instance_id || '' }}

🟡 中等

  • permissions: contents: write 权限过大 —— 该 job 只读 release、fetch tag,不用 github.token 做任何写操作(回写由 Publisher 的独立凭证完成)。对照 sync-release-to-oss.yml 使用 contents: 'read',建议改为 read。
  • executor_count 输入实际无效 —— 它写进了 receipt 和 step summary,但从未传给 pool submit,真实并发取决于常驻 pool。建议要么接通,要么移除。
  • 幂等键基于 CI run 而非 release —— workflow_dispatch 针对同一个已跑分的 release 会产生不同的 key,从而创建第二个 run;由于手动派发默认 instance_limit: '1',单 case 诊断结果可能覆盖已发布的 500 case 分数。
  • 缺少仓库守卫 —— 建议加 if: github.repository == 'QwenLM/qwen-code',否则 fork 发布 release 会让 job 挂到 15 分钟超时。
  • 没有测试,且是 .github/scripts/ 下第一个 Python —— 该目录下所有非平凡脚本都是 .mjs + 同名 .test.mjs。一个针对 --instance-id/--limit 交互的测试就能提前发现阻塞问题 3。

🔵 次要

--infra-failure-threshold 0 意味着单次 Docker/网络抖动即隔离整次 12–16 小时运行,请确认是有意为之;设计文档在公开仓库中写出了主机凭证路径与权限模式;draft 守卫在 set -e 下无诊断信息且该 API 本就不返回 draft;__pycache__ 之类目录会让实例计数变成 501 并硬失败;默认 DSN 把主机拓扑写进了源码。

✅ 已验证无问题

shellcheck -xpy_compileactionlint 均通过(仅自定义 runner label 警告,符合预期)。Benchmark-Qwen-Ref: 的 CRLF 处理是正确的 —— GitHub API 返回的 body 是 \r\n,我原本怀疑这是 bug,字节级验证确认 [[:space:]]*$ 已正确吸收 \r。该正则有锚定且不含 shell 元字符,不存在注入路径,且正确限制为仅 prerelease 生效。actions/checkout 已按仓库惯例 SHA pin。steps.releasesteps.dispatch 的环境变量传递链正确。

流程建议:与 #7584 重叠

#7584(同作者,27 个文件,2026-07-23 开启)仍处于 open,目标相同,同样挂在 release.published 上。若两者都合入,每次发布会触发 两条 完整跑分链路,在同一台 DSW 主机上把问题 1 的成本翻倍,并留下两套需要维护的实现。建议在任一合入前先明确哪条是正式生产链路,而不是合入一个"隔离的重复实现"。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants