=> {`
+ - L54: ` setSubmitError(null);`
+ - L55: ` await new Promise((resolve) => window.setTimeout(resolve, 80));`
+ - L56: ``
+ - L57: ` if (values.code !== '123456') {`
+ - L58: ` setSubmitError(intl.formatMessage({ id: 'admin.login.mockCodeError' }));`
+ - L59: ` return;`
+ - L60: ` }`
+ - L61: ``
+ - L62: ` setUser({`
+ - L63: ` id: `admin-${values.phone.slice(-4)}`,`
+ - L64: ` name: intl.formatMessage({ id: 'admin.login.mockAdminName' }, { suffix: values.phone.slice(-4) }),`
+ - L65: ` phone: values.phone,`
+ - L66: ` role: 'admin',`
+ - L67: ` });`
+ - L68: ` toast.success(intl.formatMessage({ id: 'admin.login.toastSuccess' }), {`
+ - L69: ` description: intl.formatMessage({ id: 'admin.login.toastSuccessDescription' }),`
+ - L70: ` });`
+ - L71: ` void navigate(from, { replace: true });`
+ - L72: ` };`
+ - L102: ` `
+ - L103: ` `
+ - L104: `
`
+ - L105: ` `
+ - L106: ` `
+ - L107: `
`
+ - L132: ` `
+ - L133: ` `
+ - L134: ` (path: string, schema: ZodType, signal?: AbortSignal): Promise =>`
+ - L157: ` request(path, schema, { method: 'GET', signal }),`
+ - L158: ` post: (path: string, body: B, schema: ZodType, signal?: AbortSignal): Promise =>`
+ - L159: ` request(path, schema, { method: 'POST', body, signal }),`
+ - L160: ` put: (path: string, body: B, schema: ZodType, signal?: AbortSignal): Promise =>`
+ - L161: ` request(path, schema, { method: 'PUT', body, signal }),`
+ - L162: ` patch: (path: string, body: B, schema: ZodType, signal?: AbortSignal): Promise =>`
+ - L163: ` request(path, schema, { method: 'PATCH', body, signal }),`
+ - L164: ` delete: (path: string, schema: ZodType, signal?: AbortSignal): Promise =>`
+ - L165: ` request(path, schema, { method: 'DELETE', signal }),`
+ - L166: `};`
+
+后端 `admin/auth.py` 依赖 `Authorization: Bearer`,还支持 URL query `t=` fallback:
+
+ - L115: ` credentials: Optional[HTTPAuthorizationCredentials] = Depends(_BEARER_SCHEME),`
+ - L116: ` settings: Settings = Depends(get_settings),`
+ - L117: `) -> AdminUser:`
+ - L118: ` """FastAPI 依赖:从 Authorization: Bearer *** JWT,返回 AdminUser。`
+ - L119: ``
+ - L120: ` 缺失/无效/过期一律 401 (走业务错误码 E012xx 系列).`
+ - L121: ` 支持从 URL query 参数 ``t`` 获取 token(仅用于管理后台 Web 页面场景)。`
+ - L122: ` """`
+ - L123: ` raw_token: str | None = None`
+ - L124: ` if credentials is not None and credentials.scheme.lower() == "bearer":`
+ - L125: ` raw_token = credentials.credentials`
+ - L126: ` # fallback: URL query 参数 t(管理后台 Web 登录页跳转场景)`
+ - L127: ` if raw_token is None:`
+ - L128: ` query_token = request.query_params.get("t")`
+ - L129: ` if query_token:`
+ - L130: ` raw_token = query_token`
+ - L131: ` if raw_token is None:`
+ - L132: ` raise BusinessError(`
+ - L133: ` AUTH_TOKEN_INVALID, detail={"reason": "missing bearer token"}`
+
+**影响:** 即使 H2 修复了登录,如果 apiClient 不统一注入 token,后台订单/案例/分享等受保护 API 仍会失败;若继续依赖 URL `?t=`,token 容易进入浏览器历史、日志或 Referer。
+**建议修复:** 建立 `authStore` / token provider,apiClient 统一注入 Authorization;逐步减少 URL token fallback 的使用范围,仅保留后台 Web 兼容场景并加安全注释和测试。
+
+---
+
+### H4 · Python 总门禁 mypy 当前失败,不能宣称后端质量门禁通过
+
+**证据:** `scripts/dev-verify.sh` 会运行 `python -m mypy .`:
+
+ - L87: ` log "running pytest with coverage gate"`
+ - L88: ` # Single source of truth threshold: matches scripts/check_coverage_gate.py`
+ - L89: ` if [[ "${SKIP_PRE_EXISTING}" == "1" ]]; then`
+ - L90: ` log "skip pre-existing failures: --skip-pre-existing"`
+ - L91: ` for node in "${PRE_EXISTING_IGNORES[@]}"; do`
+ - L92: ` PYTEST_IGNORE_ARGS+=("--deselect" "$node")`
+ - L93: ` done`
+ - L94: ` fi`
+ - L95: ` python -m pytest admin/tests tests data \`
+ - L96: ` --ignore=.venv \`
+ - L97: ` --ignore=.worktrees \`
+ - L98: ` --cov=admin \`
+ - L99: ` --cov=data \`
+ - L100: ` --cov=skills \`
+ - L101: ` --cov-report=term-missing \`
+ - L102: ` --cov-report=xml \`
+ - L103: ` --cov-fail-under=80 \`
+ - L104: ` -q \`
+ - L105: ` "${PYTEST_IGNORE_ARGS[@]}"`
+ - L106: ``
+ - L107: ` log "running core coverage verifier"`
+ - L108: ` python scripts/check_coverage_gate.py coverage.xml`
+ - L109: ``
+ - L110: ` log "running ruff"`
+ - L111: ` python -m ruff check . --exclude .venv,.worktrees`
+ - L112: ``
+ - L113: ` log "running mypy"`
+ - L114: ` python -m mypy .`
+ - L115: ``
+ - L116: ` log "crowd_db quality summary (防漂移监控)"`
+ - L117: ` python -m data.crowd_db.quality_summary --human`
+ - L118: ``
+ - L119: ` # P1-7/P1-8: 100-case smoke 作为独立验证步骤,失败不阻断核心门禁`
+ - L120: ` log "running 100-case smoke e2e (non-blocking)"`
+ - L121: ` python scripts/score_range_fullchain_100_e2e.py --batch smoke || log "WARN: 100-case smoke e2e failed (non-blocking, see /tmp/score-range-fullchain-100-e2e.log)"`
+
+本轮执行结果显示 mypy 有 9 个错误,涉及 `data/share/poster.py` 等文件:
+
+```text
+admin/routes/sprint3_api.py:292: error: Incompatible types in assignment (expression has type "str", variable has type "Literal['pending', 'in_progress', 'approved', 'rejected', 'changes_requested']") [assignment]
+data/cli_compat_share.py:43: error: Item "None" of "_ArgumentGroup | None" has no attribute "_group_actions" [union-attr]
+data/cli_compat_share.py:44: error: Item "Action" of "Action | Any" has no attribute "add_parser" [union-attr]
+data/share/poster.py:123: error: Argument "candidate_name" to "PosterPayload" has incompatible type "str | None"; expected "str" [arg-type]
+data/share/poster.py:273: error: Incompatible return value type (got "FreeTypeFont", expected "ImageFont") [return-value]
+data/share/poster.py:274: error: Incompatible return value type (got "FreeTypeFont | ImageFont", expected "ImageFont") [return-value]
+data/share/poster.py:319: error: Incompatible return value type (got "float", expected "int") [return-value]
+data/share/poster.py:324: error: Incompatible return value type (got "float", expected "int") [return-value]
+data/share/poster.py:329: error: Library stubs not installed for "qrcode" [import-untyped]
+Found 9 errors in 3 files (checked 268 source files)
+DEV_VERIFY_EXIT=1
+```
+
+**影响:** 类型门禁失败会影响 poster/share 相关链路长期可维护性,也说明 CI clean env 可能无法稳定通过。
+**建议修复:** 修 `data/share/poster.py` 返回类型;安装/声明 `types-qrcode` 或在 mypy 配置中有边界地忽略;修后复跑 `bash scripts/dev-verify.sh`。
+
+---
+
+---
+
+## 3. MEDIUM Findings
+
+### M0 · 前端本地门禁曾因依赖缺失不可执行;已补装依赖并 fresh gate 通过,但应沉淀环境准备要求
+
+**证据:** 首轮 `/tmp/gaokao-review-gates-20260705.log` 显示 `apps/web/node_modules missing`、`turbo: not found`;补执行 `/tmp/gaokao-frontend-gates-20260705.log` 后,前端 gate 结果为 **PASS**。
+
+```text
+=== FRONTEND FRESH GATES START ===
+--- pnpm install ---
+root node_modules exists
+apps/web/node_modules exists
+--- web typecheck ---
+--- web lint ---
+--- web test ---
+--- web build ---
+ Tasks: 2 successful, 2 total
+ Tasks: 1 successful, 1 total
+@gaokao/web:build: TOTAL │ 1.36 MB │ 393.60 KB │ ✅
+@gaokao/web:build: ✅ T-B-25 验证通过
+=== FRONTEND FRESH GATES END ===
+```
+
+**影响:** 该问题当前不再是代码质量 blocker,但说明本地 review/交接流程需要先执行依赖安装,否则容易把“环境未准备”误报为“前端 gate 失败”。
+**建议修复:** 在 review runbook / README 的前端 gate 前置条件中加入 `pnpm install --frozen-lockfile`,并在 CI/本地脚本中显式检查 `turbo` 可用后再运行 gate。
+
+---
+
+## 3. MEDIUM Findings
+
+### M1 · Chromatic job 无 token 条件保护,可能阻断 CI
+
+**证据:** `.github/workflows/web-ci.yml` 直接使用 secret:
+
+ - L96: ` chromatic:`
+ - L97: ` runs-on: ubuntu-latest`
+ - L98: ` needs: ci`
+ - L99: ` timeout-minutes: 15`
+ - L100: ` if: github.event_name == 'push' || github.event_name == 'pull_request'`
+ - L101: ` steps:`
+ - L102: ` - uses: actions/checkout@v4`
+ - L103: ` with:`
+ - L104: ` fetch-depth: 0`
+ - L105: ``
+
+**影响:** 未配置 `CHROMATIC_TOKEN` 时,push/PR 可能被 Chromatic 阻断;这与“外部 token 待配置”的状态口径不一致。
+**建议修复:** 给 chromatic job 增加 secret 存在性条件或降级为非阻塞;在报告中明确 Chromatic 是否为 release blocker。
+
+---
+
+### M2 · LHCI 配置与启动口径仍有漂移风险
+
+**证据:** workflow 使用 treosh action + `serverUrl: 8080`,而 lighthouserc 注释仍写“vite preview 启动(8081)”:
+
+`.github/workflows/web-ci.yml`:
+
+ - L106: ` - name: Setup pnpm`
+ - L107: ` uses: pnpm/action-setup@v4`
+ - L108: ` with:`
+ - L109: ` version: 10`
+ - L110: ``
+ - L111: ` - name: Setup Node.js`
+ - L112: ` uses: actions/setup-node@v4`
+ - L113: ` with:`
+ - L114: ` node-version: '20'`
+ - L115: ` cache: 'pnpm'`
+ - L116: ``
+ - L117: ` - name: Install dependencies`
+ - L118: ` run: pnpm install --frozen-lockfile`
+ - L119: ``
+ - L120: ` - name: Build`
+ - L121: ` run: pnpm --filter @gaokao/web build`
+ - L122: ``
+ - L123: ` - name: Publish to Chromatic`
+ - L124: ` uses: chromaui/action@v1`
+ - L125: ` with:`
+ - L126: ` projectToken: ${{ secrets.CHROMATIC_TOKEN }}`
+ - L127: ` workingDir: apps/web`
+ - L128: ` buildScriptName: build`
+ - L129: ` exitZeroOnChanges: true`
+ - L130: ``
+ - L131: ` lighthouse:`
+ - L132: ` # G3 闸门真实化:P/A/B/S 均 ≥ 90`
+ - L133: ` runs-on: ubuntu-latest`
+ - L134: ` needs: ci`
+ - L135: ` timeout-minutes: 15`
+ - L136: ` if: github.event_name == 'push' || github.event_name == 'pull_request'`
+ - L137: ` steps:`
+ - L138: ` - uses: actions/checkout@v4`
+ - L139: ``
+ - L140: ` - name: Setup pnpm`
+ - L141: ` uses: pnpm/action-setup@v4`
+ - L142: ``
+ - L143: ` - name: Setup Node.js`
+ - L144: ` uses: actions/setup-node@v4`
+ - L145: ` with:`
+ - L146: ` node-version: '20'`
+ - L147: ` cache: 'pnpm'`
+ - L148: ``
+ - L149: ` - name: Install dependencies`
+ - L150: ` run: pnpm install --frozen-lockfile`
+ - L151: ``
+ - L152: ` - name: Build`
+ - L153: ` run: pnpm --filter @gaokao/web build`
+ - L154: ``
+ - L155: ` - name: Run Lighthouse CI (G3 闸门:P/A/B/S ≥ 90)`
+ - L156: ` uses: treosh/lighthouse-ci-action@v12`
+ - L157: ` with:`
+ - L158: ` configPath: ./apps/web/lighthouserc.cjs`
+ - L159: ` uploadArtifacts: true`
+ - L160: ` temporaryPublicStorage: true`
+ - L161: ` runs: 3`
+ - L162: ` serverUrl: http://127.0.0.1:8080/`
+ - L163: ` # treosh action 自动启动 vite preview @ 8080`
+ - L164: ` url: |`
+ - L165: ` http://127.0.0.1:8080/`
+ - L166: ` http://127.0.0.1:8080/data-query`
+ - L167: ` http://127.0.0.1:8080/plans`
+ - L168: ` http://127.0.0.1:8080/about`
+
+`apps/web/lighthouserc.cjs`:
+
+ - L14: ` collect: {`
+ - L15: ` // 静态服务器从 vite preview 启动(8081),见 CI workflow`
+ - L16: ` url: [`
+ - L17: ` 'http://127.0.0.1:8080/',`
+ - L18: ` 'http://127.0.0.1:8080/data-query',`
+ - L19: ` 'http://127.0.0.1:8080/plans',`
+ - L20: ` 'http://127.0.0.1:8080/about',`
+ - L21: ` ],`
+ - L22: ` numberOfRuns: 3, // 取 P75(median)`
+ - L23: ` settings: {`
+ - L24: ` // P75 算分(默认 median = P50)`
+ - L25: ` preset: 'desktop',`
+ - L26: ` chromeFlags: '--no-sandbox --headless=new',`
+ - L27: ` },`
+ - L29: ` assert: {`
+ - L30: ` // G3 闸门:每类 ≥ 90`
+ - L31: ` assertions: {`
+ - L32: ` 'categories:performance': ['error', { minScore: 0.9 }],`
+ - L33: ` 'categories:accessibility': ['error', { minScore: 0.9 }],`
+ - L34: ` 'categories:best-practices': ['error', { minScore: 0.9 }],`
+ - L35: ` 'categories:seo': ['error', { minScore: 0.9 }],`
+ - L36: ` },`
+ - L37: ` },`
+ - L38: ` upload: {`
+ - L39: ` target: 'temporary-public-storage', // 公开 storage 30 天;后续接 LHCI server`
+ - L40: ` },`
+
+**影响:** LHCI 可能在 CI 上因为服务启动机制/端口口径不一致而假失败或假通过。
+**建议修复:** 在 LHCI 配置或 workflow 中显式指定 preview 启动命令、ready pattern、端口;修正文档注释;CI 上保留 artifact。
+
+---
+
+### M3 · `scripts/dev-verify.sh` 将 100-case smoke 设为 non-blocking,主链路回归可能被 warning 化
+
+**证据:**
+
+ - L87: ` log "running pytest with coverage gate"`
+ - L88: ` # Single source of truth threshold: matches scripts/check_coverage_gate.py`
+ - L89: ` if [[ "${SKIP_PRE_EXISTING}" == "1" ]]; then`
+ - L90: ` log "skip pre-existing failures: --skip-pre-existing"`
+ - L91: ` for node in "${PRE_EXISTING_IGNORES[@]}"; do`
+ - L92: ` PYTEST_IGNORE_ARGS+=("--deselect" "$node")`
+ - L93: ` done`
+ - L94: ` fi`
+ - L95: ` python -m pytest admin/tests tests data \`
+ - L96: ` --ignore=.venv \`
+ - L97: ` --ignore=.worktrees \`
+ - L98: ` --cov=admin \`
+ - L99: ` --cov=data \`
+ - L100: ` --cov=skills \`
+ - L101: ` --cov-report=term-missing \`
+ - L102: ` --cov-report=xml \`
+ - L103: ` --cov-fail-under=80 \`
+ - L104: ` -q \`
+ - L105: ` "${PYTEST_IGNORE_ARGS[@]}"`
+ - L106: ``
+ - L107: ` log "running core coverage verifier"`
+ - L108: ` python scripts/check_coverage_gate.py coverage.xml`
+ - L109: ``
+ - L110: ` log "running ruff"`
+ - L111: ` python -m ruff check . --exclude .venv,.worktrees`
+ - L112: ``
+ - L113: ` log "running mypy"`
+ - L114: ` python -m mypy .`
+ - L115: ``
+ - L116: ` log "crowd_db quality summary (防漂移监控)"`
+ - L117: ` python -m data.crowd_db.quality_summary --human`
+ - L118: ``
+ - L119: ` # P1-7/P1-8: 100-case smoke 作为独立验证步骤,失败不阻断核心门禁`
+ - L120: ` log "running 100-case smoke e2e (non-blocking)"`
+ - L121: ` python scripts/score_range_fullchain_100_e2e.py --batch smoke || log "WARN: 100-case smoke e2e failed (non-blocking, see /tmp/score-range-fullchain-100-e2e.log)"`
+
+第 119-121 行说明 100-case smoke 失败不阻断核心门禁。
+
+**影响:** 对当前高信任付费/志愿服务项目,100-case fullchain smoke 更接近业务真实回归;长期 non-blocking 可能掩盖主链路问题。
+**建议修复:** 至少把 smoke 结果拆为 `核心门禁 PASS/FAIL` 与 `业务主链路 PASS/FAIL` 两个明确状态;在 release gate 中设置必过子集。
+
+---
+
+## 4. LOW / 文档真相风险
+
+### L1 · 历史 review 与当前 review 容易混用
+
+仓库内存在多个历史 review:`reports/REVIEW_REPORT_V10_FRONTEND_2026-07-05.md`、`REVIEW_REPORT_2026-07-02_SENIOR_DEVELOPER.md`、`STRICT_SYSTEM_REVIEW_*` 等。历史报告仍有价值,但不应替代当前 HEAD + 当前门禁结果。
+
+**建议:** 将本报告作为 2026-07-05 当前全面 review 真相入口;旧报告顶部应逐步加“历史快照”提示或 CURRENT REVIEW 指针。
+
+### L2 · 旧截图报告产物仍在仓库中,易被误当当前前端验收材料
+
+`reports/user_simulation_2026_06_20/` 仍包含旧截图和 captures.json。上一轮已避免把这些旧产物合并进新提交;本轮 review 继续建议把它们标注为历史材料,不作为 V10 当前验收证据。
+
+---
+
+## 5. 已验证不是问题 / 当前正向事实
+
+- `apps/web/src/types/api-generated.d.ts` 与 `apps/web/src/schemas/api-generated.ts` 的 `any_count=0`:
+
+```text
+apps/web/src/types/api-generated.d.ts 0
+apps/web/src/schemas/api-generated.ts 0
+```
+
+- `admin/routes/health.py` 当前 dev readiness 逻辑可解释:
+
+ - L65: `def _check_settings_valid(settings: Settings) -> bool:`
+ - L66: ` """检查 prod fail-closed 通过 (JWT + admin password + payment)。`
+ - L67: ``
+ - L68: ` dev 环境允许占位密钥,仅 prod 环境强制安全密钥。`
+ - L69: ` """`
+ - L70: ` if settings.env == "dev":`
+ - L71: ` # dev 环境允许占位密钥,不做 fail-closed`
+ - L72: ` return True`
+ - L73: ` secure, _ = is_jwt_secret_secure(settings)`
+ - L74: ` return secure`
+ - L75: ``
+ - L76: ``
+ - L77: `@router.get("/health", summary="健康检查")`
+ - L78: `def health(settings: Settings = Depends(get_settings_dep)) -> JSONResponse:`
+ - L79: ` """公开端点。只返回 readiness, 不暴露环境/路径/版本细节。`
+ - L80: ``
+ - L81: ` 返回结构:`
+ - L82: ` - status: "ok" 或 "degraded"(任一 readiness 检查失败时降级)`
+ - L83: ` - checks: {db_writable, disk_writable, settings_valid} 子对象`
+ - L84: ``
+ - L85: ` readiness 语义(2026-06-27 P1-4 修复):`
+ - L86: ` - 所有 checks 通过 → status="ok", HTTP 200`
+ - L87: ` - 任一 check 失败 → status="degraded", HTTP 503`
+ - L88: ` - K8s/systemd readiness probe 应判 HTTP status,不只判 status 字段`
+ - L89: ` """`
+ - L90: ` checks = {`
+ - L91: ` "db_writable": _check_db_writable(settings),`
+ - L92: ` "disk_writable": _check_disk_writable(settings),`
+ - L93: ` "settings_valid": _check_settings_valid(settings),`
+ - L94: ` }`
+ - L95: ` all_ok = all(checks.values())`
+ - L96: ` return JSONResponse(`
+ - L97: ` status_code=200 if all_ok else 503,`
+ - L98: ` content={`
+ - L99: ` "status": "ok" if all_ok else "degraded",`
+ - L100: ` "checks": checks,`
+ - L101: ` },`
+
+- 静态扫描未发现 Python 裸 `except:`。
+
+---
+
+## 6. 建议整改优先级
+
+### P0 / 立即阻断生产完成声明
+
+1. 修复 H1 `/admin/review` 断链并补全 admin nav e2e。
+2. 修复 H2/H3:React Admin 真实登录 + JWT 注入 + auth 状态测试。
+3. 修复 H4:mypy 9 errors,复跑 `scripts/dev-verify.sh` 到 green。
+4. 前端依赖已补装并复跑 typecheck/lint/test/build;后续仍需补 Playwright e2e / LHCI / Chromatic fresh evidence。
+
+### P1 / CI 与验收口径收敛
+
+1. Chromatic token 缺失时不阻断或明确设为必配 release gate。
+2. LHCI preview 启动和端口口径统一。
+3. 100-case smoke 的 release gate 语义升级,不再只作为 warning。
+
+### P2 / 文档与证据治理
+
+1. 给历史 review 报告加当前真相入口。
+2. 标注旧截图/旧 user simulation 产物为历史材料。
+
+---
+
+## 7. 本轮命令与证据索引
+
+```text
+## main
+4059f144c49398aec2fcc3040e639e1312061086
+4059f14 (HEAD -> main, tksea/main, origin/main, gitea/main) fix(health): allow dev placeholder readiness
+da99dec docs: add systemic frontend review findings
+5d52e07 docs: update frontend review status after e2e fixes
+```
+
+本轮主要证据:
+
+- 本地 gate 日志:`/tmp/gaokao-review-gates-20260705.log`
+- 当前报告:`reports/REVIEW_REPORT_2026-07-05_COMPREHENSIVE_PROJECT_REVIEW.md`
+- 核查文件:
+ - `apps/web/src/layouts/AdminLayout.tsx`
+ - `apps/web/src/router.tsx`
+ - `apps/web/src/pages/admin/LoginPage.tsx`
+ - `apps/web/src/lib/api-client.ts`
+ - `.github/workflows/web-ci.yml`
+ - `apps/web/lighthouserc.cjs`
+ - `scripts/dev-verify.sh`
+ - `admin/routes/health.py`
+ - `admin/auth.py`
+
+---
+
+## 8. 最终判断
+
+**不能宣称项目整体生产级完成。**
+
+可以宣称:
+
+- 本轮已完成一次当前 HEAD 的多维真实 review。
+- 已发现并固化当前有效问题到今日 review 报告。
+- 当前最大阻断不是“未知”,而是明确集中在:后台真实鉴权闭环、admin nav 路由完整性、Python 类型门禁、Playwright/LHCI/Chromatic 与真实视觉验收 fresh evidence、CI 外部服务口径。
+
+仍不能宣称:
+
+- 前端 V10 全部门禁(含 Playwright e2e / LHCI / Chromatic / 真实视觉验收)当前全部通过。
+- Admin 真实 JWT 登录已接通。
+- CI/LHCI/Chromatic 生产级验收闭环。
+- 线上真实支付/真实域名/真实用户流量验收完成。