From 501b661e2e75487fe6e2eb632581f2ea5cb4732b Mon Sep 17 00:00:00 2001 From: DeliciousBuding Date: Thu, 3 Sep 2026 14:49:51 +0800 Subject: [PATCH 1/2] =?UTF-8?q?fix(ci):=20=E4=B8=89=E4=B8=AA=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=20lint=20step=20=E9=87=8C=E6=9C=89=E4=B8=A4=E4=B8=AA?= =?UTF-8?q?=E3=80=8C=E7=BB=93=E6=9E=84=E4=B8=8A=E4=B8=8D=E5=8F=AF=E8=83=BD?= =?UTF-8?q?=E8=AE=A9=20CI=20=E5=8F=98=E7=BA=A2=E3=80=8D=E2=80=94=E2=80=94?= =?UTF-8?q?=E6=8A=8A=E8=83=BD=E8=AF=81=E6=98=8E=E7=BB=BF=E7=9A=84=E7=BF=BB?= =?UTF-8?q?=E6=88=90=E7=9C=9F=E9=97=A8=E7=A6=81=EF=BC=8C=E5=B9=B6=E6=8A=8A?= =?UTF-8?q?=E4=B8=8D=E8=83=BD=E7=BF=BB=E7=9A=84=E9=82=A3=E4=B8=AA=E7=9A=84?= =?UTF-8?q?=E5=8E=9F=E5=9B=A0=E5=86=99=E8=BF=9B=20YAML=20=E8=80=8C?= =?UTF-8?q?=E4=B8=8D=E6=98=AF=E7=95=99=E4=B8=80=E4=B8=AA=E8=A3=B8=20flag?= =?UTF-8?q?=E3=80=82web=20=E7=9A=84=206=20=E4=B8=AA=20no-explicit-any=20?= =?UTF-8?q?=E5=85=A8=E9=83=A8=E6=B6=88=E9=99=A4=EF=BC=88=E5=90=8C=E4=B8=80?= =?UTF-8?q?=E4=B8=AA=E6=89=8B=E6=8A=84=E7=AD=BE=E5=90=8D=20`(key:=20string?= =?UTF-8?q?,=20options=3F:=20any)=20=3D>=20string`=20=E5=9C=A8=20web=206?= =?UTF-8?q?=20=E5=A4=84=20+=20desktop=206=20=E5=A4=84=E5=85=B1=2012=20?= =?UTF-8?q?=E4=BB=BD=EF=BC=9Bweb=20=E7=9A=84=205=20=E5=A4=84=E7=9B=B4?= =?UTF-8?q?=E6=8E=A5=E5=88=A0=E6=8E=89=E4=BB=8E=E6=9C=AA=E8=A2=AB=E4=BC=A0?= =?UTF-8?q?=E8=BF=87=E7=9A=84=20options=20=E5=8F=82=E6=95=B0=E3=80=811=20?= =?UTF-8?q?=E5=A4=84=20createDefaultAgentProfileRequest=20=E7=9C=9F?= =?UTF-8?q?=E7=9A=84=E6=8F=92=E5=80=BC=E6=95=85=E4=BF=9D=E7=95=99=E4=BD=86?= =?UTF-8?q?=E6=94=B9=E4=B8=BA=20Record=EF=BC=89?= =?UTF-8?q?=EF=BC=8Cdesktop=206=20=E5=A4=84=E5=88=BB=E6=84=8F=E4=B8=8D?= =?UTF-8?q?=E5=8A=A8=EF=BC=9A=E5=AE=83=E7=9A=84=E8=B0=83=E7=94=A8=E6=96=B9?= =?UTF-8?q?=E4=BC=A0=E7=9A=84=E6=98=AF=20i18next=20=E8=87=AA=E5=B7=B1?= =?UTF-8?q?=E7=9A=84=20TFunction=EF=BC=8C=E5=8F=AA=E6=9C=89=20options=20?= =?UTF-8?q?=E4=B8=BA=20any=20=E6=97=B6=E6=89=8D=E8=B5=8B=E5=BE=97=E4=B8=8A?= =?UTF-8?q?=EF=BC=88=E6=94=B6=E7=AA=84=E4=BC=9A=E5=9C=A8=20App.tsx:265=20?= =?UTF-8?q?=E6=8A=A5=20TS2345=EF=BC=8C=E5=AE=9E=E6=B5=8B=EF=BC=89=EF=BC=8C?= =?UTF-8?q?=E4=B8=94=20desktop=20=E7=9A=84=20eslint=20=E9=85=8D=E7=BD=AE?= =?UTF-8?q?=E6=A0=B9=E6=9C=AC=E6=B2=A1=E5=BC=80=20no-explicit-any=20(#2274?= =?UTF-8?q?)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 病灶:`Lint Web` / `Lint (mobile rules)` / desktop `Lint` 三个 step 都挂着 `continue-on-error: true`,即无论多红都不会让 CI 失败。这就是 6 个 `no-explicit-any` 能长期活在 master 上的原因——不是没人跑 lint,是跑了也不算。 Go 侧同样的 report-only 有指纹棘轮当硬门(#1840),前端 eslint 什么硬门都没有。 按「能不能用 CI 的原命令证明它绿」逐个处置,而不是三个一起翻: | step | CI 原命令 | 实测 | 处置 | |---|---|---|---| | Lint Web | `pnpm lint` = `eslint src --max-warnings 0` | 改前 6 errors;改后 **0 problems** | **翻硬**(删 continue-on-error) | | Lint (mobile rules) | `pnpm --filter agenthub-mobile-rn lint` | **0 errors / 1 warning**,脚本不带 --max-warnings | **翻硬** | | desktop Lint | `pnpm lint --max-warnings 0` | **0 errors / 4 个 react-hooks/exhaustive-deps warning ⇒ exit 1** | **保持 advisory**,但把这条实测理由写进 YAML 注释 | desktop 不翻的原因是测出来的、不是习惯性的:那 4 条 warning 是 useMemo 依赖数组 缺 `t` / `tApp`,修它等于改记忆化依赖 ⇒ 行为变更,需要自己一个被 review 的切片, 不能塞进一个 lint 清理里。翻早了会让每个 desktop PR 恒红。 已把这条债务写进 #2251(CI 关键路径 frontend 半)。 消融过程中的两次自我纠错(都由编译器抓出,不是 grep): 1. 我先按「12 处 options 参数全是死表面」把它们全删了 —— `pnpm -r typecheck` 立刻红两处:web `agentQueries.ts:300` 的 `t?.('agents.newDefault', { index })` 与 desktop `documentQueries.ts:65,68,70` 的 `{ time }` / `{ month, day, time }` 真的在插值。⇒ 逐点修法:真插值的保留参数并给精确类型,其余删掉。 2. 给 desktop 的 `AppTranslate` 也换成 `Record` 后 typecheck 再红: `TFunction<"translation", undefined>` 赋不给它(TS2345 @ App.tsx:265)。 ⇒ desktop 6 处整体回退到 master(0 diff),并在 web 那处留下注释说明 **为什么两端不对称、以及"重新对称"会炸在哪里**,防止下一个人好心改回去。 刻意不做: - 不为这个回调类型新造 shared 跨包导出(`verify-shared-barrel` / `verify-shared-boundary` 两个门禁都盯着 shared 的导出面;一个一行的函数类型 别名不值得为它开一条跨包契约,那正是本 PR 要消的过度抽象方向)。 - 不动另外两个 advisory step(i18n callsites ratchet、cargo clippy):它们的 YAML 注释已写明是"已知债务在分批处理期间保持 advisory",翻硬会立刻恒红。 - 不动 edge golangci-lint action 的 continue-on-error(checks.yml:84):那一处 有完整注释说明硬门在指纹棘轮,属既定设计而非遗漏。 验证:`pnpm -r typecheck` 5 包全绿;`pnpm --filter agenthub-web test` 33 文件 / 268 测试全绿;web lint 用 CI 原命令 0 problems;mobile lint 用 CI 原命令 0 errors;desktop lint 用 CI 原命令 exit 1(与保留 advisory 的决定一致); `verify-ci-gates` ok(改 step 未破坏 job 结构)、`verify-doc-ssot` ok、 `verify-action-runtimes` ok;`git diff --check` 无空白错误。 Co-authored-by: Cursor --- .github/workflows/checks.yml | 17 +++++++++++++-- app/web/src/api/agentQueries.ts | 21 ++++++++++++++++--- app/web/src/platform/webPlatformMapping.ts | 2 +- app/web/src/platform/webWorkbenchProjects.ts | 2 +- .../src/platform/webWorkbenchProjectsPort.ts | 2 +- 5 files changed, 36 insertions(+), 8 deletions(-) diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 04c727ef1..0addc8cae 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1134,6 +1134,13 @@ jobs: - name: Type check run: pnpm typecheck + # Still advisory, and the reason is measured rather than habitual: this + # step runs `eslint src --max-warnings 0`, and desktop currently reports + # 0 errors but **4 `react-hooks/exhaustive-deps` warnings** (missing `t` / + # `tApp` in useMemo dependency arrays). Fixing those means changing + # memoization dependencies, which is a behaviour change, not a lint + # cleanup — so it needs its own reviewed slice. Removing + # continue-on-error before that would hard-fail every desktop PR (#2274). - name: Lint continue-on-error: true run: pnpm lint --max-warnings 0 @@ -1252,8 +1259,12 @@ jobs: - name: Install run: pnpm install --frozen-lockfile + # #2274: was `continue-on-error: true`, i.e. structurally incapable of + # failing — which is how six `no-explicit-any` errors lived on master + # unnoticed (all the same hand-copied `options?: any` translate signature). + # They are fixed in this PR, so the step is a real gate now: web's lint + # script is `eslint src --max-warnings 0` and reports 0 problems. - name: Lint Web - continue-on-error: true run: pnpm lint - name: Build Web @@ -1319,8 +1330,10 @@ jobs: - name: Type check (mobile) run: pnpm --filter agenthub-mobile-rn typecheck + # #2274: was `continue-on-error: true` (advisory). The exact CI command + # reports 0 errors / 1 react-hooks warning, and the mobile lint script does + # not pass --max-warnings, so this is a real gate now. - name: Lint (mobile rules) - continue-on-error: true run: pnpm --filter agenthub-mobile-rn lint # ── Hardened: Test with coverage reporting ── diff --git a/app/web/src/api/agentQueries.ts b/app/web/src/api/agentQueries.ts index c7150a919..58d2b3a22 100644 --- a/app/web/src/api/agentQueries.ts +++ b/app/web/src/api/agentQueries.ts @@ -237,7 +237,7 @@ export function mapHubAgentProfileToAgentInfo(profile: AgentProfile): AgentInfo }; } -export function agentConfigToCreateAgentProfileRequest(agent: AgentConfig, t?: (key: string, options?: any) => string): CreateAgentProfileRequest { +export function agentConfigToCreateAgentProfileRequest(agent: AgentConfig, t?: (key: string) => string): CreateAgentProfileRequest { const model = splitModelLabel(agent.model); const description = persistableAgentDescription(agent.role); const reasoningEffort = reasoningEffortFromMode(agent.mode); @@ -265,7 +265,7 @@ export function agentConfigToCreateAgentProfileRequest(agent: AgentConfig, t?: ( }; } -export function agentConfigToUpdateAgentProfileRequest(agent: AgentConfig, t?: (key: string, options?: any) => string): UpdateAgentProfileRequest { +export function agentConfigToUpdateAgentProfileRequest(agent: AgentConfig, t?: (key: string) => string): UpdateAgentProfileRequest { const model = splitModelLabel(agent.model); const description = persistableAgentDescription(agent.role); const runtimeID = optionalRuntimeInput(agent.engine); @@ -295,7 +295,22 @@ export function agentConfigToUpdateAgentProfileRequest(agent: AgentConfig, t?: ( }; } -export function createDefaultAgentProfileRequest(index: number, t?: (key: string, options?: any) => string): CreateAgentProfileRequest { +export function createDefaultAgentProfileRequest( + index: number, + // The only web mapper callback that actually interpolates — it calls + // t('agents.newDefault', { index }) — so it is the only one that keeps an + // options bag, and it is typed rather than `any` (which is what + // `eslint --max-warnings 0` in the web lint gate rejects). + // + // The other five web copies of this signature declared the same parameter and + // never passed anything, so they dropped it. Do not "re-symmetrise" this with + // desktop: desktop's six copies keep `options?: any` on purpose, because their + // callers hand in i18next's own `TFunction`, which is only assignable to a + // callback whose options parameter is `any` — narrowing it there fails + // typecheck with TS2345 at App.tsx:265 (measured, not guessed). desktop's + // eslint config does not enable no-explicit-any, so nothing flags it. + t?: (key: string, options?: Record) => string, +): CreateAgentProfileRequest { return { name: t?.('agents.newDefault', { index }) ?? `新 Agent ${index}`, runtime_id: 'codex', diff --git a/app/web/src/platform/webPlatformMapping.ts b/app/web/src/platform/webPlatformMapping.ts index de0efc7bd..80b0560b0 100644 --- a/app/web/src/platform/webPlatformMapping.ts +++ b/app/web/src/platform/webPlatformMapping.ts @@ -42,7 +42,7 @@ export function resolveWebWorkbenchAgents( return mapped.length > 0 ? mapped : webAgents; } -type WebTranslate = (key: string, options?: any) => string; +type WebTranslate = (key: string) => string; export function hubSessionToWorkbenchConversation(session: Session, t?: WebTranslate): WorkbenchConversation | null { const id = session.id ?? session.session_id; diff --git a/app/web/src/platform/webWorkbenchProjects.ts b/app/web/src/platform/webWorkbenchProjects.ts index cfbbb0817..dcbe33af9 100644 --- a/app/web/src/platform/webWorkbenchProjects.ts +++ b/app/web/src/platform/webWorkbenchProjects.ts @@ -44,7 +44,7 @@ export interface ParsedProjectThreadMessageContent { } | undefined; } -type WebTranslate = (key: string, options?: any) => string; +type WebTranslate = (key: string) => string; export function workspaceProjectToProjectInfo( project: WorkspaceProject, diff --git a/app/web/src/platform/webWorkbenchProjectsPort.ts b/app/web/src/platform/webWorkbenchProjectsPort.ts index 079cb0b83..558442ce0 100644 --- a/app/web/src/platform/webWorkbenchProjectsPort.ts +++ b/app/web/src/platform/webWorkbenchProjectsPort.ts @@ -12,7 +12,7 @@ import { projectDraftToHubRequest } from './webWorkbenchProjects'; * (#1546). Wraps the HubClient transport; the shared UI only ever sees the * narrow `WorkbenchProjectsPort` contract. */ -type WebTranslate = (key: string, options?: any) => string; +type WebTranslate = (key: string) => string; export function createWebWorkbenchProjectsPort(t?: WebTranslate): WorkbenchProjectsPort { const hubClient = createHubClient({ getToken: getAccessToken }); From 65f741de666e055371eed333317ec3c32d6fb1da Mon Sep 17 00:00:00 2001 From: DeliciousBuding Date: Thu, 3 Sep 2026 15:02:38 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(gov):=20=E9=94=80=E6=8E=89=E8=B4=A8?= =?UTF-8?q?=E9=87=8F=E5=80=BA=E5=9F=BA=E7=BA=BF=E9=87=8C=E4=B8=A4=E6=9D=A1?= =?UTF-8?q?=E5=B7=B2=E8=A2=AB=E6=9C=AC=20PR=20=E4=BF=AE=E5=A5=BD=E7=9A=84?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E8=BD=AF=E9=97=A8=E8=B1=81=E5=85=8D=E2=80=94?= =?UTF-8?q?=E2=80=94quality-debt=20=E6=A3=98=E8=BD=AE=E7=9A=84=20QDR-SOFT-?= =?UTF-8?q?GATE-ZOMBIE=20=E5=9C=A8=20CI=20=E4=B8=8A=E6=8A=93=E4=BD=8F?= =?UTF-8?q?=E4=BA=86=E6=88=91=EF=BC=9A=E6=91=98=E6=8E=89=20checks.yml=20?= =?UTF-8?q?=E7=9A=84=20continue-on-error=20=E5=8D=B4=E5=BF=98=E4=BA=86?= =?UTF-8?q?=E4=BB=8E=20quality-debt-baseline.json=20=E6=B3=A8=E9=94=80?= =?UTF-8?q?=E5=AF=B9=E5=BA=94=E6=9D=A1=E7=9B=AE=EF=BC=8C=E7=99=BB=E8=AE=B0?= =?UTF-8?q?=E5=9C=A8=E5=86=8C=E7=9A=84=E5=80=BA=E5=8A=A1=E4=B8=8E=E5=AE=9E?= =?UTF-8?q?=E5=86=B5=E5=88=86=E5=B2=94=E5=8D=B3=E7=BA=A2=E3=80=82=E8=BF=99?= =?UTF-8?q?=E4=B8=A4=E6=9D=A1=E7=99=BB=E8=AE=B0=E5=8E=9F=E5=9B=A0=E6=9C=AC?= =?UTF-8?q?=E5=B0=B1=E6=98=AF=E3=80=8Cadvisory=20while=20Wave=209-10=20des?= =?UTF-8?q?ign-system=20refactor=20settles=E3=80=8D=EF=BC=88introduced=5Fa?= =?UTF-8?q?t=202026-08-11=E3=80=81review=5Fby=202026-10-01=EF=BC=89?= =?UTF-8?q?=E5=8D=B3=E5=B8=A6=E5=A4=8D=E5=AE=A1=E6=9C=9F=E9=99=90=E7=9A=84?= =?UTF-8?q?=E4=B8=B4=E6=97=B6=E8=B1=81=E5=85=8D=EF=BC=8C=E9=94=80=E8=B4=A6?= =?UTF-8?q?=E6=AD=A3=E6=98=AF=E6=A3=98=E8=BD=AE=E8=A6=81=E7=9A=84=E7=BB=93?= =?UTF-8?q?=E5=B1=80=EF=BC=9B=E5=90=8C=E6=97=B6=E6=8A=8A=20desktop=20?= =?UTF-8?q?=E9=82=A3=E6=9D=A1=E7=9A=84=20reason=20=E4=BB=8E=E5=90=8C?= =?UTF-8?q?=E6=AC=BE=E5=90=AB=E7=B3=8A=E6=8E=AA=E8=BE=9E=E6=8D=A2=E6=88=90?= =?UTF-8?q?=E5=AE=9E=E6=B5=8B=E4=BA=8B=E5=AE=9E=EF=BC=88CI=20=E5=91=BD?= =?UTF-8?q?=E4=BB=A4=E5=B8=A6=20--max-warnings=200=E3=80=810=20errors=20?= =?UTF-8?q?=E4=BD=86=204=20=E4=B8=AA=20react-hooks/exhaustive-deps=20warni?= =?UTF-8?q?ng=20=E2=87=92=20exit=201=E3=80=81=E4=BF=AE=E5=AE=83=3D?= =?UTF-8?q?=E6=94=B9=E8=AE=B0=E5=BF=86=E5=8C=96=E4=BE=9D=E8=B5=96=E5=B1=9E?= =?UTF-8?q?=E8=A1=8C=E4=B8=BA=E5=8F=98=E6=9B=B4=E9=9C=80=E7=8B=AC=E7=AB=8B?= =?UTF-8?q?=E5=88=87=E7=89=87=EF=BC=89=EF=BC=8C=E5=B9=B6=E6=8A=8A=20issue?= =?UTF-8?q?=20=E4=BB=8E=201575=20=E6=94=B9=E6=8C=87=20#2251=20(#2274)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增耦合事实(写进本 commit 以便下次不再漏):**改 checks.yml 的 continue-on-error 必须同步改 scripts/verify/quality-debt-baseline.json 的 soft_gates,并跑 verify-quality-debt-ratchet.py**。本 PR 第一版只在本地跑了 ci-gates / doc-ssot / action-runtimes 三个与 checks.yml 相关的门禁,漏了这个 棘轮,于是 CI 的 validate 红了一轮。soft_gates 从 6 条降到 4 条: - 销账 2 条:frontend-web: Lint Web、frontend-mobile: Lint (mobile rules) (已翻硬,本 PR 用 CI 原命令证明 0 problems / 0 errors) - 保留 4 条:go-edge-static: Lint(硬门在指纹棘轮,既定设计)、 frontend-desktop: Lint(实测理由已更新,债务转 #2251)、 validate: Verify i18n callsites ratchet(已知 CJK 基线回归在分批处理)、 以及第 6 条原样不动。 验证:verify-quality-debt-ratchet.py **9 pass / 0 FAIL**(改前 2 FAIL / 8 pass)、 其负向自测 verify-quality-debt-ratchet.Tests.py **15 tests OK**、 verify-ci-gates / verify-doc-ssot / verify-action-runtimes 全 exit 0、 git diff --check 无空白错误。 Co-authored-by: Cursor --- scripts/verify/quality-debt-baseline.json | 22 ++-------------------- 1 file changed, 2 insertions(+), 20 deletions(-) diff --git a/scripts/verify/quality-debt-baseline.json b/scripts/verify/quality-debt-baseline.json index 4142960d5..323882a65 100644 --- a/scripts/verify/quality-debt-baseline.json +++ b/scripts/verify/quality-debt-baseline.json @@ -12,30 +12,12 @@ { "location": "frontend-desktop: Lint", "kind": "continue-on-error", - "reason": "desktop eslint advisory while Wave 9-10 design-system refactor settles (#1575)", - "issue": 1575, + "reason": "desktop eslint advisory for a measured reason, not a habitual one (#2278): the CI step runs `pnpm lint --max-warnings 0` and desktop currently reports 0 errors but 4 `react-hooks/exhaustive-deps` warnings (useMemo dependency arrays missing `t` / `tApp` in useDesktopWorkbenchModel.ts) => exit 1. Fixing them changes memoization dependencies, i.e. a behaviour change that needs its own reviewed slice; flipping the gate before that would hard-fail every desktop PR. The two sibling soft gates (frontend-web Lint Web, frontend-mobile Lint (mobile rules)) were retired by #2278 and are tracked in #2251.", + "issue": 2251, "owner": "desktop-owners", "introduced_at": "2026-08-11", "review_by": "2026-10-01" }, - { - "location": "frontend-web: Lint Web", - "kind": "continue-on-error", - "reason": "web eslint advisory while Wave 9-10 design-system refactor settles (#1581)", - "issue": 1581, - "owner": "web-owners", - "introduced_at": "2026-08-11", - "review_by": "2026-10-01" - }, - { - "location": "frontend-mobile: Lint (mobile rules)", - "kind": "continue-on-error", - "reason": "mobile eslint advisory while Wave 9-10 design-system refactor settles (#1575)", - "issue": 1573, - "owner": "mobile-owners", - "introduced_at": "2026-08-11", - "review_by": "2026-10-01" - }, { "location": "validate: Verify i18n callsites ratchet (#1612)", "kind": "continue-on-error",