From 094a8701fe4703b5b57720357c2b2f18e31fdfdf Mon Sep 17 00:00:00 2001 From: turtle1999 Date: Tue, 29 Sep 2026 12:48:04 +0800 Subject: [PATCH 01/10] Publish weighted approval as a completed check run Unmet weighted approval conditions previously left a pending commit status that CI watchers treat as running work, so gh pr checks --watch and similar monitors never finished. Publish the decision as the weighted approval decision check run instead: action_required blocks merging while completing the check, and failure reports evaluation errors. --- ...eighted-approvals-remain-pending.i18n.yaml | 22 --- ...ocked-weighted-approvals-remain-pending.md | 33 ---- ...ed-weighted-approvals-remain-pending.zh.md | 33 ---- ...pproval-review-workflow-identity.i18n.yaml | 4 +- ...09-10-approval-review-workflow-identity.md | 2 +- ...10-approval-review-workflow-identity.zh.md | 2 +- ...production-blame-approval-weight.i18n.yaml | 8 +- ...-09-11-production-blame-approval-weight.md | 4 +- ...-11-production-blame-approval-weight.zh.md | 4 +- ...026-09-15-pr-approval-delegation.i18n.yaml | 4 +- .../2026-09-15-pr-approval-delegation.md | 2 +- .../2026-09-15-pr-approval-delegation.zh.md | 2 +- ...hted-approval-decision-check-run.i18n.yaml | 22 +++ ...29-weighted-approval-decision-check-run.md | 50 ++++++ ...weighted-approval-decision-check-run.zh.md | 50 ++++++ .github/review-ownership/README.md | 12 +- .github/review-ownership/check-approval.mjs | 66 ++++--- .../review-ownership/check-approval.test.mjs | 169 +++++++++++------- .github/workflows/weighted-approval.yml | 14 +- scripts/ci-workflow.spec.ts | 8 +- 20 files changed, 293 insertions(+), 218 deletions(-) delete mode 100644 .agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.i18n.yaml delete mode 100644 .agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.md delete mode 100644 .agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.zh.md create mode 100644 .agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml create mode 100644 .agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md create mode 100644 .agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md diff --git a/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.i18n.yaml b/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.i18n.yaml deleted file mode 100644 index 3cf9104f6e..0000000000 --- a/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.i18n.yaml +++ /dev/null @@ -1,22 +0,0 @@ -# Bilingual-pair consistency record for 2026-09-09-blocked-weighted-approvals-remain-pending.md (docs/i18n/README.md): per heading -# section, a hash of its English and Chinese blocks outside code blocks and generated regions. -# After editing either side, bring the other along and re-record with: -# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.md -/agent-note-blocked-weighted-approvals-remain-pending: - en: 6a5da25eb874aece - zh: 654aa967a9222501 -/agent-note-blocked-weighted-approvals-remain-pending/problem: - en: 0afdda30d02b666b - zh: 6624aa9c8c58ea0a -/agent-note-blocked-weighted-approvals-remain-pending/decision: - en: cf7ac63f422b4cfb - zh: 250385e42d546d30 -/agent-note-blocked-weighted-approvals-remain-pending/verification: - en: af674f57088d303a - zh: b9c42f04beec9be5 -/agent-note-blocked-weighted-approvals-remain-pending/alternatives-considered: - en: 13d5f67070bd3396 - zh: c75db3746d31272e -/agent-note-blocked-weighted-approvals-remain-pending/consequences: - en: 284f19608b0d449f - zh: 1d9bd95239ba6ffa diff --git a/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.md b/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.md deleted file mode 100644 index 50ab64dadc..0000000000 --- a/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.md +++ /dev/null @@ -1,33 +0,0 @@ -# Agent Note: Blocked weighted approvals remain pending - -Status: implemented - -English | [中文](2026-09-09-blocked-weighted-approvals-remain-pending.zh.md) - -## Problem - -The weighted approval commit status must distinguish an unmet merge condition from a failed policy evaluation. An effective `CHANGES_REQUESTED` review from a write-capable reviewer prevents a pull request from satisfying the approval policy, but it is a reversible review state rather than an evaluation failure. - -Publishing `failure` for that review state conflates the approval decision with the health of the publisher. It also treats one unmet policy condition differently from a draft pull request or insufficient approval points, which remain pending while contributors can resolve them. - -## Decision - -A completed weighted approval evaluation publishes `pending` when the pull request is a draft, has fewer than the required approval points, or has an effective `CHANGES_REQUESTED` review from a write-capable reviewer. A blocking review dominates the point total, so the status remains pending even when counted approvals reach the threshold. - -The evaluation publishes `success` only when the pull request is ready, the point threshold is met, and no blocking review exists. The separate `weighted approval publisher` Actions job reports whether evaluation and status publication completed. An evaluation failure publishes an `error` commit status and fails that job. - -## Verification - -[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case and the exact published `pending` payload. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the separate publisher job name. - -## Alternatives considered - -**Publish `failure` for a blocking review.** This keeps a visibly failed status until the review changes, but it represents an unmet and reversible merge condition as a malfunction and conflates policy outcome with publisher health. - -**Let approval points override a blocking review.** This makes the score the only success condition, but it permits a successful status while a write-capable reviewer's effective decision still requests changes. - -## Consequences - -Required-status branch rules block a pull request because `pending` does not satisfy the required status. Contributors can distinguish review work that remains from a failed approval evaluation, while the publisher job and `error` status retain the operational failure signal. - -Consumers do not receive a failed commit status solely because a blocking review exists. They must inspect the status description or effective reviews when they need to distinguish a blocker from other pending approval conditions. diff --git a/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.zh.md b/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.zh.md deleted file mode 100644 index 58ab10b3ac..0000000000 --- a/.agents/notes/implemented/process/2026-09-09-blocked-weighted-approvals-remain-pending.zh.md +++ /dev/null @@ -1,33 +0,0 @@ -# Agent Note: 阻塞中的加权批准保持 pending - -Status: implemented - -[English](2026-09-09-blocked-weighted-approvals-remain-pending.md) | 中文 - -## 问题 - -`weighted approval` commit status 必须区分尚未满足的合并条件与失败的策略评估。具有写权限的评审人所提交且仍然生效的 `CHANGES_REQUESTED` 评审会阻止 PR 满足批准策略,但它是一种可撤销的评审状态,而不是评估故障。 - -为这种评审状态发布 `failure` 会混淆批准决策与 publisher 的健康状态。它还会让一个尚未满足的策略条件区别于 draft PR 或批准点数不足;后两种情况在贡献者能够解决问题期间会保持 pending。 - -## 决策 - -完成的加权批准评估会在 PR 为 draft、批准点数少于要求,或具有写权限的评审人存在仍然生效的 `CHANGES_REQUESTED` 评审时发布 `pending`。阻塞性评审的优先级高于点数总和,因此即使计入的批准已经达到阈值,状态仍保持 pending。 - -只有在 PR 已进入 ready 状态、达到点数阈值且不存在阻塞性评审时,评估才发布 `success`。独立的 `weighted approval publisher` Actions job 报告评估和状态发布是否完成。评估故障会发布 `error` commit status,并使该 job 失败。 - -## 验证 - -[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景,以及准确发布的 `pending` payload。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定独立的 publisher job 名称。 - -## 考虑过的替代方案 - -**为阻塞性评审发布 `failure`。** 该方案会在评审改变之前保持明显的失败状态,但它会把尚未满足且可撤销的合并条件表示为故障,并混淆策略结果与 publisher 的健康状态。 - -**允许批准点数覆盖阻塞性评审。** 该方案会让分数成为唯一的成功条件,但也允许在具有写权限的评审人仍然有效地要求修改时发布成功状态。 - -## 后果 - -需要该状态的分支规则会阻止 PR,因为 `pending` 不满足必需状态。贡献者可以区分尚待处理的评审工作与失败的批准评估,而 publisher job 和 `error` 状态保留运行故障信号。 - -消费方不会仅因存在阻塞性评审而收到失败的 commit status。如果需要区分 blocker 与其他 pending 批准条件,它们必须检查状态描述或仍然生效的评审。 diff --git a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml index 521e943a63..1dfe18dbe3 100644 --- a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml @@ -15,5 +15,5 @@ en: f8fdd869a03ac5ff zh: c355eb36e9cba350 /agent-note-identify-approval-review-workflows-by-file-path/consequences: - en: 9f31e023a04a7849 - zh: 66831c450af4c88f + en: 3edc29dc7c926f64 + zh: a84264a691a6248b diff --git a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md index 77164134e8..de1e08ef78 100644 --- a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md +++ b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md @@ -22,4 +22,4 @@ The [approval publisher](../../../../.github/review-ownership/check-approval.mjs Run-title expansion does not prevent approval refreshes, while an unexpected workflow file still fails validation. Moving the review-event workflow requires updating the publisher's expected path. -[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) cover a numbered run name, invalid source paths and events, unsuccessful runs, invalid titles, and superseded heads. The [approval outcome policy](2026-09-09-blocked-weighted-approvals-remain-pending.md) continues to own pending and successful status semantics. +[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) cover a numbered run name, invalid source paths and events, unsuccessful runs, invalid titles, and superseded heads. The [approval decision check run](2026-09-29-weighted-approval-decision-check-run.md) continues to own the published conclusions. diff --git a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md index 736851d6f3..6b3aa3d359 100644 --- a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md +++ b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md @@ -22,4 +22,4 @@ GitHub 可能用展开后的 `run-name` 填充工作流运行的 `name`。审批 运行标题的展开不会阻止审批刷新,而非预期的工作流文件仍无法通过验证。移动评审事件工作流时,需要更新发布器预期的路径。 -[审批策略测试](../../../../.github/review-ownership/check-approval.test.mjs) 覆盖带编号的运行名称、无效的来源路径和事件、未成功的运行、无效标题以及已被替代的头提交。[审批结果策略](2026-09-09-blocked-weighted-approvals-remain-pending.zh.md) 继续负责待定与成功状态的语义。 +[审批策略测试](../../../../.github/review-ownership/check-approval.test.mjs) 覆盖带编号的运行名称、无效的来源路径和事件、未成功的运行、无效标题以及已被替代的头提交。[审批决策 check run](2026-09-29-weighted-approval-decision-check-run.zh.md) 继续负责所发布的 conclusion。 diff --git a/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.i18n.yaml b/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.i18n.yaml index 9272a63484..683ee70272 100644 --- a/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.i18n.yaml @@ -9,14 +9,14 @@ en: 2dbd821b8d65b054 zh: 9bef2993db51a00d /agent-note-weight-approvals-by-changed-production-line-ownership/decision: - en: 7dc225f91538ace1 - zh: a22d05ffd8d32b6d + en: a0b790864eae2f43 + zh: aa2bd4ed8d569180 /agent-note-weight-approvals-by-changed-production-line-ownership/alternatives-considered: en: 5e8af03e3ff6018a zh: 6b518c8eee8c5172 /agent-note-weight-approvals-by-changed-production-line-ownership/consequences: - en: 08495483b77771be - zh: a798d4af0eda55fe + en: bcf33d4ce1d367c1 + zh: 0623ac49e33a59e6 /agent-note-weight-approvals-by-changed-production-line-ownership/verification: en: cb604625373372b9 zh: f9056d20ab612537 diff --git a/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.md b/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.md index d9a223c2f3..84ca47b4d8 100644 --- a/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.md +++ b/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.md @@ -12,7 +12,7 @@ A fixed one-point reviewer weight does not reflect authorship of the code a pull The [approval policy](../../../../.github/review-ownership/README.md) scales a one-point approval by `min(2, 1 + 4 × ownedLines / totalLines)` over changed old production lines. Ownership of 0% gives one point, 12.5% gives 1.5 points, and 25% or more gives two points. Scores are not rounded before comparison with the two-point success threshold. The merge base supplies both classification and blame, while GitHub associates blame commits with reviewer accounts. Unlinked authors remain in the denominator. Additions have no prior owner and contribute no lines; an empty denominator produces no boost. -The publisher marks the head pending before dependency setup and evaluation, so an interrupted history fetch cannot preserve an earlier success. It uses the live base branch and exact reviewed head, reads complete Git history without checking out PR code, and skips attribution when base points or blockers already decide the result. A maintained lexer separates comments from code across the repository’s source languages. Author lookups batch commits and all reviewers share one measurement. Existing [pending-status semantics](2026-09-09-blocked-weighted-approvals-remain-pending.md) and [review-event validation](2026-09-10-approval-review-workflow-identity.md) remain independent requirements. +The publisher marks the decision check run in progress before dependency setup and evaluation, so an interrupted history fetch cannot preserve an earlier success. It uses the live base branch and exact reviewed head, reads complete Git history without checking out PR code, and skips attribution when base points or blockers already decide the result. A maintained lexer separates comments from code across the repository’s source languages. Author lookups batch commits and all reviewers share one measurement. Existing [decision check-run semantics](2026-09-29-weighted-approval-decision-check-run.md) and [review-event validation](2026-09-10-approval-review-workflow-identity.md) remain independent requirements. ## Alternatives considered @@ -28,7 +28,7 @@ The publisher marks the head pending before dependency setup and evaluation, so History fetching dominates cold execution. Complete history is required before selecting the merge base: a shallow apparent ancestor can exclude changes from the denominator, so shallow classification cannot safely decide that blame is unnecessary. On 2026-09-11, local measurements of PRs #3969 and #3977 count 73 and 535 old production lines across 4 and 14 files. Three local runs take 0.31–0.32 seconds and 0.87–0.93 seconds; one batched author query adds approximately one second per PR. A fresh single-branch bare clone of master takes 47 seconds on the same host. These are host observations, not CI time guarantees. -Blame measures last-touch authorship, not review quality or semantic expertise. The production classifier excludes unsupported source locations and file extensions; adding shipped source outside its inventory requires extending that classifier. Lexer classification is lexical rather than a semantic test of executable behavior. Evaluation errors retain the error status instead of silently awarding or withholding the boost. +Blame measures last-touch authorship, not review quality or semantic expertise. The production classifier excludes unsupported source locations and file extensions; adding shipped source outside its inventory requires extending that classifier. Lexer classification is lexical rather than a semantic test of executable behavior. Evaluation errors conclude the check as `failure` instead of silently awarding or withholding the boost. ## Verification diff --git a/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.zh.md b/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.zh.md index f4dfc9d4fc..5c29e0938d 100644 --- a/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.zh.md +++ b/.agents/notes/implemented/process/2026-09-11-production-blame-approval-weight.zh.md @@ -12,7 +12,7 @@ Status: implemented [审批策略](../../../../.github/review-ownership/README.md) 按变更旧生产代码行的归属比例,以 `min(2, 1 + 4 × ownedLines / totalLines)` 调整一分批准的权重。归属比例为 0% 时计一分,12.5% 时计 1.5 分,25% 及以上时计两分。分数在与两分通过线比较前不做舍入。合并基点同时提供代码分类和 blame 依据,GitHub 将归属提交关联到评审者账号。无法关联账号的作者仍计入分母。新增行没有原作者,不计入行数;分母为空时不提升权重。 -发布器在依赖安装和评估前将头提交标记为待定,避免历史拉取中断后保留此前的成功状态。它使用实时 base 分支和被评审的精确 head,读取完整 Git 历史,不检出 PR 代码;基础分数或阻塞评审已决定结果时跳过归属计算。维护中的词法分析器区分仓库各源码语言中的注释与代码。作者查询按提交批量执行,所有评审者共用一次统计。现有的[待定状态语义](2026-09-09-blocked-weighted-approvals-remain-pending.zh.md)和[评审事件验证](2026-09-10-approval-review-workflow-identity.zh.md)仍是独立要求。 +发布器在依赖安装和评估前将决策 check run 标记为进行中,避免历史拉取中断后保留此前的成功状态。它使用实时 base 分支和被评审的精确 head,读取完整 Git 历史,不检出 PR 代码;基础分数或阻塞评审已决定结果时跳过归属计算。维护中的词法分析器区分仓库各源码语言中的注释与代码。作者查询按提交批量执行,所有评审者共用一次统计。现有的[决策 check run 语义](2026-09-29-weighted-approval-decision-check-run.zh.md)和[评审事件验证](2026-09-10-approval-review-workflow-identity.zh.md)仍是独立要求。 ## Alternatives considered @@ -28,7 +28,7 @@ Status: implemented 冷启动的主要开销是拉取历史。选择合并基点前必须具备完整历史:浅历史中看似共同祖先的提交可能使部分变更被排除出分母,因此不能据此安全判定无需 blame。2026-09-11,在本机测量 PR #3969 和 #3977,得到 4 和 14 个文件中的 73 和 535 行旧生产代码。三次本地运行分别耗时 0.31–0.32 秒和 0.87–0.93 秒;每个 PR 的一次批量作者查询额外耗时约一秒。同一主机上,全新单分支裸克隆 master 耗时 47 秒。这些是本机观测值,不是 CI 耗时保证。 -Blame 衡量最后修改者归属,不代表评审质量或语义上的专业能力。生产代码分类器排除未支持的源码位置和扩展名;在其清单之外添加交付源码时,必须扩展分类器。词法分类不判断代码是否在语义上可执行。评估错误保留错误状态,不静默授予或取消加权。 +Blame 衡量最后修改者归属,不代表评审质量或语义上的专业能力。生产代码分类器排除未支持的源码位置和扩展名;在其清单之外添加交付源码时,必须扩展分类器。词法分类不判断代码是否在语义上可执行。评估错误使该检查以 `failure` 结束,不静默授予或取消加权。 ## Verification diff --git a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml index 14bf4d7016..50bca79957 100644 --- a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml @@ -9,8 +9,8 @@ en: 5fe2f3b3cb7ce046 zh: ed27d4efc995b8b5 /agent-note-pr-scoped-approval-delegation/decision: - en: 41c5c90b82d323c6 - zh: 55d5ed09d88bb821 + en: bf8f1ff30114310f + zh: a163e1276dd384f8 /agent-note-pr-scoped-approval-delegation/alternatives-considered: en: a6006217cb29613a zh: 83968328096f1d91 diff --git a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md index 4dcf0e8a8d..0282f9c058 100644 --- a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md +++ b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md @@ -10,7 +10,7 @@ A reviewer may trust another reviewer to decide a particular PR while retaining ## Decision -The [approval policy](../../../../.github/review-ownership/README.md#delegating-points) accepts `/delegate @username` in PR conversation comments. Each eligible sender's points follow the named recipient's effective approval, once, using the sender's weight. Delegation applies only to that PR and only between write-capable accounts other than the PR author. Received points cannot be forwarded. The command dismisses the sender's prior approvals and change requests through GitHub. Other effective blocking reviews still follow [blocking-review policy](2026-09-09-blocked-weighted-approvals-remain-pending.md). +The [approval policy](../../../../.github/review-ownership/README.md#delegating-points) accepts `/delegate @username` in PR conversation comments. Each eligible sender's points follow the named recipient's effective approval, once, using the sender's weight. Delegation applies only to that PR and only between write-capable accounts other than the PR author. Received points cannot be forwarded. The command dismisses the sender's prior approvals and change requests through GitHub. Other effective blocking reviews still follow [blocking-review policy](2026-09-29-weighted-approval-decision-check-run.md). The latest surviving eligible command in comment creation order determines the recipient. Edited commands require the latest editor to match the author’s immutable account ID; other writers cannot transfer or cancel that author’s points by editing a comment. A self-delegation or a subsequently submitted review restores the sender's own decision. Comment-only reviews also reclaim the points; pending reviews do not. Equal timestamps favor the review, and subsequent dismissal cannot revive the delegation. A fresh command after the review can delegate again. Edits and deletions recompute from current comments, including restoration of older commands. The trusted publisher filters conversation comment changes for `/delegate`, resolves the live open PR head, and reads comments and editor identity as API data. Every evaluation reconciles active eligible commands, dismissing prior decision reviews and requesting recipients unless already approved or requested. This covers replaced queued events and draft-to-ready transitions; other requested reviewers remain unchanged. Scores refresh after dismissal, which retains the old submission timestamps and cannot cancel delegation. Removing the recipient's approval removes counted points while retaining delegation for their next approval. Drafts do not dismiss or request reviews. Dismissal failures fail evaluation; request failures are logged separately and retried on later evaluations. Closed or merged PRs are skipped before status writes and dependency setup. It preserves the separate [review-workflow validation](2026-09-10-approval-review-workflow-identity.md). diff --git a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md index bf21724d67..7d26a42d0d 100644 --- a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md +++ b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md @@ -10,7 +10,7 @@ Status: implemented ## Decision -[批准策略](../../../../.github/review-ownership/README.md#delegating-points) 接受 PR 普通评论中的 `/delegate @username`。每位符合资格的委托人,其分数跟随指定受托人的有效批准,按委托人的权重计入一次。委托仅适用于当前 PR,且双方都必须具有写权限,不能是 PR 作者。收到的分数不能继续转交。命令会通过 GitHub 撤销委托人原有的批准和变更请求。其他有效阻塞评审仍遵循[阻塞评审策略](2026-09-09-blocked-weighted-approvals-remain-pending.zh.md)。 +[批准策略](../../../../.github/review-ownership/README.md#delegating-points) 接受 PR 普通评论中的 `/delegate @username`。每位符合资格的委托人,其分数跟随指定受托人的有效批准,按委托人的权重计入一次。委托仅适用于当前 PR,且双方都必须具有写权限,不能是 PR 作者。收到的分数不能继续转交。命令会通过 GitHub 撤销委托人原有的批准和变更请求。其他有效阻塞评审仍遵循[阻塞评审策略](2026-09-29-weighted-approval-decision-check-run.zh.md)。 按评论创建顺序,最新、仍存在且符合资格的命令决定受托人。编辑过的命令要求最近编辑者与作者的不可变账户 ID 一致;其他有写权限的人不能通过编辑评论转交或取消作者的分数。委托给自己或随后提交评审会恢复使用委托人本人的评审决定。仅评论的评审也会收回分数;尚未提交的评审不会。时间戳相同时以评审为准,随后撤销评审也不会重新启用委托。评审之后的新命令可以再次委托。编辑和删除会根据当前评论重算,包括恢复较早的命令。可信发布器按 `/delegate` 筛选普通评论变更,解析实时且未关闭的 PR 头提交,并将评论及编辑者身份作为 API 数据读取。每次评估都会处理所有有效且符合资格的命令,撤销旧的决定性评审,并请求尚未批准、尚未收到请求的受托人进行评审。这覆盖了排队事件被替换以及草稿转为 ready 的情况;其他 requested reviewers 保持不变。撤销后刷新分数;撤销保留旧提交时间戳,不会取消委托。移除受托人的批准只会移除计入的分数,委托仍然保留,供其下次批准使用。草稿 PR 不撤销或请求评审。撤销失败会使评估失败;请求失败单独记录日志,并在后续评估中重试。已关闭或合并的 PR 会在写入状态和安装依赖之前跳过。独立的[评审工作流验证](2026-09-10-approval-review-workflow-identity.zh.md) 保持不变。 diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml new file mode 100644 index 0000000000..e3d242d672 --- /dev/null +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml @@ -0,0 +1,22 @@ +# Bilingual-pair consistency record for 2026-09-29-weighted-approval-decision-check-run.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md +/agent-note-weighted-approval-decisions-are-completed-check-runs: + en: 1b120e96b0f5f340 + zh: ff20c20fe0558964 +/agent-note-weighted-approval-decisions-are-completed-check-runs/problem: + en: 6ba6f6a15a1562ec + zh: 1cbef3e736c258e6 +/agent-note-weighted-approval-decisions-are-completed-check-runs/decision: + en: 933e4c1438771907 + zh: f6056382f3b555c9 +/agent-note-weighted-approval-decisions-are-completed-check-runs/verification: + en: d1f65b5b577b08b8 + zh: 7a193cec1acec003 +/agent-note-weighted-approval-decisions-are-completed-check-runs/alternatives-considered: + en: a7ef91c0506766a8 + zh: d6584e31652b14ca +/agent-note-weighted-approval-decisions-are-completed-check-runs/consequences: + en: 4cafdf12b326c68a + zh: c37a57d26d2f0894 diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md new file mode 100644 index 0000000000..f4d8f9e639 --- /dev/null +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md @@ -0,0 +1,50 @@ +# Agent Note: Weighted approval decisions are completed check runs + +Status: implemented + +English | [中文](2026-09-29-weighted-approval-decision-check-run.zh.md) + +## Problem + +The weighted approval policy must block merging until its conditions hold, distinguish an unmet condition from a failed evaluation, and let tools that wait for pull-request checks to finish terminate. A draft pull request, insufficient approval points, and an effective `CHANGES_REQUESTED` review from a write-capable reviewer are reversible review states rather than evaluation failures. + +Commit statuses offer only `pending`, `success`, `failure`, and `error`. A `pending` commit status blocks merging, but `gh pr checks --watch` and other CI watchers treat it as running work and wait until someone approves. A `failure` commit status ends the wait but reports an unmet, reversible condition as a malfunction and conflates the policy outcome with publisher health. + +## Decision + +The [publisher](../../../../.github/review-ownership/check-approval.mjs) reports the decision as the `weighted approval decision` check run on the pull-request head, written with the job's GitHub Actions token. Branch rules require that check run with GitHub Actions as its expected source. + +| Condition | Check run | +|---|---| +| Dependency setup or evaluation in progress | `in_progress` | +| Draft, fewer than the required points, or an effective write-capable `CHANGES_REQUESTED` review | `completed`, conclusion `action_required` | +| Ready, threshold met, and no blocking review | `completed`, conclusion `success` | +| Setup or evaluation failure | `completed`, conclusion `failure` | + +A blocking review dominates the point total, so the conclusion remains `action_required` even when counted approvals reach the threshold. GitHub accepts only `success`, `neutral`, and `skipped` for a required check, so `action_required` blocks merging while completing the check. + +The publisher lists the head's check runs with that name, updates every run owned by GitHub Actions, and creates one only when none exists. An incomplete list or a run without a valid ID fails the publication. + +## Verification + +[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case, the exact published check-run payloads, updating an existing run instead of creating another, and invalid list responses. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the `checks: write` permission, the `in_progress` and `failure` phase commands, and the separate publisher job name. + +## Alternatives considered + +**Keep the `pending` commit status.** Branch rules block correctly, but CI watchers cannot distinguish an approval wait from running checks and do not finish. + +**Publish `failure` for unmet conditions.** This ends the wait, but it represents an unmet, reversible merge condition as a malfunction and conflates the policy outcome with publisher health. + +**Publish nothing until success.** A required check that was never reported remains expected and is invisible to many watchers. Revoking a success already published on the same head still requires a non-success write, and each watcher's treatment of a missing required check is unspecified. + +**Keep the `weighted approval` name.** When a check and a commit status share a required name, GitHub requires both to pass. Heads that carry a `pending` commit status from the earlier publisher would stay blocked until a new push. + +**Create a new check run on every evaluation.** Several same-name check runs on one head make the result depend on how GitHub and each watcher select among them. + +**Let approval points override a blocking review.** This makes the score the only success condition, but it permits success while a write-capable reviewer's effective decision still requests changes. + +## Consequences + +CI watchers finish while the decision waits on reviewers. `gh pr checks` groups `action_required` with failed checks and exits non-zero; consumers distinguish it by the check name, the `ACTION_REQUIRED` conclusion, or the output title, which carries the score or blocker. The `weighted approval publisher` Actions job and the `failure` conclusion carry operational failures. + +Revoking an earlier success on the same head returns the completed check run to `in_progress`, a transition the GitHub REST reference does not restrict. diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md new file mode 100644 index 0000000000..4dee85c8f7 --- /dev/null +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md @@ -0,0 +1,50 @@ +# Agent Note: 加权批准决策使用已完成的 check run + +Status: implemented + +[English](2026-09-29-weighted-approval-decision-check-run.md) | 中文 + +## 问题 + +加权批准策略必须在条件满足之前阻止合并,区分尚未满足的条件与失败的评估,并让等待 PR 检查结束的工具能够退出。draft PR、批准点数不足,以及具有写权限的评审人仍然生效的 `CHANGES_REQUESTED` 评审,都是可撤销的评审状态,而不是评估故障。 + +commit status 只提供 `pending`、`success`、`failure` 和 `error`。`pending` commit status 会阻止合并,但 `gh pr checks --watch` 和其他 CI 监视工具把它当作仍在运行的工作,会一直等到有人批准。`failure` commit status 能结束等待,但会把尚未满足且可撤销的条件报告为故障,并混淆策略结果与 publisher 的健康状态。 + +## 决策 + +[publisher](../../../../.github/review-ownership/check-approval.mjs) 使用该 job 的 GitHub Actions token,在 PR head 上以 `weighted approval decision` check run 报告决策。分支规则要求该 check run,并把 GitHub Actions 设为预期来源。 + +| 条件 | Check run | +|---|---| +| 依赖安装或评估进行中 | `in_progress` | +| draft、点数少于要求,或存在具有写权限的评审人仍然生效的 `CHANGES_REQUESTED` 评审 | `completed`,conclusion 为 `action_required` | +| 已 ready、达到阈值且没有阻塞性评审 | `completed`,conclusion 为 `success` | +| 安装或评估失败 | `completed`,conclusion 为 `failure` | + +阻塞性评审的优先级高于点数总和,因此即使计入的批准已经达到阈值,conclusion 仍为 `action_required`。GitHub 只接受 `success`、`neutral` 和 `skipped` 作为必需检查的通过结果,因此 `action_required` 会阻止合并,同时让该检查处于完成状态。 + +publisher 列出 head 上同名的 check run,更新所有属于 GitHub Actions 的 run,只有在不存在时才创建一个。列表不完整或 run 没有有效 ID 时发布失败。 + +## 验证 + +[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景、准确发布的 check run payload、更新已有 run 而不是另建一个,以及无效的列表响应。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定 `checks: write` 权限、`in_progress` 和 `failure` 阶段命令,以及独立的 publisher job 名称。 + +## 考虑过的替代方案 + +**保留 `pending` commit status。** 分支规则能正确阻止合并,但 CI 监视工具无法区分等待批准与仍在运行的检查,因此不会结束。 + +**为尚未满足的条件发布 `failure`。** 该方案能结束等待,但会把尚未满足且可撤销的合并条件表示为故障,并混淆策略结果与 publisher 的健康状态。 + +**在成功之前不发布任何结果。** 从未上报的必需检查保持 expected 状态,许多监视工具看不到它。撤销同一 head 上已发布的成功仍然需要写入非成功结果,而各监视工具如何对待缺失的必需检查没有规定。 + +**保留 `weighted approval` 名称。** 当 check 和 commit status 使用同一个必需名称时,GitHub 要求两者都通过。带有旧 publisher 所写 `pending` commit status 的 head 会一直被阻止,直到出现新的 push。 + +**每次评估都创建新的 check run。** 同一 head 上存在多个同名 check run 时,结果取决于 GitHub 和各监视工具如何在其中选择。 + +**允许批准点数覆盖阻塞性评审。** 该方案会让分数成为唯一的成功条件,但也允许在具有写权限的评审人仍然有效地要求修改时发布成功结果。 + +## 后果 + +决策等待评审人时,CI 监视工具会结束。`gh pr checks` 把 `action_required` 与失败的检查归为一组并以非零状态退出;消费方可以通过 check 名称、`ACTION_REQUIRED` conclusion 或携带分数或 blocker 的输出标题加以区分。`weighted approval publisher` Actions job 和 `failure` conclusion 承载运行故障信号。 + +撤销同一 head 上此前的成功结果时,已完成的 check run 会回到 `in_progress`;GitHub REST 参考文档没有限制这一转换。 diff --git a/.github/review-ownership/README.md b/.github/review-ownership/README.md index 2da4c5e2cd..7b461ae864 100644 --- a/.github/review-ownership/README.md +++ b/.github/review-ownership/README.md @@ -16,11 +16,11 @@ The [`weighted-approval` workflow](../workflows/weighted-approval.yml) publishes ## Approval scoring -The weighted approval workflow exposes two pull-request checks. The `weighted approval publisher` Actions job reports whether evaluation and status publication completed, while the `weighted approval` commit status carries the approval decision on the pull request head. Branch rules must require only the commit status with GitHub Actions as its expected source; a context-only requirement can accept a same-named status from another integration. The publisher marks the head pending before Python setup or lexer installation, so failed setup or an interrupted history fetch cannot leave a previous success in place. Setup and evaluation failures publish an error status. A completed evaluation returns `pending` below two approval points, while the pull request is a draft, or while a write-capable reviewer has an effective `CHANGES_REQUESTED` review; the blocker keeps the status pending even when counted approvals reach the threshold. It returns `success` only when the threshold is met, the pull request is ready, and no such blocker exists. If evaluation fails, the publisher writes an `error` status. +The weighted approval workflow exposes two pull-request checks. The `weighted approval publisher` Actions job reports whether evaluation and check publication completed, while the `weighted approval decision` check run carries the approval decision on the pull request head. Branch rules must require that check run with GitHub Actions as its expected source; a name-only requirement can accept a same-named check or status from another integration. The publisher updates the head's existing GitHub Actions decision check run and creates one only when none exists. It marks the check run `in_progress` before Python setup or lexer installation, so failed setup or an interrupted history fetch cannot leave a previous success in place. Setup and evaluation failures conclude it as `failure`. A completed evaluation concludes `action_required` below two approval points, while the pull request is a draft, or while a write-capable reviewer has an effective `CHANGES_REQUESTED` review; the blocker keeps that conclusion even when counted approvals reach the threshold. It concludes `success` only when the threshold is met, the pull request is ready, and no such blocker exists. `action_required` blocks merging but completes the check, so tools that wait for checks to finish do not wait for reviewers; the check-run title carries the score or blocker. Reviewers whose calculated base repository permission is `write` or `admin` count. The [approval policy](approval-policy.json) gives `@07akioni`, `@imccyu`, `@tianyicui`, `@tianyicui-bot`, `@turtle1999`, and `@turtle2099` two points each; every other write-capable reviewer gets one point. The pull-request author’s own review and reviewers without write permission do not count. -The PR author contributes `min(100, mergedPRCount) × 11 / 1000` points: 0 merged PRs → 0 points, 50 → 0.55, and 100 or more → 1.1. The publisher counts only merged PRs in this repository using the author’s immutable account ID, excluding the current PR. It stops at 100 matches and rejects incomplete history responses. Counts refresh at the next subscribed evaluation event. Author credit is separate from reviewer approvals and cannot satisfy the two-point requirement alone; the author’s own review remains excluded. Human and bot authors, including Dependabot, use the same rule. Drafts, blocking reviews, and sufficient reviewer points skip history lookup; logs mark credit as not evaluated. Otherwise, logs show author credit and the capped count separately. Below the cap, counting may scan the repository’s entire merged history; a failed required lookup publishes an error. +The PR author contributes `min(100, mergedPRCount) × 11 / 1000` points: 0 merged PRs → 0 points, 50 → 0.55, and 100 or more → 1.1. The publisher counts only merged PRs in this repository using the author’s immutable account ID, excluding the current PR. It stops at 100 matches and rejects incomplete history responses. Counts refresh at the next subscribed evaluation event. Author credit is separate from reviewer approvals and cannot satisfy the two-point requirement alone; the author’s own review remains excluded. Human and bot authors, including Dependabot, use the same rule. Drafts, blocking reviews, and sufficient reviewer points skip history lookup; logs mark credit as not evaluated. Otherwise, logs show author credit and the capped count separately. Below the cap, counting may scan the repository’s entire merged history; a failed required lookup concludes the check as `failure`. A one-point approval receives weight `min(2, 1 + 4 × ownedLines / totalLines)` from modified or deleted old production-code lines, attributed by `git blame` at the merge base of the live base branch and exact reviewed head. Ownership of 0%, 12.5%, and 25% gives 1, 1.5, and 2 points; higher ownership remains capped at 2. The success threshold remains 2 total points, without rounding the score. New lines do not enter the denominator, and an empty denominator gives no boost. Existing two-point weights remain unchanged. GitHub commit-author accounts identify reviewers across author emails; unlinked authors remain in the denominator without contributing to a reviewer. The publisher logs measured ownership. It skips attribution when reviewer points plus author credit already meet the threshold or a blocking review exists. Displayed scores use at most three decimal places; the decision uses unrounded scores with a tolerance of `1e-12` points for floating-point error. The curve endpoints come from the policy’s default and required points. @@ -28,13 +28,13 @@ Production source means supported code files under `src/` in `packages/`, `apps/ Each reviewer contributes only the current `APPROVED` or `CHANGES_REQUESTED` decision that GitHub returns. A `DISMISSED` record clears that reviewer's standing decision, including earlier approvals. Comment-only and pending records do not replace a decision. Reviews from deleted accounts and reviewers without current repository access do not count. The workflow does not invalidate an approval by its review commit; the repository's native pull-request rules own stale-review and latest-push requirements. -The publisher runs when a pull request opens, synchronizes, reopens, becomes ready, becomes a draft, or is edited, including a base-branch change. Conversation comment events run the publisher only when the current or previous body contains `/delegate`. Closed or merged PRs and stale review heads are skipped before status writes or dependency setup; the workflow does not subscribe to master pushes. GitHub associates `workflow_run` publisher runs with the default branch even though approval statuses target the open PR head. Pull-request, review, and comment events share one concurrency group per PR. Review submissions, edits, and dismissals run the no-permission [`weighted-approval-review-event` workflow](../workflows/weighted-approval-review-event.yml); its validated run title supplies the pull-request number to the default-branch publisher. The publisher validates the current head, fetches every review and conversation comment, and resolves current repository permission before publishing the status. Permission changes take effect on the next subscribed event. +The publisher runs when a pull request opens, synchronizes, reopens, becomes ready, becomes a draft, or is edited, including a base-branch change. Conversation comment events run the publisher only when the current or previous body contains `/delegate`. Closed or merged PRs and stale review heads are skipped before check-run writes or dependency setup; the workflow does not subscribe to master pushes. GitHub associates `workflow_run` publisher runs with the default branch even though decision check runs target the open PR head. Pull-request, review, and comment events share one concurrency group per PR. Review submissions, edits, and dismissals run the no-permission [`weighted-approval-review-event` workflow](../workflows/weighted-approval-review-event.yml); its validated run title supplies the pull-request number to the default-branch publisher. The publisher validates the current head, fetches every review and conversation comment, and resolves current repository permission before publishing the check run. Permission changes take effect on the next subscribed event. ## Delegating points -Post `/delegate @username` as the entire text of a PR conversation comment to transfer your reviewer points to that user's effective `APPROVED` decision on this PR. For example, `/delegate @turtle1999` lets @turtle1999's approval count your points alongside their own. Until they approve, your points do not count, even if your own approval remains active. Each evaluation reconciles every active eligible command: it dismisses the sender's existing `APPROVED` and `CHANGES_REQUESTED` reviews, then requests review from recipients who have neither approved nor already been requested. This also handles commands posted on drafts once the PR becomes ready and commands whose original event was replaced in the concurrency queue. Other requested reviewers remain unchanged. The command does not submit a review; drafts do not dismiss or request reviews. Comment-only and pending reviews cannot be dismissed. A failed dismissal publishes an error and prevents review requests; scores are refreshed after successful dismissal. Request failures are logged without changing the published score, and subsequent evaluations retry outstanding requests. +Post `/delegate @username` as the entire text of a PR conversation comment to transfer your reviewer points to that user's effective `APPROVED` decision on this PR. For example, `/delegate @turtle1999` lets @turtle1999's approval count your points alongside their own. Until they approve, your points do not count, even if your own approval remains active. Each evaluation reconciles every active eligible command: it dismisses the sender's existing `APPROVED` and `CHANGES_REQUESTED` reviews, then requests review from recipients who have neither approved nor already been requested. This also handles commands posted on drafts once the PR becomes ready and commands whose original event was replaced in the concurrency queue. Other requested reviewers remain unchanged. The command does not submit a review; drafts do not dismiss or request reviews. Comment-only and pending reviews cannot be dismissed. A failed dismissal concludes the check as `failure` and prevents review requests; scores are refreshed after successful dismissal. Request failures are logged without changing the published score, and subsequent evaluations retry outstanding requests. Both accounts must currently have write or admin permission, and neither may be the PR author. Commands involving ineligible accounts are ignored. A sender's newest surviving eligible, author-authorized command wins, in comment creation order; editing an older comment does not move it after newer comments. Submitting any review after delegation automatically takes the points back, including an approval, change request, or comment-only review. Submitting an inline diff comment with “Add single comment” also creates a comment-only review and reclaims the points. Pending reviews do not count; a later dismissal does not restore the delegation. When timestamps are equal, the review takes precedence. A new command after the review can delegate again; editing an older command cannot reactivate it. Post `/delegate @your-own-username` to restore your own review decision. Editing or deleting a command recomputes delegation from remaining comments, so deleting the latest command can restore an older one. Quoted commands, code blocks, review bodies, inline review comments, and commands mixed with other text do not count. Surrounding whitespace is allowed; account matching is case-insensitive. An edited command is accepted only when GitHub identifies the original author as its latest editor; edits by other accounts cannot delegate or cancel that author’s points. @@ -44,7 +44,7 @@ Each sender contributes at most once, using their own fixed weight or production ## Security -All actions in the status-writing job are pinned to commit SHAs. The job checks out only the repository default branch. It does not check out or execute pull-request code and does not use repository secrets. Only when there is no blocker, reviewer points plus author credit are insufficient, and an approval has the policy’s default weight, it fetches complete history using the job token, passes Git objects to the trusted classifier as data, and resolves commit authors in batches of 50. Fetch credentials exist only in the Git child environment. Missing history, parsing failures, or incomplete author queries fail evaluation rather than producing a partial score. The review-event workflow has no `GITHUB_TOKEN` permissions and passes only a decimal pull-request number in its run title. The publisher accepts only successful `pull_request_review` runs from the review-event workflow file, identified by `workflow_run.path`; GitHub can populate `workflow_run.name` with the expanded run title. The publisher rejects an invalid run title and a number that does not resolve to the workflow run's current pull-request head. Pull-request reviews and comments are treated as API data and escaped in logs. All publisher triggers and steps, including dependency setup, have pull-request write permission; the policy uses it to dismiss eligible senders' prior decision reviews and request recipients. Every evaluation verifies command authors and latest editors by immutable account ID through GraphQL, matching the body to the REST comment history before counting points or acting. Missing or inconsistent editor metadata fails evaluation. Non-author edits are ignored. Only active eligible delegations are reconciled; superseded or revoked commands have no effects. Dismissal failures publish an error; request failures remain separate from the approval decision. +All actions in the check-writing job are pinned to commit SHAs. The job checks out only the repository default branch. It does not check out or execute pull-request code and does not use repository secrets. Only when there is no blocker, reviewer points plus author credit are insufficient, and an approval has the policy’s default weight, it fetches complete history using the job token, passes Git objects to the trusted classifier as data, and resolves commit authors in batches of 50. Fetch credentials exist only in the Git child environment. Missing history, parsing failures, or incomplete author queries fail evaluation rather than producing a partial score. The review-event workflow has no `GITHUB_TOKEN` permissions and passes only a decimal pull-request number in its run title. The publisher accepts only successful `pull_request_review` runs from the review-event workflow file, identified by `workflow_run.path`; GitHub can populate `workflow_run.name` with the expanded run title. The publisher rejects an invalid run title and a number that does not resolve to the workflow run's current pull-request head. Pull-request reviews and comments are treated as API data and escaped in logs. All publisher triggers and steps, including dependency setup, have pull-request write permission; the policy uses it to dismiss eligible senders' prior decision reviews and request recipients. Every evaluation verifies command authors and latest editors by immutable account ID through GraphQL, matching the body to the REST comment history before counting points or acting. Missing or inconsistent editor metadata fails evaluation. Non-author edits are ignored. Only active eligible delegations are reconciled; superseded or revoked commands have no effects. Dismissal failures conclude the check as `failure`; request failures remain separate from the approval decision. Approval policy changes take effect only after they merge into the default branch. This prevents an untrusted pull request from changing the program or policy for its own run. @@ -52,7 +52,7 @@ Approval policy changes take effect only after they merge into the default branc ## Verification -Run `pnpm run test:approval-policy` for policy parsing, effective review decisions, review-event validation, merged-history pagination and account matching, lazy author credit, permission filtering, weighted scoring, delegation conservation and revocation, editor authorization, comment pagination, dismissal and review-request reconciliation, blockers, drafts, closed-PR skips, status publication, and API failures. [Workflow tests](../../scripts/ci-workflow.spec.ts) pin the trusted checkout, no-permission review handoff, permissions, events, and commands. The repository gate graph runs the approval policy and workflow tests in CI. The Python SDK job runs `uv run --python 3.10 --with-requirements .github/review-ownership/requirements.txt python -m unittest discover -s .github/review-ownership -p 'test_*.py'` for real Git histories, lexers, renames, shallow-history rejection, and the publisher’s fetch/analysis integration. +Run `pnpm run test:approval-policy` for policy parsing, effective review decisions, review-event validation, merged-history pagination and account matching, lazy author credit, permission filtering, weighted scoring, delegation conservation and revocation, editor authorization, comment pagination, dismissal and review-request reconciliation, blockers, drafts, closed-PR skips, check-run publication, and API failures. [Workflow tests](../../scripts/ci-workflow.spec.ts) pin the trusted checkout, no-permission review handoff, permissions, events, and commands. The repository gate graph runs the approval policy and workflow tests in CI. The Python SDK job runs `uv run --python 3.10 --with-requirements .github/review-ownership/requirements.txt python -m unittest discover -s .github/review-ownership -p 'test_*.py'` for real Git histories, lexers, renames, shallow-history rejection, and the publisher’s fetch/analysis integration. diff --git a/.github/review-ownership/check-approval.mjs b/.github/review-ownership/check-approval.mjs index 5792299c46..489acd1779 100644 --- a/.github/review-ownership/check-approval.mjs +++ b/.github/review-ownership/check-approval.mjs @@ -10,7 +10,7 @@ import { authorCreditPoints, countMergedAuthorPulls } from './author-weight.mjs' const API_VERSION = '2026-03-10' const MAX_PULL_REQUEST_RECORDS = 3_000 const PAGE_SIZE = 100 -const STATUS_CONTEXT = 'weighted approval' +const CHECK_NAME = 'weighted approval decision' const WRITABLE_PERMISSIONS = new Set(['admin', 'write']) const REVIEW_STATES = new Set(['APPROVED', 'CHANGES_REQUESTED', 'COMMENTED', 'DISMISSED', 'PENDING']) @@ -209,13 +209,13 @@ function timestamp(value, subject) { /** * Evaluate approval points from current reviews and repository permissions. * @param {{event: unknown, policySource: string, api: (path: string, options?: {method?: string, body?: unknown}) => Promise, getOwnership?: typeof productionOwnership, getMergedCount?: typeof countMergedAuthorPulls}} options Runtime inputs. - * @returns {Promise<{pull: {repository: string, number: number, headSha: string}, state: 'pending' | 'success', description: string, points: number, authorCredit: {mergedCount: number, points: number} | null, requiredPoints: number, approvals: Array<{login: string, points: number, delegatedTo?: string, ownership?: {ownedLines: number, totalLines: number}}>, delegations: Array<{login: string, delegatedTo: string, commentId: number, reviewIds: number[], approved: boolean}>, blockers: string[], ignoredReviewers: string[]}>} Approval decision and status payload fields; approval login owns the points, delegatedTo supplies its decision, delegations contains eligible active commands and their superseded decision review IDs, and null author credit means history was not evaluated. + * @returns {Promise<{pull: {repository: string, number: number, headSha: string}, state: 'action_required' | 'success', description: string, points: number, authorCredit: {mergedCount: number, points: number} | null, requiredPoints: number, approvals: Array<{login: string, points: number, delegatedTo?: string, ownership?: {ownedLines: number, totalLines: number}}>, delegations: Array<{login: string, delegatedTo: string, commentId: number, reviewIds: number[], approved: boolean}>, blockers: string[], ignoredReviewers: string[]}>} Approval decision and check-run output fields; approval login owns the points, delegatedTo supplies its decision, delegations contains eligible active commands and their superseded decision review IDs, and null author credit means history was not evaluated. */ export async function evaluateApproval({ event, policySource, api, getOwnership = productionOwnership, getMergedCount = countMergedAuthorPulls }) { const pull = pullRequestFromEvent(event) const policy = parseApprovalPolicy(policySource) if (pull.draft) { - return approvalResult(pull, policy.requiredPoints, [], [], [], 'pending', 'draft pull request') + return approvalResult(pull, policy.requiredPoints, [], [], [], 'action_required', 'draft pull request') } const reviews = await listPullRequestReviews(api, pull.repository, pull.number) @@ -286,11 +286,11 @@ export async function evaluateApproval({ event, policySource, api, getOwnership return next }, authorCredit?.points ?? 0) if (blockers.length > 0) { - return approvalResult(pull, policy.requiredPoints, approvals, blockers, ignoredReviewers, 'pending', + return approvalResult(pull, policy.requiredPoints, approvals, blockers, ignoredReviewers, 'action_required', `${blockers.length} blocking change request${blockers.length === 1 ? '' : 's'}`, authorCredit, activeDelegations) } // Tolerate floating-point addition error without rounding approval scores. - const state = points + 1e-12 >= policy.requiredPoints ? 'success' : 'pending' + const state = points + 1e-12 >= policy.requiredPoints ? 'success' : 'action_required' return approvalResult( pull, policy.requiredPoints, @@ -305,13 +305,13 @@ export async function evaluateApproval({ event, policySource, api, getOwnership } /** - * Evaluate and publish the required commit status, publishing an error status when evaluation fails. + * Evaluate and publish the required check run, concluding it as failure when evaluation fails. * @param {{event: unknown, policySource: string, api: (path: string, options?: {method?: string, body?: unknown}) => Promise, runUrl: string, write?: (line: string) => void, getOwnership?: typeof productionOwnership, getMergedCount?: typeof countMergedAuthorPulls}} options Runtime inputs. * @returns {Promise>>} Published approval decision. */ export async function runApprovalCheck({ event, policySource, api, runUrl, getOwnership = productionOwnership, getMergedCount = countMergedAuthorPulls, write = line => process.stdout.write(`${line}\n`) }) { const pull = pullRequestFromEvent(event) - await publishStatus(api, pull, 'pending', 'Evaluating approval points.', runUrl) + await publishCheck(api, pull, 'in_progress', 'Evaluating approval points.', runUrl) let result try { result = await evaluateApproval({ event, policySource, api, getOwnership, getMergedCount }) @@ -331,7 +331,7 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw result = await evaluateApproval({ event, policySource, api, getOwnership, getMergedCount }) } } catch (error) { - await publishStatus(api, pull, 'error', 'Approval evaluation failed.', runUrl) + await publishCheck(api, pull, 'failure', 'Approval evaluation failed.', runUrl) throw error } write(result.authorCredit @@ -342,8 +342,8 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw `@${login}: ${points}${delegatedTo ? ` (delegated to @${delegatedTo})` : ''}${ownership ? ` (${ownership.ownedLines}/${ownership.totalLines} old production lines)` : ''}`)) writeList(write, 'Blocking change requests', result.blockers.map(login => `@${login}`)) writeList(write, 'Ignored reviewers without write access', result.ignoredReviewers.map(login => `@${login}`)) - await publishStatus(api, pull, result.state, result.description, runUrl) - write(`Published ${JSON.stringify(STATUS_CONTEXT)} status ${JSON.stringify(result.state)}.`) + await publishCheck(api, pull, result.state, result.description, runUrl) + write(`Published ${JSON.stringify(CHECK_NAME)} check run ${JSON.stringify(result.state)}.`) try { await requestDelegatedReviews(result, api, write) } catch (error) { @@ -353,14 +353,14 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw } /** - * Revoke a previous success before dependency installation, or report its failure. - * @param {{event: unknown, api: (path: string, options: object) => Promise, runUrl: string, phase: string}} options Publication inputs. - * @returns {Promise} Completion of the status write. + * Revoke a previous conclusion before dependency installation, or report its failure. + * @param {{event: unknown, api: (path: string, options?: {method?: string, body?: unknown}) => Promise, runUrl: string, phase: string}} options Publication inputs. + * @returns {Promise} Completion of the check-run write. */ export async function publishApprovalPhase({ event, api, runUrl, phase }) { - if (!['pending', 'error'].includes(phase)) throw new Error('invalid approval setup phase') - await publishStatus(api, pullRequestFromEvent(event), phase, - phase === 'pending' ? 'Preparing approval evaluation.' : 'Approval setup or evaluation failed.', runUrl) + if (!['in_progress', 'failure'].includes(phase)) throw new Error('invalid approval setup phase') + await publishCheck(api, pullRequestFromEvent(event), phase, + phase === 'in_progress' ? 'Preparing approval evaluation.' : 'Approval setup or evaluation failed.', runUrl) } /** @@ -414,7 +414,7 @@ export async function approvalEventFromComment({ event, api }) { } /** - * Refresh a pull-request event before any status write; closed PRs and stale heads are skipped. + * Refresh a pull-request event before any check-run write; closed PRs and stale heads are skipped. * @param {{event: unknown, api: (path: string) => Promise}} options Pull-request event and API caller. * @returns {Promise | null>} Live event, or null for a closed PR or superseded head. */ @@ -467,18 +467,26 @@ async function reviewerPermission(api, repository, login) { return response.permission.toLowerCase() } -async function publishStatus(api, pull, state, description, runUrl) { +// Updating the head's existing GitHub Actions check run keeps one decision per head for branch rules and CI watchers. +async function publishCheck(api, pull, state, title, runUrl) { if (!/^https:\/\/[^\s]+$/u.test(runUrl)) throw new Error('workflow run URL must use HTTPS') - if (description.length > 140) throw new Error('commit status description exceeds 140 characters') - await api(`/repos/${pull.repository}/statuses/${pull.headSha}`, { - method: 'POST', - body: { - state, - context: STATUS_CONTEXT, - description, - target_url: runUrl, - }, - }) + const body = { + ...(state === 'in_progress' ? { status: state } : { status: 'completed', conclusion: state }), + details_url: runUrl, + output: { title, summary: `${title} [Workflow run](${runUrl})` }, + } + const listed = await api(`/repos/${pull.repository}/commits/${pull.headSha}/check-runs?check_name=${encodeURIComponent(CHECK_NAME)}&filter=all&per_page=${PAGE_SIZE}`) + if (!isRecord(listed) || !Array.isArray(listed.check_runs) || listed.total_count !== listed.check_runs.length) { + throw new Error('check-run list response is invalid or incomplete') + } + const runs = listed.check_runs.filter(run => isRecord(run) && isRecord(run.app) && run.app.slug === 'github-actions') + if (runs.length === 0) { + await api(`/repos/${pull.repository}/check-runs`, { method: 'POST', body: { name: CHECK_NAME, head_sha: pull.headSha, ...body } }) + } + for (const { id } of runs) { + if (!Number.isSafeInteger(id)) throw new Error('check run has no valid id') + await api(`/repos/${pull.repository}/check-runs/${id}`, { method: 'PATCH', body }) + } } function pullRequestFromEvent(event) { @@ -571,7 +579,7 @@ async function main() { const phase = process.argv[2] if (phase) { await publishApprovalPhase({ event, api, runUrl: process.env.GITHUB_RUN_URL ?? '', phase }) - if (phase === 'pending' && process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, 'active=true\n') + if (phase === 'in_progress' && process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, 'active=true\n') return } await runApprovalCheck({ diff --git a/.github/review-ownership/check-approval.test.mjs b/.github/review-ownership/check-approval.test.mjs index b5d0b6cbb3..186e97d1b3 100644 --- a/.github/review-ownership/check-approval.test.mjs +++ b/.github/review-ownership/check-approval.test.mjs @@ -11,14 +11,18 @@ import { evaluateApproval as evaluateWithHistory, listPullRequestReviews, parseApprovalPolicy, - runApprovalCheck as runWithHistory, + runApprovalCheck as runPublisher, publishApprovalPhase, } from './check-approval.mjs' const policySource = readFileSync(new URL('approval-policy.json', import.meta.url), 'utf8') -const withoutComments = api => (path, options) => path.includes('/comments?') ? [] : api(path, options) -const evaluateApproval = options => evaluateWithHistory({ getMergedCount: async () => 0, ...options, api: withoutComments(options.api) }) -const runApprovalCheck = options => runWithHistory({ getMergedCount: async () => 0, ...options, api: withoutComments(options.api) }) +const withoutCheckRuns = api => (path, options) => + path.includes('/check-runs?') ? { total_count: 0, check_runs: [] } : api(path, options) +const withEmptyReads = api => withoutCheckRuns((path, options) => path.includes('/comments?') ? [] : api(path, options)) +const evaluateApproval = options => evaluateWithHistory({ getMergedCount: async () => 0, ...options, api: withEmptyReads(options.api) }) +const runWithHistory = options => runPublisher({ ...options, api: withoutCheckRuns(options.api) }) +const runApprovalCheck = options => runWithHistory({ getMergedCount: async () => 0, ...options, api: withEmptyReads(options.api) }) +const publishedState = body => body.conclusion ?? body.status const HEAD_SHA = '1234567890abcdef1234567890abcdef12345678' const pullRequestEvent = ({ author = 'author', draft = false } = {}) => ({ @@ -198,7 +202,7 @@ test('accepts two one-point approvals and ignores reviews without write access', assert.deepEqual(result.ignoredReviewers, ['reader']) }) -test('keeps one one-point approval pending without failing the status', async () => { +test('requires action for one one-point approval without failing the check', async () => { const result = await evaluateApproval({ event: pullRequestEvent(), policySource, @@ -209,7 +213,7 @@ test('keeps one one-point approval pending without failing the status', async () throw new Error(`unexpected API path ${path}`) }, }) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') assert.equal(result.points, 1) }) @@ -228,11 +232,11 @@ test('ignores a reviewer whose collaborator permission lookup returns 404', asyn }, }) const result = await evaluateApproval({ event: pullRequestEvent(), policySource, api }) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') assert.deepEqual(result.ignoredReviewers, ['former-writer']) }) -test('keeps the status pending on a write-capable change request while ignoring the author and read-only reviewers', async () => { +test('requires action on a write-capable change request while ignoring the author and read-only reviewers', async () => { const statuses = [] const result = await runApprovalCheck({ event: pullRequestEvent({ author: 'author' }), @@ -251,7 +255,7 @@ test('keeps the status pending on a write-capable change request while ignoring if (path.includes('/collaborators/turtle1999/permission')) return { permission: 'admin' } if (path.includes('/collaborators/blocker/permission')) return { permission: 'write' } if (path.includes('/collaborators/reader/permission')) return { permission: 'read' } - if (path.includes('/statuses/')) { + if (path.includes('/check-runs')) { statuses.push(options.body) return {} } @@ -259,32 +263,35 @@ test('keeps the status pending on a write-capable change request while ignoring }, write: () => {}, }) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') assert.equal(result.description, '1 blocking change request.') assert.equal(result.points, 2) assert.deepEqual(result.blockers, ['blocker']) assert.deepEqual(result.ignoredReviewers, ['reader']) assert.deepEqual(statuses, [{ - state: 'pending', - context: 'weighted approval', - description: 'Evaluating approval points.', - target_url: 'https://github.example/actions/runs/1', + name: 'weighted approval decision', + head_sha: HEAD_SHA, + status: 'in_progress', + details_url: 'https://github.example/actions/runs/1', + output: { title: 'Evaluating approval points.', summary: 'Evaluating approval points. [Workflow run](https://github.example/actions/runs/1)' }, }, { - state: 'pending', - context: 'weighted approval', - description: '1 blocking change request.', - target_url: 'https://github.example/actions/runs/1', + name: 'weighted approval decision', + head_sha: HEAD_SHA, + status: 'completed', + conclusion: 'action_required', + details_url: 'https://github.example/actions/runs/1', + output: { title: '1 blocking change request.', summary: '1 blocking change request. [Workflow run](https://github.example/actions/runs/1)' }, }]) }) -test('keeps drafts pending without reading reviews', async () => { +test('requires action on drafts without reading reviews', async () => { const result = await evaluateApproval({ event: pullRequestEvent({ draft: true }), policySource, getOwnership: async () => ({ totalLines: 0, reviewerLines: {} }), api: async () => { throw new Error('draft evaluation must not call GitHub') }, }) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') assert.equal(result.points, 0) assert.match(result.description, /draft pull request/u) }) @@ -301,21 +308,23 @@ test('publishes the required status and replaces stale success with error on eva calls.push({ path, options }) if (path.includes('/reviews?')) return [review('turtle2099', 'APPROVED')] if (path.includes('/collaborators/turtle2099/permission')) return { permission: 'write' } - if (path.includes('/statuses/')) return {} + if (path.includes('/check-runs')) return {} throw new Error(`unexpected API path ${path}`) }, write: line => output.push(line), }) assert.equal(result.state, 'success') assert.deepEqual(calls.at(-1), { - path: `/repos/deepseek-harness/deepseek-harness/statuses/${HEAD_SHA}`, + path: '/repos/deepseek-harness/deepseek-harness/check-runs', options: { method: 'POST', body: { - state: 'success', - context: 'weighted approval', - description: '2/2 approval points.', - target_url: 'https://github.example/actions/runs/1', + name: 'weighted approval decision', + head_sha: HEAD_SHA, + status: 'completed', + conclusion: 'success', + details_url: 'https://github.example/actions/runs/1', + output: { title: '2/2 approval points.', summary: '2/2 approval points. [Workflow run](https://github.example/actions/runs/1)' }, }, }, }) @@ -330,7 +339,7 @@ test('publishes the required status and replaces stale success with error on eva runUrl: 'https://github.example/actions/runs/2', api: async (path, options = {}) => { if (path.includes('/reviews?')) throw new Error('reviews unavailable') - if (path.includes('/statuses/')) { + if (path.includes('/check-runs')) { failures.push({ path, options }) return {} } @@ -338,9 +347,9 @@ test('publishes the required status and replaces stale success with error on eva }, write: () => {}, }), /reviews unavailable/u) - assert.equal(failures[0].options.body.state, 'pending') - assert.equal(failures[1].options.body.state, 'error') - assert.equal(failures[1].options.body.description, 'Approval evaluation failed.') + assert.equal(publishedState(failures[0].options.body), 'in_progress') + assert.equal(publishedState(failures[1].options.body), 'failure') + assert.equal(failures[1].options.body.output.title, 'Approval evaluation failed.') }) test('sends authenticated JSON and escapes an API error body', async () => { @@ -383,7 +392,7 @@ for (const [ownedLines, totalLines, expectedPoints] of [[9, 100, 1.3599999999999 }) assert.equal(result.points, expectedPoints) assert.equal(result.description, `${Number(expectedPoints.toFixed(3))}/2 approval points (author 0).`) - assert.equal(result.state, expectedPoints === 2 ? 'success' : 'pending') + assert.equal(result.state, expectedPoints === 2 ? 'success' : 'action_required') assert.equal(measurements, 1) assert.deepEqual(result.approvals[0].ownership, { ownedLines, totalLines }) }) @@ -403,7 +412,7 @@ test('does not fetch ownership when a change request blocks approval', async () }) assert.equal(measurements, 0) assert.equal(result.points, 2) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') }) test('does not fetch history when approvals already have two-point weights', async () => { @@ -424,11 +433,11 @@ test('publishes error when production attribution fails', async () => { if (path.includes('/reviews?')) return [review('writer', 'APPROVED')] if (path.includes('/permission')) return { permission: 'write' } if (path.includes('/comments?')) return [] - states.push(options.body.state) + states.push(publishedState(options.body)) return {} }, }), /incomplete history/u) - assert.deepEqual(states, ['pending', 'error']) + assert.deepEqual(states, ['in_progress', 'failure']) }) @@ -437,17 +446,17 @@ test('revokes a previous success before starting expensive attribution', async ( await runApprovalCheck({ event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', write: () => {}, getOwnership: async () => { - assert.deepEqual(states, ['pending']) + assert.deepEqual(states, ['in_progress']) return { totalLines: 100, reviewerLines: { writer: 25 } } }, api: async (path, options) => { if (path.includes('/reviews?')) return [review('writer', 'APPROVED')] if (path.includes('/permission')) return { permission: 'write' } - states.push(options.body.state) + states.push(publishedState(options.body)) return {} }, }) - assert.deepEqual(states, ['pending', 'success']) + assert.deepEqual(states, ['in_progress', 'success']) }) for (const reviewers of [['first', 'second'], ['turtle1999', 'first']]) { @@ -477,14 +486,38 @@ test('publishes setup phases without evaluating or installing dependencies', asy const states = [] const options = { event: pullRequestEvent(), runUrl: 'https://github.example/run/1', - api: async (path, { body }) => { assert.match(path, /\/statuses\//u); states.push(body.state) }, + api: withEmptyReads(async (path, { body }) => { assert.match(path, /\/check-runs$/u); states.push(publishedState(body)) }), } - await publishApprovalPhase({ ...options, phase: 'pending' }) - await publishApprovalPhase({ ...options, phase: 'error' }) - assert.deepEqual(states, ['pending', 'error']) + await publishApprovalPhase({ ...options, phase: 'in_progress' }) + await publishApprovalPhase({ ...options, phase: 'failure' }) + assert.deepEqual(states, ['in_progress', 'failure']) await assert.rejects(publishApprovalPhase({ ...options, phase: 'success' }), /invalid approval setup phase/u) }) +test('updates the head\'s GitHub Actions decision check run instead of creating another', async () => { + const calls = [] + const checkRuns = [ + { id: 7, app: { slug: 'github-actions' } }, + { id: 8, app: { slug: 'other-app' } }, + ] + await publishApprovalPhase({ + event: pullRequestEvent(), runUrl: 'https://github.example/run/1', phase: 'in_progress', + api: async (path, options) => { + calls.push({ path, method: options?.method }) + return { total_count: checkRuns.length, check_runs: checkRuns } + }, + }) + assert.deepEqual(calls, [ + { path: `/repos/deepseek-harness/deepseek-harness/commits/${HEAD_SHA}/check-runs?check_name=weighted%20approval%20decision&filter=all&per_page=100`, method: undefined }, + { path: '/repos/deepseek-harness/deepseek-harness/check-runs/7', method: 'PATCH' }, + ]) + for (const response of [{}, { total_count: 101, check_runs: [] }, { total_count: 1, check_runs: [{ id: '7', app: { slug: 'github-actions' } }] }]) { + await assert.rejects(publishApprovalPhase({ + event: pullRequestEvent(), runUrl: 'https://github.example/run/1', phase: 'failure', api: async () => response, + }), /check-run list response|valid id/u) + } +}) + for (const [mergedCount, credit] of [[0, 0], [1, 0.011], [50, 0.55], [99, 1.089], [100, 1.1], [101, 1.1], [200, 1.1]]) { test(`author with ${mergedCount} merged PRs contributes ${credit} points but cannot approve alone`, async () => { const result = await evaluateApproval({ @@ -493,7 +526,7 @@ for (const [mergedCount, credit] of [[0, 0], [1, 0.011], [50, 0.55], [99, 1.089] }) assert.equal(result.points, credit) assert.equal(result.authorCredit.points, credit) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') assert.deepEqual(result.approvals, []) }) } @@ -509,7 +542,7 @@ test('combines author credit and reviewer ownership at the passing threshold', a } }) -for (const [mergedCount, state] of [[90, 'pending'], [91, 'success'], [100, 'success']]) { +for (const [mergedCount, state] of [[90, 'action_required'], [91, 'success'], [100, 'success']]) { test(`one ordinary approval with ${mergedCount} merged PRs is ${state}`, async () => { const result = await evaluateApproval({ event: pullRequestEvent(), policySource, getMergedCount: async () => mergedCount, @@ -532,7 +565,7 @@ test('blockers skip history and ignore non-write reviewers', async () => { ? [review('writer', 'APPROVED'), review('blocker', 'CHANGES_REQUESTED'), review('reader', 'APPROVED')] : { permission: path.includes('/reader/') ? 'read' : 'write' }, }) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') assert.equal(result.points, 1) assert.equal(result.authorCredit, null) assert.deepEqual(result.blockers, ['blocker']) @@ -550,20 +583,20 @@ test('drafts skip history and history failures revoke success with error', async getMergedCount: async () => { throw new Error('history unavailable') }, api: async (path, options) => { if (path.includes('/reviews?')) return [] - states.push(options.body.state) + states.push(publishedState(options.body)) }, }), /history unavailable/u) - assert.deepEqual(states, ['pending', 'error']) + assert.deepEqual(states, ['in_progress', 'failure']) }) -test('a score just below the threshold remains pending even if its display rounds to two', async () => { +test('a score just below the threshold requires action even if its display rounds to two', async () => { const result = await evaluateApproval({ event: pullRequestEvent(), policySource, getMergedCount: async () => 50, getOwnership: async () => ({ totalLines: 1000000, reviewerLines: { writer: 112499 } }), api: async path => path.includes('/reviews?') ? [review('writer', 'APPROVED')] : { permission: 'write' }, }) - assert.equal(result.state, 'pending') + assert.equal(result.state, 'action_required') assert.ok(result.points < 2) }) @@ -587,14 +620,14 @@ test('the publisher counts merged history through the production API path', asyn } if (path.includes('/reviews?')) return [review('writer', 'APPROVED')] if (path.includes('/permission')) return { permission: 'write' } - states.push(options.body.state) + states.push(publishedState(options.body)) return {} }, }) assert.equal(result.points, 2) assert.equal(result.authorCredit.points, 0.55) assert.equal(result.approvals[0].points, 1.45) - assert.deepEqual(states, ['pending', 'success']) + assert.deepEqual(states, ['in_progress', 'success']) assert.equal(output[0], 'Author credit: 0.55 (50 merged PRs).') }) @@ -612,7 +645,7 @@ test('sufficient reviewer points and drafts publish without querying author hist return {} }, }) - assert.equal(result.state, draft ? 'pending' : 'success') + assert.equal(result.state, draft ? 'action_required' : 'success') assert.equal(result.authorCredit, null) assert.equal(output[0], `Author credit: not evaluated (${draft ? 'draft' : 'reviewer points suffice'}).`) } @@ -678,7 +711,7 @@ test('delegates the sender fixed points once after the recipient approves, retai }) }) const points = recipientState === 'APPROVED' ? 3 : 0 assert.equal(result.points, points) - assert.equal(result.state, points === 3 && senderState !== 'CHANGES_REQUESTED' ? 'success' : 'pending') + assert.equal(result.state, points === 3 && senderState !== 'CHANGES_REQUESTED' ? 'success' : 'action_required') assert.deepEqual(result.blockers, [ ...(senderState === 'CHANGES_REQUESTED' ? ['turtle1999'] : []), ...(recipientState === 'CHANGES_REQUESTED' ? ['writer'] : []), @@ -786,7 +819,7 @@ test('a sender submitted review takes back delegation, including comment-only an }) }) assert.equal(result.delegations.length, state === 'PENDING' ? 1 : 0) assert.equal(result.points, state === 'APPROVED' || state === 'PENDING' ? 3 : 1) - assert.equal(result.state, state === 'APPROVED' || state === 'PENDING' ? 'success' : 'pending') + assert.equal(result.state, state === 'APPROVED' || state === 'PENDING' ? 'success' : 'action_required') if (state === 'APPROVED') assert.equal(result.approvals.find(({ login }) => login === 'Turtle1999').delegatedTo, undefined) } } @@ -829,7 +862,7 @@ test('an active delegate command requests review once and preserves other reques if (options?.method === 'POST') { requests.push(options.body); return {} } return { users: [{ login: 'another-reviewer' }, ...(alreadyRequested ? [{ login: 'WRITER' }] : [])], teams: [] } } - if (path.includes('/statuses/')) return {} + if (path.includes('/check-runs')) return {} return api(path, options) }, }) @@ -857,7 +890,7 @@ test('inactive commands and drafts do not request review', async () => { policySource, runUrl: 'https://github.example/run/1', getMergedCount: async () => 0, write: () => {}, api: async (path, options) => { assert.ok(!path.endsWith('/requested_reviewers')) - if (path.includes('/statuses/')) return {} + if (path.includes('/check-runs')) return {} return api(path, options) }, }) @@ -873,7 +906,7 @@ test('failed review requests preserve the computed score and report the retry', event: { ...pullRequestEvent(), issue: { number: 42, pull_request: {} }, action: 'created', comment: { id: 1 } }, policySource, runUrl: 'https://github.example/run/1', getMergedCount: async () => 0, getOwnership: async () => ({ totalLines: 0, reviewerLines: {} }), write: line => output.push(line), api: async (path, options) => { - if (path.includes('/statuses/')) { statuses.push(options.body.state); return {} } + if (path.includes('/check-runs')) { statuses.push(publishedState(options.body)); return {} } if (path.endsWith('/requested_reviewers')) { if (options?.method === 'POST') throw new Error('request rejected') return failedRead ? {} : { users: [] } @@ -883,7 +916,7 @@ test('failed review requests preserve the computed score and report the retry', }) assert.match(output.at(-1), failedRead ? /no users array/u : /request rejected/u) assert.match(output.at(-1), /score is unchanged.*will retry/u) - assert.deepEqual(statuses, ['pending', 'success']) + assert.deepEqual(statuses, ['in_progress', 'success']) } }) @@ -895,7 +928,7 @@ test('delegation dismisses only the sender old decisions and stays active across const states = [] const read = delegationApi({ comments: [comment('turtle1999', '/delegate @writer')], reviews }) const api = async (path, options) => { - if (path.includes('/statuses/')) { states.push(options.body.state); return {} } + if (path.includes('/check-runs')) { states.push(publishedState(options.body)); return {} } if (path.endsWith('/dismissals')) { calls.push(options) const id = Number(path.split('/').at(-2)) @@ -919,13 +952,13 @@ test('delegation dismisses only the sender old decisions and stays active across assert.deepEqual(result.blockers, ['other']) assert.equal(result.delegations.length, 1) assert.deepEqual(result.delegations[0].reviewIds, []) - assert.deepEqual(states, ['pending', 'pending']) + assert.deepEqual(states, ['in_progress', 'action_required']) reviews.find(review => review.id === 21).state = 'DISMISSED' for (const state of ['APPROVED', 'DISMISSED', 'COMMENTED', 'APPROVED']) { reviews.find(review => review.id === 20).state = state const next = await evaluateDelegation({ api }) assert.equal(next.delegations.length, 1) - assert.equal(next.state, state === 'APPROVED' ? 'success' : 'pending') + assert.equal(next.state, state === 'APPROVED' ? 'success' : 'action_required') } reviews.push(review('turtle1999', 'COMMENTED', '2026-09-16T00:00:00Z', 30)) const reclaimed = await evaluateDelegation({ api }) @@ -939,7 +972,7 @@ test('a new sender review arriving during dismissal is not dismissed or delegate const result = await runWithHistory({ event: { ...pullRequestEvent(), issue: { number: 42, pull_request: {} }, action: 'created', comment: { id: 1 } }, policySource, runUrl: 'https://github.example/run/1', write: () => {}, getMergedCount: async () => 0, api: async (path, options) => { - if (path.includes('/statuses/')) return {} + if (path.includes('/check-runs')) return {} if (path.endsWith('/dismissals')) { assert.ok(path.endsWith('/reviews/10/dismissals')) reviews[0].state = 'DISMISSED' @@ -961,7 +994,7 @@ test('a failed or unconfirmed dismissal prevents review requests and publishes e await assert.rejects(runWithHistory({ event: { ...pullRequestEvent(), issue: { number: 42, pull_request: {} }, action: 'created', comment: { id: 1 } }, policySource, runUrl: 'https://github.example/run/1', write: () => {}, api: async (path, options) => { - if (path.includes('/statuses/')) { states.push(options.body.state); return {} } + if (path.includes('/check-runs')) { states.push(publishedState(options.body)); return {} } if (path.endsWith('/dismissals')) { if (rejected) throw new Error('dismissal denied') return { id: 10, state: 'CHANGES_REQUESTED' } @@ -970,7 +1003,7 @@ test('a failed or unconfirmed dismissal prevents review requests and publishes e return read(path, options) }, }), rejected ? /dismissal denied/u : /dismissal was not confirmed/u) - assert.deepEqual(states, ['pending', 'error']) + assert.deepEqual(states, ['in_progress', 'failure']) } }) @@ -983,7 +1016,7 @@ test('reads all comment pages, logs the score owner, and fails closed on missing event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', getMergedCount: async () => 0, write: line => output.push(line), api: async (path, options) => { - if (path.includes('/statuses/')) { statuses.push(options.body.state); return {} } + if (path.includes('/check-runs')) { statuses.push(publishedState(options.body)); return {} } if (path.includes('/comments?')) return comments(path) return api(path, options) }, @@ -996,7 +1029,7 @@ test('reads all comment pages, logs the score owner, and fails closed on missing }) assert.equal(pages.length, 2) assert.ok(output.includes('- @turtle1999: 2 (delegated to @writer)')) - assert.deepEqual(statuses.splice(0), ['pending', 'success']) + assert.deepEqual(statuses.splice(0), ['in_progress', 'success']) for (const [comments, message] of [ [() => { throw new Error('comment API unavailable') }, /comment API unavailable/u], [() => ({}), /comments response is not an array/u], @@ -1006,7 +1039,7 @@ test('reads all comment pages, logs the score owner, and fails closed on missing [() => Array.from({ length: 100 }, () => comment('writer', 'text')), /comments exceed 3000/u], ]) { await assert.rejects(run(comments), message) - assert.deepEqual(statuses.splice(0), ['pending', 'error']) + assert.deepEqual(statuses.splice(0), ['in_progress', 'failure']) } }) @@ -1090,7 +1123,7 @@ test('later events reconcile all active delegations after a draft or replaced co const effects = [] const requested = [] const api = async (path, options) => { - if (path.includes('/statuses/')) return {} + if (path.includes('/check-runs')) return {} if (path.endsWith('/dismissals')) { const old = reviews.find(review => path.endsWith(`/reviews/${review.id}/dismissals`)) effects.push(old.id) @@ -1122,7 +1155,7 @@ test('already approved delegates need no new request', async () => { }) const result = await runWithHistory({ event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', write: () => {}, api: (path, options) => { - if (path.includes('/statuses/')) return {} + if (path.includes('/check-runs')) return {} assert.ok(!path.endsWith('/requested_reviewers')) return read(path, options) }, diff --git a/.github/workflows/weighted-approval.yml b/.github/workflows/weighted-approval.yml index eb6f2fe3ba..896fc262c4 100644 --- a/.github/workflows/weighted-approval.yml +++ b/.github/workflows/weighted-approval.yml @@ -10,9 +10,9 @@ on: types: [completed] permissions: + checks: write contents: read pull-requests: write - statuses: write concurrency: group: weighted-approval-${{ (github.event.pull_request.number || github.event.issue.number) && format('weighted-approval-review-event:{0}', github.event.pull_request.number || github.event.issue.number) || github.event.workflow_run.display_title }} @@ -29,20 +29,20 @@ jobs: runs-on: ubuntu-latest timeout-minutes: 5 steps: - # SECURITY: the status-writing job executes policy from the trusted default + # SECURITY: the check-writing job executes policy from the trusted default # branch and reads pull-request reviews and comments only as API data. - name: Check out trusted approval policy uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 with: ref: ${{ github.event.repository.default_branch }} persist-credentials: false - - name: Revoke previous approval status + - name: Revoke previous approval decision id: revoke env: GITHUB_TOKEN: ${{ github.token }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - run: node .github/review-ownership/check-approval.mjs pending - # SECURITY: dependency setup runs in the status-writing job. + run: node .github/review-ownership/check-approval.mjs in_progress + # SECURITY: dependency setup runs in the check-writing job. - if: steps.revoke.outputs.active == 'true' uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 with: @@ -52,7 +52,7 @@ jobs: - name: Install production lexer if: steps.revoke.outputs.active == 'true' run: python3 -m pip install -r .github/review-ownership/requirements.txt - - name: Publish weighted approval status + - name: Publish weighted approval decision if: steps.revoke.outputs.active == 'true' env: GITHUB_TOKEN: ${{ github.token }} @@ -63,4 +63,4 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - run: node .github/review-ownership/check-approval.mjs error + run: node .github/review-ownership/check-approval.mjs failure diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 86179be885..1275d4a7b0 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -972,7 +972,7 @@ describe('Weighted approval workflow', () => { if (!Array.isArray(recordJob.steps)) throw new TypeError('weighted-approval review event job must define steps') const steps = job.steps.filter(isRecord) const checkout = steps.find(step => step.name === 'Check out trusted approval policy') - const publish = steps.find(step => step.name === 'Publish weighted approval status') + const publish = steps.find(step => step.name === 'Publish weighted approval decision') const recordSteps = recordJob.steps.filter(isRecord) const record = recordSteps.find(step => step.name === 'Record review event') @@ -987,9 +987,9 @@ describe('Weighted approval workflow', () => { expect(review.types).toEqual(['submitted', 'edited', 'dismissed']) expect(reviewEvent.permissions).toEqual({}) expect(publisher.permissions).toEqual({ + checks: 'write', contents: 'read', 'pull-requests': 'write', - statuses: 'write', }) expect(publisher.concurrency).toEqual({ group: "weighted-approval-${{ (github.event.pull_request.number || github.event.issue.number) && format('weighted-approval-review-event:{0}', github.event.pull_request.number || github.event.issue.number) || github.event.workflow_run.display_title }}", @@ -1017,10 +1017,10 @@ describe('Weighted approval workflow', () => { const revokeIndex = steps.findIndex(step => step.id === 'revoke') expect(revokeIndex).toBeGreaterThan(steps.indexOf(checkout!)) expect(revokeIndex).toBeLessThan(setupIndex) - expect(steps[revokeIndex]?.run).toBe('node .github/review-ownership/check-approval.mjs pending') + expect(steps[revokeIndex]?.run).toBe('node .github/review-ownership/check-approval.mjs in_progress') expect(steps.at(-1)).toMatchObject({ if: "failure() && steps.revoke.outputs.active == 'true'", - run: 'node .github/review-ownership/check-approval.mjs error', + run: 'node .github/review-ownership/check-approval.mjs failure', }) const pythonJob = workflowJob(loadWorkflow('.github/workflows/ci.yml'), 'python-sdk') expect(pythonJob.steps).toContainEqual({ From a0ed3981fe287fbe17fbf0cac10e6547595773d6 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Wed, 30 Sep 2026 13:42:02 +0800 Subject: [PATCH 02/10] fix(ci): revoke the approval decision through its conclusion A check run that reached completed never returns to in_progress: GitHub answers 200 and leaves the run completed, so the revoke write was a silent no-op on every re-evaluation and a published success survived an interrupted setup or history fetch. Revoke a published success by concluding it action_required, start a head that has no decision in progress, and drop the status-only check-run update. The setup-failure step ran only when the revoke step succeeded, so a failed or cancelled publisher left the earlier conclusion standing. It now runs on failure and on a cancellation that precedes the decision. The decision note, the review-ownership README, and the two related notes record the measured transition matrix and the fallback condition. --- ...pproval-review-workflow-identity.i18n.yaml | 4 +- ...09-10-approval-review-workflow-identity.md | 2 +- ...10-approval-review-workflow-identity.zh.md | 2 +- ...026-09-15-pr-approval-delegation.i18n.yaml | 4 +- .../2026-09-15-pr-approval-delegation.md | 2 +- .../2026-09-15-pr-approval-delegation.zh.md | 2 +- ...hted-approval-decision-check-run.i18n.yaml | 16 +-- ...29-weighted-approval-decision-check-run.md | 17 +-- ...weighted-approval-decision-check-run.zh.md | 17 +-- .agents/skills/dsh-pre-push-checks/SKILL.md | 2 +- .github/review-ownership/README.md | 2 +- .github/review-ownership/check-approval.mjs | 71 ++++++++---- .../review-ownership/check-approval.test.mjs | 102 +++++++++++------- .github/workflows/weighted-approval.yml | 7 +- scripts/ci-workflow.spec.ts | 5 +- 15 files changed, 162 insertions(+), 93 deletions(-) diff --git a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml index 1dfe18dbe3..335e5a5f86 100644 --- a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.i18n.yaml @@ -6,8 +6,8 @@ en: 8a332b9d080f3d62 zh: 29f1de05d7867a77 /agent-note-identify-approval-review-workflows-by-file-path/problem: - en: 6b927a7545ac2c48 - zh: 0902f03aa478d5ee + en: 63913473297103c4 + zh: 42d7312b9bc439e7 /agent-note-identify-approval-review-workflows-by-file-path/decision: en: 82802438f1b8d16c zh: ffe8c1fcbcc39abe diff --git a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md index de1e08ef78..f6a9cc7c17 100644 --- a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md +++ b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.md @@ -6,7 +6,7 @@ English | [中文](2026-09-10-approval-review-workflow-identity.zh.md) ## Problem -GitHub can populate a workflow run's `name` with its expanded `run-name`. The approval review workflow includes the pull-request number in that title, so comparing `workflow_run.name` with the static workflow name rejects valid review events before refreshing the approval status. +GitHub can populate a workflow run's `name` with its expanded `run-name`. The approval review workflow includes the pull-request number in that title, so comparing `workflow_run.name` with the static workflow name rejects valid review events before refreshing the approval decision. ## Decision diff --git a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md index 6b3aa3d359..2ed453467f 100644 --- a/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md +++ b/.agents/notes/implemented/process/2026-09-10-approval-review-workflow-identity.zh.md @@ -6,7 +6,7 @@ Status: implemented ## 问题 -GitHub 可能用展开后的 `run-name` 填充工作流运行的 `name`。审批评审工作流在该标题中包含拉取请求编号,因此将 `workflow_run.name` 与静态工作流名称比较,会在刷新审批状态前拒绝有效的评审事件。 +GitHub 可能用展开后的 `run-name` 填充工作流运行的 `name`。审批评审工作流在该标题中包含拉取请求编号,因此将 `workflow_run.name` 与静态工作流名称比较,会在刷新审批决策前拒绝有效的评审事件。 ## 决策 diff --git a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml index 50bca79957..f01abc3399 100644 --- a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.i18n.yaml @@ -9,8 +9,8 @@ en: 5fe2f3b3cb7ce046 zh: ed27d4efc995b8b5 /agent-note-pr-scoped-approval-delegation/decision: - en: bf8f1ff30114310f - zh: a163e1276dd384f8 + en: 87114939824c3994 + zh: 34d71c57f0459017 /agent-note-pr-scoped-approval-delegation/alternatives-considered: en: a6006217cb29613a zh: 83968328096f1d91 diff --git a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md index 0282f9c058..cbc7d7fbec 100644 --- a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md +++ b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.md @@ -12,7 +12,7 @@ A reviewer may trust another reviewer to decide a particular PR while retaining The [approval policy](../../../../.github/review-ownership/README.md#delegating-points) accepts `/delegate @username` in PR conversation comments. Each eligible sender's points follow the named recipient's effective approval, once, using the sender's weight. Delegation applies only to that PR and only between write-capable accounts other than the PR author. Received points cannot be forwarded. The command dismisses the sender's prior approvals and change requests through GitHub. Other effective blocking reviews still follow [blocking-review policy](2026-09-29-weighted-approval-decision-check-run.md). -The latest surviving eligible command in comment creation order determines the recipient. Edited commands require the latest editor to match the author’s immutable account ID; other writers cannot transfer or cancel that author’s points by editing a comment. A self-delegation or a subsequently submitted review restores the sender's own decision. Comment-only reviews also reclaim the points; pending reviews do not. Equal timestamps favor the review, and subsequent dismissal cannot revive the delegation. A fresh command after the review can delegate again. Edits and deletions recompute from current comments, including restoration of older commands. The trusted publisher filters conversation comment changes for `/delegate`, resolves the live open PR head, and reads comments and editor identity as API data. Every evaluation reconciles active eligible commands, dismissing prior decision reviews and requesting recipients unless already approved or requested. This covers replaced queued events and draft-to-ready transitions; other requested reviewers remain unchanged. Scores refresh after dismissal, which retains the old submission timestamps and cannot cancel delegation. Removing the recipient's approval removes counted points while retaining delegation for their next approval. Drafts do not dismiss or request reviews. Dismissal failures fail evaluation; request failures are logged separately and retried on later evaluations. Closed or merged PRs are skipped before status writes and dependency setup. It preserves the separate [review-workflow validation](2026-09-10-approval-review-workflow-identity.md). +The latest surviving eligible command in comment creation order determines the recipient. Edited commands require the latest editor to match the author’s immutable account ID; other writers cannot transfer or cancel that author’s points by editing a comment. A self-delegation or a subsequently submitted review restores the sender's own decision. Comment-only reviews also reclaim the points; pending reviews do not. Equal timestamps favor the review, and subsequent dismissal cannot revive the delegation. A fresh command after the review can delegate again. Edits and deletions recompute from current comments, including restoration of older commands. The trusted publisher filters conversation comment changes for `/delegate`, resolves the live open PR head, and reads comments and editor identity as API data. Every evaluation reconciles active eligible commands, dismissing prior decision reviews and requesting recipients unless already approved or requested. This covers replaced queued events and draft-to-ready transitions; other requested reviewers remain unchanged. Scores refresh after dismissal, which retains the old submission timestamps and cannot cancel delegation. Removing the recipient's approval removes counted points while retaining delegation for their next approval. Drafts do not dismiss or request reviews. Dismissal failures fail evaluation; request failures are logged separately and retried on later evaluations. Closed or merged PRs are skipped before check-run writes and dependency setup. It preserves the separate [review-workflow validation](2026-09-10-approval-review-workflow-identity.md). ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md index 7d26a42d0d..348c62055f 100644 --- a/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md +++ b/.agents/notes/implemented/process/2026-09-15-pr-approval-delegation.zh.md @@ -12,7 +12,7 @@ Status: implemented [批准策略](../../../../.github/review-ownership/README.md#delegating-points) 接受 PR 普通评论中的 `/delegate @username`。每位符合资格的委托人,其分数跟随指定受托人的有效批准,按委托人的权重计入一次。委托仅适用于当前 PR,且双方都必须具有写权限,不能是 PR 作者。收到的分数不能继续转交。命令会通过 GitHub 撤销委托人原有的批准和变更请求。其他有效阻塞评审仍遵循[阻塞评审策略](2026-09-29-weighted-approval-decision-check-run.zh.md)。 -按评论创建顺序,最新、仍存在且符合资格的命令决定受托人。编辑过的命令要求最近编辑者与作者的不可变账户 ID 一致;其他有写权限的人不能通过编辑评论转交或取消作者的分数。委托给自己或随后提交评审会恢复使用委托人本人的评审决定。仅评论的评审也会收回分数;尚未提交的评审不会。时间戳相同时以评审为准,随后撤销评审也不会重新启用委托。评审之后的新命令可以再次委托。编辑和删除会根据当前评论重算,包括恢复较早的命令。可信发布器按 `/delegate` 筛选普通评论变更,解析实时且未关闭的 PR 头提交,并将评论及编辑者身份作为 API 数据读取。每次评估都会处理所有有效且符合资格的命令,撤销旧的决定性评审,并请求尚未批准、尚未收到请求的受托人进行评审。这覆盖了排队事件被替换以及草稿转为 ready 的情况;其他 requested reviewers 保持不变。撤销后刷新分数;撤销保留旧提交时间戳,不会取消委托。移除受托人的批准只会移除计入的分数,委托仍然保留,供其下次批准使用。草稿 PR 不撤销或请求评审。撤销失败会使评估失败;请求失败单独记录日志,并在后续评估中重试。已关闭或合并的 PR 会在写入状态和安装依赖之前跳过。独立的[评审工作流验证](2026-09-10-approval-review-workflow-identity.zh.md) 保持不变。 +按评论创建顺序,最新、仍存在且符合资格的命令决定受托人。编辑过的命令要求最近编辑者与作者的不可变账户 ID 一致;其他有写权限的人不能通过编辑评论转交或取消作者的分数。委托给自己或随后提交评审会恢复使用委托人本人的评审决定。仅评论的评审也会收回分数;尚未提交的评审不会。时间戳相同时以评审为准,随后撤销评审也不会重新启用委托。评审之后的新命令可以再次委托。编辑和删除会根据当前评论重算,包括恢复较早的命令。可信发布器按 `/delegate` 筛选普通评论变更,解析实时且未关闭的 PR 头提交,并将评论及编辑者身份作为 API 数据读取。每次评估都会处理所有有效且符合资格的命令,撤销旧的决定性评审,并请求尚未批准、尚未收到请求的受托人进行评审。这覆盖了排队事件被替换以及草稿转为 ready 的情况;其他 requested reviewers 保持不变。撤销后刷新分数;撤销保留旧提交时间戳,不会取消委托。移除受托人的批准只会移除计入的分数,委托仍然保留,供其下次批准使用。草稿 PR 不撤销或请求评审。撤销失败会使评估失败;请求失败单独记录日志,并在后续评估中重试。已关闭或合并的 PR 会在写入 check run 和安装依赖之前跳过。独立的[评审工作流验证](2026-09-10-approval-review-workflow-identity.zh.md) 保持不变。 ## Alternatives considered diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml index e3d242d672..89856bb840 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml @@ -9,14 +9,14 @@ en: 6ba6f6a15a1562ec zh: 1cbef3e736c258e6 /agent-note-weighted-approval-decisions-are-completed-check-runs/decision: - en: 933e4c1438771907 - zh: f6056382f3b555c9 + en: b070a25a6d990e8c + zh: fa555f8e418dad1f /agent-note-weighted-approval-decisions-are-completed-check-runs/verification: - en: d1f65b5b577b08b8 - zh: 7a193cec1acec003 + en: 5e5de198a6fbdcca + zh: 74c6dccdea7984b8 /agent-note-weighted-approval-decisions-are-completed-check-runs/alternatives-considered: - en: a7ef91c0506766a8 - zh: d6584e31652b14ca + en: b8daea66b0a90d26 + zh: fb7079c815b38281 /agent-note-weighted-approval-decisions-are-completed-check-runs/consequences: - en: 4cafdf12b326c68a - zh: c37a57d26d2f0894 + en: 772e9b9c0133d264 + zh: 0c36b0819adaa622 diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md index f4d8f9e639..2c4550ba4a 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md @@ -16,18 +16,21 @@ The [publisher](../../../../.github/review-ownership/check-approval.mjs) reports | Condition | Check run | |---|---| -| Dependency setup or evaluation in progress | `in_progress` | +| First evaluation of a head | `in_progress` until the decision is written | +| Re-evaluating a head that published `success` | conclusion `action_required` until the decision is written | | Draft, fewer than the required points, or an effective write-capable `CHANGES_REQUESTED` review | `completed`, conclusion `action_required` | | Ready, threshold met, and no blocking review | `completed`, conclusion `success` | -| Setup or evaluation failure | `completed`, conclusion `failure` | +| Setup, evaluation, or cancellation failure | `completed`, conclusion `failure` | + +A check run that reached `completed` never returns to `in_progress` or `queued`: GitHub answers `200` to that update and leaves the run completed, so only the conclusion is mutable. The publisher therefore revokes a published success by writing the blocking conclusion before dependency setup, which covers the setup and history fetch that dominate a cold evaluation. The head keeps one GitHub Actions run with the required name: the publisher updates the head's existing run and creates one only when none exists. An incomplete list or a run without a valid ID fails the publication. A run cancelled after the decision was published keeps that decision; every other failed or cancelled run concludes `failure`. A blocking review dominates the point total, so the conclusion remains `action_required` even when counted approvals reach the threshold. GitHub accepts only `success`, `neutral`, and `skipped` for a required check, so `action_required` blocks merging while completing the check. -The publisher lists the head's check runs with that name, updates every run owned by GitHub Actions, and creates one only when none exists. An incomplete list or a run without a valid ID fails the publication. - ## Verification -[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case, the exact published check-run payloads, updating an existing run instead of creating another, and invalid list responses. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the `checks: write` permission, the `in_progress` and `failure` phase commands, and the separate publisher job name. +[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case, the exact published check-run payloads, revoking a published success through its conclusion, updating an existing run instead of creating another, and invalid list responses. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the `checks: write` permission, the `revoke` and `failure` phase commands, the publish step identity, and the fallback that runs when a run fails or is cancelled before publishing. + +The transition matrix and the fallback condition were measured on 2026-09-30 with throwaway tag runs in this repository, using the same GitHub Actions token as the publisher. A run created as `in_progress` and completed as `success` accepted `status: in_progress` and `status: queued` with `200` and stayed `completed`, while conclusion-only updates moved it to `action_required`, back to `success`, and to `failure`. The fallback step ran for a failed publish and for a cancellation during the publish step, and did not run when the job was cancelled after the decision was published. ## Alternatives considered @@ -35,6 +38,8 @@ The publisher lists the head's check runs with that name, updates every run owne **Publish `failure` for unmet conditions.** This ends the wait, but it represents an unmet, reversible merge condition as a malfunction and conflates the policy outcome with publisher health. +**Revoke by returning the completed run to `in_progress`.** GitHub accepts that update without applying it, so the published success would stand through dependency setup and the history fetch. + **Publish nothing until success.** A required check that was never reported remains expected and is invisible to many watchers. Revoking a success already published on the same head still requires a non-success write, and each watcher's treatment of a missing required check is unspecified. **Keep the `weighted approval` name.** When a check and a commit status share a required name, GitHub requires both to pass. Heads that carry a `pending` commit status from the earlier publisher would stay blocked until a new push. @@ -47,4 +52,4 @@ The publisher lists the head's check runs with that name, updates every run owne CI watchers finish while the decision waits on reviewers. `gh pr checks` groups `action_required` with failed checks and exits non-zero; consumers distinguish it by the check name, the `ACTION_REQUIRED` conclusion, or the output title, which carries the score or blocker. The `weighted approval publisher` Actions job and the `failure` conclusion carry operational failures. -Revoking an earlier success on the same head returns the completed check run to `in_progress`, a transition the GitHub REST reference does not restrict. +Re-evaluating a head that already published `success` reports `action_required` until the new decision is written, so a watcher that polls inside that window sees a failed check. Heads created before this change keep their `weighted approval` commit status: no API completes or removes a commit status, so those heads stay pending for watchers until they receive a new commit. diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md index 4dee85c8f7..d8b894351d 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md @@ -16,18 +16,21 @@ commit status 只提供 `pending`、`success`、`failure` 和 `error`。`pending | 条件 | Check run | |---|---| -| 依赖安装或评估进行中 | `in_progress` | +| head 的首次评估 | 写入决策前为 `in_progress` | +| 重新评估已发布 `success` 的 head | 写入决策前 conclusion 为 `action_required` | | draft、点数少于要求,或存在具有写权限的评审人仍然生效的 `CHANGES_REQUESTED` 评审 | `completed`,conclusion 为 `action_required` | | 已 ready、达到阈值且没有阻塞性评审 | `completed`,conclusion 为 `success` | -| 安装或评估失败 | `completed`,conclusion 为 `failure` | +| 安装、评估或取消导致的失败 | `completed`,conclusion 为 `failure` | + +已经到达 `completed` 的 check run 不会再回到 `in_progress` 或 `queued`:GitHub 对该更新返回 `200`,但让 run 保持已完成,因此只有 conclusion 可变。publisher 因此在依赖安装前用阻塞性 conclusion 撤销已发布的成功,覆盖冷启动中占主要耗时的安装与历史拉取。同一 head 上只保留一个同名 GitHub Actions run:publisher 更新 head 上已有的 run,仅在不存在时创建一个。列表不完整或 run 没有有效 ID 时发布失败。决策已发布后被取消的 run 保留该决策;其余失败或被取消的 run 都以 `failure` 结束。 阻塞性评审的优先级高于点数总和,因此即使计入的批准已经达到阈值,conclusion 仍为 `action_required`。GitHub 只接受 `success`、`neutral` 和 `skipped` 作为必需检查的通过结果,因此 `action_required` 会阻止合并,同时让该检查处于完成状态。 -publisher 列出 head 上同名的 check run,更新所有属于 GitHub Actions 的 run,只有在不存在时才创建一个。列表不完整或 run 没有有效 ID 时发布失败。 - ## 验证 -[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景、准确发布的 check run payload、更新已有 run 而不是另建一个,以及无效的列表响应。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定 `checks: write` 权限、`in_progress` 和 `failure` 阶段命令,以及独立的 publisher job 名称。 +[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景、准确发布的 check run payload、通过 conclusion 撤销已发布的成功、更新已有 run 而不是另建一个,以及无效的列表响应。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定 `checks: write` 权限、`revoke` 和 `failure` 阶段命令、publish 步骤的身份,以及运行失败或在发布前被取消时执行的兜底步骤。 + +转换矩阵与该兜底条件于 2026-09-30 在本仓库用一次性 tag 运行实测,使用的是与 publisher 相同的 GitHub Actions token。以 `in_progress` 创建、以 `success` 完成的 run,接受 `status: in_progress` 和 `status: queued` 的更新时都返回 `200` 并保持 `completed`;只改 conclusion 的更新把它依次改为 `action_required`、`success` 和 `failure`。兜底步骤在发布失败以及在发布步骤执行期间被取消时运行,而在决策发布之后才取消 job 时不运行。 ## 考虑过的替代方案 @@ -35,6 +38,8 @@ publisher 列出 head 上同名的 check run,更新所有属于 GitHub Actions **为尚未满足的条件发布 `failure`。** 该方案能结束等待,但会把尚未满足且可撤销的合并条件表示为故障,并混淆策略结果与 publisher 的健康状态。 +**通过把已完成的 run 改回 `in_progress` 来撤销。** GitHub 接受该更新但不执行,因此已发布的成功会在依赖安装与历史拉取期间继续存在。 + **在成功之前不发布任何结果。** 从未上报的必需检查保持 expected 状态,许多监视工具看不到它。撤销同一 head 上已发布的成功仍然需要写入非成功结果,而各监视工具如何对待缺失的必需检查没有规定。 **保留 `weighted approval` 名称。** 当 check 和 commit status 使用同一个必需名称时,GitHub 要求两者都通过。带有旧 publisher 所写 `pending` commit status 的 head 会一直被阻止,直到出现新的 push。 @@ -47,4 +52,4 @@ publisher 列出 head 上同名的 check run,更新所有属于 GitHub Actions 决策等待评审人时,CI 监视工具会结束。`gh pr checks` 把 `action_required` 与失败的检查归为一组并以非零状态退出;消费方可以通过 check 名称、`ACTION_REQUIRED` conclusion 或携带分数或 blocker 的输出标题加以区分。`weighted approval publisher` Actions job 和 `failure` conclusion 承载运行故障信号。 -撤销同一 head 上此前的成功结果时,已完成的 check run 会回到 `in_progress`;GitHub REST 参考文档没有限制这一转换。 +重新评估已经发布 `success` 的 head 时,在新决策写入前会报告 `action_required`,因此在该窗口内轮询的监视工具会看到失败的检查。本次变更之前创建的 head 会保留原有的 `weighted approval` commit status:没有任何 API 能完成或删除 commit status,因此这些 head 对监视工具一直是 pending,直到它们收到新的 commit。 diff --git a/.agents/skills/dsh-pre-push-checks/SKILL.md b/.agents/skills/dsh-pre-push-checks/SKILL.md index 6567dde5dc..3ab326a4b3 100644 --- a/.agents/skills/dsh-pre-push-checks/SKILL.md +++ b/.agents/skills/dsh-pre-push-checks/SKILL.md @@ -123,7 +123,7 @@ For GitHub PRs, inspect remote CI after the push: gh pr checks ``` -Report pending checks as pending. Inspect failures before attributing them to the branch or the environment. +Report pending checks as pending. Inspect failures before attributing them to the branch or the environment. A `weighted approval decision` check that `gh pr checks` lists as failed means the approval gate is waiting on reviewers (`ACTION_REQUIRED`), not that the branch broke. When `gh pr checks` reports "no checks reported" and `/actions/runs?head_sha=` returns `total_count: 0`, read mergeability before suspecting the push or a dropped GitHub event: diff --git a/.github/review-ownership/README.md b/.github/review-ownership/README.md index 7b461ae864..0d99299847 100644 --- a/.github/review-ownership/README.md +++ b/.github/review-ownership/README.md @@ -16,7 +16,7 @@ The [`weighted-approval` workflow](../workflows/weighted-approval.yml) publishes ## Approval scoring -The weighted approval workflow exposes two pull-request checks. The `weighted approval publisher` Actions job reports whether evaluation and check publication completed, while the `weighted approval decision` check run carries the approval decision on the pull request head. Branch rules must require that check run with GitHub Actions as its expected source; a name-only requirement can accept a same-named check or status from another integration. The publisher updates the head's existing GitHub Actions decision check run and creates one only when none exists. It marks the check run `in_progress` before Python setup or lexer installation, so failed setup or an interrupted history fetch cannot leave a previous success in place. Setup and evaluation failures conclude it as `failure`. A completed evaluation concludes `action_required` below two approval points, while the pull request is a draft, or while a write-capable reviewer has an effective `CHANGES_REQUESTED` review; the blocker keeps that conclusion even when counted approvals reach the threshold. It concludes `success` only when the threshold is met, the pull request is ready, and no such blocker exists. `action_required` blocks merging but completes the check, so tools that wait for checks to finish do not wait for reviewers; the check-run title carries the score or blocker. +The weighted approval workflow exposes two pull-request checks. The `weighted approval publisher` Actions job reports whether evaluation and check publication completed, while the `weighted approval decision` check run carries the approval decision on the pull request head. Branch rules must require that check run with GitHub Actions as its expected source; a name-only requirement can accept a same-named check or status from another integration. The publisher updates the head's existing GitHub Actions decision check run and creates one only when none exists; a head without one starts `in_progress` and a re-evaluated head that published `success` is concluded `action_required` before Python setup or lexer installation, so failed setup or an interrupted history fetch cannot leave a previous success in place. A completed check run only ever changes conclusion, so every later write completes it again. Setup, evaluation, and cancellation failures conclude it as `failure`. A completed evaluation concludes `action_required` below two approval points, while the pull request is a draft, or while a write-capable reviewer has an effective `CHANGES_REQUESTED` review; the blocker keeps that conclusion even when counted approvals reach the threshold. It concludes `success` only when the threshold is met, the pull request is ready, and no such blocker exists. `action_required` blocks merging but completes the check, so tools that wait for checks to finish do not wait for reviewers; the check-run title carries the score or blocker. Reviewers whose calculated base repository permission is `write` or `admin` count. The [approval policy](approval-policy.json) gives `@07akioni`, `@imccyu`, `@tianyicui`, `@tianyicui-bot`, `@turtle1999`, and `@turtle2099` two points each; every other write-capable reviewer gets one point. The pull-request author’s own review and reviewers without write permission do not count. diff --git a/.github/review-ownership/check-approval.mjs b/.github/review-ownership/check-approval.mjs index 489acd1779..83b6eb68a2 100644 --- a/.github/review-ownership/check-approval.mjs +++ b/.github/review-ownership/check-approval.mjs @@ -311,7 +311,6 @@ export async function evaluateApproval({ event, policySource, api, getOwnership */ export async function runApprovalCheck({ event, policySource, api, runUrl, getOwnership = productionOwnership, getMergedCount = countMergedAuthorPulls, write = line => process.stdout.write(`${line}\n`) }) { const pull = pullRequestFromEvent(event) - await publishCheck(api, pull, 'in_progress', 'Evaluating approval points.', runUrl) let result try { result = await evaluateApproval({ event, policySource, api, getOwnership, getMergedCount }) @@ -353,14 +352,15 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw } /** - * Revoke a previous conclusion before dependency installation, or report its failure. + * Revoke a published success before dependency installation, or conclude a failed setup. * @param {{event: unknown, api: (path: string, options?: {method?: string, body?: unknown}) => Promise, runUrl: string, phase: string}} options Publication inputs. * @returns {Promise} Completion of the check-run write. */ export async function publishApprovalPhase({ event, api, runUrl, phase }) { - if (!['in_progress', 'failure'].includes(phase)) throw new Error('invalid approval setup phase') - await publishCheck(api, pullRequestFromEvent(event), phase, - phase === 'in_progress' ? 'Preparing approval evaluation.' : 'Approval setup or evaluation failed.', runUrl) + const pull = pullRequestFromEvent(event) + if (phase === 'revoke') return revokeApproval(api, pull, runUrl) + if (phase === 'failure') return publishCheck(api, pull, 'failure', 'Approval setup or evaluation failed.', runUrl) + throw new Error('invalid approval setup phase') } /** @@ -467,21 +467,15 @@ async function reviewerPermission(api, repository, login) { return response.permission.toLowerCase() } -// Updating the head's existing GitHub Actions check run keeps one decision per head for branch rules and CI watchers. -async function publishCheck(api, pull, state, title, runUrl) { - if (!/^https:\/\/[^\s]+$/u.test(runUrl)) throw new Error('workflow run URL must use HTTPS') - const body = { - ...(state === 'in_progress' ? { status: state } : { status: 'completed', conclusion: state }), - details_url: runUrl, - output: { title, summary: `${title} [Workflow run](${runUrl})` }, - } - const listed = await api(`/repos/${pull.repository}/commits/${pull.headSha}/check-runs?check_name=${encodeURIComponent(CHECK_NAME)}&filter=all&per_page=${PAGE_SIZE}`) - if (!isRecord(listed) || !Array.isArray(listed.check_runs) || listed.total_count !== listed.check_runs.length) { - throw new Error('check-run list response is invalid or incomplete') - } - const runs = listed.check_runs.filter(run => isRecord(run) && isRecord(run.app) && run.app.slug === 'github-actions') +// One decision per head keeps branch rules and CI watchers unambiguous; the head's existing GitHub +// Actions run is updated and a run is created only when none exists. +async function publishCheck(api, pull, conclusion, title, runUrl) { + requireHttpsRunUrl(runUrl) + const body = { status: 'completed', conclusion, details_url: runUrl, output: { title, summary: `${title} [Workflow run](${runUrl})` } } + const runs = await decisionRuns(api, pull) if (runs.length === 0) { await api(`/repos/${pull.repository}/check-runs`, { method: 'POST', body: { name: CHECK_NAME, head_sha: pull.headSha, ...body } }) + return } for (const { id } of runs) { if (!Number.isSafeInteger(id)) throw new Error('check run has no valid id') @@ -489,6 +483,45 @@ async function publishCheck(api, pull, state, title, runUrl) { } } +// A head without a decision starts in progress; a published success becomes action_required, so an +// interrupted evaluation cannot leave a success that its current inputs no longer support. +async function revokeApproval(api, pull, runUrl) { + requireHttpsRunUrl(runUrl) + const title = 'Preparing approval evaluation.' + const output = { title, summary: `${title} [Workflow run](${runUrl})` } + const runs = await decisionRuns(api, pull) + if (runs.length === 0) { + await api(`/repos/${pull.repository}/check-runs`, { + method: 'POST', + body: { name: CHECK_NAME, head_sha: pull.headSha, status: 'in_progress', details_url: runUrl, output }, + }) + return + } + for (const { id, conclusion } of runs) { + if (!Number.isSafeInteger(id)) throw new Error('check run has no valid id') + // Only a published success satisfies the branch rule; every other conclusion already blocks it. + if (conclusion !== 'success') continue + await api(`/repos/${pull.repository}/check-runs/${id}`, { + method: 'PATCH', + body: { status: 'completed', conclusion: 'action_required', details_url: runUrl, output }, + }) + } +} + +// A check run that reached completed never returns to in_progress: GitHub answers 200 and leaves the +// run completed, so a published decision can only be replaced by another conclusion. +async function decisionRuns(api, pull) { + const listed = await api(`/repos/${pull.repository}/commits/${pull.headSha}/check-runs?check_name=${encodeURIComponent(CHECK_NAME)}&filter=all&per_page=${PAGE_SIZE}`) + if (!isRecord(listed) || !Array.isArray(listed.check_runs) || listed.total_count !== listed.check_runs.length) { + throw new Error('check-run list response is invalid or incomplete') + } + return listed.check_runs.filter(run => isRecord(run) && isRecord(run.app) && run.app.slug === 'github-actions') +} + +function requireHttpsRunUrl(runUrl) { + if (!/^https:\/\/[^\s]+$/u.test(runUrl)) throw new Error('workflow run URL must use HTTPS') +} + function pullRequestFromEvent(event) { const repository = repositoryFromEvent(event) if (!isRecord(event.pull_request) || !isRecord(event.pull_request.user) @@ -579,7 +612,7 @@ async function main() { const phase = process.argv[2] if (phase) { await publishApprovalPhase({ event, api, runUrl: process.env.GITHUB_RUN_URL ?? '', phase }) - if (phase === 'in_progress' && process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, 'active=true\n') + if (phase === 'revoke' && process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, 'active=true\n') return } await runApprovalCheck({ diff --git a/.github/review-ownership/check-approval.test.mjs b/.github/review-ownership/check-approval.test.mjs index 186e97d1b3..fe7582108d 100644 --- a/.github/review-ownership/check-approval.test.mjs +++ b/.github/review-ownership/check-approval.test.mjs @@ -16,8 +16,10 @@ import { } from './check-approval.mjs' const policySource = readFileSync(new URL('approval-policy.json', import.meta.url), 'utf8') +const emptyCheckRuns = { total_count: 0, check_runs: [] } +const isCheckRunsList = path => path.includes('/commits/') && path.includes('/check-runs?') const withoutCheckRuns = api => (path, options) => - path.includes('/check-runs?') ? { total_count: 0, check_runs: [] } : api(path, options) + isCheckRunsList(path) ? emptyCheckRuns : api(path, options) const withEmptyReads = api => withoutCheckRuns((path, options) => path.includes('/comments?') ? [] : api(path, options)) const evaluateApproval = options => evaluateWithHistory({ getMergedCount: async () => 0, ...options, api: withEmptyReads(options.api) }) const runWithHistory = options => runPublisher({ ...options, api: withoutCheckRuns(options.api) }) @@ -237,7 +239,7 @@ test('ignores a reviewer whose collaborator permission lookup returns 404', asyn }) test('requires action on a write-capable change request while ignoring the author and read-only reviewers', async () => { - const statuses = [] + const writes = [] const result = await runApprovalCheck({ event: pullRequestEvent({ author: 'author' }), policySource, @@ -255,8 +257,9 @@ test('requires action on a write-capable change request while ignoring the autho if (path.includes('/collaborators/turtle1999/permission')) return { permission: 'admin' } if (path.includes('/collaborators/blocker/permission')) return { permission: 'write' } if (path.includes('/collaborators/reader/permission')) return { permission: 'read' } + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) { - statuses.push(options.body) + writes.push(options.body) return {} } throw new Error(`unexpected API path ${path}`) @@ -268,13 +271,7 @@ test('requires action on a write-capable change request while ignoring the autho assert.equal(result.points, 2) assert.deepEqual(result.blockers, ['blocker']) assert.deepEqual(result.ignoredReviewers, ['reader']) - assert.deepEqual(statuses, [{ - name: 'weighted approval decision', - head_sha: HEAD_SHA, - status: 'in_progress', - details_url: 'https://github.example/actions/runs/1', - output: { title: 'Evaluating approval points.', summary: 'Evaluating approval points. [Workflow run](https://github.example/actions/runs/1)' }, - }, { + assert.deepEqual(writes, [{ name: 'weighted approval decision', head_sha: HEAD_SHA, status: 'completed', @@ -296,7 +293,7 @@ test('requires action on drafts without reading reviews', async () => { assert.match(result.description, /draft pull request/u) }) -test('publishes the required status and replaces stale success with error on evaluation failure', async () => { +test('publishes the decision check run and concludes failure when evaluation fails', async () => { const calls = [] const output = [] const result = await runApprovalCheck({ @@ -308,6 +305,7 @@ test('publishes the required status and replaces stale success with error on eva calls.push({ path, options }) if (path.includes('/reviews?')) return [review('turtle2099', 'APPROVED')] if (path.includes('/collaborators/turtle2099/permission')) return { permission: 'write' } + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) return {} throw new Error(`unexpected API path ${path}`) }, @@ -339,6 +337,7 @@ test('publishes the required status and replaces stale success with error on eva runUrl: 'https://github.example/actions/runs/2', api: async (path, options = {}) => { if (path.includes('/reviews?')) throw new Error('reviews unavailable') + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) { failures.push({ path, options }) return {} @@ -347,9 +346,10 @@ test('publishes the required status and replaces stale success with error on eva }, write: () => {}, }), /reviews unavailable/u) - assert.equal(publishedState(failures[0].options.body), 'in_progress') - assert.equal(publishedState(failures[1].options.body), 'failure') - assert.equal(failures[1].options.body.output.title, 'Approval evaluation failed.') + assert.deepEqual(failures.map(({ path }) => path), ['/repos/deepseek-harness/deepseek-harness/check-runs']) + assert.equal(failures[0].options.body.status, 'completed') + assert.equal(failures[0].options.body.conclusion, 'failure') + assert.equal(failures[0].options.body.output.title, 'Approval evaluation failed.') }) test('sends authenticated JSON and escapes an API error body', async () => { @@ -424,7 +424,7 @@ test('does not fetch history when approvals already have two-point weights', asy assert.equal(result.points, 2) }) -test('publishes error when production attribution fails', async () => { +test('concludes failure when production attribution fails', async () => { const states = [] await assert.rejects(runApprovalCheck({ event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', @@ -433,30 +433,43 @@ test('publishes error when production attribution fails', async () => { if (path.includes('/reviews?')) return [review('writer', 'APPROVED')] if (path.includes('/permission')) return { permission: 'write' } if (path.includes('/comments?')) return [] + if (isCheckRunsList(path)) return emptyCheckRuns states.push(publishedState(options.body)) return {} }, }), /incomplete history/u) - assert.deepEqual(states, ['in_progress', 'failure']) + assert.deepEqual(states, ['failure']) }) -test('revokes a previous success before starting expensive attribution', async () => { - const states = [] - await runApprovalCheck({ - event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', write: () => {}, - getOwnership: async () => { - assert.deepEqual(states, ['in_progress']) - return { totalLines: 100, reviewerLines: { writer: 25 } } - }, +test('revokes a published success without returning a completed run to in progress', async () => { + const calls = [] + const checkRuns = [ + { id: 7, app: { slug: 'github-actions' }, conclusion: 'success' }, + { id: 8, app: { slug: 'github-actions' }, conclusion: 'action_required' }, + { id: 9, app: { slug: 'other-app' }, conclusion: 'success' }, + ] + await publishApprovalPhase({ + event: pullRequestEvent(), runUrl: 'https://github.example/run/1', phase: 'revoke', api: async (path, options) => { - if (path.includes('/reviews?')) return [review('writer', 'APPROVED')] - if (path.includes('/permission')) return { permission: 'write' } - states.push(publishedState(options.body)) - return {} + calls.push({ path, method: options?.method, body: options?.body }) + return isCheckRunsList(path) ? { total_count: checkRuns.length, check_runs: checkRuns } : {} }, }) - assert.deepEqual(states, ['in_progress', 'success']) + assert.deepEqual(calls, [{ + path: `/repos/deepseek-harness/deepseek-harness/commits/${HEAD_SHA}/check-runs?check_name=weighted%20approval%20decision&filter=all&per_page=100`, + method: undefined, + body: undefined, + }, { + path: '/repos/deepseek-harness/deepseek-harness/check-runs/7', + method: 'PATCH', + body: { + status: 'completed', + conclusion: 'action_required', + details_url: 'https://github.example/run/1', + output: { title: 'Preparing approval evaluation.', summary: 'Preparing approval evaluation. [Workflow run](https://github.example/run/1)' }, + }, + }]) }) for (const reviewers of [['first', 'second'], ['turtle1999', 'first']]) { @@ -488,7 +501,7 @@ test('publishes setup phases without evaluating or installing dependencies', asy event: pullRequestEvent(), runUrl: 'https://github.example/run/1', api: withEmptyReads(async (path, { body }) => { assert.match(path, /\/check-runs$/u); states.push(publishedState(body)) }), } - await publishApprovalPhase({ ...options, phase: 'in_progress' }) + await publishApprovalPhase({ ...options, phase: 'revoke' }) await publishApprovalPhase({ ...options, phase: 'failure' }) assert.deepEqual(states, ['in_progress', 'failure']) await assert.rejects(publishApprovalPhase({ ...options, phase: 'success' }), /invalid approval setup phase/u) @@ -497,11 +510,11 @@ test('publishes setup phases without evaluating or installing dependencies', asy test('updates the head\'s GitHub Actions decision check run instead of creating another', async () => { const calls = [] const checkRuns = [ - { id: 7, app: { slug: 'github-actions' } }, - { id: 8, app: { slug: 'other-app' } }, + { id: 7, app: { slug: 'github-actions' }, conclusion: 'action_required' }, + { id: 8, app: { slug: 'other-app' }, conclusion: 'action_required' }, ] await publishApprovalPhase({ - event: pullRequestEvent(), runUrl: 'https://github.example/run/1', phase: 'in_progress', + event: pullRequestEvent(), runUrl: 'https://github.example/run/1', phase: 'failure', api: async (path, options) => { calls.push({ path, method: options?.method }) return { total_count: checkRuns.length, check_runs: checkRuns } @@ -586,7 +599,7 @@ test('drafts skip history and history failures revoke success with error', async states.push(publishedState(options.body)) }, }), /history unavailable/u) - assert.deepEqual(states, ['in_progress', 'failure']) + assert.deepEqual(states, ['failure']) }) @@ -627,7 +640,7 @@ test('the publisher counts merged history through the production API path', asyn assert.equal(result.points, 2) assert.equal(result.authorCredit.points, 0.55) assert.equal(result.approvals[0].points, 1.45) - assert.deepEqual(states, ['in_progress', 'success']) + assert.deepEqual(states, ['success']) assert.equal(output[0], 'Author credit: 0.55 (50 merged PRs).') }) @@ -862,6 +875,7 @@ test('an active delegate command requests review once and preserves other reques if (options?.method === 'POST') { requests.push(options.body); return {} } return { users: [{ login: 'another-reviewer' }, ...(alreadyRequested ? [{ login: 'WRITER' }] : [])], teams: [] } } + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) return {} return api(path, options) }, @@ -890,6 +904,7 @@ test('inactive commands and drafts do not request review', async () => { policySource, runUrl: 'https://github.example/run/1', getMergedCount: async () => 0, write: () => {}, api: async (path, options) => { assert.ok(!path.endsWith('/requested_reviewers')) + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) return {} return api(path, options) }, @@ -906,6 +921,7 @@ test('failed review requests preserve the computed score and report the retry', event: { ...pullRequestEvent(), issue: { number: 42, pull_request: {} }, action: 'created', comment: { id: 1 } }, policySource, runUrl: 'https://github.example/run/1', getMergedCount: async () => 0, getOwnership: async () => ({ totalLines: 0, reviewerLines: {} }), write: line => output.push(line), api: async (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) { statuses.push(publishedState(options.body)); return {} } if (path.endsWith('/requested_reviewers')) { if (options?.method === 'POST') throw new Error('request rejected') @@ -916,7 +932,7 @@ test('failed review requests preserve the computed score and report the retry', }) assert.match(output.at(-1), failedRead ? /no users array/u : /request rejected/u) assert.match(output.at(-1), /score is unchanged.*will retry/u) - assert.deepEqual(statuses, ['in_progress', 'success']) + assert.deepEqual(statuses, ['success']) } }) @@ -928,6 +944,7 @@ test('delegation dismisses only the sender old decisions and stays active across const states = [] const read = delegationApi({ comments: [comment('turtle1999', '/delegate @writer')], reviews }) const api = async (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) { states.push(publishedState(options.body)); return {} } if (path.endsWith('/dismissals')) { calls.push(options) @@ -952,7 +969,7 @@ test('delegation dismisses only the sender old decisions and stays active across assert.deepEqual(result.blockers, ['other']) assert.equal(result.delegations.length, 1) assert.deepEqual(result.delegations[0].reviewIds, []) - assert.deepEqual(states, ['in_progress', 'action_required']) + assert.deepEqual(states, ['action_required']) reviews.find(review => review.id === 21).state = 'DISMISSED' for (const state of ['APPROVED', 'DISMISSED', 'COMMENTED', 'APPROVED']) { reviews.find(review => review.id === 20).state = state @@ -972,6 +989,7 @@ test('a new sender review arriving during dismissal is not dismissed or delegate const result = await runWithHistory({ event: { ...pullRequestEvent(), issue: { number: 42, pull_request: {} }, action: 'created', comment: { id: 1 } }, policySource, runUrl: 'https://github.example/run/1', write: () => {}, getMergedCount: async () => 0, api: async (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) return {} if (path.endsWith('/dismissals')) { assert.ok(path.endsWith('/reviews/10/dismissals')) @@ -994,6 +1012,7 @@ test('a failed or unconfirmed dismissal prevents review requests and publishes e await assert.rejects(runWithHistory({ event: { ...pullRequestEvent(), issue: { number: 42, pull_request: {} }, action: 'created', comment: { id: 1 } }, policySource, runUrl: 'https://github.example/run/1', write: () => {}, api: async (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) { states.push(publishedState(options.body)); return {} } if (path.endsWith('/dismissals')) { if (rejected) throw new Error('dismissal denied') @@ -1003,7 +1022,7 @@ test('a failed or unconfirmed dismissal prevents review requests and publishes e return read(path, options) }, }), rejected ? /dismissal denied/u : /dismissal was not confirmed/u) - assert.deepEqual(states, ['in_progress', 'failure']) + assert.deepEqual(states, ['failure']) } }) @@ -1016,6 +1035,7 @@ test('reads all comment pages, logs the score owner, and fails closed on missing event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', getMergedCount: async () => 0, write: line => output.push(line), api: async (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) { statuses.push(publishedState(options.body)); return {} } if (path.includes('/comments?')) return comments(path) return api(path, options) @@ -1029,7 +1049,7 @@ test('reads all comment pages, logs the score owner, and fails closed on missing }) assert.equal(pages.length, 2) assert.ok(output.includes('- @turtle1999: 2 (delegated to @writer)')) - assert.deepEqual(statuses.splice(0), ['in_progress', 'success']) + assert.deepEqual(statuses.splice(0), ['success']) for (const [comments, message] of [ [() => { throw new Error('comment API unavailable') }, /comment API unavailable/u], [() => ({}), /comments response is not an array/u], @@ -1039,7 +1059,7 @@ test('reads all comment pages, logs the score owner, and fails closed on missing [() => Array.from({ length: 100 }, () => comment('writer', 'text')), /comments exceed 3000/u], ]) { await assert.rejects(run(comments), message) - assert.deepEqual(statuses.splice(0), ['in_progress', 'failure']) + assert.deepEqual(statuses.splice(0), ['failure']) } }) @@ -1123,6 +1143,7 @@ test('later events reconcile all active delegations after a draft or replaced co const effects = [] const requested = [] const api = async (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) return {} if (path.endsWith('/dismissals')) { const old = reviews.find(review => path.endsWith(`/reviews/${review.id}/dismissals`)) @@ -1155,6 +1176,7 @@ test('already approved delegates need no new request', async () => { }) const result = await runWithHistory({ event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', write: () => {}, api: (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns if (path.includes('/check-runs')) return {} assert.ok(!path.endsWith('/requested_reviewers')) return read(path, options) diff --git a/.github/workflows/weighted-approval.yml b/.github/workflows/weighted-approval.yml index 896fc262c4..f72ada5942 100644 --- a/.github/workflows/weighted-approval.yml +++ b/.github/workflows/weighted-approval.yml @@ -41,7 +41,7 @@ jobs: env: GITHUB_TOKEN: ${{ github.token }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} - run: node .github/review-ownership/check-approval.mjs in_progress + run: node .github/review-ownership/check-approval.mjs revoke # SECURITY: dependency setup runs in the check-writing job. - if: steps.revoke.outputs.active == 'true' uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0 @@ -53,13 +53,16 @@ jobs: if: steps.revoke.outputs.active == 'true' run: python3 -m pip install -r .github/review-ownership/requirements.txt - name: Publish weighted approval decision + id: publish if: steps.revoke.outputs.active == 'true' env: GITHUB_TOKEN: ${{ github.token }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: node .github/review-ownership/check-approval.mjs + # A failed or cancelled run must not leave an earlier conclusion standing. A job cancelled + # after the decision was published keeps that decision instead of overwriting it with failure. - name: Publish approval setup failure - if: failure() && steps.revoke.outputs.active == 'true' + if: failure() || (cancelled() && steps.publish.outcome != 'success') env: GITHUB_TOKEN: ${{ github.token }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index 1275d4a7b0..e69d659644 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -1017,9 +1017,9 @@ describe('Weighted approval workflow', () => { const revokeIndex = steps.findIndex(step => step.id === 'revoke') expect(revokeIndex).toBeGreaterThan(steps.indexOf(checkout!)) expect(revokeIndex).toBeLessThan(setupIndex) - expect(steps[revokeIndex]?.run).toBe('node .github/review-ownership/check-approval.mjs in_progress') + expect(steps[revokeIndex]?.run).toBe('node .github/review-ownership/check-approval.mjs revoke') expect(steps.at(-1)).toMatchObject({ - if: "failure() && steps.revoke.outputs.active == 'true'", + if: "failure() || (cancelled() && steps.publish.outcome != 'success')", run: 'node .github/review-ownership/check-approval.mjs failure', }) const pythonJob = workflowJob(loadWorkflow('.github/workflows/ci.yml'), 'python-sdk') @@ -1032,6 +1032,7 @@ describe('Weighted approval workflow', () => { run: 'python3 -m pip install -r .github/review-ownership/requirements.txt', }) expect(publish).toMatchObject({ + id: 'publish', if: "steps.revoke.outputs.active == 'true'", env: { GITHUB_TOKEN: '${{ github.token }}', From 5a31bcc3e051efe9ef03b993933f18be4225eb29 Mon Sep 17 00:00:00 2001 From: EricMoin Date: Wed, 14 Oct 2026 16:04:11 +0800 Subject: [PATCH 03/10] feat(web): ship launcher-ready install icons A launcher masks the icon it puts on the home screen, and the install manifest advertised the bare whale favicon: an SVG mark drawn edge to edge on a transparent canvas, with no tile and no background of its own. Handed to a mask it loses its fins, and iOS reads no manifest icon at all, so an installed Web App showed a generic tile. Ship four rasters rendered from the desktop application tile: 192px and 512px `any`, a 512px `maskable` whose artwork is scaled into the circle a platform mask is guaranteed to show, and a 180px `apple-touch-icon`. Request `display: standalone` so the phone keeps its status bar, and declare `png` in the static MIME table so a launcher receives an image. Agent Note: .agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md --- ...install-icons-and-launch-display.i18n.yaml | 19 ++ ...13-web-install-icons-and-launch-display.md | 41 +++++ ...web-install-icons-and-launch-display.zh.md | 41 +++++ apps/web/index.html | 3 + apps/web/package.json | 4 +- apps/web/public/apple-touch-icon.png | Bin 0 -> 13355 bytes apps/web/public/icon-192.png | Bin 0 -> 14392 bytes apps/web/public/icon-512.png | Bin 0 -> 50600 bytes apps/web/public/icon-maskable-512.png | Bin 0 -> 41757 bytes apps/web/public/manifest.webmanifest | 22 ++- apps/web/scripts/render-app-icons.ts | 174 ++++++++++++++++++ apps/web/tests/app-icons.spec.ts | 81 ++++++++ apps/web/tests/pwa-manifest.e2e.ts | 50 ++++- apps/web/tsconfig.json | 1 + packages/host/frontend-static/src/index.ts | 4 + .../tests/frontend-static.spec.ts | 3 + pnpm-lock.yaml | 3 + 17 files changed, 434 insertions(+), 12 deletions(-) create mode 100644 .agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml create mode 100644 .agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md create mode 100644 .agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md create mode 100644 apps/web/public/apple-touch-icon.png create mode 100644 apps/web/public/icon-192.png create mode 100644 apps/web/public/icon-512.png create mode 100644 apps/web/public/icon-maskable-512.png create mode 100644 apps/web/scripts/render-app-icons.ts create mode 100644 apps/web/tests/app-icons.spec.ts diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml new file mode 100644 index 0000000000..a5f37e6942 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml @@ -0,0 +1,19 @@ +# Bilingual-pair consistency record for 2026-10-13-web-install-icons-and-launch-display.md (docs/i18n/README.md): per heading +# section, a hash of its English and Chinese blocks outside code blocks and generated regions. +# After editing either side, bring the other along and re-record with: +# pnpm run verify-translation-pairing --write .agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md +/agent-note-web-install-icons-and-launch-display: + en: 99458c7724588fd3 + zh: 1af39a382e1a8b6a +/agent-note-web-install-icons-and-launch-display/problem: + en: 726b732fcaf49153 + zh: 74f854807a14409d +/agent-note-web-install-icons-and-launch-display/decision: + en: ac98a944eeedf562 + zh: 2802162ddb00c1b0 +/agent-note-web-install-icons-and-launch-display/alternatives-considered: + en: 49f105c70f8453c0 + zh: aed4457cfe9d41df +/agent-note-web-install-icons-and-launch-display/consequences: + en: d0caf7aca43fa1db + zh: a354287e7aa1343d diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md new file mode 100644 index 0000000000..86a72133df --- /dev/null +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md @@ -0,0 +1,41 @@ +# Agent Note: Web install icons and launch display + +Status: implemented + +English | [中文](2026-10-13-web-install-icons-and-launch-display.zh.md) + +## Problem + +A launcher masks the icon it puts on the home screen and chooses that icon itself. The install manifest advertised the whale favicon: an SVG mark drawn edge to edge on a transparent canvas, with no tile and no background of its own. Handed to a mask, the mark loses its fins and fills the tile at the wrong proportion. iOS reads no manifest icon at all and had no `apple-touch-icon` to fall back on, so an installed web app showed a generic tile. The manifest also requested `display: "fullscreen"`, which removes both system bars on Android and leaves a chat surface with no clock, battery, or navigation. + +## Decision + +`apps/web/public/` carries four install icons: a 192px and a 512px `any` raster, a 512px `maskable` raster, and a 180px `apple-touch-icon`. [`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) renders all four from the official application tile in `apps/desktop/resources/icon.svg`, and `pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` regenerates the committed files. Every variant crops the tile's desktop drop shadow. The maskable variant scales the whale about the tile centre by `MASKABLE_ARTWORK_SCALE` while the tile's gradient keeps its own extent, so the border a mask may crop is still the real tile rather than a flat colour meeting it at a seam. + +[`app-icons.spec.ts`](../../../../apps/web/tests/app-icons.spec.ts) measures the whale's ink radius and fails when it leaves the circle a platform mask is guaranteed to show; the unscaled rendering is its negative control, so the assertion cannot pass vacuously. + +The manifest requests `display: "standalone"`, names `#ffffff` as its `theme_color` and `background_color`, and lists the three rasters. `apps/web/index.html` adds the `apple-touch-icon` link, `mobile-web-app-capable`, and `apple-mobile-web-app-title`. It adds no `theme-color`: `ThemePresenter` owns the single such node and follows the resolved in-application theme, which a static media query cannot. The tab favicons are unchanged. + +This supersedes the icon entry and the display mode in the [archived Web install manifest note](../../archived/feature/2026-08-06-web-install-manifest.md); its link to the manifest, product name, compact name, and root identity remain current. + +## Alternatives considered + +**Keep `favicon.svg` as the only icon.** It leaves iOS, which reads no manifest icon, with a generic tile, and the mark already reaches its own edges, so a launcher's mask crops it. The generator spec keeps that overflow as its negative control. + +**Commit the rasters without a generator.** The tile artwork moves with the desktop application's icon. A hand-exported set drifts silently and records neither the crop nor the safe-zone scale that makes it installable. + +**Draw a separate browser mark.** The product has one approved tile. A second mark would need designer approval and would not match the installed desktop application beside it. + +**Keep `display: "fullscreen"`.** Hiding both system bars suits an immersive surface, not a conversation the user reads beside the clock and returns to from the app switcher; `fullscreen` also offers no way back to the bars without a swipe. + +**Ship a service worker for offline start.** Installation, launch, and reconnect work without one. Offline use is a separate capability with its own cache-invalidation contract, and no current requirement asks for it. + +## Consequences + +The repository carries four committed rasters, all rendered by one script from `apps/desktop/resources/icon.svg`. Changing the desktop tile means rerunning that script and reading the spec's diff. + +The manifest's `theme_color` is one value and cannot follow the in-application theme, so a user whose theme differs from the system scheme sees that value until `ThemePresenter` writes the synchronized node shortly after boot. The presenter stays the only owner of `meta[name="theme-color"]`: a second static node wins document order and desynchronizes surrounding browser chrome from the rendered palette, which `settings-chrome.e2e.ts` pins. + +iOS ignores `background_color` and masks the touch icon with its own rounded rectangle; the tile's own margins keep the whale clear of that mask. Android selects the maskable raster for the home screen and an `any` raster for the install surface. The install icons are not a service worker: an installed Web App still needs the Host reachable. + +Verification covers each declared edge, the maskable safe zone with its negative control, opaque maskable corners, and the built `dist/` shipping every icon the manifest declares at the size it declares. Real-device installation, launch display, and window mode remain a device check. diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md new file mode 100644 index 0000000000..126c95cb81 --- /dev/null +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md @@ -0,0 +1,41 @@ +# Agent Note: Web 安装图标与启动显示 + +Status: implemented + +[English](2026-10-13-web-install-icons-and-launch-display.md) | 中文 + +## Problem + +启动器会自行选择放到桌面上的图标,并对它套用蒙层。安装 manifest 使用的却是鲸鱼 favicon:一个铺满整张画布、背景透明的 SVG 标记,既没有底板,也没有自己的背景。交给蒙层后,标记会丢掉尾鳍,并且以错误的比充满图标。iOS 完全不读 manifest 图标,此前又没有 `apple-touch-icon` 可回退,因此安装后的 web app 只显示通用图标。manifest 还请求了 `display: "fullscreen"`,这在 Android 上会同时移除两条系统栏,让一个对话界面失去时钟、电量和导航。 + +## Decision + +`apps/web/public/` 承载四个安装图标:192px 与 512px 的 `any` 位图、512px 的 `maskable` 位图,以及 180px 的 `apple-touch-icon`。[`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) 从 `apps/desktop/resources/icon.svg` 的官方应用底板渲染这四个文件,`pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` 重新生成已提交的文件。每个变体都会裁掉底板的桌面投影。maskable 变体围绕底板中心按 `MASKABLE_ARTWORK_SCALE` 缩放鲸鱼,而底板渐变保持自身范围,因此可能被蒙层裁掉的那圈边框仍是真实底板,而不是与其相接的纯色。 + +[`app-icons.spec.ts`](../../../../apps/web/tests/app-icons.spec.ts) 测量鲸鱼墨迹半径,一旦它越出平台蒙层保证显示的范围就失败;未缩放的渲染结果是它的反向对照,因此该断言不可能空泛通过。 + +manifest 请求 `display: "standalone"`,把 `theme_color` 与 `background_color` 定为 `#ffffff`,并列出三个位图。`apps/web/index.html` 增加 `apple-touch-icon` 链接、`mobile-web-app-capable` 与 `apple-mobile-web-app-title`。它不添加 `theme-color`:该节点由 `ThemePresenter` 独占并跟随解析后的应用内主题,这是静态媒体查询做不到的。标签页 favicon 保持不变。 + +本决策取代[已归档的 Web 安装 manifest 记录](../../archived/feature/2026-08-06-web-install-manifest.md)中的图标条目与显示模式;其指向 manifest 的链接、产品名、简称与根路径身份仍然有效。 + +## Alternatives considered + +**只保留 `favicon.svg` 作为图标。** 这会让不读 manifest 图标的 iOS 只显示通用图标,而该标记已经抵达自身边界,启动器的蒙层必然裁切它。生成器 spec 把这一越界保留为反向对照。 + +**不使用生成器,直接提交位图。** 底板素材随桌面应用图标变化。手工导出的图标集会静默漂移,也无法记录使其可安装的裁剪方式与安全区缩放。 + +**另画一个浏览器专用标记。** 产品只有一个已批准的底板。第二个标记需要设计师批准,并且无法与旁边已安装的桌面应用保持一致。 + +**保留 `display: "fullscreen"`。** 隐藏两条系统栏适合沉浸式界面,而不适合用户对着时钟阅读、并从应用切换器返回的对话;`fullscreen` 也无法在不滑动的情况下恢复系统栏。 + +**为离线启动引入 service worker。** 安装、启动与重连都不需要它。离线使用是独立能力,有自己的缓存失效约定,当前没有需求要求它。 + +## Consequences + +仓库承载四个已提交位图,全部由一个脚本从 `apps/desktop/resources/icon.svg` 渲染。底板变化意味着重跑该脚本并阅读 spec 的 diff。 + +manifest 的 `theme_color` 是单个值,无法跟随应用内主题;因此当用户选择的主题与系统方案不同时,该值会一直生效,直到 `ThemePresenter` 在启动后不久写入同步节点。该 presenter 仍是 `meta[name="theme-color"]` 的唯一所有者:第二个静态节点会在文档顺序上胜出,使周边浏览器界面与已渲染的配色脱节,这一点由 `settings-chrome.e2e.ts` 固定。 + +iOS 忽略 `background_color`,并用自己的圆角矩形裁切触摸图标;底板自带的留白使鲸鱼避开该蒙层。Android 为桌面选择 maskable 位图,为安装界面选择 `any` 位图。安装图标不是 service worker:安装后的 Web App 仍要求 Host 可达。 + +验证覆盖每个声明的边长、maskable 安全区及其反向对照、maskable 四角的不透明,以及构建出的 `dist/` 确实按 manifest 声明的尺寸承载每个图标。真机安装、启动显示与窗口模式仍属设备检查。 diff --git a/apps/web/index.html b/apps/web/index.html index f5c2ffac06..ff3ca620b7 100644 --- a/apps/web/index.html +++ b/apps/web/index.html @@ -4,6 +4,9 @@ + + + DSH Local Build diff --git a/apps/web/package.json b/apps/web/package.json index 6e90bc5d2c..90e6296c65 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -26,7 +26,8 @@ "dev": "vite", "watch": "vite build --watch --no-emptyOutDir", "build:preview": "pnpm --filter @deepseek-ai/dsh-experimental-webworker-runtime exec tsdown && pnpm --filter @deepseek-ai/dsh-experimental-webworker-packer exec tsdown && vite build && dsh-pack-vfs-image --out dist/preview/vfs-image.tar.gz", - "serve:preview": "http-server dist -a 0.0.0.0 -p 4173 -c-1" + "serve:preview": "http-server dist -a 0.0.0.0 -p 4173 -c-1", + "render:app-icons": "tsx scripts/render-app-icons.ts" }, "license": "MIT", "devDependencies": { @@ -56,6 +57,7 @@ "playwright": "^1.49.0", "react": "^18.2.0", "react-dom": "^18.2.0", + "sharp": "0.35.5", "typescript": "^6.0.3", "vite": "^6.4.3", "vitest": "^4.1.11", diff --git a/apps/web/public/apple-touch-icon.png b/apps/web/public/apple-touch-icon.png new file mode 100644 index 0000000000000000000000000000000000000000..c63e916b5b0346da7d5b1606dbbc9c4cbfe4aafe GIT binary patch literal 13355 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 AY5)KL literal 0 HcmV?d00001 diff --git a/apps/web/public/icon-512.png b/apps/web/public/icon-512.png new file mode 100644 index 0000000000000000000000000000000000000000..3fb02cd67289633be26e7fe1877edb5c89fcc4cc GIT binary patch literal 50600 zcmZ_0cT^MK);@k_G6_Qf83m=O2^NY4P^wrmh@C267l{o)q*%ZPWDo=kDk=(s3O2BV zA5j5?!~!-D1QZk`QIsx4S^`Ptcktf(zV}`0`}>0x*CcahpIx5)oW1vW)ZKNl23wyE z0MJ;vWPt|&DEuc1a2EXN-B&dTKU71Otl13!^XTv&66kjcUgUzM3mm-mU2MP9biQ`U zKlg?rI`%kC@tM|nXVRnVHk-@#D-LGf_*37d(s9fgBQ7k_q*TvzZ_fJce8+T*SCQh^ zh_k#CuNw}1QdsV5^9a5hbou$ZgbkTXv!;c!%%?@N6v~yZh92L9Q(yG|rwh13q9o zD@tB0I*@vNn+S+B4lyjgUGZlbQj1xmv)?G5zyGnVYbFK1vY=EEk*jae1%C{>++V$q-j%8xOM+Rsmr+_&du*9M85Etina~xfhA-LVD1l3D zE2EqM!y+8+s__Fip*B43Y8~ed)P}h_kd^K=`7!-ao<>?F^{+j@;rAA+l0lZf#GAlp zj#^2~T?W?1sJe=}O7MF0Ozs64@+Q~Iee`EQODG=XSg4$Xn19T>oWA&Qg>6!>??mP_g#3gO_T~$ z_+Qo0JdG3w$}m0Ei#woVe?0BE8iR~vj6Wvbb)_x?>Zc=iZV<7>bBo1+28{be&uuOi zAIDFBcvWwjnz|@FZj#M7s2YZVwI0=_94w)^HnR{G0?q0B>cD33Ch*KdGgrnggL#=q zo?GKZR`!>fJD%-C6Kdo2uw4e7<*%SaEGpfK#6~C0I`DT9z1{i=wfo{G{l_DcR|l(u zJdIr&hJTt-22GNvht=%yY3$MqK`j7d{2^TwuJ1?&Ir`#nf|uO2WxU(egy<<1}Z|`x5>a(#e zqYyq=G<(Yo?5B-^KrF@-v`o$=X#0nK}H)$nt}p(bKW z2$7L|=8a*uMUtDR9f7}W8P@hzR+K!p_^+m8d!`KkvW!TmP0&MYVtg{1|7xh=qV;eX z{X0$*#C3laS{kyU2>!B54=Qh_BS`IfFG~BXq4#UXM3X_4+o1a1siU$JC;N&)Laln! z3iZEU#HDOir({3RcwHtrtVPLLZ{R+I|0m|uQ5bP{2!G<`yan8+`#&7>B~V>V7p;-1 zxdIk{-?ZV-m(5~EAs0o^cNRk5sl(o6%+WUau7n}=FYBT;i9Y+-T%q0rUGz_Cr~51D z>em{rsOOTyufdn(CZ@BU#{b&Zg+4qSR1?QIl94^*jtzfRagk-A`>bmM*QRklq6OVA zbspT3r!T>;6nq~=Mp|xydbJf_IReAjsMH@sKUd2_eK%j$vG{KFKYB%M6gp6|P654thCgK)-eoxRQKe++}F`Wck1BtQnwNM*Z8*EN~0#*$12O zVP|#R3@u?B0=)?0;RY7}ojTZYOr1GvR8)K8ET@#`3;1BU+&!^2cXjYZU}0im60(N- z9&DbQG!CB8^Zf(ZfnP z1JU$zQJ<`3KWh$CWHh%j;RRIb%We3Zz&GlM>_q?8^i4pQTKYg2`;$5|8o04T^t)yP zPhY_I++rfJ;sXdb;FQ{~rIM{+Q7J@t#1TniviLRY$4Ckw*uWq6G>-avd4N@t;G6A` zpo8y>mb8TwLE%R3))+Y~TyGPGvC5NeC;hUS_t(LDHfl8Kb=iNf%iyUHBCd$&vY#fE zU=aCV>K9u|hHX)%imOYodGMC!NwdblIE|09zPn8XQJ@zBNa{>S(YmnRzEF6*?8Y8F zi+_g;%tu}4zopMY{$3x@`>#9&uiwnWEPy4yw8@eWH6R`K>kEdGKqDwC!$SA-o;i%+ zl=g3=ssAU{20pfgvQGP#Nr$bmC6;>9UR(p!70L zYZ#>2JAT@fCh3hDfYH8Wb)`aG7fP?ZNQr=}#L*+DC;S331AaxWuB6q6@l1K~Z>kba zWsv=v;bVBiBC&X{xfYa`G$C|C9FT|3@3`|4(g9fBJp5=KpJZ&yNh}4fb8x0oJhM(x8_cT4K+( z+6q{IcO99A{1P)bTKFHX goopEPU)Db7JBT}7YNXP#lQx}xv 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 S2TKw7x5{CSeUU9c?*9N*b}KUg literal 0 HcmV?d00001 diff --git a/apps/web/public/manifest.webmanifest b/apps/web/public/manifest.webmanifest index 6592d6a700..bcd217d859 100644 --- a/apps/web/public/manifest.webmanifest +++ b/apps/web/public/manifest.webmanifest @@ -3,13 +3,27 @@ "short_name": "DSH", "start_url": "./", "scope": "./", - "display": "fullscreen", + "display": "standalone", + "background_color": "#ffffff", + "theme_color": "#ffffff", "icons": [ { - "src": "favicon.svg", - "sizes": "any", - "type": "image/svg+xml", + "src": "icon-192.png", + "sizes": "192x192", + "type": "image/png", "purpose": "any" + }, + { + "src": "icon-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "any" + }, + { + "src": "icon-maskable-512.png", + "sizes": "512x512", + "type": "image/png", + "purpose": "maskable" } ] } diff --git a/apps/web/scripts/render-app-icons.ts b/apps/web/scripts/render-app-icons.ts new file mode 100644 index 0000000000..8a5d358cf2 --- /dev/null +++ b/apps/web/scripts/render-app-icons.ts @@ -0,0 +1,174 @@ +/** + * Render the install icons of the Web application from the official + * application tile. + * + * A browser installs a Web App only with a raster 192px and a 512px icon, masks + * the icon it puts on the home screen, and has no manifest support for iOS, + * which reads `apple-touch-icon` instead. The whale favicon is the bare mark + * drawn edge to edge: handed to a launcher directly it loses its fins to the + * mask, and it carries neither the tile nor a background of its own. This + * script rasterizes the official application tile (`apps/desktop/resources/icon.svg`, + * the artwork the desktop application ships) into the committed PNGs under + * `public/`. Every variant crops the tile's drop shadow, which belongs to a + * desktop window rather than a launcher; the maskable variant additionally + * scales the whale inside the safe zone a platform mask may crop, leaving the + * tile's own gradient to fill the icon so no mask meets a seam. + * + * The committed PNGs are the output; rerun + * `pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` after the + * tile artwork changes. + * @module render-app-icons + */ + +import { readFile, writeFile } from 'node:fs/promises' +import { resolve } from 'node:path' +import { fileURLToPath } from 'node:url' +import sharp from 'sharp' + +/** Vector source owned by the desktop application; 1104 units square with the tile inset. */ +export const TILE_SOURCE = fileURLToPath(new URL('../../desktop/resources/icon.svg', import.meta.url)) + +/** Directory the generated PNGs are committed to. */ +export const ICON_OUTPUT_DIRECTORY = fileURLToPath(new URL('../public/', import.meta.url)) + +/** Tile edge in source units; every render density is expressed against it. */ +export const TILE_EDGE = 1024 + +/** Tile inset in the source canvas: the drop shadow occupies the surrounding band. */ +const TILE_INSET = { x: 40, y: 32 } as const + +/** Tile centre in source units, the fixed point of the maskable scale. */ +const TILE_CENTER = { x: TILE_INSET.x + TILE_EDGE / 2, y: TILE_INSET.y + TILE_EDGE / 2 } as const + +/** Density at which the source's own 1:1 scale renders. */ +const SOURCE_DENSITY = 72 + +/** + * Fraction of a maskable icon the artwork keeps: the platform may crop the + * border outside it. The tile's own ink reaches 0.461 of its edge from the + * centre, so 0.868 is the largest scale a circular mask still shows whole; + * `app-icons.spec.ts` re-derives this and fails when the artwork overflows. + */ +export const MASKABLE_ARTWORK_SCALE = 0.82 + +/** Radius a platform mask is guaranteed to show, as a fraction of the icon edge. */ +export const MASKABLE_SAFE_RADIUS_RATIO = 0.4 + +/** Root element of the source document, rewritten to crop the canvas to the tile. */ +const SOURCE_ROOT = /^]*>/u + +/** The tile group's drop shadow; stripped because a launcher icon has no window around it. */ +const TILE_SHADOW_GROUP = '' + +/** The whale group inside the tile; the only element a maskable icon scales. */ +const TILE_ARTWORK_GROUP = '' + +/** One committed install icon: the manifest or link member it serves and its edge in pixels. */ +export interface AppIconVariant { + /** File name inside `public/`. */ + readonly file: string + /** Square edge in pixels. */ + readonly size: number + /** Which rendering the platform receives. */ + readonly kind: 'tile' | 'maskable' +} + +/** + * Install icons the Web application ships. Chrome requires the 192px and 512px + * raster pair to offer installation, prefers a maskable icon for the home + * screen, and iOS reads the touch icon alone. + */ +export const APP_ICON_VARIANTS: readonly AppIconVariant[] = [ + { file: 'icon-192.png', size: 192, kind: 'tile' }, + { file: 'icon-512.png', size: 512, kind: 'tile' }, + { file: 'icon-maskable-512.png', size: 512, kind: 'maskable' }, + { file: 'apple-touch-icon.png', size: 180, kind: 'tile' }, +] + +/** The cropped 1:1 root every variant shares, at the tile's own square extent. */ +function croppedRoot(): string { + return `' +} + +/** A scale about the tile centre, so a masked crop stays symmetric. */ +function aboutCenter(scale: number): string { + return `translate(${String(TILE_CENTER.x)} ${String(TILE_CENTER.y)}) scale(${String(scale)})` + + ` translate(${String(-TILE_CENTER.x)} ${String(-TILE_CENTER.y)})` +} + +/** Replace the source root so the icon canvas is exactly the tile. */ +function reparent(source: string): string { + const trimmed = source.trim() + const root = SOURCE_ROOT.exec(trimmed) + if (root === null) throw new Error('app icons: the tile source has no root element') + if (!trimmed.includes(TILE_SHADOW_GROUP)) throw new Error('app icons: the tile source has no shadowed tile group') + if (!trimmed.includes(TILE_ARTWORK_GROUP)) throw new Error('app icons: the tile source has no artwork group') + return `${croppedRoot()}${trimmed.slice(root[0].length).replace(TILE_SHADOW_GROUP, '')}` +} + +/** + * Rewrite the tile source so the icon canvas is exactly the tile. + * @param source - complete `icon.svg` document. + * @returns an SVG document whose 1024-unit square is the tile without its shadow. + */ +export function tileDocument(source: string): string { + return reparent(source) +} + +/** + * Rewrite the tile source into a maskable icon. The tile keeps its own gradient + * and extent, so the icon's border is the real tile rather than an extension; + * only the whale scales into the safe zone a platform mask may crop. + * @param source - complete `icon.svg` document. + * @returns an SVG document whose artwork survives a circular mask whole. + */ +export function maskableDocument(source: string): string { + const relocated = reparent(source) + return relocated.replace( + TILE_ARTWORK_GROUP, + `${TILE_ARTWORK_GROUP.slice(0, -1)} transform="${aboutCenter(MASKABLE_ARTWORK_SCALE)}">`, + ) +} + +/** + * Rasterize one rewritten document at a square edge. + * @param document - output of {@link tileDocument} or {@link maskableDocument}. + * @param size - square edge in pixels. + * @returns the PNG bytes. + */ +async function renderPng(document: string, size: number): Promise { + // Render at the source scale the requested edge needs, then resize as a + // guarantee: the rasterizer must see the target density, not a large bitmap + // it would have to antialias down. + return sharp(Buffer.from(document), { density: SOURCE_DENSITY * size / TILE_EDGE }) + .resize(size, size) + .png() + .toBuffer() +} + +/** + * Render every install icon from the tile source. + * @param source - complete `icon.svg` document. + * @returns one PNG per {@link APP_ICON_VARIANTS} entry, in declaration order. + */ +export async function renderAppIcons(source: string): Promise { + return Promise.all(APP_ICON_VARIANTS.map(async variant => ({ + variant, + png: await renderPng( + variant.kind === 'maskable' ? maskableDocument(source) : tileDocument(source), + variant.size, + ), + }))) +} + +async function main(): Promise { + const icons = await renderAppIcons(await readFile(TILE_SOURCE, 'utf8')) + await Promise.all(icons.map(async ({ variant, png }) => { + await writeFile(resolve(ICON_OUTPUT_DIRECTORY, variant.file), png) + })) + console.info(`app icons: wrote ${icons.map(icon => icon.variant.file).join(', ')} to ${ICON_OUTPUT_DIRECTORY}`) +} + +if (process.argv[1] !== undefined && import.meta.filename === resolve(process.argv[1])) await main() diff --git a/apps/web/tests/app-icons.spec.ts b/apps/web/tests/app-icons.spec.ts new file mode 100644 index 0000000000..084654b634 --- /dev/null +++ b/apps/web/tests/app-icons.spec.ts @@ -0,0 +1,81 @@ +/** + * The install icons are committed binaries, so this suite renders them from the + * tile source and asserts the two properties a launcher depends on: every + * declared edge is really produced, and the maskable variant's artwork stays + * inside the circle a platform mask is guaranteed to show. Without the second + * the whale's tail is cropped on a round-masked home screen, which is the + * defect the dedicated icon set exists to remove. + */ +import { readFile } from 'node:fs/promises' +import { describe, expect, it } from 'vitest' +import sharp from 'sharp' +import { + APP_ICON_VARIANTS, MASKABLE_ARTWORK_SCALE, MASKABLE_SAFE_RADIUS_RATIO, TILE_SOURCE, + maskableDocument, renderAppIcons, tileDocument, +} from '../scripts/render-app-icons.ts' + +/** Channel sum above which a pixel is tile background rather than whale ink. */ +const LIGHT_PIXEL_SUM = 384 + +/** Center-to-corner distance of the darkest pixel, in pixels, from the icon centre. */ +async function inkRadius(png: Buffer): Promise { + const { data, info } = await sharp(png).ensureAlpha().raw().toBuffer({ resolveWithObject: true }) + const centre = (info.width - 1) / 2 + let radius = 0 + for (let y = 0; y < info.height; y += 1) { + for (let x = 0; x < info.width; x += 1) { + const at = (y * info.width + x) * 4 + const alpha = data[at + 3] ?? 0 + const sum = (data[at] ?? 0) + (data[at + 1] ?? 0) + (data[at + 2] ?? 0) + if (alpha === 0 || sum >= LIGHT_PIXEL_SUM) continue + radius = Math.max(radius, Math.hypot(x - centre, y - centre)) + } + } + return radius +} + +/** The four corner pixels, where a maskable icon must be opaque. */ +async function cornerAlphas(png: Buffer): Promise { + const { data, info } = await sharp(png).ensureAlpha().raw().toBuffer({ resolveWithObject: true }) + const last = info.width - 1 + return [[0, 0], [last, 0], [0, last], [last, last]].map(([x, y]) => data[((y ?? 0) * info.width + (x ?? 0)) * 4 + 3] ?? 0) +} + +describe('web install icons', () => { + it('renders every declared variant at its declared edge', async () => { + const icons = await renderAppIcons(await readFile(TILE_SOURCE, 'utf8')) + expect(icons.map(icon => icon.variant)).toEqual(APP_ICON_VARIANTS) + for (const { variant, png } of icons) { + const metadata = await sharp(png).metadata() + expect([variant.file, metadata.format, metadata.width, metadata.height]) + .toEqual([variant.file, 'png', variant.size, variant.size]) + } + }) + + it('keeps the maskable artwork inside the circle a platform mask shows', async () => { + const source = await readFile(TILE_SOURCE, 'utf8') + const edge = 512 + const maskable = (await renderAppIcons(source)) + .find(icon => icon.variant.kind === 'maskable') + expect(maskable).toBeDefined() + const safeRadius = MASKABLE_SAFE_RADIUS_RATIO * edge + expect(await inkRadius(maskable!.png)).toBeLessThanOrEqual(safeRadius) + + // Negative control: the same artwork without the maskable scale overflows + // the circle, so the assertion above is not vacuous. + const unscaled = await sharp(Buffer.from(tileDocument(source)), { density: 72 }) + .resize(edge, edge).png().toBuffer() + expect(await inkRadius(unscaled)).toBeGreaterThan(safeRadius) + }) + + it('fills a maskable icon to its edges so no mask meets transparency', async () => { + const maskable = maskableDocument(await readFile(TILE_SOURCE, 'utf8')) + const png = await sharp(Buffer.from(maskable), { density: 72 }).resize(512, 512).png().toBuffer() + expect(await cornerAlphas(png)).toEqual([255, 255, 255, 255]) + }) + + it('fails loudly on a source without the tile it transforms', () => { + expect(() => tileDocument('')).toThrow(/tile source/) + expect(MASKABLE_ARTWORK_SCALE).toBeLessThan(1) + }) +}) diff --git a/apps/web/tests/pwa-manifest.e2e.ts b/apps/web/tests/pwa-manifest.e2e.ts index 2cfc21e2c9..2c6619b114 100644 --- a/apps/web/tests/pwa-manifest.e2e.ts +++ b/apps/web/tests/pwa-manifest.e2e.ts @@ -2,12 +2,28 @@ import { readFile } from 'node:fs/promises' import { fileURLToPath } from 'node:url' import { join } from 'node:path' import { expect, it } from 'vitest' +import sharp from 'sharp' const DIST_ROOT = fileURLToPath(new URL('../dist', import.meta.url)) +/** One install icon the manifest declares. */ +interface ManifestIcon { + readonly src: string + readonly sizes: string + readonly type: string + readonly purpose: string +} + it('ships install metadata with the built web application', async () => { const index = await readFile(join(DIST_ROOT, 'index.html'), 'utf8') expect(index).toContain('') + expect(index).toContain('') + expect(index).toContain('') + expect(index).toContain('') + // No `theme-color` here: `ThemePresenter` owns the single metadata node and + // follows the resolved in-application theme, which a static media query + // cannot. The manifest's `theme_color` covers the surrounding chrome before + // the presenter runs, and `settings-chrome.e2e.ts` pins the live contract. const manifest: unknown = JSON.parse(await readFile(join(DIST_ROOT, 'manifest.webmanifest'), 'utf8')) // No `id`: a browser resolves an explicit `id` against the start URL's origin, @@ -18,16 +34,36 @@ it('ships install metadata with the built web application', async () => { short_name: 'DSH', start_url: './', scope: './', - display: 'fullscreen', - icons: [{ - src: 'favicon.svg', - sizes: 'any', - type: 'image/svg+xml', - purpose: 'any', - }], + // `standalone` keeps the phone's status bar; `fullscreen` hides it and the + // navigation bar, which strands a chat surface with no clock or battery. + display: 'standalone', + background_color: '#ffffff', + theme_color: '#ffffff', + icons: [ + { src: 'icon-192.png', sizes: '192x192', type: 'image/png', purpose: 'any' }, + { src: 'icon-512.png', sizes: '512x512', type: 'image/png', purpose: 'any' }, + { src: 'icon-maskable-512.png', sizes: '512x512', type: 'image/png', purpose: 'maskable' }, + ], }) }) +it('ships every declared install icon as a raster of its declared size', async () => { + // A launcher has no fallback: an icon the manifest names but the build does + // not carry is an install that silently shows nothing. + const manifest = JSON.parse( + await readFile(join(DIST_ROOT, 'manifest.webmanifest'), 'utf8'), + ) as { icons: ManifestIcon[] } + expect(manifest.icons).not.toHaveLength(0) + for (const icon of manifest.icons) { + const [edge] = icon.sizes.split('x') + const metadata = await sharp(await readFile(join(DIST_ROOT, icon.src))).metadata() + expect([icon.src, metadata.format, metadata.width, metadata.height]) + .toEqual([icon.src, 'png', Number(edge), Number(edge)]) + } + const touch = await sharp(await readFile(join(DIST_ROOT, 'apple-touch-icon.png'))).metadata() + expect([touch.format, touch.width, touch.height]).toEqual(['png', 180, 180]) +}) + it('ships fixed-color favicons selected by document media queries', async () => { const index = await readFile(join(DIST_ROOT, 'index.html'), 'utf8') expect(index).toContain('') diff --git a/apps/web/tsconfig.json b/apps/web/tsconfig.json index db02a233ec..aef0dc4108 100644 --- a/apps/web/tsconfig.json +++ b/apps/web/tsconfig.json @@ -15,6 +15,7 @@ }, "include": [ "src", + "scripts", "tests" ], // The web e2e lane (scaffold + replay specs) boots the host spine and reads diff --git a/packages/host/frontend-static/src/index.ts b/packages/host/frontend-static/src/index.ts index e497efd228..2a9483c8bd 100644 --- a/packages/host/frontend-static/src/index.ts +++ b/packages/host/frontend-static/src/index.ts @@ -42,6 +42,10 @@ const MIME: Record = { '.html': HTML_MIME, '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', + // The install icons are the only images the shell serves directly. A launcher + // reads them through a Content-Type that names an image, so the entries the + // Web build emits are declared here rather than left to octet-stream. + '.png': 'image/png', '.svg': 'image/svg+xml', '.json': 'application/json', '.map': 'application/json', diff --git a/packages/host/frontend-static/tests/frontend-static.spec.ts b/packages/host/frontend-static/tests/frontend-static.spec.ts index f586164a9c..d6227fb36e 100644 --- a/packages/host/frontend-static/tests/frontend-static.spec.ts +++ b/packages/host/frontend-static/tests/frontend-static.spec.ts @@ -39,6 +39,7 @@ async function loadComposition(): Promise { await writeFile(join(dist, 'app.js'), 'export {}') await writeFile(join(dist, 'blob.bin'), 'BLOB') await writeFile(join(dist, 'manifest.webmanifest'), '{}') + await writeFile(join(dist, 'icon-192.png'), 'PNG') await mkdir(join(dist, 'empty')) const configPath = join(root, 'cordis.yml') await writeFile(configPath, [ @@ -133,6 +134,8 @@ describe('real Loader composition', () => { type: 'application/manifest+json', body: '{}', }) + // The install icons are images; a launcher must not receive them as octet-stream. + expect(await request(port, '/icon-192.png')).toMatchObject({ status: 200, type: 'image/png', body: 'PNG' }) expect(await request(port, '/app.js', { method: 'HEAD' })).toEqual({ status: 200, type: 'text/javascript; charset=utf-8', diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2edabccb3f..26653dc389 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -991,6 +991,9 @@ importers: react-dom: specifier: ^18.2.0 version: 18.3.1(react@18.3.1) + sharp: + specifier: 0.35.5 + version: 0.35.5(@types/node@22.20.1) typescript: specifier: ^6.0.3 version: 6.0.3 From 63b3d2ce4dcecdad809d5bcbf99a7dbfb5356c8f Mon Sep 17 00:00:00 2001 From: EricMoin Date: Tue, 20 Oct 2026 11:24:07 +0800 Subject: [PATCH 04/10] test(web): remove native icon test decoding --- ...install-icons-and-launch-display.i18n.yaml | 8 +- ...13-web-install-icons-and-launch-display.md | 4 +- ...web-install-icons-and-launch-display.zh.md | 4 +- apps/web/scripts/render-app-icons.ts | 4 +- apps/web/tests/app-icons.spec.ts | 81 +++++-------------- apps/web/tests/png-size.ts | 13 +++ apps/web/tests/pwa-manifest.e2e.ts | 48 +++++++++-- 7 files changed, 84 insertions(+), 78 deletions(-) create mode 100644 apps/web/tests/png-size.ts diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml index a5f37e6942..4d6578d26e 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml @@ -9,11 +9,11 @@ en: 726b732fcaf49153 zh: 74f854807a14409d /agent-note-web-install-icons-and-launch-display/decision: - en: ac98a944eeedf562 - zh: 2802162ddb00c1b0 + en: a3417e4f8a4a7823 + zh: 0e1bef4b86dd0bd4 /agent-note-web-install-icons-and-launch-display/alternatives-considered: en: 49f105c70f8453c0 zh: aed4457cfe9d41df /agent-note-web-install-icons-and-launch-display/consequences: - en: d0caf7aca43fa1db - zh: a354287e7aa1343d + en: 0083cf91a9a53d83 + zh: 15ce227df95ca334 diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md index 86a72133df..845b6b208f 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md @@ -12,7 +12,7 @@ A launcher masks the icon it puts on the home screen and chooses that icon itsel `apps/web/public/` carries four install icons: a 192px and a 512px `any` raster, a 512px `maskable` raster, and a 180px `apple-touch-icon`. [`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) renders all four from the official application tile in `apps/desktop/resources/icon.svg`, and `pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` regenerates the committed files. Every variant crops the tile's desktop drop shadow. The maskable variant scales the whale about the tile centre by `MASKABLE_ARTWORK_SCALE` while the tile's gradient keeps its own extent, so the border a mask may crop is still the real tile rather than a flat colour meeting it at a seam. -[`app-icons.spec.ts`](../../../../apps/web/tests/app-icons.spec.ts) measures the whale's ink radius and fails when it leaves the circle a platform mask is guaranteed to show; the unscaled rendering is its negative control, so the assertion cannot pass vacuously. +[`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) uses the browser image decoder to measure the committed maskable whale's ink radius and fails when it leaves the circle a platform mask is guaranteed to show; the unscaled rendering is its negative control, so the assertion cannot pass vacuously. The manifest requests `display: "standalone"`, names `#ffffff` as its `theme_color` and `background_color`, and lists the three rasters. `apps/web/index.html` adds the `apple-touch-icon` link, `mobile-web-app-capable`, and `apple-mobile-web-app-title`. It adds no `theme-color`: `ThemePresenter` owns the single such node and follows the resolved in-application theme, which a static media query cannot. The tab favicons are unchanged. @@ -32,7 +32,7 @@ This supersedes the icon entry and the display mode in the [archived Web install ## Consequences -The repository carries four committed rasters, all rendered by one script from `apps/desktop/resources/icon.svg`. Changing the desktop tile means rerunning that script and reading the spec's diff. +The repository carries four committed rasters, all rendered by one script from `apps/desktop/resources/icon.svg`. Changing the desktop tile means rerunning that script and reviewing the PNG diff. Unit tests inspect committed PNG dimensions without loading the native renderer. Browser tests check the committed maskable safe zone and opaque corners, with an unscaled SVG as the negative control. No automated check compares committed pixels with freshly generated output. The manifest's `theme_color` is one value and cannot follow the in-application theme, so a user whose theme differs from the system scheme sees that value until `ThemePresenter` writes the synchronized node shortly after boot. The presenter stays the only owner of `meta[name="theme-color"]`: a second static node wins document order and desynchronizes surrounding browser chrome from the rendered palette, which `settings-chrome.e2e.ts` pins. diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md index 126c95cb81..046b63e626 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md @@ -12,7 +12,7 @@ Status: implemented `apps/web/public/` 承载四个安装图标:192px 与 512px 的 `any` 位图、512px 的 `maskable` 位图,以及 180px 的 `apple-touch-icon`。[`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) 从 `apps/desktop/resources/icon.svg` 的官方应用底板渲染这四个文件,`pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` 重新生成已提交的文件。每个变体都会裁掉底板的桌面投影。maskable 变体围绕底板中心按 `MASKABLE_ARTWORK_SCALE` 缩放鲸鱼,而底板渐变保持自身范围,因此可能被蒙层裁掉的那圈边框仍是真实底板,而不是与其相接的纯色。 -[`app-icons.spec.ts`](../../../../apps/web/tests/app-icons.spec.ts) 测量鲸鱼墨迹半径,一旦它越出平台蒙层保证显示的范围就失败;未缩放的渲染结果是它的反向对照,因此该断言不可能空泛通过。 +[`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) 使用浏览器图像解码器测量已提交 maskable 图标的鲸鱼墨迹半径,一旦它越出平台蒙层保证显示的范围就失败;未缩放的渲染结果是它的反向对照,因此该断言不可能空泛通过。 manifest 请求 `display: "standalone"`,把 `theme_color` 与 `background_color` 定为 `#ffffff`,并列出三个位图。`apps/web/index.html` 增加 `apple-touch-icon` 链接、`mobile-web-app-capable` 与 `apple-mobile-web-app-title`。它不添加 `theme-color`:该节点由 `ThemePresenter` 独占并跟随解析后的应用内主题,这是静态媒体查询做不到的。标签页 favicon 保持不变。 @@ -32,7 +32,7 @@ manifest 请求 `display: "standalone"`,把 `theme_color` 与 `background_colo ## Consequences -仓库承载四个已提交位图,全部由一个脚本从 `apps/desktop/resources/icon.svg` 渲染。底板变化意味着重跑该脚本并阅读 spec 的 diff。 +仓库携带四张已提交的栅格图,均由同一个脚本从 `apps/desktop/resources/icon.svg` 渲染。桌面图块变化时,需要重新运行该脚本并审查 PNG diff。单元测试直接检查已提交 PNG 的尺寸,不加载原生渲染器。浏览器测试检查已提交 maskable 图标的安全区和不透明角落,并以未缩放 SVG 作为反向对照。没有自动检查将已提交像素与重新生成的结果进行比较。 manifest 的 `theme_color` 是单个值,无法跟随应用内主题;因此当用户选择的主题与系统方案不同时,该值会一直生效,直到 `ThemePresenter` 在启动后不久写入同步节点。该 presenter 仍是 `meta[name="theme-color"]` 的唯一所有者:第二个静态节点会在文档顺序上胜出,使周边浏览器界面与已渲染的配色脱节,这一点由 `settings-chrome.e2e.ts` 固定。 diff --git a/apps/web/scripts/render-app-icons.ts b/apps/web/scripts/render-app-icons.ts index 8a5d358cf2..e6ba689c64 100644 --- a/apps/web/scripts/render-app-icons.ts +++ b/apps/web/scripts/render-app-icons.ts @@ -23,7 +23,6 @@ import { readFile, writeFile } from 'node:fs/promises' import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' -import sharp from 'sharp' /** Vector source owned by the desktop application; 1104 units square with the tile inset. */ export const TILE_SOURCE = fileURLToPath(new URL('../../desktop/resources/icon.svg', import.meta.url)) @@ -47,7 +46,7 @@ const SOURCE_DENSITY = 72 * Fraction of a maskable icon the artwork keeps: the platform may crop the * border outside it. The tile's own ink reaches 0.461 of its edge from the * centre, so 0.868 is the largest scale a circular mask still shows whole; - * `app-icons.spec.ts` re-derives this and fails when the artwork overflows. + * `pwa-manifest.e2e.ts` checks the committed artwork against that circle. */ export const MASKABLE_ARTWORK_SCALE = 0.82 @@ -142,6 +141,7 @@ async function renderPng(document: string, size: number): Promise { // Render at the source scale the requested edge needs, then resize as a // guarantee: the rasterizer must see the target density, not a large bitmap // it would have to antialias down. + const { default: sharp } = await import('sharp') return sharp(Buffer.from(document), { density: SOURCE_DENSITY * size / TILE_EDGE }) .resize(size, size) .png() diff --git a/apps/web/tests/app-icons.spec.ts b/apps/web/tests/app-icons.spec.ts index 084654b634..7d79d3760e 100644 --- a/apps/web/tests/app-icons.spec.ts +++ b/apps/web/tests/app-icons.spec.ts @@ -1,77 +1,34 @@ -/** - * The install icons are committed binaries, so this suite renders them from the - * tile source and asserts the two properties a launcher depends on: every - * declared edge is really produced, and the maskable variant's artwork stays - * inside the circle a platform mask is guaranteed to show. Without the second - * the whale's tail is cropped on a round-masked home screen, which is the - * defect the dedicated icon set exists to remove. - */ +/** Committed install icon dimensions and the generator's SVG crop and safe-zone scale. */ import { readFile } from 'node:fs/promises' +import { join } from 'node:path' import { describe, expect, it } from 'vitest' -import sharp from 'sharp' import { - APP_ICON_VARIANTS, MASKABLE_ARTWORK_SCALE, MASKABLE_SAFE_RADIUS_RATIO, TILE_SOURCE, - maskableDocument, renderAppIcons, tileDocument, + APP_ICON_VARIANTS, ICON_OUTPUT_DIRECTORY, MASKABLE_ARTWORK_SCALE, TILE_SOURCE, + maskableDocument, tileDocument, } from '../scripts/render-app-icons.ts' - -/** Channel sum above which a pixel is tile background rather than whale ink. */ -const LIGHT_PIXEL_SUM = 384 - -/** Center-to-corner distance of the darkest pixel, in pixels, from the icon centre. */ -async function inkRadius(png: Buffer): Promise { - const { data, info } = await sharp(png).ensureAlpha().raw().toBuffer({ resolveWithObject: true }) - const centre = (info.width - 1) / 2 - let radius = 0 - for (let y = 0; y < info.height; y += 1) { - for (let x = 0; x < info.width; x += 1) { - const at = (y * info.width + x) * 4 - const alpha = data[at + 3] ?? 0 - const sum = (data[at] ?? 0) + (data[at + 1] ?? 0) + (data[at + 2] ?? 0) - if (alpha === 0 || sum >= LIGHT_PIXEL_SUM) continue - radius = Math.max(radius, Math.hypot(x - centre, y - centre)) - } - } - return radius -} - -/** The four corner pixels, where a maskable icon must be opaque. */ -async function cornerAlphas(png: Buffer): Promise { - const { data, info } = await sharp(png).ensureAlpha().raw().toBuffer({ resolveWithObject: true }) - const last = info.width - 1 - return [[0, 0], [last, 0], [0, last], [last, last]].map(([x, y]) => data[((y ?? 0) * info.width + (x ?? 0)) * 4 + 3] ?? 0) -} +import { pngSize } from './png-size.ts' describe('web install icons', () => { - it('renders every declared variant at its declared edge', async () => { - const icons = await renderAppIcons(await readFile(TILE_SOURCE, 'utf8')) - expect(icons.map(icon => icon.variant)).toEqual(APP_ICON_VARIANTS) - for (const { variant, png } of icons) { - const metadata = await sharp(png).metadata() - expect([variant.file, metadata.format, metadata.width, metadata.height]) - .toEqual([variant.file, 'png', variant.size, variant.size]) + it('ships every declared variant at its declared edge', async () => { + for (const variant of APP_ICON_VARIANTS) { + expect(pngSize(await readFile(join(ICON_OUTPUT_DIRECTORY, variant.file)))) + .toEqual([variant.size, variant.size]) } }) - it('keeps the maskable artwork inside the circle a platform mask shows', async () => { + it('crops the desktop shadow and scales only the maskable artwork', async () => { const source = await readFile(TILE_SOURCE, 'utf8') - const edge = 512 - const maskable = (await renderAppIcons(source)) - .find(icon => icon.variant.kind === 'maskable') - expect(maskable).toBeDefined() - const safeRadius = MASKABLE_SAFE_RADIUS_RATIO * edge - expect(await inkRadius(maskable!.png)).toBeLessThanOrEqual(safeRadius) - - // Negative control: the same artwork without the maskable scale overflows - // the circle, so the assertion above is not vacuous. - const unscaled = await sharp(Buffer.from(tileDocument(source)), { density: 72 }) - .resize(edge, edge).png().toBuffer() - expect(await inkRadius(unscaled)).toBeGreaterThan(safeRadius) + const tile = tileDocument(source) + const maskable = maskableDocument(source) + expect(tile).toContain('viewBox="40 32 1024 1024"') + expect(tile).not.toContain('') + expect(tile).not.toContain('scale(0.82)') + expect(maskable).toContain(`transform="translate(552 544) scale(${MASKABLE_ARTWORK_SCALE}) translate(-552 -544)"`) }) - it('fills a maskable icon to its edges so no mask meets transparency', async () => { - const maskable = maskableDocument(await readFile(TILE_SOURCE, 'utf8')) - const png = await sharp(Buffer.from(maskable), { density: 72 }).resize(512, 512).png().toBuffer() - expect(await cornerAlphas(png)).toEqual([255, 255, 255, 255]) + it('rejects non-PNG bytes and incomplete image headers', () => { + expect(() => pngSize(Buffer.from(''))).toThrow(/PNG IHDR/) + expect(() => pngSize(Buffer.alloc(33))).toThrow(/PNG IHDR/) }) it('fails loudly on a source without the tile it transforms', () => { diff --git a/apps/web/tests/png-size.ts b/apps/web/tests/png-size.ts new file mode 100644 index 0000000000..9715d4223b --- /dev/null +++ b/apps/web/tests/png-size.ts @@ -0,0 +1,13 @@ +/** PNG header inspection without a native image decoder. */ +const PNG_IHDR = Buffer.from([137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82]) + +/** + * Read the dimensions of a PNG with its mandatory first IHDR chunk. + * @param png - encoded image bytes. + * @returns width and height in pixels. + * @throws when the PNG signature or IHDR chunk is missing. + */ +export function pngSize(png: Buffer): readonly [number, number] { + if (png.length < 33 || !png.subarray(0, PNG_IHDR.length).equals(PNG_IHDR)) throw new Error('Expected a PNG IHDR chunk') + return [png.readUInt32BE(16), png.readUInt32BE(20)] +} diff --git a/apps/web/tests/pwa-manifest.e2e.ts b/apps/web/tests/pwa-manifest.e2e.ts index 2c6619b114..1b393aed1c 100644 --- a/apps/web/tests/pwa-manifest.e2e.ts +++ b/apps/web/tests/pwa-manifest.e2e.ts @@ -2,7 +2,9 @@ import { readFile } from 'node:fs/promises' import { fileURLToPath } from 'node:url' import { join } from 'node:path' import { expect, it } from 'vitest' -import sharp from 'sharp' +import { chromium } from 'playwright' +import { pngSize } from './png-size.ts' +import { TILE_SOURCE, tileDocument } from '../scripts/render-app-icons.ts' const DIST_ROOT = fileURLToPath(new URL('../dist', import.meta.url)) @@ -16,6 +18,7 @@ interface ManifestIcon { it('ships install metadata with the built web application', async () => { const index = await readFile(join(DIST_ROOT, 'index.html'), 'utf8') + expect(index).toContain('') expect(index).toContain('') expect(index).toContain('') expect(index).toContain('') @@ -56,12 +59,10 @@ it('ships every declared install icon as a raster of its declared size', async ( expect(manifest.icons).not.toHaveLength(0) for (const icon of manifest.icons) { const [edge] = icon.sizes.split('x') - const metadata = await sharp(await readFile(join(DIST_ROOT, icon.src))).metadata() - expect([icon.src, metadata.format, metadata.width, metadata.height]) - .toEqual([icon.src, 'png', Number(edge), Number(edge)]) + expect(pngSize(await readFile(join(DIST_ROOT, icon.src)))) + .toEqual([Number(edge), Number(edge)]) } - const touch = await sharp(await readFile(join(DIST_ROOT, 'apple-touch-icon.png'))).metadata() - expect([touch.format, touch.width, touch.height]).toEqual(['png', 180, 180]) + expect(pngSize(await readFile(join(DIST_ROOT, 'apple-touch-icon.png')))).toEqual([180, 180]) }) it('ships fixed-color favicons selected by document media queries', async () => { @@ -75,3 +76,38 @@ it('ships fixed-color favicons selected by document media queries', async () => expect(dark).toContain('fill="#fff"') expect(dark.replace('fill="#fff"', 'fill="#000"')).toBe(light) }) + +it('keeps the shipped maskable artwork inside its opaque platform safe zone', async () => { + const browser = await chromium.launch({ headless: true }) + try { + const page = await browser.newPage() + const maskable = await readFile(join(DIST_ROOT, 'icon-maskable-512.png')) + const tile = tileDocument(await readFile(TILE_SOURCE, 'utf8')) + const [scaled, unscaled] = await page.evaluate(async (sources) => { + return Promise.all(sources.map(async (source) => { + const image = new Image() + image.src = source + await image.decode() + const canvas = document.createElement('canvas') + canvas.width = canvas.height = 512 + const context = canvas.getContext('2d')! + context.drawImage(image, 0, 0, 512, 512) + const { data } = context.getImageData(0, 0, 512, 512) + let radius = 0 + for (let y = 0; y < 512; y += 1) { + for (let x = 0; x < 512; x += 1) { + const at = (y * 512 + x) * 4 + if (data[at + 3] === 0 || data[at]! + data[at + 1]! + data[at + 2]! >= 384) continue + radius = Math.max(radius, Math.hypot(x - 255.5, y - 255.5)) + } + } + return { radius, corners: [0, 511, 511 * 512, 512 * 512 - 1].map(index => data[index * 4 + 3]) } + })) + }, [`data:image/png;base64,${maskable.toString('base64')}`, `data:image/svg+xml;base64,${Buffer.from(tile).toString('base64')}`]) + expect(scaled!.radius).toBeLessThanOrEqual(0.4 * 512) + expect(scaled!.corners).toEqual([255, 255, 255, 255]) + expect(unscaled!.radius).toBeGreaterThan(0.4 * 512) + } finally { + await browser.close() + } +}) From 025723a7c74bb9775ccb761a885020d2450f9311 Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Tue, 20 Oct 2026 14:34:22 +0800 Subject: [PATCH 05/10] fix(ci): keep the evaluation failure title the setup step would overwrite The setup-failure step runs after an evaluation failure has already concluded the check run as failure with a title that names the failure, so its generic title replaced the specific one. Keep a failure that already points at the current run and update every other conclusion. Record the transition gaps and the filtered list count in the decision note: the combined status resolves a context to its latest status, so the rollout retires the earlier pending status instead of leaving it for CI watchers. --- ...hted-approval-decision-check-run.i18n.yaml | 8 +++---- ...29-weighted-approval-decision-check-run.md | 8 +++++-- ...weighted-approval-decision-check-run.zh.md | 8 +++++-- .github/review-ownership/check-approval.mjs | 16 +++++++++---- .../review-ownership/check-approval.test.mjs | 24 +++++++++++++++++-- 5 files changed, 49 insertions(+), 15 deletions(-) diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml index 89856bb840..674e8e13c6 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml @@ -12,11 +12,11 @@ en: b070a25a6d990e8c zh: fa555f8e418dad1f /agent-note-weighted-approval-decisions-are-completed-check-runs/verification: - en: 5e5de198a6fbdcca - zh: 74c6dccdea7984b8 + en: 0cd6f1b14b31db64 + zh: 7fc266aa0006473d /agent-note-weighted-approval-decisions-are-completed-check-runs/alternatives-considered: en: b8daea66b0a90d26 zh: fb7079c815b38281 /agent-note-weighted-approval-decisions-are-completed-check-runs/consequences: - en: 772e9b9c0133d264 - zh: 0c36b0819adaa622 + en: 72881a2a72c37e4e + zh: b69bd79b0bb46edb diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md index 2c4550ba4a..545c051b75 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md @@ -28,10 +28,12 @@ A blocking review dominates the point total, so the conclusion remains `action_r ## Verification -[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case, the exact published check-run payloads, revoking a published success through its conclusion, updating an existing run instead of creating another, and invalid list responses. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the `checks: write` permission, the `revoke` and `failure` phase commands, the publish step identity, and the fallback that runs when a run fails or is cancelled before publishing. +[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case, the exact published check-run payloads, revoking a published success through its conclusion, updating an existing run instead of creating another, keeping the evaluation failure title when the setup-failure step follows it, and invalid list responses. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the `checks: write` permission, the `revoke` and `failure` phase commands, the publish step identity, and the fallback that runs when a run fails or is cancelled before publishing. The transition matrix and the fallback condition were measured on 2026-09-30 with throwaway tag runs in this repository, using the same GitHub Actions token as the publisher. A run created as `in_progress` and completed as `success` accepted `status: in_progress` and `status: queued` with `200` and stayed `completed`, while conclusion-only updates moved it to `action_required`, back to `success`, and to `failure`. The fallback step ran for a failed publish and for a cancellation during the publish step, and did not run when the job was cancelled after the decision was published. +The list response's `total_count` counts the `check_name`-filtered runs rather than every check on the head: on 2026-10-20 a head carrying 33 check runs reported `total_count` 0 for a name that matches none and 2 for an existing name, so an equality between `total_count` and the returned array length rejects a truncated page. + ## Alternatives considered **Keep the `pending` commit status.** Branch rules block correctly, but CI watchers cannot distinguish an approval wait from running checks and do not finish. @@ -52,4 +54,6 @@ The transition matrix and the fallback condition were measured on 2026-09-30 wit CI watchers finish while the decision waits on reviewers. `gh pr checks` groups `action_required` with failed checks and exits non-zero; consumers distinguish it by the check name, the `ACTION_REQUIRED` conclusion, or the output title, which carries the score or blocker. The `weighted approval publisher` Actions job and the `failure` conclusion carry operational failures. -Re-evaluating a head that already published `success` reports `action_required` until the new decision is written, so a watcher that polls inside that window sees a failed check. Heads created before this change keep their `weighted approval` commit status: no API completes or removes a commit status, so those heads stay pending for watchers until they receive a new commit. +Re-evaluating a head that already published `success` reports `action_required` until the new decision is written, so a watcher that polls inside that window sees a failed check. + +The branch-rule switch leaves two gaps on heads the publisher has not evaluated since it. A head without the decision check run reports the required check as expected until a push, a review, or a re-run of its latest publisher run creates it. A head that also carries the earlier `weighted approval` commit status keeps that pending status until the same event: the combined status resolves each context to its latest status, so the rollout posts a terminal `success` status for that context on every open head instead of waiting for its next push. diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md index d8b894351d..18227898a9 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md @@ -28,10 +28,12 @@ commit status 只提供 `pending`、`success`、`failure` 和 `error`。`pending ## 验证 -[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景、准确发布的 check run payload、通过 conclusion 撤销已发布的成功、更新已有 run 而不是另建一个,以及无效的列表响应。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定 `checks: write` 权限、`revoke` 和 `failure` 阶段命令、publish 步骤的身份,以及运行失败或在发布前被取消时执行的兜底步骤。 +[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景、准确发布的 check run payload、通过 conclusion 撤销已发布的成功、更新已有 run 而不是另建一个、安装失败步骤跟在评估失败之后时保留该标题,以及无效的列表响应。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定 `checks: write` 权限、`revoke` 和 `failure` 阶段命令、publish 步骤的身份,以及运行失败或在发布前被取消时执行的兜底步骤。 转换矩阵与该兜底条件于 2026-09-30 在本仓库用一次性 tag 运行实测,使用的是与 publisher 相同的 GitHub Actions token。以 `in_progress` 创建、以 `success` 完成的 run,接受 `status: in_progress` 和 `status: queued` 的更新时都返回 `200` 并保持 `completed`;只改 conclusion 的更新把它依次改为 `action_required`、`success` 和 `failure`。兜底步骤在发布失败以及在发布步骤执行期间被取消时运行,而在决策发布之后才取消 job 时不运行。 +列表响应中的 `total_count` 统计的是经 `check_name` 过滤后的 run,而不是该 head 上的全部检查:2026-10-20 实测,一个带有 33 个 check run 的 head,对不匹配任何 run 的名称返回 `total_count` 0,对已存在的名称返回 2,因此用 `total_count` 与返回数组长度相等来拒绝被截断的分页是有效的。 + ## 考虑过的替代方案 **保留 `pending` commit status。** 分支规则能正确阻止合并,但 CI 监视工具无法区分等待批准与仍在运行的检查,因此不会结束。 @@ -52,4 +54,6 @@ commit status 只提供 `pending`、`success`、`failure` 和 `error`。`pending 决策等待评审人时,CI 监视工具会结束。`gh pr checks` 把 `action_required` 与失败的检查归为一组并以非零状态退出;消费方可以通过 check 名称、`ACTION_REQUIRED` conclusion 或携带分数或 blocker 的输出标题加以区分。`weighted approval publisher` Actions job 和 `failure` conclusion 承载运行故障信号。 -重新评估已经发布 `success` 的 head 时,在新决策写入前会报告 `action_required`,因此在该窗口内轮询的监视工具会看到失败的检查。本次变更之前创建的 head 会保留原有的 `weighted approval` commit status:没有任何 API 能完成或删除 commit status,因此这些 head 对监视工具一直是 pending,直到它们收到新的 commit。 +重新评估已经发布 `success` 的 head 时,在新决策写入前会报告 `action_required`,因此在该窗口内轮询的监视工具会看到失败的检查。 + +分支规则切换后,对切换以来 publisher 尚未评估过的 head 存在两个缺口。没有该决策 check run 的 head 会把必需检查报告为 expected,直到一次 push、一次评审,或重跑其最新的 publisher run 创建它。同时也带有先前 `weighted approval` commit status 的 head 会一直保留那个 pending status 直到同一事件:combined status 对每个 context 取最新的 status,因此 rollout 对每个开放 head 发布该 context 的终态 `success` status,而不是等它下一次 push。 diff --git a/.github/review-ownership/check-approval.mjs b/.github/review-ownership/check-approval.mjs index 83b6eb68a2..55de5a7c12 100644 --- a/.github/review-ownership/check-approval.mjs +++ b/.github/review-ownership/check-approval.mjs @@ -359,7 +359,12 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw export async function publishApprovalPhase({ event, api, runUrl, phase }) { const pull = pullRequestFromEvent(event) if (phase === 'revoke') return revokeApproval(api, pull, runUrl) - if (phase === 'failure') return publishCheck(api, pull, 'failure', 'Approval setup or evaluation failed.', runUrl) + // The setup-failure step runs after an evaluation failure already concluded this run as failure + // with a title that names what failed; keep that write instead of replacing its title. + if (phase === 'failure') { + return publishCheck(api, pull, 'failure', 'Approval setup or evaluation failed.', runUrl, + run => run.conclusion === 'failure' && run.details_url === runUrl) + } throw new Error('invalid approval setup phase') } @@ -469,7 +474,7 @@ async function reviewerPermission(api, repository, login) { // One decision per head keeps branch rules and CI watchers unambiguous; the head's existing GitHub // Actions run is updated and a run is created only when none exists. -async function publishCheck(api, pull, conclusion, title, runUrl) { +async function publishCheck(api, pull, conclusion, title, runUrl, keep = () => false) { requireHttpsRunUrl(runUrl) const body = { status: 'completed', conclusion, details_url: runUrl, output: { title, summary: `${title} [Workflow run](${runUrl})` } } const runs = await decisionRuns(api, pull) @@ -477,9 +482,10 @@ async function publishCheck(api, pull, conclusion, title, runUrl) { await api(`/repos/${pull.repository}/check-runs`, { method: 'POST', body: { name: CHECK_NAME, head_sha: pull.headSha, ...body } }) return } - for (const { id } of runs) { - if (!Number.isSafeInteger(id)) throw new Error('check run has no valid id') - await api(`/repos/${pull.repository}/check-runs/${id}`, { method: 'PATCH', body }) + for (const run of runs) { + if (!Number.isSafeInteger(run.id)) throw new Error('check run has no valid id') + if (keep(run)) continue + await api(`/repos/${pull.repository}/check-runs/${run.id}`, { method: 'PATCH', body }) } } diff --git a/.github/review-ownership/check-approval.test.mjs b/.github/review-ownership/check-approval.test.mjs index fe7582108d..f406b03dc4 100644 --- a/.github/review-ownership/check-approval.test.mjs +++ b/.github/review-ownership/check-approval.test.mjs @@ -531,6 +531,26 @@ test('updates the head\'s GitHub Actions decision check run instead of creating } }) +test('setup failure keeps the evaluation failure title published for the same run', async () => { + const calls = [] + const checkRuns = [ + { id: 7, app: { slug: 'github-actions' }, conclusion: 'failure', details_url: 'https://github.example/run/1' }, + { id: 8, app: { slug: 'github-actions' }, conclusion: 'failure', details_url: 'https://github.example/old-run' }, + { id: 9, app: { slug: 'github-actions' }, conclusion: 'action_required', details_url: 'https://github.example/run/1' }, + ] + await publishApprovalPhase({ + event: pullRequestEvent(), runUrl: 'https://github.example/run/1', phase: 'failure', + api: async (path, options) => { + if (!isCheckRunsList(path)) calls.push({ path, method: options?.method }) + return isCheckRunsList(path) ? { total_count: checkRuns.length, check_runs: checkRuns } : {} + }, + }) + assert.deepEqual(calls, [ + { path: '/repos/deepseek-harness/deepseek-harness/check-runs/8', method: 'PATCH' }, + { path: '/repos/deepseek-harness/deepseek-harness/check-runs/9', method: 'PATCH' }, + ]) +}) + for (const [mergedCount, credit] of [[0, 0], [1, 0.011], [50, 0.55], [99, 1.089], [100, 1.1], [101, 1.1], [200, 1.1]]) { test(`author with ${mergedCount} merged PRs contributes ${credit} points but cannot approve alone`, async () => { const result = await evaluateApproval({ @@ -585,7 +605,7 @@ test('blockers skip history and ignore non-write reviewers', async () => { assert.deepEqual(result.ignoredReviewers, ['reader']) }) -test('drafts skip history and history failures revoke success with error', async () => { +test('drafts skip history and a failed history lookup concludes the check as failure', async () => { await evaluateApproval({ event: pullRequestEvent({ draft: true }), policySource, getMergedCount: async () => { throw new Error('draft must skip history') }, @@ -1005,7 +1025,7 @@ test('a new sender review arriving during dismissal is not dismissed or delegate assert.deepEqual(result.blockers, ['turtle1999']) }) -test('a failed or unconfirmed dismissal prevents review requests and publishes error', async () => { +test('a failed or unconfirmed dismissal prevents review requests and concludes failure', async () => { for (const rejected of [false, true]) { const states = [] const read = delegationApi({ comments: [comment('turtle1999', '/delegate @writer')], reviews: [review('turtle1999', 'CHANGES_REQUESTED')] }) From b9e4a29210ad7dc3900ce411399e5825cccacc57 Mon Sep 17 00:00:00 2001 From: EricMoin Date: Tue, 20 Oct 2026 14:35:00 +0800 Subject: [PATCH 06/10] fix(web): preserve detail in install icon exports --- ...install-icons-and-launch-display.i18n.yaml | 8 ++-- ...13-web-install-icons-and-launch-display.md | 4 +- ...web-install-icons-and-launch-display.zh.md | 4 +- apps/web/public/apple-touch-icon.png | Bin 13355 -> 19258 bytes apps/web/public/icon-192.png | Bin 14392 -> 21220 bytes apps/web/public/icon-512.png | Bin 50600 -> 80224 bytes apps/web/public/icon-maskable-512.png | Bin 41757 -> 66105 bytes apps/web/scripts/render-app-icons.ts | 16 ++++--- apps/web/tests/app-icons.spec.ts | 3 ++ apps/web/tests/pwa-manifest.e2e.ts | 41 +++++++++++++++++- 10 files changed, 62 insertions(+), 14 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml index 4d6578d26e..ecfdefd930 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml @@ -9,11 +9,11 @@ en: 726b732fcaf49153 zh: 74f854807a14409d /agent-note-web-install-icons-and-launch-display/decision: - en: a3417e4f8a4a7823 - zh: 0e1bef4b86dd0bd4 + en: e5f6dbce09b698ca + zh: 7e4e5e4f25b698f8 /agent-note-web-install-icons-and-launch-display/alternatives-considered: en: 49f105c70f8453c0 zh: aed4457cfe9d41df /agent-note-web-install-icons-and-launch-display/consequences: - en: 0083cf91a9a53d83 - zh: 15ce227df95ca334 + en: afef3bbd1a9ef108 + zh: 6d5250eaa4ded3f7 diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md index 845b6b208f..4a257efcbd 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md @@ -10,7 +10,7 @@ A launcher masks the icon it puts on the home screen and chooses that icon itsel ## Decision -`apps/web/public/` carries four install icons: a 192px and a 512px `any` raster, a 512px `maskable` raster, and a 180px `apple-touch-icon`. [`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) renders all four from the official application tile in `apps/desktop/resources/icon.svg`, and `pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` regenerates the committed files. Every variant crops the tile's desktop drop shadow. The maskable variant scales the whale about the tile centre by `MASKABLE_ARTWORK_SCALE` while the tile's gradient keeps its own extent, so the border a mask may crop is still the real tile rather than a flat colour meeting it at a seam. +`apps/web/public/` carries four install icons: a 192px and a 512px `any` raster, a 512px `maskable` raster, and a 180px `apple-touch-icon`. [`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) renders all four from the official application tile in `apps/desktop/resources/icon.svg`, and `pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` regenerates the committed files. Each variant preserves the whale's geometry, gradients, opacity, and filters. Rendering first produces a bitmap at no less than the 1024px source extent and four times the output edge, then reduces it with Lanczos3. This separate rasterization retains the original shading while smoothing the small icon's curves. Every variant crops the tile's desktop drop shadow. The maskable variant scales the whale about the tile centre by `MASKABLE_ARTWORK_SCALE` while the tile's gradient keeps its own extent, so the border a mask may crop is still the real tile rather than a flat colour meeting it at a seam. [`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) uses the browser image decoder to measure the committed maskable whale's ink radius and fails when it leaves the circle a platform mask is guaranteed to show; the unscaled rendering is its negative control, so the assertion cannot pass vacuously. @@ -32,7 +32,7 @@ This supersedes the icon entry and the display mode in the [archived Web install ## Consequences -The repository carries four committed rasters, all rendered by one script from `apps/desktop/resources/icon.svg`. Changing the desktop tile means rerunning that script and reviewing the PNG diff. Unit tests inspect committed PNG dimensions without loading the native renderer. Browser tests check the committed maskable safe zone and opaque corners, with an unscaled SVG as the negative control. No automated check compares committed pixels with freshly generated output. +The repository carries four committed rasters, all rendered by one script from `apps/desktop/resources/icon.svg`. Changing the desktop tile means rerunning that script and reviewing the PNG diff. Unit tests inspect committed PNG dimensions without loading the native renderer. Browser tests check the committed maskable safe zone and opaque corners, with an unscaled SVG as the negative control. They also check fractional coverage along the Apple icon's curves against direct small-size SVG rendering, which retains the source's hard pixel edges. No automated check compares committed pixels with freshly generated output. The manifest's `theme_color` is one value and cannot follow the in-application theme, so a user whose theme differs from the system scheme sees that value until `ThemePresenter` writes the synchronized node shortly after boot. The presenter stays the only owner of `meta[name="theme-color"]`: a second static node wins document order and desynchronizes surrounding browser chrome from the rendered palette, which `settings-chrome.e2e.ts` pins. diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md index 046b63e626..35bde3b363 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md @@ -10,7 +10,7 @@ Status: implemented ## Decision -`apps/web/public/` 承载四个安装图标:192px 与 512px 的 `any` 位图、512px 的 `maskable` 位图,以及 180px 的 `apple-touch-icon`。[`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) 从 `apps/desktop/resources/icon.svg` 的官方应用底板渲染这四个文件,`pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` 重新生成已提交的文件。每个变体都会裁掉底板的桌面投影。maskable 变体围绕底板中心按 `MASKABLE_ARTWORK_SCALE` 缩放鲸鱼,而底板渐变保持自身范围,因此可能被蒙层裁掉的那圈边框仍是真实底板,而不是与其相接的纯色。 +`apps/web/public/` 承载四个安装图标:192px 与 512px 的 `any` 位图、512px 的 `maskable` 位图,以及 180px 的 `apple-touch-icon`。[`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) 从 `apps/desktop/resources/icon.svg` 的官方应用底板渲染这四个文件,`pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` 重新生成已提交的文件。每个变体都保留鲸鱼的几何路径、渐变、透明度和滤镜。渲染先生成不小于源图 1024px、且边长至少为输出尺寸四倍的位图,再以 Lanczos3 下采样。分开的栅格化步骤在平滑小图标曲线的同时保留原有明暗细节。每个变体都会裁掉底板的桌面投影。maskable 变体围绕底板中心按 `MASKABLE_ARTWORK_SCALE` 缩放鲸鱼,而底板渐变保持自身范围,因此可能被蒙层裁掉的那圈边框仍是真实底板,而不是与其相接的纯色。 [`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) 使用浏览器图像解码器测量已提交 maskable 图标的鲸鱼墨迹半径,一旦它越出平台蒙层保证显示的范围就失败;未缩放的渲染结果是它的反向对照,因此该断言不可能空泛通过。 @@ -32,7 +32,7 @@ manifest 请求 `display: "standalone"`,把 `theme_color` 与 `background_colo ## Consequences -仓库携带四张已提交的栅格图,均由同一个脚本从 `apps/desktop/resources/icon.svg` 渲染。桌面图块变化时,需要重新运行该脚本并审查 PNG diff。单元测试直接检查已提交 PNG 的尺寸,不加载原生渲染器。浏览器测试检查已提交 maskable 图标的安全区和不透明角落,并以未缩放 SVG 作为反向对照。没有自动检查将已提交像素与重新生成的结果进行比较。 +仓库携带四张已提交的栅格图,均由同一个脚本从 `apps/desktop/resources/icon.svg` 渲染。桌面图块变化时,需要重新运行该脚本并审查 PNG diff。单元测试直接检查已提交 PNG 的尺寸,不加载原生渲染器。浏览器测试检查已提交 maskable 图标的安全区和不透明角落,并以未缩放 SVG 作为反向对照。测试也将 Apple 图标曲线边缘的部分像素覆盖与直接小尺寸渲染的 SVG 对比;后者保留源图的硬像素边缘。没有自动检查将已提交像素与重新生成的结果进行比较。 manifest 的 `theme_color` 是单个值,无法跟随应用内主题;因此当用户选择的主题与系统方案不同时,该值会一直生效,直到 `ThemePresenter` 在启动后不久写入同步节点。该 presenter 仍是 `meta[name="theme-color"]` 的唯一所有者:第二个静态节点会在文档顺序上胜出,使周边浏览器界面与已渲染的配色脱节,这一点由 `settings-chrome.e2e.ts` 固定。 diff --git a/apps/web/public/apple-touch-icon.png b/apps/web/public/apple-touch-icon.png index c63e916b5b0346da7d5b1606dbbc9c4cbfe4aafe..273d776a7641c0f099fb33e96d3988358bdfaf12 100644 GIT binary patch literal 19258 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�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 literal 13355 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 literal 14392 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

Faq?sfHUAP9mTMJVTJtJ_P6! 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 AY5)KL diff --git a/apps/web/public/icon-512.png b/apps/web/public/icon-512.png index 3fb02cd67289633be26e7fe1877edb5c89fcc4cc..26c44db5d378e5ff899944fe73248a7c3c7d01d8 100644 GIT binary patch literal 80224 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≦ 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 LBdsD0n}GiVSTE_8 literal 50600 zcmZ_0cT^MK);@k_G6_Qf83m=O2^NY4P^wrmh@C267l{o)q*%ZPWDo=kDk=(s3O2BV zA5j5?!~!-D1QZk`QIsx4S^`Ptcktf(zV}`0`}>0x*CcahpIx5)oW1vW)ZKNl23wyE z0MJ;vWPt|&DEuc1a2EXN-B&dTKU71Otl13!^XTv&66kjcUgUzM3mm-mU2MP9biQ`U zKlg?rI`%kC@tM|nXVRnVHk-@#D-LGf_*37d(s9fgBQ7k_q*TvzZ_fJce8+T*SCQh^ zh_k#CuNw}1QdsV5^9a5hbou$ZgbkTXv!;c!%%?@N6v~yZh92L9Q(yG|rwh13q9o zD@tB0I*@vNn+S+B4lyjgUGZlbQj1xmv)?G5zyGnVYbFK1vY=EEk*jae1%C{>++V$q-j%8xOM+Rsmr+_&du*9M85Etina~xfhA-LVD1l3D zE2EqM!y+8+s__Fip*B43Y8~ed)P}h_kd^K=`7!-ao<>?F^{+j@;rAA+l0lZf#GAlp zj#^2~T?W?1sJe=}O7MF0Ozs64@+Q~Iee`EQODG=XSg4$Xn19T>oWA&Qg>6!>??mP_g#3gO_T~$ z_+Qo0JdG3w$}m0Ei#woVe?0BE8iR~vj6Wvbb)_x?>Zc=iZV<7>bBo1+28{be&uuOi zAIDFBcvWwjnz|@FZj#M7s2YZVwI0=_94w)^HnR{G0?q0B>cD33Ch*KdGgrnggL#=q zo?GKZR`!>fJD%-C6Kdo2uw4e7<*%SaEGpfK#6~C0I`DT9z1{i=wfo{G{l_DcR|l(u zJdIr&hJTt-22GNvht=%yY3$MqK`j7d{2^TwuJ1?&Ir`#nf|uO2WxU(egy<<1}Z|`x5>a(#e zqYyq=G<(Yo?5B-^KrF@-v`o$=X#0nK}H)$nt}p(bKW z2$7L|=8a*uMUtDR9f7}W8P@hzR+K!p_^+m8d!`KkvW!TmP0&MYVtg{1|7xh=qV;eX z{X0$*#C3laS{kyU2>!B54=Qh_BS`IfFG~BXq4#UXM3X_4+o1a1siU$JC;N&)Laln! z3iZEU#HDOir({3RcwHtrtVPLLZ{R+I|0m|uQ5bP{2!G<`yan8+`#&7>B~V>V7p;-1 zxdIk{-?ZV-m(5~EAs0o^cNRk5sl(o6%+WUau7n}=FYBT;i9Y+-T%q0rUGz_Cr~51D z>em{rsOOTyufdn(CZ@BU#{b&Zg+4qSR1?QIl94^*jtzfRagk-A`>bmM*QRklq6OVA zbspT3r!T>;6nq~=Mp|xydbJf_IReAjsMH@sKUd2_eK%j$vG{KFKYB%M6gp6|P654thCgK)-eoxRQKe++}F`Wck1BtQnwNM*Z8*EN~0#*$12O zVP|#R3@u?B0=)?0;RY7}ojTZYOr1GvR8)K8ET@#`3;1BU+&!^2cXjYZU}0im60(N- z9&DbQG!CB8^Zf(ZfnP z1JU$zQJ<`3KWh$CWHh%j;RRIb%We3Zz&GlM>_q?8^i4pQTKYg2`;$5|8o04T^t)yP zPhY_I++rfJ;sXdb;FQ{~rIM{+Q7J@t#1TniviLRY$4Ckw*uWq6G>-avd4N@t;G6A` zpo8y>mb8TwLE%R3))+Y~TyGPGvC5NeC;hUS_t(LDHfl8Kb=iNf%iyUHBCd$&vY#fE zU=aCV>K9u|hHX)%imOYodGMC!NwdblIE|09zPn8XQJ@zBNa{>S(YmnRzEF6*?8Y8F zi+_g;%tu}4zopMY{$3x@`>#9&uiwnWEPy4yw8@eWH6R`K>kEdGKqDwC!$SA-o;i%+ zl=g3=ssAU{20pfgvQGP#Nr$bmC6;>9UR(p!70L zYZ#>2JAT@fCh3hDfYH8Wb)`aG7fP?ZNQr=}#L*+DC;S331AaxWuB6q6@l1K~Z>kba zWsv=v;bVBiBC&X{xfYa`G$C|C9FT|3@3`|4(g9fBJp5=KpJZ&yNh}4fb8x0oJhM(x8_cT4K+( z+6q{IcO99A{1P)bTKFHX goopEPU)Db7JBT}7YNXP#lQx}xv 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≷*<0sf_gWl8TdFWmJ{~_N@}x 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 S2TKw7x5{CSeUU9c?*9N*b}KUg diff --git a/apps/web/scripts/render-app-icons.ts b/apps/web/scripts/render-app-icons.ts index e6ba689c64..9d09f55498 100644 --- a/apps/web/scripts/render-app-icons.ts +++ b/apps/web/scripts/render-app-icons.ts @@ -42,6 +42,9 @@ const TILE_CENTER = { x: TILE_INSET.x + TILE_EDGE / 2, y: TILE_INSET.y + TILE_ED /** Density at which the source's own 1:1 scale renders. */ const SOURCE_DENSITY = 72 +/** Samples per output pixel along each axis before reduction. */ +const RENDER_SCALE = 4 + /** * Fraction of a maskable icon the artwork keeps: the platform may crop the * border outside it. The tile's own ink reaches 0.461 of its edge from the @@ -138,12 +141,15 @@ export function maskableDocument(source: string): string { * @returns the PNG bytes. */ async function renderPng(document: string, size: number): Promise { - // Render at the source scale the requested edge needs, then resize as a - // guarantee: the rasterizer must see the target density, not a large bitmap - // it would have to antialias down. const { default: sharp } = await import('sharp') - return sharp(Buffer.from(document), { density: SOURCE_DENSITY * size / TILE_EDGE }) - .resize(size, size) + const renderSize = Math.max(TILE_EDGE, size * RENDER_SCALE) + // A separate raster preserves the source's filters at full resolution and + // prevents SVG resize optimizations from bypassing the downsampling step. + const raster = await sharp(Buffer.from(document), { density: SOURCE_DENSITY * renderSize / TILE_EDGE }) + .png() + .toBuffer() + return sharp(raster) + .resize(size, size, { kernel: 'lanczos3' }) .png() .toBuffer() } diff --git a/apps/web/tests/app-icons.spec.ts b/apps/web/tests/app-icons.spec.ts index 7d79d3760e..e0e3d388f4 100644 --- a/apps/web/tests/app-icons.spec.ts +++ b/apps/web/tests/app-icons.spec.ts @@ -23,6 +23,9 @@ describe('web install icons', () => { expect(tile).toContain('viewBox="40 32 1024 1024"') expect(tile).not.toContain('') expect(tile).not.toContain('scale(0.82)') + expect(tile).toContain('') + expect(tile).toContain(source.match(/]*>/u)![0]) + expect(tile).toContain(source.match(/[\s\S]*<\/defs>/u)![0]) expect(maskable).toContain(`transform="translate(552 544) scale(${MASKABLE_ARTWORK_SCALE}) translate(-552 -544)"`) }) diff --git a/apps/web/tests/pwa-manifest.e2e.ts b/apps/web/tests/pwa-manifest.e2e.ts index 1b393aed1c..255deb0833 100644 --- a/apps/web/tests/pwa-manifest.e2e.ts +++ b/apps/web/tests/pwa-manifest.e2e.ts @@ -4,7 +4,7 @@ import { join } from 'node:path' import { expect, it } from 'vitest' import { chromium } from 'playwright' import { pngSize } from './png-size.ts' -import { TILE_SOURCE, tileDocument } from '../scripts/render-app-icons.ts' +import { ICON_OUTPUT_DIRECTORY, TILE_SOURCE, tileDocument } from '../scripts/render-app-icons.ts' const DIST_ROOT = fileURLToPath(new URL('../dist', import.meta.url)) @@ -77,6 +77,45 @@ it('ships fixed-color favicons selected by document media queries', async () => expect(dark.replace('fill="#fff"', 'fill="#000"')).toBe(light) }) +it('preserves fractional coverage along the Apple icon curves', async () => { + const browser = await chromium.launch({ headless: true }) + try { + const page = await browser.newPage() + const png = await readFile(join(ICON_OUTPUT_DIRECTORY, 'apple-touch-icon.png')) + const tile = tileDocument(await readFile(TILE_SOURCE, 'utf8')) + const [generated, direct] = await page.evaluate(async sources => Promise.all(sources.map(async (source) => { + const image = new Image() + image.src = source + await image.decode() + const canvas = document.createElement('canvas') + canvas.width = canvas.height = 180 + const context = canvas.getContext('2d')! + context.drawImage(image, 0, 0, 180, 180) + const { data } = context.getImageData(0, 0, 180, 180) + const brightness = (x: number, y: number): number => { + const at = (y * 180 + x) * 4 + return (data[at]! + data[at + 1]! + data[at + 2]!) / 3 + } + let partial = 0 + for (let y = 1; y < 179; y += 1) { + for (let x = 1; x < 179; x += 1) { + const value = brightness(x, y) + if (value <= 85 || value >= 200) continue + // Neighboring ink and background distinguish edge coverage from the + // source's gradients and shadows, which remain in both renderings. + const neighbors: number[] = [] + for (let dy = -1; dy <= 1; dy += 1) { + for (let dx = -1; dx <= 1; dx += 1) neighbors.push(brightness(x + dx, y + dy)) + } + if (Math.min(...neighbors) < 70 && Math.max(...neighbors) > 220) partial += 1 + } + } + return partial + })), [`data:image/png;base64,${png.toString('base64')}`, `data:image/svg+xml;base64,${Buffer.from(tile).toString('base64')}`]) + expect(generated!).toBeGreaterThan(direct!) + } finally { await browser.close() } +}) + it('keeps the shipped maskable artwork inside its opaque platform safe zone', async () => { const browser = await chromium.launch({ headless: true }) try { From d98ff5d9fad2c34b708f907d55b068cf7048e5cf Mon Sep 17 00:00:00 2001 From: Chinesezjc Date: Tue, 20 Oct 2026 15:01:34 +0800 Subject: [PATCH 07/10] fix(ci): gate the setup failure step on the recorded conclusion The evaluation failure title was preserved by matching details_url, a field GitHub may rewrite for check runs created with the workflow token, so the setup-failure step could replace the specific title after all. Record the conclusion the publish step wrote in a step output and run the setup-failure step only when no conclusion was recorded; a published decision now survives a later failure or cancellation without relying on an API field. Record the measured timeout cancellation path and the re-run migration limit in the decision note, and scope the pre-push guidance to ACTION_REQUIRED so a failure conclusion is not read as an approval wait. --- ...hted-approval-decision-check-run.i18n.yaml | 12 ++-- ...29-weighted-approval-decision-check-run.md | 8 +-- ...weighted-approval-decision-check-run.zh.md | 8 +-- .agents/skills/dsh-pre-push-checks/SKILL.md | 2 +- .github/review-ownership/check-approval.mjs | 27 +++++---- .../review-ownership/check-approval.test.mjs | 58 +++++++++++++------ .github/workflows/weighted-approval.yml | 7 ++- scripts/ci-workflow.spec.ts | 2 +- 8 files changed, 74 insertions(+), 50 deletions(-) diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml index 674e8e13c6..aea251ed42 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.i18n.yaml @@ -9,14 +9,14 @@ en: 6ba6f6a15a1562ec zh: 1cbef3e736c258e6 /agent-note-weighted-approval-decisions-are-completed-check-runs/decision: - en: b070a25a6d990e8c - zh: fa555f8e418dad1f + en: 7932e500d3fb794f + zh: 576fffbf7525c12b /agent-note-weighted-approval-decisions-are-completed-check-runs/verification: - en: 0cd6f1b14b31db64 - zh: 7fc266aa0006473d + en: f0b7fb23bab7d23c + zh: d67e23a22b763bae /agent-note-weighted-approval-decisions-are-completed-check-runs/alternatives-considered: en: b8daea66b0a90d26 zh: fb7079c815b38281 /agent-note-weighted-approval-decisions-are-completed-check-runs/consequences: - en: 72881a2a72c37e4e - zh: b69bd79b0bb46edb + en: d39c80f2e8c802ee + zh: 4e72296a9495d02c diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md index 545c051b75..b78e25827b 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.md @@ -22,15 +22,15 @@ The [publisher](../../../../.github/review-ownership/check-approval.mjs) reports | Ready, threshold met, and no blocking review | `completed`, conclusion `success` | | Setup, evaluation, or cancellation failure | `completed`, conclusion `failure` | -A check run that reached `completed` never returns to `in_progress` or `queued`: GitHub answers `200` to that update and leaves the run completed, so only the conclusion is mutable. The publisher therefore revokes a published success by writing the blocking conclusion before dependency setup, which covers the setup and history fetch that dominate a cold evaluation. The head keeps one GitHub Actions run with the required name: the publisher updates the head's existing run and creates one only when none exists. An incomplete list or a run without a valid ID fails the publication. A run cancelled after the decision was published keeps that decision; every other failed or cancelled run concludes `failure`. +A check run that reached `completed` never returns to `in_progress` or `queued`: GitHub answers `200` to that update and leaves the run completed, so only the conclusion is mutable. The publisher therefore revokes a published success by writing the blocking conclusion before dependency setup, which covers the setup and history fetch that dominate a cold evaluation. The head keeps one GitHub Actions run with the required name: the publisher updates the head's existing run and creates one only when none exists. An incomplete list or a run without a valid ID fails the publication. The publish step records the conclusion it wrote in a step output, and the setup-failure step runs only when no conclusion was recorded: a published decision survives a later failure or cancellation, while a run cancelled before that write concludes `failure`. During a re-evaluation, the earlier decision's conclusion and title stay visible until the new decision is written. A blocking review dominates the point total, so the conclusion remains `action_required` even when counted approvals reach the threshold. GitHub accepts only `success`, `neutral`, and `skipped` for a required check, so `action_required` blocks merging while completing the check. ## Verification -[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case, the exact published check-run payloads, revoking a published success through its conclusion, updating an existing run instead of creating another, keeping the evaluation failure title when the setup-failure step follows it, and invalid list responses. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the `checks: write` permission, the `revoke` and `failure` phase commands, the publish step identity, and the fallback that runs when a run fails or is cancelled before publishing. +[Approval policy tests](../../../../.github/review-ownership/check-approval.test.mjs) pin the threshold-reaching blocker case, the exact published check-run payloads, revoking a published success through its conclusion, updating an existing run instead of creating another, recording the conclusion that the setup-failure step reads, and invalid list responses. [Workflow tests](../../../../scripts/ci-workflow.spec.ts) pin the `checks: write` permission, the `revoke` and `failure` phase commands, the publish step identity, and the fallback that runs when a run fails or is cancelled before publishing. -The transition matrix and the fallback condition were measured on 2026-09-30 with throwaway tag runs in this repository, using the same GitHub Actions token as the publisher. A run created as `in_progress` and completed as `success` accepted `status: in_progress` and `status: queued` with `200` and stayed `completed`, while conclusion-only updates moved it to `action_required`, back to `success`, and to `failure`. The fallback step ran for a failed publish and for a cancellation during the publish step, and did not run when the job was cancelled after the decision was published. +The transition matrix and the fallback condition were measured on 2026-09-30 with throwaway tag runs in this repository, using the same GitHub Actions token as the publisher. A run created as `in_progress` and completed as `success` accepted `status: in_progress` and `status: queued` with `200` and stayed `completed`, while conclusion-only updates moved it to `action_required`, back to `success`, and to `failure`. The fallback step ran for a failed publish and for a cancellation during the publish step, and did not run when the job was cancelled after the decision was published. A `timeout-minutes` cancellation was measured separately on 2026-10-20: a job that exceeded its one-minute timeout on a throwaway `push` branch still ran its `cancelled()` step, so the setup-failure step executes on that path too. The list response's `total_count` counts the `check_name`-filtered runs rather than every check on the head: on 2026-10-20 a head carrying 33 check runs reported `total_count` 0 for a name that matches none and 2 for an existing name, so an equality between `total_count` and the returned array length rejects a truncated page. @@ -56,4 +56,4 @@ CI watchers finish while the decision waits on reviewers. `gh pr checks` groups Re-evaluating a head that already published `success` reports `action_required` until the new decision is written, so a watcher that polls inside that window sees a failed check. -The branch-rule switch leaves two gaps on heads the publisher has not evaluated since it. A head without the decision check run reports the required check as expected until a push, a review, or a re-run of its latest publisher run creates it. A head that also carries the earlier `weighted approval` commit status keeps that pending status until the same event: the combined status resolves each context to its latest status, so the rollout posts a terminal `success` status for that context on every open head instead of waiting for its next push. +The branch-rule switch leaves two gaps on heads the publisher has not evaluated since it. A head without the decision check run reports the required check as expected until the next publisher event creates it; re-running a pre-switch run does not, because a re-run replays that run's earlier workflow file, which calls the removed `pending` phase against the new script. A re-run of a pre-switch run on 2026-10-20 executed the pre-switch step names and no check-run write. A head that also carries the earlier `weighted approval` commit status keeps that pending status until the same event: the combined status resolves each context to its latest status, so the rollout posts a terminal `success` status for that context on every open head instead of waiting for its next push. diff --git a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md index 18227898a9..8a12d784a6 100644 --- a/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md +++ b/.agents/notes/implemented/process/2026-09-29-weighted-approval-decision-check-run.zh.md @@ -22,15 +22,15 @@ commit status 只提供 `pending`、`success`、`failure` 和 `error`。`pending | 已 ready、达到阈值且没有阻塞性评审 | `completed`,conclusion 为 `success` | | 安装、评估或取消导致的失败 | `completed`,conclusion 为 `failure` | -已经到达 `completed` 的 check run 不会再回到 `in_progress` 或 `queued`:GitHub 对该更新返回 `200`,但让 run 保持已完成,因此只有 conclusion 可变。publisher 因此在依赖安装前用阻塞性 conclusion 撤销已发布的成功,覆盖冷启动中占主要耗时的安装与历史拉取。同一 head 上只保留一个同名 GitHub Actions run:publisher 更新 head 上已有的 run,仅在不存在时创建一个。列表不完整或 run 没有有效 ID 时发布失败。决策已发布后被取消的 run 保留该决策;其余失败或被取消的 run 都以 `failure` 结束。 +已经到达 `completed` 的 check run 不会再回到 `in_progress` 或 `queued`:GitHub 对该更新返回 `200`,但让 run 保持已完成,因此只有 conclusion 可变。publisher 因此在依赖安装前用阻塞性 conclusion 撤销已发布的成功,覆盖冷启动中占主要耗时的安装与历史拉取。同一 head 上只保留一个同名 GitHub Actions run:publisher 更新 head 上已有的 run,仅在不存在时创建一个。列表不完整或 run 没有有效 ID 时发布失败。publish 步骤把自己写入的 conclusion 记录到 step output,安装失败步骤只在该 output 未记录时运行:已发布的决策在之后的失败或取消中保留,而在该写入之前被取消的 run 以 `failure` 结束。重新评估期间,先前的 conclusion 与标题会一直显示到新决策写入为止。 阻塞性评审的优先级高于点数总和,因此即使计入的批准已经达到阈值,conclusion 仍为 `action_required`。GitHub 只接受 `success`、`neutral` 和 `skipped` 作为必需检查的通过结果,因此 `action_required` 会阻止合并,同时让该检查处于完成状态。 ## 验证 -[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景、准确发布的 check run payload、通过 conclusion 撤销已发布的成功、更新已有 run 而不是另建一个、安装失败步骤跟在评估失败之后时保留该标题,以及无效的列表响应。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定 `checks: write` 权限、`revoke` 和 `failure` 阶段命令、publish 步骤的身份,以及运行失败或在发布前被取消时执行的兜底步骤。 +[批准策略测试](../../../../.github/review-ownership/check-approval.test.mjs)锁定已达到阈值但仍有 blocker 的场景、准确发布的 check run payload、通过 conclusion 撤销已发布的成功、更新已有 run 而不是另建一个、记录安装失败步骤所读的 conclusion,以及无效的列表响应。[工作流测试](../../../../scripts/ci-workflow.spec.ts)锁定 `checks: write` 权限、`revoke` 和 `failure` 阶段命令、publish 步骤的身份,以及运行失败或在发布前被取消时执行的兜底步骤。 -转换矩阵与该兜底条件于 2026-09-30 在本仓库用一次性 tag 运行实测,使用的是与 publisher 相同的 GitHub Actions token。以 `in_progress` 创建、以 `success` 完成的 run,接受 `status: in_progress` 和 `status: queued` 的更新时都返回 `200` 并保持 `completed`;只改 conclusion 的更新把它依次改为 `action_required`、`success` 和 `failure`。兜底步骤在发布失败以及在发布步骤执行期间被取消时运行,而在决策发布之后才取消 job 时不运行。 +转换矩阵与该兜底条件于 2026-09-30 在本仓库用一次性 tag 运行实测,使用的是与 publisher 相同的 GitHub Actions token。以 `in_progress` 创建、以 `success` 完成的 run,接受 `status: in_progress` 和 `status: queued` 的更新时都返回 `200` 并保持 `completed`;只改 conclusion 的更新把它依次改为 `action_required`、`success` 和 `failure`。兜底步骤在发布失败以及在发布步骤执行期间被取消时运行,而在决策发布之后才取消 job 时不运行。`timeout-minutes` 取消路径于 2026-10-20 单独实测:一个在一次性 `push` 分支上超过一分钟超时的 job 仍然执行了 `cancelled()` 步骤,因此安装失败步骤在该路径上同样会运行。 列表响应中的 `total_count` 统计的是经 `check_name` 过滤后的 run,而不是该 head 上的全部检查:2026-10-20 实测,一个带有 33 个 check run 的 head,对不匹配任何 run 的名称返回 `total_count` 0,对已存在的名称返回 2,因此用 `total_count` 与返回数组长度相等来拒绝被截断的分页是有效的。 @@ -56,4 +56,4 @@ commit status 只提供 `pending`、`success`、`failure` 和 `error`。`pending 重新评估已经发布 `success` 的 head 时,在新决策写入前会报告 `action_required`,因此在该窗口内轮询的监视工具会看到失败的检查。 -分支规则切换后,对切换以来 publisher 尚未评估过的 head 存在两个缺口。没有该决策 check run 的 head 会把必需检查报告为 expected,直到一次 push、一次评审,或重跑其最新的 publisher run 创建它。同时也带有先前 `weighted approval` commit status 的 head 会一直保留那个 pending status 直到同一事件:combined status 对每个 context 取最新的 status,因此 rollout 对每个开放 head 发布该 context 的终态 `success` status,而不是等它下一次 push。 +分支规则切换后,对切换以来 publisher 尚未评估过的 head 存在两个缺口。没有该决策 check run 的 head 会把必需检查报告为 expected,直到下一次 publisher 事件创建它;重跑切换前的 run 不能完成迁移,因为重跑会回放该 run 当时的 workflow 文件,而它调用的 `pending` 阶段已被新脚本拒绝。2026-10-20 对切换前 run 的重跑执行的是切换前的步骤名,没有写入 check run。同时也带有先前 `weighted approval` commit status 的 head 会一直保留那个 pending status 直到同一事件:combined status 对每个 context 取最新的 status,因此 rollout 对每个开放 head 发布该 context 的终态 `success` status,而不是等它下一次 push。 diff --git a/.agents/skills/dsh-pre-push-checks/SKILL.md b/.agents/skills/dsh-pre-push-checks/SKILL.md index ec49e2b223..838e93e0b3 100644 --- a/.agents/skills/dsh-pre-push-checks/SKILL.md +++ b/.agents/skills/dsh-pre-push-checks/SKILL.md @@ -124,7 +124,7 @@ For GitHub PRs, inspect remote CI after the push: gh pr checks ``` -Report pending checks as pending. Inspect failures before attributing them to the branch or the environment. A `weighted approval decision` check that `gh pr checks` lists as failed means the approval gate is waiting on reviewers (`ACTION_REQUIRED`), not that the branch broke. +Report pending checks as pending. Inspect failures before attributing them to the branch or the environment. A `weighted approval decision` check that `gh pr checks` lists as failed means the approval gate is waiting on reviewers only when its conclusion is `ACTION_REQUIRED`; the same check concluded `FAILURE` is a publisher fault, so read the `weighted approval publisher` job before attributing it to the branch. When `gh pr checks` reports "no checks reported" and `/actions/runs?head_sha=` returns `total_count: 0`, read mergeability before suspecting the push or a dropped GitHub event: diff --git a/.github/review-ownership/check-approval.mjs b/.github/review-ownership/check-approval.mjs index 55de5a7c12..52cd3276e4 100644 --- a/.github/review-ownership/check-approval.mjs +++ b/.github/review-ownership/check-approval.mjs @@ -331,6 +331,7 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw } } catch (error) { await publishCheck(api, pull, 'failure', 'Approval evaluation failed.', runUrl) + recordConclusion('failure') throw error } write(result.authorCredit @@ -342,6 +343,7 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw writeList(write, 'Blocking change requests', result.blockers.map(login => `@${login}`)) writeList(write, 'Ignored reviewers without write access', result.ignoredReviewers.map(login => `@${login}`)) await publishCheck(api, pull, result.state, result.description, runUrl) + recordConclusion(result.state) write(`Published ${JSON.stringify(CHECK_NAME)} check run ${JSON.stringify(result.state)}.`) try { await requestDelegatedReviews(result, api, write) @@ -359,12 +361,7 @@ export async function runApprovalCheck({ event, policySource, api, runUrl, getOw export async function publishApprovalPhase({ event, api, runUrl, phase }) { const pull = pullRequestFromEvent(event) if (phase === 'revoke') return revokeApproval(api, pull, runUrl) - // The setup-failure step runs after an evaluation failure already concluded this run as failure - // with a title that names what failed; keep that write instead of replacing its title. - if (phase === 'failure') { - return publishCheck(api, pull, 'failure', 'Approval setup or evaluation failed.', runUrl, - run => run.conclusion === 'failure' && run.details_url === runUrl) - } + if (phase === 'failure') return publishCheck(api, pull, 'failure', 'Approval setup or evaluation failed.', runUrl) throw new Error('invalid approval setup phase') } @@ -474,7 +471,7 @@ async function reviewerPermission(api, repository, login) { // One decision per head keeps branch rules and CI watchers unambiguous; the head's existing GitHub // Actions run is updated and a run is created only when none exists. -async function publishCheck(api, pull, conclusion, title, runUrl, keep = () => false) { +async function publishCheck(api, pull, conclusion, title, runUrl) { requireHttpsRunUrl(runUrl) const body = { status: 'completed', conclusion, details_url: runUrl, output: { title, summary: `${title} [Workflow run](${runUrl})` } } const runs = await decisionRuns(api, pull) @@ -482,10 +479,9 @@ async function publishCheck(api, pull, conclusion, title, runUrl, keep = () => f await api(`/repos/${pull.repository}/check-runs`, { method: 'POST', body: { name: CHECK_NAME, head_sha: pull.headSha, ...body } }) return } - for (const run of runs) { - if (!Number.isSafeInteger(run.id)) throw new Error('check run has no valid id') - if (keep(run)) continue - await api(`/repos/${pull.repository}/check-runs/${run.id}`, { method: 'PATCH', body }) + for (const { id } of runs) { + if (!Number.isSafeInteger(id)) throw new Error('check run has no valid id') + await api(`/repos/${pull.repository}/check-runs/${id}`, { method: 'PATCH', body }) } } @@ -505,7 +501,8 @@ async function revokeApproval(api, pull, runUrl) { } for (const { id, conclusion } of runs) { if (!Number.isSafeInteger(id)) throw new Error('check run has no valid id') - // Only a published success satisfies the branch rule; every other conclusion already blocks it. + // This publisher concludes a run success only when the policy is met, so a published success is + // the only conclusion here that satisfies the branch rule; it never writes neutral or skipped. if (conclusion !== 'success') continue await api(`/repos/${pull.repository}/check-runs/${id}`, { method: 'PATCH', @@ -524,6 +521,12 @@ async function decisionRuns(api, pull) { return listed.check_runs.filter(run => isRecord(run) && isRecord(run.app) && run.app.slug === 'github-actions') } +// The setup-failure step reads this output so it replaces a conclusion only when this run did not +// already publish one; a cancelled run cannot rely on the step outcome for that distinction. +function recordConclusion(conclusion) { + if (process.env.GITHUB_OUTPUT) appendFileSync(process.env.GITHUB_OUTPUT, `concluded=${conclusion}\n`) +} + function requireHttpsRunUrl(runUrl) { if (!/^https:\/\/[^\s]+$/u.test(runUrl)) throw new Error('workflow run URL must use HTTPS') } diff --git a/.github/review-ownership/check-approval.test.mjs b/.github/review-ownership/check-approval.test.mjs index f406b03dc4..ab279b7e62 100644 --- a/.github/review-ownership/check-approval.test.mjs +++ b/.github/review-ownership/check-approval.test.mjs @@ -1,5 +1,7 @@ import assert from 'node:assert/strict' -import { readFileSync } from 'node:fs' +import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs' +import { tmpdir } from 'node:os' +import { join } from 'node:path' import test from 'node:test' import { @@ -531,24 +533,42 @@ test('updates the head\'s GitHub Actions decision check run instead of creating } }) -test('setup failure keeps the evaluation failure title published for the same run', async () => { - const calls = [] - const checkRuns = [ - { id: 7, app: { slug: 'github-actions' }, conclusion: 'failure', details_url: 'https://github.example/run/1' }, - { id: 8, app: { slug: 'github-actions' }, conclusion: 'failure', details_url: 'https://github.example/old-run' }, - { id: 9, app: { slug: 'github-actions' }, conclusion: 'action_required', details_url: 'https://github.example/run/1' }, - ] - await publishApprovalPhase({ - event: pullRequestEvent(), runUrl: 'https://github.example/run/1', phase: 'failure', - api: async (path, options) => { - if (!isCheckRunsList(path)) calls.push({ path, method: options?.method }) - return isCheckRunsList(path) ? { total_count: checkRuns.length, check_runs: checkRuns } : {} - }, - }) - assert.deepEqual(calls, [ - { path: '/repos/deepseek-harness/deepseek-harness/check-runs/8', method: 'PATCH' }, - { path: '/repos/deepseek-harness/deepseek-harness/check-runs/9', method: 'PATCH' }, - ]) +test('records the concluded decision so the failure step never replaces it', async () => { + const directory = mkdtempSync(join(tmpdir(), 'approval-output-')) + const output = join(directory, 'output') + writeFileSync(output, '') + const previous = process.env.GITHUB_OUTPUT + process.env.GITHUB_OUTPUT = output + const api = async (path, options) => { + if (isCheckRunsList(path)) return emptyCheckRuns + if (path.includes('/reviews?')) return [review('turtle2099', 'APPROVED')] + if (path.includes('/permission')) return { permission: 'write' } + if (path.includes('/comments?')) return [] + assert.match(path, /\/check-runs$/u) + return {} + } + try { + await runApprovalCheck({ + event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/1', api, write: () => {}, + }) + assert.match(readFileSync(output, 'utf8'), /^concluded=success$/mu) + writeFileSync(output, '') + await assert.rejects(runApprovalCheck({ + event: pullRequestEvent(), policySource, runUrl: 'https://github.example/run/2', write: () => {}, + getMergedCount: async () => { throw new Error('history unavailable') }, + api: async path => { + if (isCheckRunsList(path)) return emptyCheckRuns + if (path.includes('/reviews?')) return [] + if (path.includes('/comments?')) return [] + return {} + }, + }), /history unavailable/u) + assert.match(readFileSync(output, 'utf8'), /^concluded=failure$/mu) + } finally { + if (previous === undefined) delete process.env.GITHUB_OUTPUT + else process.env.GITHUB_OUTPUT = previous + rmSync(directory, { recursive: true, force: true }) + } }) for (const [mergedCount, credit] of [[0, 0], [1, 0.011], [50, 0.55], [99, 1.089], [100, 1.1], [101, 1.1], [200, 1.1]]) { diff --git a/.github/workflows/weighted-approval.yml b/.github/workflows/weighted-approval.yml index f72ada5942..e406d5ee2a 100644 --- a/.github/workflows/weighted-approval.yml +++ b/.github/workflows/weighted-approval.yml @@ -59,10 +59,11 @@ jobs: GITHUB_TOKEN: ${{ github.token }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} run: node .github/review-ownership/check-approval.mjs - # A failed or cancelled run must not leave an earlier conclusion standing. A job cancelled - # after the decision was published keeps that decision instead of overwriting it with failure. + # A failed or cancelled run must not leave an earlier conclusion standing. The publish step + # records the conclusion it wrote, so a decision already published survives a later failure or + # cancellation, and a cancelled publish step that wrote nothing still concludes failure. - name: Publish approval setup failure - if: failure() || (cancelled() && steps.publish.outcome != 'success') + if: (failure() || cancelled()) && steps.publish.outputs.concluded == '' env: GITHUB_TOKEN: ${{ github.token }} GITHUB_RUN_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }} diff --git a/scripts/ci-workflow.spec.ts b/scripts/ci-workflow.spec.ts index fc20391e2a..9c0ffc9d2d 100644 --- a/scripts/ci-workflow.spec.ts +++ b/scripts/ci-workflow.spec.ts @@ -1304,7 +1304,7 @@ describe('Weighted approval workflow', () => { expect(revokeIndex).toBeLessThan(setupIndex) expect(steps[revokeIndex]?.run).toBe('node .github/review-ownership/check-approval.mjs revoke') expect(steps.at(-1)).toMatchObject({ - if: "failure() || (cancelled() && steps.publish.outcome != 'success')", + if: "(failure() || cancelled()) && steps.publish.outputs.concluded == ''", run: 'node .github/review-ownership/check-approval.mjs failure', }) const pythonJob = workflowJob(loadWorkflow('.github/workflows/ci.yml'), 'python-sdk') From 7bc928c4498319774479d478f706946ebd567ae5 Mon Sep 17 00:00:00 2001 From: EricMoin Date: Tue, 20 Oct 2026 15:06:37 +0800 Subject: [PATCH 08/10] fix(web): strengthen install icon validation --- ...install-icons-and-launch-display.i18n.yaml | 14 +-- ...13-web-install-icons-and-launch-display.md | 8 +- ...web-install-icons-and-launch-display.zh.md | 10 +- apps/web/scripts/render-app-icons.ts | 13 +- apps/web/tests/app-icons.spec.ts | 8 +- apps/web/tests/pwa-manifest.e2e.ts | 118 +++++++++++------- packages/host/frontend-static/src/index.ts | 4 +- 7 files changed, 105 insertions(+), 70 deletions(-) diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml index ecfdefd930..6b05794b2c 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.i18n.yaml @@ -7,13 +7,13 @@ zh: 1af39a382e1a8b6a /agent-note-web-install-icons-and-launch-display/problem: en: 726b732fcaf49153 - zh: 74f854807a14409d + zh: b04aad2d0a65a42d /agent-note-web-install-icons-and-launch-display/decision: - en: e5f6dbce09b698ca - zh: 7e4e5e4f25b698f8 + en: 65c6eb6a3ad5ab30 + zh: 46ca48685f38269c /agent-note-web-install-icons-and-launch-display/alternatives-considered: - en: 49f105c70f8453c0 - zh: aed4457cfe9d41df + en: 588d709bd7c8c95b + zh: 871b618b86247926 /agent-note-web-install-icons-and-launch-display/consequences: - en: afef3bbd1a9ef108 - zh: 6d5250eaa4ded3f7 + en: 79695897b5237603 + zh: b61304c9527e8dfc diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md index 4a257efcbd..69ea6b05ae 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.md @@ -12,7 +12,7 @@ A launcher masks the icon it puts on the home screen and chooses that icon itsel `apps/web/public/` carries four install icons: a 192px and a 512px `any` raster, a 512px `maskable` raster, and a 180px `apple-touch-icon`. [`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) renders all four from the official application tile in `apps/desktop/resources/icon.svg`, and `pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` regenerates the committed files. Each variant preserves the whale's geometry, gradients, opacity, and filters. Rendering first produces a bitmap at no less than the 1024px source extent and four times the output edge, then reduces it with Lanczos3. This separate rasterization retains the original shading while smoothing the small icon's curves. Every variant crops the tile's desktop drop shadow. The maskable variant scales the whale about the tile centre by `MASKABLE_ARTWORK_SCALE` while the tile's gradient keeps its own extent, so the border a mask may crop is still the real tile rather than a flat colour meeting it at a seam. -[`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) uses the browser image decoder to measure the committed maskable whale's ink radius and fails when it leaves the circle a platform mask is guaranteed to show; the unscaled rendering is its negative control, so the assertion cannot pass vacuously. +[`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) uses the browser image decoder to measure the committed maskable whale’s ink radius and rejects artwork outside the platform safe zone or smaller than the intended scale. The unscaled tile exceeds that zone and serves as the negative control. The manifest requests `display: "standalone"`, names `#ffffff` as its `theme_color` and `background_color`, and lists the three rasters. `apps/web/index.html` adds the `apple-touch-icon` link, `mobile-web-app-capable`, and `apple-mobile-web-app-title`. It adds no `theme-color`: `ThemePresenter` owns the single such node and follows the resolved in-application theme, which a static media query cannot. The tab favicons are unchanged. @@ -20,7 +20,7 @@ This supersedes the icon entry and the display mode in the [archived Web install ## Alternatives considered -**Keep `favicon.svg` as the only icon.** It leaves iOS, which reads no manifest icon, with a generic tile, and the mark already reaches its own edges, so a launcher's mask crops it. The generator spec keeps that overflow as its negative control. +**Keep `favicon.svg` as the only icon.** It leaves iOS, which reads no manifest icon, with a generic tile, and the mark already reaches its own edges, so a launcher's mask crops it. `pwa-manifest.e2e.ts` keeps the unscaled tile’s overflow as its browser-pixel negative control. **Commit the rasters without a generator.** The tile artwork moves with the desktop application's icon. A hand-exported set drifts silently and records neither the crop nor the safe-zone scale that makes it installable. @@ -32,10 +32,10 @@ This supersedes the icon entry and the display mode in the [archived Web install ## Consequences -The repository carries four committed rasters, all rendered by one script from `apps/desktop/resources/icon.svg`. Changing the desktop tile means rerunning that script and reviewing the PNG diff. Unit tests inspect committed PNG dimensions without loading the native renderer. Browser tests check the committed maskable safe zone and opaque corners, with an unscaled SVG as the negative control. They also check fractional coverage along the Apple icon's curves against direct small-size SVG rendering, which retains the source's hard pixel edges. No automated check compares committed pixels with freshly generated output. +The repository carries four committed rasters, all rendered by one script from `apps/desktop/resources/icon.svg`. Changing the desktop tile means rerunning that script and reviewing the PNG diff. Unit tests inspect committed PNG dimensions without loading the native renderer. Browser tests execute the generator and decode its output and all four built PNGs, checking declared edges, opaque corners, and artwork extents against the source tile with a two-pixel rasterization tolerance. Maskable checks bound its artwork above and below, with the unscaled tile as the safe-zone negative control. Tests also compare fractional coverage along the Apple icon’s curves against direct small-size SVG rendering, which retains the source’s hard pixel edges. The manifest's `theme_color` is one value and cannot follow the in-application theme, so a user whose theme differs from the system scheme sees that value until `ThemePresenter` writes the synchronized node shortly after boot. The presenter stays the only owner of `meta[name="theme-color"]`: a second static node wins document order and desynchronizes surrounding browser chrome from the rendered palette, which `settings-chrome.e2e.ts` pins. iOS ignores `background_color` and masks the touch icon with its own rounded rectangle; the tile's own margins keep the whale clear of that mask. Android selects the maskable raster for the home screen and an `any` raster for the install surface. The install icons are not a service worker: an installed Web App still needs the Host reachable. -Verification covers each declared edge, the maskable safe zone with its negative control, opaque maskable corners, and the built `dist/` shipping every icon the manifest declares at the size it declares. Real-device installation, launch display, and window mode remain a device check. +Real-device installation, launch display, and window mode remain a device check. diff --git a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md index 35bde3b363..dbb77d24dc 100644 --- a/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md +++ b/.agents/notes/implemented/feature/2026-10-13-web-install-icons-and-launch-display.zh.md @@ -6,13 +6,13 @@ Status: implemented ## Problem -启动器会自行选择放到桌面上的图标,并对它套用蒙层。安装 manifest 使用的却是鲸鱼 favicon:一个铺满整张画布、背景透明的 SVG 标记,既没有底板,也没有自己的背景。交给蒙层后,标记会丢掉尾鳍,并且以错误的比充满图标。iOS 完全不读 manifest 图标,此前又没有 `apple-touch-icon` 可回退,因此安装后的 web app 只显示通用图标。manifest 还请求了 `display: "fullscreen"`,这在 Android 上会同时移除两条系统栏,让一个对话界面失去时钟、电量和导航。 +启动器会自行选择放到桌面上的图标,并对它套用蒙层。安装 manifest 使用的却是鲸鱼 favicon:一个铺满整张画布、背景透明的 SVG 标记,既没有底板,也没有自己的背景。交给蒙层后,标记会丢掉尾鳍,并且以错误的比例填满图标。iOS 完全不读 manifest 图标,此前又没有 `apple-touch-icon` 可回退,因此安装后的 web app 只显示通用图标。manifest 还请求了 `display: "fullscreen"`,这在 Android 上会同时移除两条系统栏,让一个对话界面失去时钟、电量和导航。 ## Decision `apps/web/public/` 承载四个安装图标:192px 与 512px 的 `any` 位图、512px 的 `maskable` 位图,以及 180px 的 `apple-touch-icon`。[`render-app-icons.ts`](../../../../apps/web/scripts/render-app-icons.ts) 从 `apps/desktop/resources/icon.svg` 的官方应用底板渲染这四个文件,`pnpm --filter @deepseek-ai/dsh-web-frontend run render:app-icons` 重新生成已提交的文件。每个变体都保留鲸鱼的几何路径、渐变、透明度和滤镜。渲染先生成不小于源图 1024px、且边长至少为输出尺寸四倍的位图,再以 Lanczos3 下采样。分开的栅格化步骤在平滑小图标曲线的同时保留原有明暗细节。每个变体都会裁掉底板的桌面投影。maskable 变体围绕底板中心按 `MASKABLE_ARTWORK_SCALE` 缩放鲸鱼,而底板渐变保持自身范围,因此可能被蒙层裁掉的那圈边框仍是真实底板,而不是与其相接的纯色。 -[`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) 使用浏览器图像解码器测量已提交 maskable 图标的鲸鱼墨迹半径,一旦它越出平台蒙层保证显示的范围就失败;未缩放的渲染结果是它的反向对照,因此该断言不可能空泛通过。 +[`pwa-manifest.e2e.ts`](../../../../apps/web/tests/pwa-manifest.e2e.ts) 使用浏览器图像解码器测量已提交 maskable 图标的鲸鱼墨迹半径,拒绝越出平台安全区或小于预期比例的图案。未缩放图块超出该安全区,用作反向对照。 manifest 请求 `display: "standalone"`,把 `theme_color` 与 `background_color` 定为 `#ffffff`,并列出三个位图。`apps/web/index.html` 增加 `apple-touch-icon` 链接、`mobile-web-app-capable` 与 `apple-mobile-web-app-title`。它不添加 `theme-color`:该节点由 `ThemePresenter` 独占并跟随解析后的应用内主题,这是静态媒体查询做不到的。标签页 favicon 保持不变。 @@ -20,7 +20,7 @@ manifest 请求 `display: "standalone"`,把 `theme_color` 与 `background_colo ## Alternatives considered -**只保留 `favicon.svg` 作为图标。** 这会让不读 manifest 图标的 iOS 只显示通用图标,而该标记已经抵达自身边界,启动器的蒙层必然裁切它。生成器 spec 把这一越界保留为反向对照。 +**只保留 `favicon.svg` 作为图标。** 这会让不读 manifest 图标的 iOS 只显示通用图标,而该标记已经抵达自身边界,启动器的蒙层必然裁切它。`pwa-manifest.e2e.ts` 把未缩放图块的越界保留为浏览器像素反向对照。 **不使用生成器,直接提交位图。** 底板素材随桌面应用图标变化。手工导出的图标集会静默漂移,也无法记录使其可安装的裁剪方式与安全区缩放。 @@ -32,10 +32,10 @@ manifest 请求 `display: "standalone"`,把 `theme_color` 与 `background_colo ## Consequences -仓库携带四张已提交的栅格图,均由同一个脚本从 `apps/desktop/resources/icon.svg` 渲染。桌面图块变化时,需要重新运行该脚本并审查 PNG diff。单元测试直接检查已提交 PNG 的尺寸,不加载原生渲染器。浏览器测试检查已提交 maskable 图标的安全区和不透明角落,并以未缩放 SVG 作为反向对照。测试也将 Apple 图标曲线边缘的部分像素覆盖与直接小尺寸渲染的 SVG 对比;后者保留源图的硬像素边缘。没有自动检查将已提交像素与重新生成的结果进行比较。 +仓库携带四张已提交的栅格图,均由同一个脚本从 `apps/desktop/resources/icon.svg` 渲染。桌面图块变化时,需要重新运行该脚本并审查 PNG diff。单元测试直接检查已提交 PNG 的尺寸,不加载原生渲染器。浏览器测试执行生成器,并解码生成结果和全部四张构建后的 PNG,以两个像素的栅格化容差检查声明边长、不透明角落及相对于源图块的图案范围。maskable 检查同时限制图案范围的上界和下界,并以未缩放图块作为安全区反向对照。测试也将 Apple 图标曲线边缘的部分像素覆盖与直接小尺寸渲染的 SVG 对比;后者保留源图的硬像素边缘。 manifest 的 `theme_color` 是单个值,无法跟随应用内主题;因此当用户选择的主题与系统方案不同时,该值会一直生效,直到 `ThemePresenter` 在启动后不久写入同步节点。该 presenter 仍是 `meta[name="theme-color"]` 的唯一所有者:第二个静态节点会在文档顺序上胜出,使周边浏览器界面与已渲染的配色脱节,这一点由 `settings-chrome.e2e.ts` 固定。 iOS 忽略 `background_color`,并用自己的圆角矩形裁切触摸图标;底板自带的留白使鲸鱼避开该蒙层。Android 为桌面选择 maskable 位图,为安装界面选择 `any` 位图。安装图标不是 service worker:安装后的 Web App 仍要求 Host 可达。 -验证覆盖每个声明的边长、maskable 安全区及其反向对照、maskable 四角的不透明,以及构建出的 `dist/` 确实按 manifest 声明的尺寸承载每个图标。真机安装、启动显示与窗口模式仍属设备检查。 +真机安装、启动显示与窗口模式仍属设备检查。 diff --git a/apps/web/scripts/render-app-icons.ts b/apps/web/scripts/render-app-icons.ts index 9d09f55498..443b53121f 100644 --- a/apps/web/scripts/render-app-icons.ts +++ b/apps/web/scripts/render-app-icons.ts @@ -23,6 +23,7 @@ import { readFile, writeFile } from 'node:fs/promises' import { resolve } from 'node:path' import { fileURLToPath } from 'node:url' +import { assertNever } from '@deepseek-ai/dsh-util-values' /** Vector source owned by the desktop application; 1104 units square with the tile inset. */ export const TILE_SOURCE = fileURLToPath(new URL('../../desktop/resources/icon.svg', import.meta.url)) @@ -31,7 +32,7 @@ export const TILE_SOURCE = fileURLToPath(new URL('../../desktop/resources/icon.s export const ICON_OUTPUT_DIRECTORY = fileURLToPath(new URL('../public/', import.meta.url)) /** Tile edge in source units; every render density is expressed against it. */ -export const TILE_EDGE = 1024 +const TILE_EDGE = 1024 /** Tile inset in the source canvas: the drop shadow occupies the surrounding band. */ const TILE_INSET = { x: 40, y: 32 } as const @@ -154,6 +155,14 @@ async function renderPng(document: string, size: number): Promise { .toBuffer() } +function documentForVariant(source: string, kind: AppIconVariant['kind']): string { + switch (kind) { + case 'tile': return tileDocument(source) + case 'maskable': return maskableDocument(source) + default: return assertNever(kind) + } +} + /** * Render every install icon from the tile source. * @param source - complete `icon.svg` document. @@ -163,7 +172,7 @@ export async function renderAppIcons(source: string): Promise ({ variant, png: await renderPng( - variant.kind === 'maskable' ? maskableDocument(source) : tileDocument(source), + documentForVariant(source, variant.kind), variant.size, ), }))) diff --git a/apps/web/tests/app-icons.spec.ts b/apps/web/tests/app-icons.spec.ts index e0e3d388f4..3dac243c68 100644 --- a/apps/web/tests/app-icons.spec.ts +++ b/apps/web/tests/app-icons.spec.ts @@ -22,7 +22,7 @@ describe('web install icons', () => { const maskable = maskableDocument(source) expect(tile).toContain('viewBox="40 32 1024 1024"') expect(tile).not.toContain('') - expect(tile).not.toContain('scale(0.82)') + expect(tile).not.toContain(`scale(${MASKABLE_ARTWORK_SCALE})`) expect(tile).toContain('') expect(tile).toContain(source.match(/]*>/u)![0]) expect(tile).toContain(source.match(/[\s\S]*<\/defs>/u)![0]) @@ -35,7 +35,9 @@ describe('web install icons', () => { }) it('fails loudly on a source without the tile it transforms', () => { - expect(() => tileDocument('')).toThrow(/tile source/) - expect(MASKABLE_ARTWORK_SCALE).toBeLessThan(1) + expect(() => tileDocument('')).toThrow(/no root element/) + expect(() => tileDocument('')).toThrow(/no shadowed tile group/) + expect(() => maskableDocument('')) + .toThrow(/no artwork group/) }) }) diff --git a/apps/web/tests/pwa-manifest.e2e.ts b/apps/web/tests/pwa-manifest.e2e.ts index 255deb0833..851ae40341 100644 --- a/apps/web/tests/pwa-manifest.e2e.ts +++ b/apps/web/tests/pwa-manifest.e2e.ts @@ -2,18 +2,47 @@ import { readFile } from 'node:fs/promises' import { fileURLToPath } from 'node:url' import { join } from 'node:path' import { expect, it } from 'vitest' -import { chromium } from 'playwright' -import { pngSize } from './png-size.ts' -import { ICON_OUTPUT_DIRECTORY, TILE_SOURCE, tileDocument } from '../scripts/render-app-icons.ts' +import { chromium, type Page } from 'playwright' +import { + APP_ICON_VARIANTS, ICON_OUTPUT_DIRECTORY, MASKABLE_ARTWORK_SCALE, MASKABLE_SAFE_RADIUS_RATIO, + TILE_SOURCE, renderAppIcons, tileDocument, +} from '../scripts/render-app-icons.ts' const DIST_ROOT = fileURLToPath(new URL('../dist', import.meta.url)) -/** One install icon the manifest declares. */ -interface ManifestIcon { - readonly src: string - readonly sizes: string - readonly type: string - readonly purpose: string +async function pixels(page: Page, url: string, size: number) { + return page.evaluate(async ({ url, size }) => { + const image = new Image() + image.src = url + await image.decode() + const canvas = document.createElement('canvas') + canvas.width = canvas.height = size + const context = canvas.getContext('2d')! + context.drawImage(image, 0, 0, size, size) + const { data } = context.getImageData(0, 0, size, size) + let radius = 0 + let ink = 0 + for (let y = 0; y < size; y += 1) { + for (let x = 0; x < size; x += 1) { + const at = (y * size + x) * 4 + if (data[at + 3] === 0 || data[at]! + data[at + 1]! + data[at + 2]! >= 384) continue + ink += 1 + radius = Math.max(radius, Math.hypot(x - (size - 1) / 2, y - (size - 1) / 2)) + } + } + return { + width: image.naturalWidth, height: image.naturalHeight, radius, ink, + corners: [0, size - 1, (size - 1) * size, size * size - 1].map(index => data[index * 4 + 3]), + } + }, { url, size }) +} + +function pngUrl(png: Buffer): string { + return `data:image/png;base64,${png.toString('base64')}` +} + +function svgUrl(svg: string): string { + return `data:image/svg+xml;base64,${Buffer.from(svg).toString('base64')}` } it('ships install metadata with the built web application', async () => { @@ -50,19 +79,33 @@ it('ships install metadata with the built web application', async () => { }) }) -it('ships every declared install icon as a raster of its declared size', async () => { - // A launcher has no fallback: an icon the manifest names but the build does - // not carry is an install that silently shows nothing. - const manifest = JSON.parse( - await readFile(join(DIST_ROOT, 'manifest.webmanifest'), 'utf8'), - ) as { icons: ManifestIcon[] } - expect(manifest.icons).not.toHaveLength(0) - for (const icon of manifest.icons) { - const [edge] = icon.sizes.split('x') - expect(pngSize(await readFile(join(DIST_ROOT, icon.src)))) - .toEqual([Number(edge), Number(edge)]) - } - expect(pngSize(await readFile(join(DIST_ROOT, 'apple-touch-icon.png')))).toEqual([180, 180]) +it('decodes every generated and shipped icon with its full artwork and opaque corners', async () => { + const browser = await chromium.launch({ headless: true }) + try { + const page = await browser.newPage() + const source = await readFile(TILE_SOURCE, 'utf8') + const generated = await renderAppIcons(source) + const manifest = JSON.parse(await readFile(join(DIST_ROOT, 'manifest.webmanifest'), 'utf8')) as { + icons: readonly { src: string; sizes: string }[] + } + const declared = [...manifest.icons, { src: 'apple-touch-icon.png', sizes: '180x180' }] + expect(generated.map(({ variant }) => [variant.file, `${variant.size}x${variant.size}`])) + .toEqual(declared.map(icon => [icon.src, icon.sizes])) + for (const { variant, png } of generated) { + const reference = await pixels(page, svgUrl(tileDocument(source)), variant.size) + expect(reference.ink).toBeGreaterThan(0) + const scale = variant.kind === 'maskable' ? MASKABLE_ARTWORK_SCALE : 1 + for (const bytes of [png, await readFile(join(DIST_ROOT, variant.file))]) { + const image = await pixels(page, pngUrl(bytes), variant.size) + expect([image.width, image.height], variant.file).toEqual([variant.size, variant.size]) + expect(image.corners, variant.file).toEqual([255, 255, 255, 255]) + expect(image.ink, variant.file).toBeGreaterThan(0) + // Two pixels allow rasterizer and downsampling differences without + // accepting missing or undersized artwork in the published PNG. + expect(Math.abs(image.radius - reference.radius * scale), variant.file).toBeLessThan(2) + } + } + } finally { await browser.close() } }) it('ships fixed-color favicons selected by document media queries', async () => { @@ -122,30 +165,13 @@ it('keeps the shipped maskable artwork inside its opaque platform safe zone', as const page = await browser.newPage() const maskable = await readFile(join(DIST_ROOT, 'icon-maskable-512.png')) const tile = tileDocument(await readFile(TILE_SOURCE, 'utf8')) - const [scaled, unscaled] = await page.evaluate(async (sources) => { - return Promise.all(sources.map(async (source) => { - const image = new Image() - image.src = source - await image.decode() - const canvas = document.createElement('canvas') - canvas.width = canvas.height = 512 - const context = canvas.getContext('2d')! - context.drawImage(image, 0, 0, 512, 512) - const { data } = context.getImageData(0, 0, 512, 512) - let radius = 0 - for (let y = 0; y < 512; y += 1) { - for (let x = 0; x < 512; x += 1) { - const at = (y * 512 + x) * 4 - if (data[at + 3] === 0 || data[at]! + data[at + 1]! + data[at + 2]! >= 384) continue - radius = Math.max(radius, Math.hypot(x - 255.5, y - 255.5)) - } - } - return { radius, corners: [0, 511, 511 * 512, 512 * 512 - 1].map(index => data[index * 4 + 3]) } - })) - }, [`data:image/png;base64,${maskable.toString('base64')}`, `data:image/svg+xml;base64,${Buffer.from(tile).toString('base64')}`]) - expect(scaled!.radius).toBeLessThanOrEqual(0.4 * 512) - expect(scaled!.corners).toEqual([255, 255, 255, 255]) - expect(unscaled!.radius).toBeGreaterThan(0.4 * 512) + const size = APP_ICON_VARIANTS.find(variant => variant.kind === 'maskable')!.size + const scaled = await pixels(page, pngUrl(maskable), size) + const unscaled = await pixels(page, svgUrl(tile), size) + expect(scaled.radius).toBeLessThanOrEqual(MASKABLE_SAFE_RADIUS_RATIO * size) + expect(scaled.radius).toBeGreaterThan(unscaled.radius * MASKABLE_ARTWORK_SCALE - 2) + expect(scaled.corners).toEqual([255, 255, 255, 255]) + expect(unscaled.radius).toBeGreaterThan(MASKABLE_SAFE_RADIUS_RATIO * size) } finally { await browser.close() } diff --git a/packages/host/frontend-static/src/index.ts b/packages/host/frontend-static/src/index.ts index 2a9483c8bd..71ccdd127c 100644 --- a/packages/host/frontend-static/src/index.ts +++ b/packages/host/frontend-static/src/index.ts @@ -42,9 +42,7 @@ const MIME: Record = { '.html': HTML_MIME, '.js': 'text/javascript; charset=utf-8', '.css': 'text/css; charset=utf-8', - // The install icons are the only images the shell serves directly. A launcher - // reads them through a Content-Type that names an image, so the entries the - // Web build emits are declared here rather than left to octet-stream. + // Launchers require an image Content-Type for the install icons. '.png': 'image/png', '.svg': 'image/svg+xml', '.json': 'application/json', From 5ff29190ed8e2bba8211035375ba7c733b332879 Mon Sep 17 00:00:00 2001 From: Yifffan <133629972+Yifffan@users.noreply.github.com> Date: Tue, 20 Oct 2026 15:17:13 +0800 Subject: [PATCH 09/10] feat(ui): add navigation entry customization (#6247) * fix(ui): avoid tooltip trigger overlap when a side fits * feat(ui): add navigation entry customization * fix(test): keep tooltip geometry independent of JSX types * fix(ui): align customization catalogs and integration fixtures * test(ui): complete customization lifecycle and release integration coverage * fix(ui): return entry discovery to the installed plugin list * docs: refresh module graph for entry customization * build: refresh workspace links for the moved entry customization package * docs: regenerate catalogs and pairing hashes for the moved entry customization package * test(web): cover default-loaded entry customization in the Web e2e * test(ui): describe entry customization as a browser plugin in the host entry spec * build: order entry customization publication files as workspace constraints require * test(release): drop the moved entry customization package from the experimental family list * style(ui): use the shared focus ring color on entry customization triggers * test(web): re-record goldens for the default-loaded More navigation menus * test(web): add the More conversation views trigger to the remaining CI-reported goldens * docs: record region-owned pins for plugin entry customization * refactor(ui): rebuild entry customization on region-owned pins and More menus - Each navigation region (left sidebar, Conversation header, right sidebar guide) persists its own unpinned list under dsh.slot.manual-order..v1, filters its own launchers, and draws its own More button and Menu around its actions slot. - ui-entry-customization only registers the shared menu rows (entries with pin buttons, Browse other plugins, Create your plugin) into the three actions slots and keeps no state. - Remove ctx.layout.entryVisibility, the plugin store, left-sidebar drag reordering, and agentPresetNavigation; ui-layout and ui-agent-preset match release/dsh-0.2.1 again. - Keep the release

;(p}* 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 literal 41757 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