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 }); 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",