文档先行:AGC 认证失败的 JS 侧载体与抛出时机
- 新增 ADR:认证命令统一经 invokeClientAuth 把结构化拒绝转成 ClientAuthFailure(payload 判别联合);判定只写在 catch 子句里,系统变体原样抛出经 unhandledrejection 入池,default: expectNever 编译期挡住漏接变体 - 修订【ADR】AGC命令错误结构化与错误报告口径 §3 与后果:撤销"未识别变体上调是故意的",改为编译期穷尽 - 技术方案同步认证命令的封装形态与两个 ADR 链接 - decision-log / pitfalls 记录第二轮口径:删除形状读取与文案回落层、Rust 失败不再降级成字符串 - docs/README.md 登记新 ADR
This commit is contained in:
@@ -8,6 +8,10 @@
|
||||
- 影响范围:`apps/ai-game-creator-shell/src-tauri/src/{auth_error.rs,auth_session.rs}`、`apps/ai-game-creator-shell/src/services/{clientAuthError.ts,clientActionError.ts,errorReporting.ts,clientAuth.ts}`、`apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx`、`apps/ai-game-creator-shell/src/services/generated/`(ts-rs 生成:`ClientAuthError.ts` + 每个变体一个载荷文件)。
|
||||
- 决策(补充):TS 形状用**每个变体一个具名载荷类型**——Rust 枚举是 newtype 变体持有同名 `#[ts(export)]` 结构体,ts-rs 生成 `{ type: 'x' } & X` 与 `src/services/generated/X.ts`,前端 `switch (error.type)` 的每个分支直接拿到具名类型。不允许在前端手写这层类型,也不再包派生分类 / 提示文案函数(`clientAuthErrorKind`、`clientAuthErrorNotice`、`resolveClientAuthFailure` 已删除)。
|
||||
- 验证:见 [`【ADR】AGC命令错误结构化与错误报告口径-2026-10-01`](../../adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md) 的验收清单;关键判据是"登录 400/401 业务变体不产生 `report_client_error`、不弹「发现问题」"。
|
||||
- 决策(2026-10-01,JS 侧载体与抛出时机):认证命令统一经 `invokeClientAuth(command, args)` 调用;结构化拒绝转成 `ClientAuthFailure`(`extends ClientActionError`,`readonly payload` 是 ts-rs 判别联合,`context = { source: 'auth', action: command }`,`cause` 是原始拒绝值),非结构化拒绝原样抛出。形状读取 / 文案回落 / 提示分类三层(`isClientAuthError`、`getClientAuthErrorMessage`、`presentAuthFailure`)全部删除。
|
||||
- 决策(2026-10-01,判定位置与出口):要不要上报只由 catch 子句里的 `switch (error.payload.type)` 判,每个 `case` 用 `as` 取具名载荷类型;业务 / 会话变体把载荷自带 `message` 原样给用户(无兜底文案),系统变体 `throw error` 经全局 `unhandledrejection` 入池(`captureClientError` 用 `instanceof` 解包上下文),`default: expectNever(error.payload)` 让漏接变体编译失败。取代"未识别变体上调是故意的"。
|
||||
- 决策(2026-10-01,Rust 侧不再降级):`refresh_session_inner` 的非权威失败直接 `Err(ClientAuthError)`,`ClientAuthStateView` / `ClientAuthRefreshView` 删除 `errorMessage`,续期结果删除 `failed`;`ClientAuthState` 收敛为 `authenticated | unauthenticated`,`ClientAuthRefreshResult` 收敛为 `refreshed | unauthenticated | stale`。
|
||||
- 影响范围(2026-10-01 第二轮):`apps/ai-game-creator-shell/src/services/{clientAuthFailure.ts,clientAuth.ts,platformSession.ts}`(`clientAuthError.ts` 删除)、`apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx`、`apps/ai-game-creator-shell/src-tauri/src/auth_session.rs`、对应 vitest 用例。
|
||||
|
||||
## 2026-10-01 DirectProject 审批拒绝原因留痕
|
||||
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
|
||||
- **现象**:登录页密码输错(或密码长度不合规)后弹出「发现问题」,报告面板「错误事件(2)」列出 `密码长度需要在 6 到 128 位之间 — auth · 1 次` 与 `手机号或密码错误 — auth · 1 次`,默认全选,与 react-render / 5xx / agent-runtime 终态失败视觉等价。
|
||||
- **原因**:① 登录已下沉 Rust,`login_client_with_password` 等命令失败返回 `Err(String)`,Tauri 以**裸字符串**拒绝 `invoke`,前端拿不到任何类型信息;② `shouldCaptureClientError` 对非 object 值走默认 `return true`,`handleLoginSubmit` 的 catch 把预期业务拒绝报进了错误池。技术方案里"预期 4xx 登录/鉴权失败不进池"的口径早就成立,是错误通道的实现方式违背了它。
|
||||
- **处理(现行口径)**:命令错误一律按具体变体结构化(`Result<_, ClientAuthError>` + ts-rs 导出),UI 调用方按 `type` 分流:认得的业务变体只给用户反馈,系统变体/未识别变体/非结构化拒绝由调用方包成 `ClientActionError` 交给错误池;删除 `shouldCaptureClientError`。详见 [`【ADR】AGC命令错误结构化与错误报告口径-2026-10-01`](../../adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md)。
|
||||
- **判据/取证**:`npx vitest run apps/ai-game-creator-shell/tests/errorReporting.test.ts`——登录返回结构化业务变体时 `report_client_error` 不被调用;系统变体带上文只上报一次且 `source=auth`。Rust 侧 `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml auth_error` 钉住变体 `type` 与 400/401/429/5xx/网络映射。
|
||||
- **形状约定**:`ClientAuthError` 的每个变体在 TS 里是 `{ type: 'x' } & X`,`X` 由 ts-rs 导出到 `src/services/generated/X.ts`(Rust 侧是 newtype 变体持有同名结构体)。**不要手写这些类型**,也不要在前端再加一层分类 / 提示文案派生函数——判别一律 `switch (error.type)`,每个分支里 `error` 已经窄化成具名载荷类型。改形状只能改 Rust 再跑 `cargo test` 重新导出,生成物随后交给 prettier。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/auth_session.rs`、`apps/ai-game-creator-shell/src/services/errorReporting.ts`、`apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx`。
|
||||
- **处理(现行口径)**:命令错误一律按具体变体结构化(`Result<_, ClientAuthError>` + ts-rs 导出),认证命令统一经 `invokeClientAuth` 调用:结构化拒绝转成 `ClientAuthFailure`(`payload` 是判别联合),UI 在 catch 里按具体变体分流——认得的业务 / 会话变体只给用户反馈,系统变体原样 `throw` 经 `unhandledrejection` 入池,非结构化拒绝原样抛出;删除 `shouldCaptureClientError`。详见 [`【ADR】AGC命令错误结构化与错误报告口径-2026-10-01`](../../adr/【ADR】AGC命令错误结构化与错误报告口径-2026-10-01.md) 与 [`【ADR】AGC认证失败的JS侧载体与抛出时机-2026-10-01`](../../adr/【ADR】AGC认证失败的JS侧载体与抛出时机-2026-10-01.md)。
|
||||
- **判据/取证**:`npx vitest run apps/ai-game-creator-shell/tests/authFailureReporting.test.tsx`——登录返回结构化业务变体时 `report_client_error` 不被调用;系统变体只上报一次且 `source=auth`(`ClientAuthFailure.context.action` 是命令名)。Rust 侧 `cargo test --locked --manifest-path apps/ai-game-creator-shell/src-tauri/Cargo.toml auth_error` 钉住变体 `type` 与 400/401/429/5xx/网络映射。
|
||||
- **形状约定**:`ClientAuthError` 的每个变体在 TS 里是 `{ type: 'x' } & X`,`X` 由 ts-rs 导出到 `src/services/generated/X.ts`(Rust 侧是 newtype 变体持有同名结构体)。**不要手写这些类型**,也不要在前端再加一层分类 / 提示文案派生函数——判别一律写在 catch 子句里:`switch (error.payload.type)`,每个 `case` 用 `as` 取具名载荷类型,`default: expectNever(error.payload)` 保证漏接变体编译失败。改形状只能改 Rust 再跑 `cargo test` 重新导出,生成物随后交给 prettier。
|
||||
- **Rust 侧不得把结构化错误降级成字符串**:`refresh_session_inner` 的非权威失败直接返回 `Err(ClientAuthError)`,视图不带 `errorMessage`;一旦折成 `String`,前端就只能拿文案判断,变体信息永久丢失。
|
||||
- **关联**:`apps/ai-game-creator-shell/src-tauri/src/auth_session.rs`、`apps/ai-game-creator-shell/src/services/{clientAuthFailure.ts,clientAuth.ts,errorReporting.ts,platformSession.ts}`、`apps/ai-game-creator-shell/src/app/AuthenticatedClient.tsx`。
|
||||
|
||||
## 2026-09-30 构建期 staging 撞上不装 npm 依赖的 Linux 门禁:AGC 壳 Rust lane 全红
|
||||
|
||||
|
||||
Reference in New Issue
Block a user