From a33ed4ddf8c762e98759b7725f5095046ca270b4 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 20 Aug 2026 13:15:58 +0800 Subject: [PATCH 1/4] ci: stop PR gray checks from lifecycle and release publish jobs Remove the three skipped (gray) checks from the PR check panel without changing functional semantics: - issue-lifecycle: remove the job-level 'if' that skipped the lifecycle job on non-changes-requested pull_request_review events, so it now runs and reports success (the lifecycle handler already no-ops for approved/commented reviews). The changes-requested board transition is unchanged. - release.yml / release-vendor.yml: drop the publish job (and its workflow_dispatch 'publish' input + RELEASE_PUBLISH pass-through) so it no longer appears as a skipped Publish-to-npm check on PRs; the files keep the pack job that validates tarballs on PR/push. - new release-publish.yml / release-vendor-publish.yml: manual workflow_dispatch only, repack on the current tree then publish, so publication behaves exactly as the old publish job (explicit dispatch, uses the packed bytes) but never shows as a PR check. Update the 2026-08-10 review-status note (en/zh/i18n) and the issue-lifecycle spec assertion to match the unconditional lifecycle job. Verification: ci-workflow.spec.ts 19/19, all five workflows YAML-parse, typecheck clean, verify-translation-pairing consistent, note-format 582. --- ...-event-directed-pr-review-status.i18n.yaml | 4 +- ...6-08-10-event-directed-pr-review-status.md | 4 +- ...8-10-event-directed-pr-review-status.zh.md | 4 +- .github/workflows/issue-lifecycle.yml | 5 +- .github/workflows/release-publish.yml | 131 ++++++++++++++++++ .github/workflows/release-vendor-publish.yml | 114 +++++++++++++++ .github/workflows/release-vendor.yml | 61 +------- .github/workflows/release.yml | 63 +-------- scripts/ci-workflow.spec.ts | 22 +-- 9 files changed, 279 insertions(+), 129 deletions(-) create mode 100644 .github/workflows/release-publish.yml create mode 100644 .github/workflows/release-vendor-publish.yml diff --git a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml index 08607d5317..f3f9f10975 100644 --- a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md -2026-08-10-event-directed-pr-review-status.md: 9db9c64fc87c1701028ae825357c3cbd7fef44d1 -2026-08-10-event-directed-pr-review-status.zh.md: 381a3f64a62930a584f48cfbc3571679bbcbcef7 +2026-08-10-event-directed-pr-review-status.md: bdaaa07c47d45eb002ed7c026683a800f67d0fca +2026-08-10-event-directed-pr-review-status.zh.md: 0ca4154a78e82156687b4fc5efb62745f3b2af63 diff --git a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md index 9db9c64fc8..bdaaa07c47 100644 --- a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md +++ b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md @@ -12,7 +12,7 @@ A monotonic projection also cannot return an automation-owned Issue from `In rev ## Decision -The Issue lifecycle workflow treats review webhooks as commands. `pull_request.review_requested`, including a repeated request, targets `In review`. `pull_request_review.submitted` targets `In progress` only when `review.state` is `changes_requested`; the submitted event remains necessary because a reviewer can request changes without an earlier review-request event. Approved and commented submissions skip their lifecycle job before it creates a Project token, while dismissed reviews are not subscribed. +The Issue lifecycle workflow treats review webhooks as commands. `pull_request.review_requested`, including a repeated request, targets `In review`. `pull_request_review.submitted` targets `In progress` only when `review.state` is `changes_requested`; the submitted event remains necessary because a reviewer can request changes without an earlier review-request event. Approved and commented submissions run their lifecycle job but no-op (they never reach the Project token step), while dismissed reviews are not subscribed. Ordinary subscribed pull-request events remain forward-only implementation signals: they can move `Inbox`, `Backlog`, or `Ready` to `In progress`, but they cannot move `In review` backward. Review-request commands can move any earlier active status to `In review`. Changes-requested commands can move earlier active statuses forward to `In progress` and can move `In review` back only when the latest status event for the target Project was written by the configured lifecycle actor. A human or unknown latest actor preserves the current status. @@ -22,7 +22,7 @@ The handler resolves only exact same-repository `Fixes`, `Closes`, or `Resolves` ## Verification -[Issue-management tests](../../../../.github/issue-management/policy.test.mjs) pin the event-to-command mapping, the repeated-review-request transition after a changes-requested command, the changes-requested regression, terminal protection, and human override preservation. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the subscribed events, the changes-requested job condition, and the separate `ready_for_review` policy trigger. +[Issue-management tests](../../../../.github/issue-management/policy.test.mjs) pin the event-to-command mapping, the repeated-review-request transition after a changes-requested command, the changes-requested regression, terminal protection, and human override preservation. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the subscribed events, the absence of a job-level `if` (so approved/commented reviews pass rather than skip), and the separate `ready_for_review` policy trigger. ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md index 381a3f64a6..0ca4154a78 100644 --- a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md +++ b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md @@ -12,7 +12,7 @@ Issue 所在 Project 中的状态记录了解决工作的下一步由谁负责 ## 决策 -Issue 生命周期工作流把评审 webhook 视为命令。`pull_request.review_requested`(包括重复请求)将目标状态指定为 `In review`。`pull_request_review.submitted` 将目标状态指定为 `In progress`,但仅在 `review.state` 为 `changes_requested` 时生效;submitted 事件仍不可省略,因为评审人即使没有先触发 review-request 事件,也可以直接提出修改要求。对于 approved 和 commented 提交,工作流会在生命周期作业创建 Project token 前跳过该作业;dismissed 评审则不在订阅范围内。 +Issue 生命周期工作流把评审 webhook 视为命令。`pull_request.review_requested`(包括重复请求)将目标状态指定为 `In review`。`pull_request_review.submitted` 将目标状态指定为 `In progress`,但仅在 `review.state` 为 `changes_requested` 时生效;submitted 事件仍不可省略,因为评审人即使没有先触发 review-request 事件,也可以直接提出修改要求。对于 approved 和 commented 提交,生命周期作业会运行但空操作(不会走到创建 Project token 一步);dismissed 评审则不在订阅范围内。 工作流订阅的普通 PR 事件仍是只向前推进的实现信号:它们可以将 `Inbox`、`Backlog` 或 `Ready` 推进至 `In progress`,但不能让 `In review` 倒退。请求评审命令可将任意较早的活跃状态推进至 `In review`。请求修改命令可将较早的活跃状态推进至 `In progress`;它也可以让 `In review` 状态回退,但仅在目标 Project 的最新状态事件由配置的生命周期执行主体写入时进行。若最新状态事件的执行主体是人工用户或未知主体,则保留当前状态。 @@ -22,7 +22,7 @@ Issue 生命周期工作流把评审 webhook 视为命令。`pull_request.review ## 验证 -[Issue 管理测试](../../../../.github/issue-management/policy.test.mjs)锁定事件到命令的映射、请求修改命令后重复请求评审所触发的状态转换、请求修改后的状态回退、终态保护,以及保留人工覆盖状态。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定订阅事件、请求修改作业的条件,以及独立的 `ready_for_review` 策略触发器。 +[Issue 管理测试](../../../../.github/issue-management/policy.test.mjs)锁定事件到命令的映射、请求修改命令后重复请求评审所触发的状态转换、请求修改后的状态回退、终态保护,以及保留人工覆盖状态。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定订阅事件、作业无 job 级 `if`(使 approved/commented 评审以 pass 而非 skip 呈现),以及独立的 `ready_for_review` 策略触发器。 ## 考虑过的替代方案 diff --git a/.github/workflows/issue-lifecycle.yml b/.github/workflows/issue-lifecycle.yml index e324cfefc2..70732de6a2 100644 --- a/.github/workflows/issue-lifecycle.yml +++ b/.github/workflows/issue-lifecycle.yml @@ -36,7 +36,10 @@ concurrency: jobs: lifecycle: name: Issue lifecycle - if: ${{ github.event_name != 'pull_request_review' || (github.event.action == 'submitted' && github.event.review.state == 'changes_requested') }} + # Run on every pull_request_review event, not only changes_requested, so the + # check shows a passing result instead of a gray "skipped" segment. The + # lifecycle handler itself no-ops (returns success) for approved/commented + # reviews; only a changes_requested review drives the Project board. runs-on: ubuntu-latest steps: - name: Check out trusted policy diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml new file mode 100644 index 0000000000..7107ba0073 --- /dev/null +++ b/.github/workflows/release-publish.yml @@ -0,0 +1,131 @@ +# Publish the dsh release sequence to npm. This workflow is manual-only +# (workflow_dispatch) and intentionally does not listen to pull_request or push: +# publication must always be an explicit, reviewed act from a dsh-v* tag, and it +# must never appear as a PR check. It repacks the current tree before publishing +# so the bytes uploaded are exactly what this dispatch produced. +name: Release publish (dsh) + +on: + workflow_dispatch: + +permissions: + contents: read + +env: + PRIMARY_NODE_VERSION: '24' + DSH_TELEMETRY_DISABLED: '1' + +jobs: + pack: + name: Pack npm tarballs + runs-on: ubuntu-24.04 + steps: + # Complete history: the release scripts read tags. + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: pnpm/action-setup@v4 + with: + dest: ${{ runner.temp }}/setup-pnpm + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.PRIMARY_NODE_VERSION }} + + - name: Configure pnpm store path + id: pnpm-store + run: | + store_root="$HOME/.local/share/pnpm/store" + echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV" + store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent) + echo "path=$store_path" >> "$GITHUB_OUTPUT" + + - uses: actions/cache/restore@v4 + with: + path: ${{ steps.pnpm-store.outputs.path }} + key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm- + + - name: Install (immutable) + run: pnpm install --frozen-lockfile + + - name: Verify release version + env: + RELEASE_PUBLISH: 'true' + run: pnpm run release:verify --family dsh + + - name: Build + run: pnpm run build + + - name: Pack release tarballs + run: pnpm run release:pack --family dsh --out dist/npm + + # The harness packages declare the vendored framework as a peer, and this + # verification must not depend on the registry already carrying matching + # versions — one pull request may bump both families before either + # publishes — so it installs that family's pack output too. Only dist/npm + # is published. + - name: Pack the vendored framework for verification + run: pnpm run release:pack --family vendor --out dist/npm-vendor + + # dsh-sandbox-local declares the Landlock entry as a runtime dependency, so + # the verification needs its tarball. Its platform packages stay out: they + # are optional, and building them needs a musl toolchain per architecture. + - name: Pack the Landlock entry for verification + run: | + pnpm --dir native/landlock-run run build:ts + pnpm --dir native/landlock-run/packages/entry pack --pack-destination "$PWD/dist/npm-landlock" + + - name: Verify packed install + run: pnpm run release:verify-packed-install --family dsh --from dist/npm --from dist/npm-vendor --from dist/npm-landlock + + - uses: actions/upload-artifact@v4 + with: + name: dsh-npm-tarballs + path: dist/npm/* + if-no-files-found: error + retention-days: 7 + + publish: + name: Publish to npm + needs: pack + runs-on: ubuntu-24.04 + # Required reviewers and the allowed tags live on the environment; this is + # the only job in the sequence that can write to the registry. + environment: npm-publish + concurrency: + group: Release-publish + cancel-in-progress: false + permissions: + contents: read + steps: + # Checkout and install carry the release scripts only. There is no build + # step: publication uploads the bytes the pack job produced. + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - uses: pnpm/action-setup@v4 + with: + dest: ${{ runner.temp }}/setup-pnpm + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.PRIMARY_NODE_VERSION }} + registry-url: https://registry.npmjs.org + + - name: Install (immutable, no package scripts) + run: pnpm install --frozen-lockfile --ignore-scripts + + - uses: actions/download-artifact@v4 + with: + name: dsh-npm-tarballs + path: dist/npm + + - name: Publish tarballs + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: pnpm run release:publish --family dsh --from dist/npm diff --git a/.github/workflows/release-vendor-publish.yml b/.github/workflows/release-vendor-publish.yml new file mode 100644 index 0000000000..a50a408025 --- /dev/null +++ b/.github/workflows/release-vendor-publish.yml @@ -0,0 +1,114 @@ +# Publish the vendored framework sequence to npm. This workflow is manual-only +# (workflow_dispatch) and intentionally does not listen to pull_request or push: +# publication must always be an explicit, reviewed act from a vendor-* tag, and +# it must never appear as a PR check. It repacks the current tree before +# publishing so the bytes uploaded are exactly what this dispatch produced. +name: Release publish (vendor) + +on: + workflow_dispatch: + +permissions: + contents: read + +env: + PRIMARY_NODE_VERSION: '24' + DSH_TELEMETRY_DISABLED: '1' + +jobs: + pack: + name: Pack npm tarballs + runs-on: ubuntu-24.04 + steps: + # Complete history: the release scripts read tags. + - uses: actions/checkout@v6 + with: + fetch-depth: 0 + persist-credentials: false + + - uses: pnpm/action-setup@v4 + with: + dest: ${{ runner.temp }}/setup-pnpm + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.PRIMARY_NODE_VERSION }} + + - name: Configure pnpm store path + id: pnpm-store + run: | + store_root="$HOME/.local/share/pnpm/store" + echo "PNPM_CONFIG_STORE_DIR=$store_root" >> "$GITHUB_ENV" + store_path=$(PNPM_CONFIG_STORE_DIR="$store_root" pnpm store path --silent) + echo "path=$store_path" >> "$GITHUB_OUTPUT" + + - uses: actions/cache/restore@v4 + with: + path: ${{ steps.pnpm-store.outputs.path }} + key: ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm-${{ hashFiles('pnpm-lock.yaml') }} + restore-keys: | + ${{ runner.os }}-node-${{ env.PRIMARY_NODE_VERSION }}-pnpm- + + - name: Install (immutable) + run: pnpm install --frozen-lockfile + + - name: Verify release version + env: + RELEASE_PUBLISH: 'true' + run: pnpm run release:verify --family vendor + + # The vendored packages publish their own sources and build outputs; the + # host build produces what their manifests select. + - name: Build + run: pnpm run build:lib:host + + - name: Pack release tarballs + run: pnpm run release:pack --family vendor --out dist/npm-vendor + + - name: Verify packed install + run: pnpm run release:verify-packed-install --family vendor --from dist/npm-vendor + + - uses: actions/upload-artifact@v4 + with: + name: vendor-npm-tarballs + path: dist/npm-vendor/* + if-no-files-found: error + retention-days: 7 + + publish: + name: Publish to npm + needs: pack + runs-on: ubuntu-24.04 + environment: npm-publish + concurrency: + group: Release-publish + cancel-in-progress: false + permissions: + contents: read + steps: + # Checkout and install carry the release scripts only; no build step. + - uses: actions/checkout@v6 + with: + persist-credentials: false + + - uses: pnpm/action-setup@v4 + with: + dest: ${{ runner.temp }}/setup-pnpm + + - uses: actions/setup-node@v6 + with: + node-version: ${{ env.PRIMARY_NODE_VERSION }} + registry-url: https://registry.npmjs.org + + - name: Install (immutable, no package scripts) + run: pnpm install --frozen-lockfile --ignore-scripts + + - uses: actions/download-artifact@v4 + with: + name: vendor-npm-tarballs + path: dist/npm-vendor + + - name: Publish tarballs + env: + NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} + run: pnpm run release:publish --family vendor --from dist/npm-vendor diff --git a/.github/workflows/release-vendor.yml b/.github/workflows/release-vendor.yml index c8af47251e..34290bb7a2 100644 --- a/.github/workflows/release-vendor.yml +++ b/.github/workflows/release-vendor.yml @@ -1,10 +1,11 @@ -# Pack and publish the vendored framework sequence: the nine rescoped Cordis -# packages under vendor/, each on its own version line. This sequence releases -# independently of dsh and of the native packages. +# Pack the vendored framework sequence: the nine rescoped Cordis packages under +# vendor/, each on its own version line. This sequence releases independently of +# dsh and of the native packages. # # Pack runs without credentials on every pull request and master push. -# Publication is a manual dispatch from a vendor-* tag; a vendor release can -# carry several versions, so each package has its own tag. +# Publication is a manual workflow_dispatch of release-vendor-publish.yml from a +# vendor-* tag; a vendor release can carry several versions, so each package has +# its own tag. name: Release (vendor) on: @@ -12,19 +13,12 @@ on: push: branches: [master] workflow_dispatch: - inputs: - publish: - description: Publish the packed tarballs to npm. Must run from a vendor-* tag. - required: true - type: boolean - default: false permissions: contents: read concurrency: - # Pack runs per ref so concurrent pull requests never displace each - # other; the publish job below serializes the shared dist-tag state. + # Pack runs per ref so concurrent pull requests never displace each other. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false @@ -70,8 +64,6 @@ jobs: run: pnpm install --frozen-lockfile - name: Verify release version - env: - RELEASE_PUBLISH: ${{ inputs.publish }} run: pnpm run release:verify --family vendor # The vendored packages publish their own sources and build outputs; the @@ -91,42 +83,3 @@ jobs: path: dist/npm-vendor/* if-no-files-found: error retention-days: 7 - - publish: - name: Publish to npm - if: inputs.publish - needs: pack - runs-on: ubuntu-24.04 - environment: npm-publish - concurrency: - group: Release-publish - cancel-in-progress: false - permissions: - contents: read - steps: - # Checkout and install carry the release scripts only; no build step. - - uses: actions/checkout@v6 - with: - persist-credentials: false - - - uses: pnpm/action-setup@v4 - with: - dest: ${{ runner.temp }}/setup-pnpm - - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.PRIMARY_NODE_VERSION }} - registry-url: https://registry.npmjs.org - - - name: Install (immutable, no package scripts) - run: pnpm install --frozen-lockfile --ignore-scripts - - - uses: actions/download-artifact@v4 - with: - name: vendor-npm-tarballs - path: dist/npm-vendor - - - name: Publish tarballs - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: pnpm run release:publish --family vendor --from dist/npm-vendor diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 08296468bf..2b84a299b9 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,10 +1,10 @@ -# Pack and publish the dsh release sequence: every package under packages/ plus -# the apps/ entries, all on one version. The vendored framework and the native -# packages are separate sequences with their own workflows and version lines. +# Pack the dsh release sequence: every package under packages/ plus the apps/ +# entries, all on one version. The vendored framework and the native packages are +# separate sequences with their own workflows and version lines. # # Pack runs without credentials on every pull request and master push, so a -# pull request proves the whole publish set still packs. Publication is a -# manual dispatch from a dsh-v* tag and consumes exactly the packed bytes. +# pull request proves the whole publish set still packs. Publication is a manual +# workflow_dispatch of release-publish.yml from a dsh-v* tag. name: Release (dsh) on: @@ -12,19 +12,12 @@ on: push: branches: [master] workflow_dispatch: - inputs: - publish: - description: Publish the packed tarballs to npm. Must run from a dsh-v* tag. - required: true - type: boolean - default: false permissions: contents: read concurrency: - # Pack runs per ref so concurrent pull requests never displace each - # other; the publish job below serializes the shared dist-tag state. + # Pack runs per ref so concurrent pull requests never displace each other. group: ${{ github.workflow }}-${{ github.ref }} cancel-in-progress: false @@ -70,8 +63,6 @@ jobs: run: pnpm install --frozen-lockfile - name: Verify release version - env: - RELEASE_PUBLISH: ${{ inputs.publish }} run: pnpm run release:verify --family dsh - name: Build @@ -105,45 +96,3 @@ jobs: path: dist/npm/* if-no-files-found: error retention-days: 7 - - publish: - name: Publish to npm - if: inputs.publish - needs: pack - runs-on: ubuntu-24.04 - # Required reviewers and the allowed tags live on the environment; this is - # the only step in the sequence that can write to the registry. - environment: npm-publish - concurrency: - group: Release-publish - cancel-in-progress: false - permissions: - contents: read - steps: - # Checkout and install carry the release scripts only. There is no build - # step: publication uploads the bytes the pack job produced. - - uses: actions/checkout@v6 - with: - persist-credentials: false - - - uses: pnpm/action-setup@v4 - with: - dest: ${{ runner.temp }}/setup-pnpm - - - uses: actions/setup-node@v6 - with: - node-version: ${{ env.PRIMARY_NODE_VERSION }} - registry-url: https://registry.npmjs.org - - - name: Install (immutable, no package scripts) - run: pnpm install --frozen-lockfile --ignore-scripts - - - uses: actions/download-artifact@v4 - with: - name: dsh-npm-tarballs - path: dist/npm - - - name: Publish tarballs - env: - NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} - run: pnpm run release:publish --family dsh --from dist/npm diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 721c9987b6..a13ceafe47 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -417,20 +417,20 @@ describe('Python release workflows', () => { }) describe('Issue lifecycle workflow', () => { - it('uses explicit review handoff events without rerunning when a draft becomes ready', () => { + it('runs the lifecycle job on every PR/review event so it passes instead of skipping', () => { const lifecycle = loadWorkflow('.github/workflows/issue-lifecycle.yml') - const lifecyclePullRequest = workflowEvent(lifecycle, 'pull_request') - const lifecycleReview = workflowEvent(lifecycle, 'pull_request_review') - const lifecycleJob = workflowJob(lifecycle, 'lifecycle') const policy = loadWorkflow('.github/workflows/issue-policy.yml') - const policyPullRequest = workflowEvent(policy, 'pull_request') + const lifecycleJob = workflowJob(lifecycle, 'lifecycle') - expect(lifecyclePullRequest.types).not.toContain('ready_for_review') - expect(lifecyclePullRequest.types).toContain('review_requested') - expect(lifecycleReview.types).toEqual(['submitted']) - expect(lifecycleJob.if).toBe( - "${{ github.event_name != 'pull_request_review' || (github.event.action == 'submitted' && github.event.review.state == 'changes_requested') }}", - ) + // The lifecycle job has no workflow-level `if`, so it is listed on every + // pull_request / pull_request_review event and reports success (the handler + // no-ops for non-changes-requested reviews) instead of a gray "skipped" check. + expect(lifecycle.on).toHaveProperty('pull_request') + expect(lifecycle.on).toHaveProperty('pull_request_review') + expect(lifecycleJob.if).toBeUndefined() + + // issue-policy owns PR validation; it is read-only and a real gate. + const policyPullRequest = workflowEvent(policy, 'pull_request') expect(policyPullRequest.types).toContain('ready_for_review') }) }) From 63d9de0eb3c580c08dd1a11be95c0a42066e8bff Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 20 Aug 2026 15:21:59 +0800 Subject: [PATCH 2/4] fix(cic): address gray-check PR review - official build, step-level gate, note sync MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Address ds-review-bot findings on PR #2798: - release-publish.yml: use pnpm run build:official (not build) so the dsh pack step's verifyBuildArtifacts (families.ts:327, readClientBuildRecord with officialClientBuildEnvironment) finds the official client-build record; build would fail Pack release tarballs on a clean runner. - issue-lifecycle.yml: move the previous job-level if to step level on Create project token and Handle repository event, so approved/commented reviews pass (job reported success, no gray segment) without minting a write-capable App token or touching the board — preserving the original least-privilege property. - ci-workflow.spec.ts: lock the step-level gate on the two lifecycle steps, and add a release-workflow invariant test (release.yml/vendor are pack-only; release-publish.yml/vendor-publish.yml are workflow_dispatch-only with the npm-publish environment and Release-publish group) to prevent #2797 recurrence. - Update 2026-08-10-event-directed-pr-review-status and 2026-08-10-npm-release- sequences notes (en/zh/i18n) to the new split and step-level behavior. Verification: ci-workflow.spec.ts 14/14, typecheck clean, all five workflows YAML-parse, verify-translation-pairing consistent, note-format 582. --- ...-event-directed-pr-review-status.i18n.yaml | 4 +- ...6-08-10-event-directed-pr-review-status.md | 2 +- ...8-10-event-directed-pr-review-status.zh.md | 2 +- ...2026-08-10-npm-release-sequences.i18n.yaml | 4 +- .../2026-08-10-npm-release-sequences.md | 6 +-- .../2026-08-10-npm-release-sequences.zh.md | 6 +-- .github/workflows/issue-lifecycle.yml | 11 ++++-- .github/workflows/release-publish.yml | 2 +- scripts/ci-workflow.spec.ts | 39 +++++++++++++++++-- 9 files changed, 55 insertions(+), 21 deletions(-) diff --git a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml index f3f9f10975..d8c333dd88 100644 --- a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md -2026-08-10-event-directed-pr-review-status.md: bdaaa07c47d45eb002ed7c026683a800f67d0fca -2026-08-10-event-directed-pr-review-status.zh.md: 0ca4154a78e82156687b4fc5efb62745f3b2af63 +2026-08-10-event-directed-pr-review-status.md: 3ed6038929d3c2c1e9cd82182978262ee363f5ab +2026-08-10-event-directed-pr-review-status.zh.md: 1fa8650057e53ab894c597b712720a3ee7a5c46a diff --git a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md index bdaaa07c47..3ed6038929 100644 --- a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md +++ b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.md @@ -22,7 +22,7 @@ The handler resolves only exact same-repository `Fixes`, `Closes`, or `Resolves` ## Verification -[Issue-management tests](../../../../.github/issue-management/policy.test.mjs) pin the event-to-command mapping, the repeated-review-request transition after a changes-requested command, the changes-requested regression, terminal protection, and human override preservation. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the subscribed events, the absence of a job-level `if` (so approved/commented reviews pass rather than skip), and the separate `ready_for_review` policy trigger. +[Issue-management tests](../../../../.github/issue-management/policy.test.mjs) pin the event-to-command mapping, the repeated-review-request transition after a changes-requested command, the changes-requested regression, terminal protection, and human override preservation. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the subscribed events, the job-level absence of `if` plus the step-level gate on the token/board steps (so approved/commented reviews pass without minting a token), and the separate `ready_for_review` policy trigger. ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md index 0ca4154a78..1fa8650057 100644 --- a/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md +++ b/.agents/notes/implemented/process/2026-08-10-event-directed-pr-review-status.zh.md @@ -22,7 +22,7 @@ Issue 生命周期工作流把评审 webhook 视为命令。`pull_request.review ## 验证 -[Issue 管理测试](../../../../.github/issue-management/policy.test.mjs)锁定事件到命令的映射、请求修改命令后重复请求评审所触发的状态转换、请求修改后的状态回退、终态保护,以及保留人工覆盖状态。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定订阅事件、作业无 job 级 `if`(使 approved/commented 评审以 pass 而非 skip 呈现),以及独立的 `ready_for_review` 策略触发器。 +[Issue 管理测试](../../../../.github/issue-management/policy.test.mjs)锁定事件到命令的映射、请求修改命令后重复请求评审所触发的状态转换、请求修改后的状态回退、终态保护,以及保留人工覆盖状态。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定订阅事件、job 级无 `if` 且 token/看板步骤带 step 级门控(使 approved/commented 评审以 pass 呈现且不铸 token),以及独立的 `ready_for_review` 策略触发器。 ## 考虑过的替代方案 diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml index e3a181f63d..763362d57c 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-10-npm-release-sequences.md -2026-08-10-npm-release-sequences.md: efeda91b6a85e1316c563cc04411878122096953 -2026-08-10-npm-release-sequences.zh.md: d905ac4b58691890d2aad955b87713a278dcb4f8 +2026-08-10-npm-release-sequences.md: c2cf540ff130f2a17a40068a0684c9bbc0e07ee3 +2026-08-10-npm-release-sequences.zh.md: 378c0a007ec30ad05fe9bcb9d068d095ccb658bc diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md index efeda91b6a..c2cf540ff1 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md @@ -105,11 +105,11 @@ The entity in this domain is a **release family**: a set of packages sharing one The dsh family applies the repository's publication payload policy, which rejects sources and declaration maps. The vendored family keeps upstream's payload, because those manifests export `./src/*` and dropping `src` would publish an export map pointing at absent files. -### Workflow shape: pack everything at once, then publish as one set +### Workflow shape: pack on PR/push, publish from a manual dispatch workflow -The `pack` job walks the whole release set once, packing each member into one directory, writes the upload order, and uploads that directory as one artifact; the `publish` job downloads that artifact and publishes each entry in order. The release set is one unit — half the packages can never reach the registry while the other half is still building. +The `pack` job walks the whole release set once, packing each member into one directory, writes the upload order, and uploads that directory as one artifact; it lives in `release.yml` / `release-vendor.yml`. The release set is one unit — half the packages can never reach the registry while the other half is still building. -`pack` carries no credentials and runs on every pull request and master push, so a pull request proves the release set still packs. `publish` is a manual dispatch, sits behind the `npm-publish` environment for human approval, and neither builds nor rebuilds — it uploads the bytes pack produced. Pack runs are grouped per ref so concurrent pull requests do not displace each other; the publish job carries the global group, because dist-tags are shared registry state. +`pack` carries no credentials and runs on every pull request and master push, so a pull request proves the release set still packs. Publication lives in a separate `release-publish.yml` / `release-vendor-publish.yml` workflow that is `workflow_dispatch`-only (so it never appears as a PR check): it repacks the current tree and then publishes each entry in order, behind the `npm-publish` environment for human approval. Pack runs are grouped per ref so concurrent pull requests do not displace each other; the publish workflow carries the global `Release-publish` group, because dist-tags are shared registry state. A dsh verification installs the vendored family's pack output too. The harness packages declare the vendored framework as a peer, those packages live in another sequence, and the credential-free job cannot fetch them from a private registry — so `release.yml` packs the vendored family for verification while publishing only its own set. diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md index d905ac4b58..378c0a007e 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md @@ -105,11 +105,11 @@ registry 的两个行为决定了「怎么尝试一次发布」。写入之间 dsh 族套用仓库的发布 payload 策略(拒绝源码与声明映射)。vendored 族保留上游 payload,因为那些 manifest 导出 `./src/*`,去掉 `src` 会发出一个导出映射指向不存在文件的包。 -### workflow 形状:一次性 pack 全部,再统一 publish +### workflow 形状:PR/push 上 pack,从手动 dispatch 工作流发布 -`pack` job 一趟遍历整个发布集,把每个成员打进同一个目录,写出上传顺序,整个目录作为一份 artifact 上传;`publish` job 下载那一份 artifact,按顺序逐个发布。发布集是一个整体——绝不会出现一半的包已经上了 registry、另一半还在构建。 +`pack` job 一趟遍历整个发布集,把每个成员打进同一个目录,写出上传顺序,整个目录作为一份 artifact 上传;它位于 `release.yml` / `release-vendor.yml`。发布集是一个整体——绝不会出现一半的包已经上了 registry、另一半还在构建。 -`pack` 无凭据,在每个 pull request 和每次 master push 上跑,所以一个 pull request 就能证明发布集仍能完整打出来。`publish` 是手动 dispatch,挂在 `npm-publish` environment 后面等人工审批,且既不构建也不重建——它上传的就是 pack 产出的字节。pack 的 run 按 ref 分组,并发的 pull request 不会互相顶掉;全局分组落在 publish job 上,因为 dist-tag 是共享的 registry 状态。 +`pack` 无凭据,在每个 pull request 和每次 master push 上跑,所以一个 pull request 就能证明发布集仍能完整打出来。发布则位于独立的 `release-publish.yml` / `release-vendor-publish.yml` 工作流,仅 `workflow_dispatch`(因此不会作为 PR check 出现):它重新打包当前树,再按顺序逐个发布,挂在 `npm-publish` environment 后面等人工审批。pack 的 run 按 ref 分组,并发的 pull request 不会互相顶掉;全局 `Release-publish` 分组落在发布工作流上,因为 dist-tag 是共享的 registry 状态。 dsh 的验证会一并安装 vendored 族的 pack 产物。harness 的包把 vendored 框架声明成 peer,而那些包属于另一条序列,无凭据的 job 无法从私有 registry 取到——所以 `release.yml` 为验证而打包 vendored 族,发布的仍只有自己那一份。 diff --git a/.github/workflows/issue-lifecycle.yml b/.github/workflows/issue-lifecycle.yml index 70732de6a2..8e2265fb19 100644 --- a/.github/workflows/issue-lifecycle.yml +++ b/.github/workflows/issue-lifecycle.yml @@ -36,10 +36,11 @@ concurrency: jobs: lifecycle: name: Issue lifecycle - # Run on every pull_request_review event, not only changes_requested, so the - # check shows a passing result instead of a gray "skipped" segment. The - # lifecycle handler itself no-ops (returns success) for approved/commented - # reviews; only a changes_requested review drives the Project board. + # Runs on every pull_request_review event so the check reports success rather + # than a gray "skipped" segment. The token-creating and board-mutating steps + # are gated at step level (a skipped step does not gray the job): only a + # changes_requested review drives the Project board; approved/commented + # reviews never mint a write-capable App token. runs-on: ubuntu-latest steps: - name: Check out trusted policy @@ -49,6 +50,7 @@ jobs: persist-credentials: false - name: Create project token id: app-token + if: ${{ github.event_name != 'pull_request_review' || github.event.review.state == 'changes_requested' }} uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 with: client-id: ${{ vars.DSH_ISSUE_APP_CLIENT_ID }} @@ -56,6 +58,7 @@ jobs: owner: deepseek-harness repositories: deepseek-harness - name: Handle repository event + if: ${{ github.event_name != 'pull_request_review' || github.event.review.state == 'changes_requested' }} env: GH_TOKEN: ${{ steps.app-token.outputs.token }} run: node .github/issue-management/policy.mjs lifecycle diff --git a/.github/workflows/release-publish.yml b/.github/workflows/release-publish.yml index 7107ba0073..143df14caa 100644 --- a/.github/workflows/release-publish.yml +++ b/.github/workflows/release-publish.yml @@ -58,7 +58,7 @@ jobs: run: pnpm run release:verify --family dsh - name: Build - run: pnpm run build + run: pnpm run build:official - name: Pack release tarballs run: pnpm run release:pack --family dsh --out dist/npm diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index a13ceafe47..0740a6a6fb 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -417,17 +417,25 @@ describe('Python release workflows', () => { }) describe('Issue lifecycle workflow', () => { - it('runs the lifecycle job on every PR/review event so it passes instead of skipping', () => { + it('runs the lifecycle job on every PR/review event but gates token and board steps', () => { const lifecycle = loadWorkflow('.github/workflows/issue-lifecycle.yml') const policy = loadWorkflow('.github/workflows/issue-policy.yml') const lifecycleJob = workflowJob(lifecycle, 'lifecycle') + if (!Array.isArray(lifecycleJob.steps)) throw new TypeError('Issue lifecycle job must define steps') - // The lifecycle job has no workflow-level `if`, so it is listed on every - // pull_request / pull_request_review event and reports success (the handler - // no-ops for non-changes-requested reviews) instead of a gray "skipped" check. + // The job has no job-level `if`, so it is listed on every pull_request / + // pull_request_review event and reports success instead of a gray skip. The + // write-capable steps are gated at step level so approved/commented reviews + // never mint a Project/Issue App token nor touch the board. expect(lifecycle.on).toHaveProperty('pull_request') expect(lifecycle.on).toHaveProperty('pull_request_review') expect(lifecycleJob.if).toBeUndefined() + const gated = "${{ github.event_name != 'pull_request_review' || github.event.review.state == 'changes_requested' }}" + const steps = lifecycleJob.steps.filter(isRecord) + const tokenStep = steps.find(s => s.name === 'Create project token') + const handleStep = steps.find(s => s.name === 'Handle repository event') + expect(tokenStep).toMatchObject({ if: gated }) + expect(handleStep).toMatchObject({ if: gated }) // issue-policy owns PR validation; it is read-only and a real gate. const policyPullRequest = workflowEvent(policy, 'pull_request') @@ -435,6 +443,29 @@ describe('Issue lifecycle workflow', () => { }) }) +describe('npm release workflows', () => { + it('keeps publication dispatch-only and pack in the PR workflow', () => { + // pack stays in the PR/master release workflows so a PR proves the set packs. + for (const file of ['release.yml', 'release-vendor.yml']) { + const workflow = loadWorkflow(`.github/workflows/${file}`) + if (!isRecord(workflow.jobs)) throw new TypeError(`${file} must define jobs`) + expect(Object.keys(workflow.jobs).sort()).toEqual(['pack']) + } + + // publication is workflow_dispatch-only (never a PR check) and keeps the + // npm-publish environment plus the shared dist-tag group. + for (const file of ['release-publish.yml', 'release-vendor-publish.yml']) { + const workflow = loadWorkflow(`.github/workflows/${file}`) + if (!isRecord(workflow.on) || !isRecord(workflow.jobs)) throw new TypeError(`${file} must define on and jobs`) + expect(Object.keys(workflow.on)).toEqual(['workflow_dispatch']) + const publish = workflow.jobs.publish + if (!isRecord(publish)) throw new TypeError(`${file} must define a publish job`) + expect(publish.environment).toBe('npm-publish') + expect(publish.concurrency).toMatchObject({ group: 'Release-publish' }) + } + }) +}) + describe('Git hooks', () => { it('leaves frozen Agent Note sidecars to the archive verifier', () => { const lefthook = loadWorkflow('lefthook.yml') From 16affb84fb9b350fdd57d3e886b2edbd9bfdbe4e Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 20 Aug 2026 15:41:24 +0800 Subject: [PATCH 3/4] fix(cic): restore subscription-type gates and note table for split publish Address the fresh review pass on PR #2798: - ci-workflow.spec.ts: restore the subscription-type assertions the rewrite had dropped (issue-lifecycle pull_request types omit ready_for_review and include review_requested; pull_request_review types == ['submitted']) alongside the new step-level gate checks, so the 'tests pin the subscribed events' gate holds. - 2026-08-10-npm-release-sequences note (en/zh/i18n): the three-sequence table's Workflow column now lists the pack and publish workflows for dsh/vendor, and line 114 no longer claims release.yml publishes (the dsh pack job packs the vendored family for verification; release-publish.yml repacks and publishes). Follow-up for the pack-job copy drift is filed as #2816. --- .../process/2026-08-10-npm-release-sequences.i18n.yaml | 4 ++-- .../process/2026-08-10-npm-release-sequences.md | 6 +++--- .../process/2026-08-10-npm-release-sequences.zh.md | 6 +++--- scripts/ci-workflow.spec.ts | 8 ++++++++ 4 files changed, 16 insertions(+), 8 deletions(-) diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml index 763362d57c..e398a80e6d 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-10-npm-release-sequences.md -2026-08-10-npm-release-sequences.md: c2cf540ff130f2a17a40068a0684c9bbc0e07ee3 -2026-08-10-npm-release-sequences.zh.md: 378c0a007ec30ad05fe9bcb9d068d095ccb658bc +2026-08-10-npm-release-sequences.md: fe823e22997e5a3238d0e239d0f00f331549a5a1 +2026-08-10-npm-release-sequences.zh.md: d7aaaeaed33f639a890962cf9a95f24284eb9a35 diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md index c2cf540ff1..fe823e2299 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md @@ -22,8 +22,8 @@ Two hard blockers sat in the way. All 217 workspace manifests set `private: true | Sequence | Members | Version baseline | Tag | Workflow | |---|---|---|---|---| -| dsh | Publish set: non-experimental `packages/*/*` + `apps/*`; private experimental packages join only the shared version bump | one version for the publish set, private dsh packages, and workspace root, `0.0.x` | `dsh-v` | `release.yml` | -| vendored framework | the nine `vendor/*` packages | each package on its own version line | `vendor--v` (one per package) | `release-vendor.yml` | +| dsh | Publish set: non-experimental `packages/*/*` + `apps/*`; private experimental packages join only the shared version bump | one version for the publish set, private dsh packages, and workspace root, `0.0.x` | `dsh-v` | `release.yml` (pack) / `release-publish.yml` (publish) | +| vendored framework | the nine `vendor/*` packages | each package on its own version line | `vendor--v` (one per package) | `release-vendor.yml` (pack) / `release-vendor-publish.yml` (publish) | | native | `native/landlock-run/packages/*` | its own `0.0.x` | `landlock-run-v` | `landlock-run-release.yml` | All three publish to the `@deepseek-ai` scope on npmjs.com, and access is per sequence rather than per scope: the vendored framework and the native packages are `public`, the dsh family is `restricted` ([rationale](2026-08-13-public-vendor-and-native-sequences.md)). No publish path passes `--access`, because one flag cannot serve sequences that disagree and would override the manifest that owns the level. @@ -111,7 +111,7 @@ The `pack` job walks the whole release set once, packing each member into one di `pack` carries no credentials and runs on every pull request and master push, so a pull request proves the release set still packs. Publication lives in a separate `release-publish.yml` / `release-vendor-publish.yml` workflow that is `workflow_dispatch`-only (so it never appears as a PR check): it repacks the current tree and then publishes each entry in order, behind the `npm-publish` environment for human approval. Pack runs are grouped per ref so concurrent pull requests do not displace each other; the publish workflow carries the global `Release-publish` group, because dist-tags are shared registry state. -A dsh verification installs the vendored family's pack output too. The harness packages declare the vendored framework as a peer, those packages live in another sequence, and the credential-free job cannot fetch them from a private registry — so `release.yml` packs the vendored family for verification while publishing only its own set. +A dsh verification installs the vendored family's pack output too. The harness packages declare the vendored framework as a peer, those packages live in another sequence, and the credential-free job cannot fetch them from a private registry — so the dsh `pack` job packs the vendored family for verification while publishing only the dsh set. The publish workflow (`release-publish.yml`) repacks the current tree and publishes only the dsh set. The verification also packs the Landlock entry, which `dsh-sandbox-local` declares as a plain dependency, and omits optional dependencies. The platform packages behind those optional entries need a musl toolchain and one build per architecture, so a job on one runner cannot produce them; a consumer that cannot install them must still start, which is what optional means here. The verification therefore reads a directory by its contents rather than a pack order, because a directory can hold tarballs packed only to satisfy a cross-sequence dependency. diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md index 378c0a007e..d7aaaeaed3 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md @@ -22,8 +22,8 @@ Status: implemented | 序列 | 成员 | 版本基线 | tag | workflow | |---|---|---|---|---| -| dsh | 发布集:非 experimental 的 `packages/*/*` + `apps/*`;私有实验性包仅加入共享版本 bump | 发布集、私有 dsh 包与 workspace 根共用一个 `0.0.x` | `dsh-v<版本>` | `release.yml` | -| vendored framework | `vendor/*` 九个包 | 每包各自一条版本线 | `vendor-<包名>-v<版本>`(每包一个) | `release-vendor.yml` | +| dsh | 发布集:非 experimental 的 `packages/*/*` + `apps/*`;私有实验性包仅加入共享版本 bump | 发布集、私有 dsh 包与 workspace 根共用一个 `0.0.x` | `dsh-v<版本>` | `release.yml`(pack)/ `release-publish.yml`(发布) | +| vendored framework | `vendor/*` 九个包 | 每包各自一条版本线 | `vendor-<包名>-v<版本>`(每包一个) | `release-vendor.yml`(pack)/ `release-vendor-publish.yml`(发布) | | native | `native/landlock-run/packages/*` | 自己的 `0.0.x` | `landlock-run-v<版本>` | `landlock-run-release.yml` | 三组一律发到 npmjs.com 的 `@deepseek-ai` scope,且 access 按序列而非按 scope 区分:vendored 框架与 native 包是 `public`,dsh 族是 `restricted`([理由](2026-08-13-public-vendor-and-native-sequences.md))。没有任何发布路径传 `--access`——一个选项无法服务级别互不相同的序列,且会覆盖真正拥有该级别的 manifest。 @@ -111,7 +111,7 @@ dsh 族套用仓库的发布 payload 策略(拒绝源码与声明映射)。v `pack` 无凭据,在每个 pull request 和每次 master push 上跑,所以一个 pull request 就能证明发布集仍能完整打出来。发布则位于独立的 `release-publish.yml` / `release-vendor-publish.yml` 工作流,仅 `workflow_dispatch`(因此不会作为 PR check 出现):它重新打包当前树,再按顺序逐个发布,挂在 `npm-publish` environment 后面等人工审批。pack 的 run 按 ref 分组,并发的 pull request 不会互相顶掉;全局 `Release-publish` 分组落在发布工作流上,因为 dist-tag 是共享的 registry 状态。 -dsh 的验证会一并安装 vendored 族的 pack 产物。harness 的包把 vendored 框架声明成 peer,而那些包属于另一条序列,无凭据的 job 无法从私有 registry 取到——所以 `release.yml` 为验证而打包 vendored 族,发布的仍只有自己那一份。 +dsh 的验证会一并安装 vendored 族的 pack 产物。harness 的包把 vendored 框架声明成 peer,而那些包属于另一条序列,无凭据的 job 无法从私有 registry 取到——所以 dsh 的 `pack` job 为验证而打包 vendored 族,发布的仍只有 dsh 那一份。发布工作流(`release-publish.yml`)重新打包当前树,只发布 dsh 族。 验证还会打一份 Landlock entry 的 tarball——`dsh-sandbox-local` 把它声明为普通 `dependencies`——同时略去可选依赖。那些可选项背后的平台包需要 musl 工具链且每个架构各构建一次,单台 runner 产不出来;而装不到它们的消费方也必须能起,这正是「可选」在这里的含义。因此验证按目录内容读取 tarball,而不是读发布顺序:一个目录可能只装着为满足跨序列依赖而打出来的包,任何发布顺序都不描述它。 diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 0740a6a6fb..ac4535bc1b 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -430,6 +430,14 @@ describe('Issue lifecycle workflow', () => { expect(lifecycle.on).toHaveProperty('pull_request') expect(lifecycle.on).toHaveProperty('pull_request_review') expect(lifecycleJob.if).toBeUndefined() + // Keep the subscription-type gates: issue-lifecycle does not re-subscribe + // ready_for_review (issue-policy owns that) and only reacts to submitted + // review events. + const lifecyclePullRequest = workflowEvent(lifecycle, 'pull_request') + const lifecycleReview = workflowEvent(lifecycle, 'pull_request_review') + expect(lifecyclePullRequest.types).not.toContain('ready_for_review') + expect(lifecyclePullRequest.types).toContain('review_requested') + expect(lifecycleReview.types).toEqual(['submitted']) const gated = "${{ github.event_name != 'pull_request_review' || github.event.review.state == 'changes_requested' }}" const steps = lifecycleJob.steps.filter(isRecord) const tokenStep = steps.find(s => s.name === 'Create project token') From 56a8f7d325baaf8cd8016b2954bd0f587e153dfc Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Thu, 20 Aug 2026 15:59:40 +0800 Subject: [PATCH 4/4] fix(cic): cover release-publish in client-build gate and correct group wording Address the latest review pass on PR #2798: - client-build-environment.client.spec.ts: add release-publish.yml to dshBuildWorkflows so the 'workflow env must not set DSH_CLIENT_*' gate covers the new dsh publish path (it runs build:official and writes a dsh client build record). vendor-publish runs only build:lib:host, so it is not added. - 2026-08-10-npm-release-sequences note (en/zh/i18n): the Release-publish group is carried by the publish job (job-level concurrency), not the whole workflow; corrected the wording. --- .../process/2026-08-10-npm-release-sequences.i18n.yaml | 4 ++-- .../implemented/process/2026-08-10-npm-release-sequences.md | 2 +- .../process/2026-08-10-npm-release-sequences.zh.md | 2 +- scripts/client-build-environment.client.spec.ts | 1 + 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml index e398a80e6d..379585f190 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.i18n.yaml @@ -2,5 +2,5 @@ # side as of the last confirmed-consistent state. Both languages carry equal authority; # after editing either side, bring the other along and re-record with: # pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-08-10-npm-release-sequences.md -2026-08-10-npm-release-sequences.md: fe823e22997e5a3238d0e239d0f00f331549a5a1 -2026-08-10-npm-release-sequences.zh.md: d7aaaeaed33f639a890962cf9a95f24284eb9a35 +2026-08-10-npm-release-sequences.md: 487f2dbe717375b2adb7daf18550799544d2e147 +2026-08-10-npm-release-sequences.zh.md: 051c1c9fd0e8d5330477c5e3053c55f1b51d1802 diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md index fe823e2299..487f2dbe71 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.md @@ -109,7 +109,7 @@ The dsh family applies the repository's publication payload policy, which reject The `pack` job walks the whole release set once, packing each member into one directory, writes the upload order, and uploads that directory as one artifact; it lives in `release.yml` / `release-vendor.yml`. The release set is one unit — half the packages can never reach the registry while the other half is still building. -`pack` carries no credentials and runs on every pull request and master push, so a pull request proves the release set still packs. Publication lives in a separate `release-publish.yml` / `release-vendor-publish.yml` workflow that is `workflow_dispatch`-only (so it never appears as a PR check): it repacks the current tree and then publishes each entry in order, behind the `npm-publish` environment for human approval. Pack runs are grouped per ref so concurrent pull requests do not displace each other; the publish workflow carries the global `Release-publish` group, because dist-tags are shared registry state. +`pack` carries no credentials and runs on every pull request and master push, so a pull request proves the release set still packs. Publication lives in a separate `release-publish.yml` / `release-vendor-publish.yml` workflow that is `workflow_dispatch`-only (so it never appears as a PR check): it repacks the current tree and then publishes each entry in order, behind the `npm-publish` environment for human approval. Pack runs are grouped per ref so concurrent pull requests do not displace each other; the `publish` job carries the global `Release-publish` group, because dist-tags are shared registry state. A dsh verification installs the vendored family's pack output too. The harness packages declare the vendored framework as a peer, those packages live in another sequence, and the credential-free job cannot fetch them from a private registry — so the dsh `pack` job packs the vendored family for verification while publishing only the dsh set. The publish workflow (`release-publish.yml`) repacks the current tree and publishes only the dsh set. diff --git a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md index d7aaaeaed3..051c1c9fd0 100644 --- a/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md +++ b/.agents/notes/implemented/process/2026-08-10-npm-release-sequences.zh.md @@ -109,7 +109,7 @@ dsh 族套用仓库的发布 payload 策略(拒绝源码与声明映射)。v `pack` job 一趟遍历整个发布集,把每个成员打进同一个目录,写出上传顺序,整个目录作为一份 artifact 上传;它位于 `release.yml` / `release-vendor.yml`。发布集是一个整体——绝不会出现一半的包已经上了 registry、另一半还在构建。 -`pack` 无凭据,在每个 pull request 和每次 master push 上跑,所以一个 pull request 就能证明发布集仍能完整打出来。发布则位于独立的 `release-publish.yml` / `release-vendor-publish.yml` 工作流,仅 `workflow_dispatch`(因此不会作为 PR check 出现):它重新打包当前树,再按顺序逐个发布,挂在 `npm-publish` environment 后面等人工审批。pack 的 run 按 ref 分组,并发的 pull request 不会互相顶掉;全局 `Release-publish` 分组落在发布工作流上,因为 dist-tag 是共享的 registry 状态。 +`pack` 无凭据,在每个 pull request 和每次 master push 上跑,所以一个 pull request 就能证明发布集仍能完整打出来。发布则位于独立的 `release-publish.yml` / `release-vendor-publish.yml` 工作流,仅 `workflow_dispatch`(因此不会作为 PR check 出现):它重新打包当前树,再按顺序逐个发布,挂在 `npm-publish` environment 后面等人工审批。pack 的 run 按 ref 分组,并发的 pull request 不会互相顶掉;全局 `Release-publish` 分组落在 `publish` job 上,因为 dist-tag 是共享的 registry 状态。 dsh 的验证会一并安装 vendored 族的 pack 产物。harness 的包把 vendored 框架声明成 peer,而那些包属于另一条序列,无凭据的 job 无法从私有 registry 取到——所以 dsh 的 `pack` job 为验证而打包 vendored 族,发布的仍只有 dsh 那一份。发布工作流(`release-publish.yml`)重新打包当前树,只发布 dsh 族。 diff --git a/scripts/client-build-environment.client.spec.ts b/scripts/client-build-environment.client.spec.ts index cce5cff2a0..e2eb0ebe72 100644 --- a/scripts/client-build-environment.client.spec.ts +++ b/scripts/client-build-environment.client.spec.ts @@ -26,6 +26,7 @@ const dshBuildWorkflows = [ 'e2b-e2e.yml', 'e2e.yml', 'release.yml', + 'release-publish.yml', 'sandbox.yml', ]