Files
gaokao-volunteer-system/reports/REVIEW_REPORT_2026-07-05_COMPREHENSIVE_PROJECT_REVIEW.md
Hermes Agent 738ab68898
Some checks failed
CI / pytest (Python 3.10) (push) Has been cancelled
CI / pytest (Python 3.11) (push) Has been cancelled
CI / pytest (Python 3.12) (push) Has been cancelled
docs(review): add comprehensive project review 2026-07-05
2026-07-05 21:01:55 +08:00

31 KiB
Raw Blame History

2026-07-05 项目全面 Review 报告(真实复核版)

生成时间2026-07-05T20:59:15+08:00
审查对象:/home/long/project/gaokao-volunteer-system
当前 HEAD4059f144c49398aec2fcc3040e639e1312061086
审查方式:真实读取代码/CI/脚本/现有报告 + 本地门禁执行 + 静态扫描;未把历史报告当作当前事实。
重要边界:本报告是代码库与本地门禁 review不等同于线上真实支付、真实域名、真实用户流量验收。
复验更新:已在报告初稿后补装前端依赖并复跑 pnpm typecheck/lint/test/build,结果已回写到 §1.2 / M0。


0. 总结结论

结论REQUEST_CHANGES / 不能宣称生产级完成。

本轮 review 发现的当前有效问题:

严重级别 数量 摘要
HIGH 4 Admin 导航断链、React Admin mock 登录未接真实 JWT、前端 API client 未注入 Authorization、本地 Python 总门禁 mypy 失败
MEDIUM 4 Chromatic token 未配置会阻断 CI、LHCI 启动/端口口径仍有漂移风险、dev-verify 100-case smoke 非阻塞导致主链路回归可能被降级为 warning、前端依赖缺失曾阻断本地 fresh gate已补装并复验通过
LOW 2 文档中仍大量保留 mock/sandbox 历史语境,旧截图报告目录仍在仓库中易误导;部分静态扫描命中需后续清理白名单

已确认的正向事实:

  • main / origin / gitea / tksea 在上一轮已同步到 4059f14,本轮 review 起点工作区为 ## main
  • apps/web/src/types/api-generated.d.tsapps/web/src/schemas/api-generated.ts 当前 any_count=0
  • /health dev readiness 改动已进入 HEADsettings_valid 在 dev 环境允许占位密钥,但 prod fail-closed 仍由配置加载策略覆盖。

1. Gate 结果(当前真实输出)

1.1 Python / 后端总门禁

命令来源:/tmp/gaokao-review-gates-20260705.log,由本轮执行:

bash scripts/dev-verify.sh

结果:FAIL。关键输出:

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

影响:

  • 这不是单测功能失败而是类型门禁失败按项目质量标准不能用“pytest 局部通过”覆盖。
  • 失败集中在 poster / report 相关类型与缺失 third-party stubs说明分享海报链路仍存在类型质量债。

1.2 前端本地门禁

第一轮执行结果:BLOCKED,原因是本地缺少 node_modules / turbo,未进入真实业务门禁。随后已补执行:

pnpm install --frozen-lockfile
pnpm typecheck
pnpm lint
pnpm test
pnpm build

复验结果:PASS。fresh evidence 来自 /tmp/gaokao-frontend-gates-20260705.log

=== 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 ===

更新判断:前端本地 typecheck/lint/test/build 当前已有 fresh gate 证据;原 H5 从 HIGH blocker 降级为 MEDIUM 环境/可复现性问题。仍需注意:这不自动证明 Playwright e2e、Chromatic、LHCI 或真实浏览器视觉验收已全部闭环。


2. HIGH Findings

H1 · 后台导航 /admin/review 真实断链

证据:

apps/web/src/layouts/AdminLayout.tsx 注册了 /admin/review

  • L12: const adminNavItems = [
  • L13: { to: '/admin', labelKey: 'admin.nav.dashboard', icon: Home },
  • L14: { to: '/admin/orders', labelKey: 'admin.nav.orders', icon: FileText },
  • L15: { to: '/admin/cases', labelKey: 'admin.nav.cases', icon: Users },
  • L16: { to: '/admin/share-links', labelKey: 'admin.nav.shareLinks', icon: Link2 },
  • L17: { to: '/admin/score-lines', labelKey: 'admin.nav.scoreLines', icon: Database },
  • L18: { to: '/admin/rank-estimator', labelKey: 'admin.nav.rankEstimator', icon: TrendingUp },
  • L19: { to: '/admin/majors', labelKey: 'admin.nav.majors', icon: BarChart3 },
  • L20: { to: '/admin/schools', labelKey: 'admin.nav.schools', icon: School },
  • L21: { to: '/admin/review', labelKey: 'admin.nav.review', icon: ShieldCheck },
  • L22: { to: '/admin/posters', labelKey: 'admin.nav.posters', icon: BarChart3 },
  • L23: ] as const;

apps/web/src/router.tsx/admin 子路由没有 review

  • L64: { path: '/admin/login', element: <AdminLoginPage /> },
  • L65: { path: '/403', element: <ForbiddenPage /> },
  • L66: {
  • L67: path: '/admin',
  • L68: element: (
  • L69: <RequireAuth>
  • L70: <AdminLayout />
  • L71: </RequireAuth>
  • L72: ),
  • L73: children: [
  • L74: { index: true, element: <AdminDashboardPage /> },
  • L75: { path: 'orders', element: <AdminOrdersPage /> },
  • L76: { path: 'orders/:orderId', element: <AdminOrderDetailPage /> },
  • L77: { path: 'cases', element: <AdminCasesPage /> },
  • L78: { path: 'cases/:caseId', element: <AdminCaseDetailPage /> },
  • L79: { path: 'share-links', element: <AdminShareLinksPage /> },
  • L80: { path: 'share-links/:code', element: <AdminShareLinkDetailPage /> },
  • L81: { path: 'posters', element: <AdminPostersPage /> },
  • L82: { path: 'score-lines', element: <AdminScoreLinesPage /> },
  • L83: { path: 'rank-estimator', element: <AdminRankEstimatorPage /> },
  • L84: { path: 'majors', element: <AdminMajorsPage /> },
  • L85: { path: 'schools', element: <AdminSchoolsPage /> },
  • L86: { path: 'error', element: <AdminErrorPage /> },
  • L87: { path: '*', element: <NotFoundPage /> },
  • L88: ],

静态路由对照结果:

nav=['/admin', '/admin/orders', '/admin/cases', '/admin/share-links', '/admin/score-lines', '/admin/rank-estimator', '/admin/majors', '/admin/schools', '/admin/review', '/admin/posters']
admin_routes=['/admin/*', '/admin/cases', '/admin/cases/:caseId', '/admin/error', '/admin/majors', '/admin/orders', '/admin/orders/:orderId', '/admin/posters', '/admin/rank-estimator', '/admin/schools', '/admin/score-lines', '/admin/share-links', '/admin/share-links/:code']
missing=['/admin/review']

影响: 后台用户点击“复核 / Review”会进入 NotFound属于真实用户路径断裂。
建议修复: 新增 /admin/review 子路由并接入已有 Review 页面或移除导航项;新增 e2e 遍历所有 admin nav links断言不落入 NotFound。


H2 · React Admin 登录仍是前端 mock未接 /api/auth/login

证据:

apps/web/src/pages/admin/LoginPage.tsx 只校验固定验证码 123456,直接写 user store

  • L53: const onSubmit = async (values: LoginFormValues): Promise<void> => {
  • 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: <h2 className="text-2xl font-bold text-slate-950 dark:text-white">
  • L103: <FormattedMessage id="admin.login.title" />
  • L104: </h2>
  • L105: <p className="text-sm text-slate-500 dark:text-slate-400">
  • L106: <FormattedMessage id="admin.login.mockHint" />
  • L107: </p>
  • L132: <div className="relative mt-2">
  • L133: <LockKeyhole className="pointer-events-none absolute left-4 top-1/2 h-4 w-4 -translate-y-1/2 text-slate-400" aria-hidden="true" />
  • L134: <input
  • L135: id="admin-code"
  • L136: type="text"
  • L137: inputMode="numeric"
  • L138: autoComplete="one-time-code"
  • L139: placeholder="123456"
  • L140: className="min-h-12 w-full rounded-2xl border border-slate-200 bg-white px-11 text-sm text-slate-950 shadow-sm transition placeholder:text-slate-400 focus:border-blue-500 focus:outline-none focus:ring-2 focus:ring-blue-500 dark:border-slate-700 dark:bg-slate-950 dark:text-white"

后端真实登录端点存在:admin/routes/auth.py 声明 POST /api/auth/login : 用户名 + 密码 → JWT,但 React 登录页没有调用该端点。

影响: 真实后端环境会出现“前端显示已登录,但后续 admin API 请求没有 JWT返回 401/403”的集成失败。
建议修复: 登录页改为调用 /api/auth/login;保存 JWT 到明确策略(优先内存/session避免长期 localStorage失败态显示真实错误LoginPage 集成测试和 admin e2e 登录主链路。


H3 · apiClient 没有统一注入 Authorization: BearerAdmin API 契约未闭环

证据:

apps/web/src/lib/api-client.ts 只设置 Content-Type / Accept 和调用方传入 headers没有从 auth/user store 注入 Bearer

  • L101: const init: RequestInit = {
  • L102: method,
  • L103: headers: {
  • L104: 'Content-Type': 'application/json',
  • L105: Accept: 'application/json',
  • L106: ...headers,
  • L107: },
  • L108: signal,
  • L109: };
  • L110: ``
  • L111: if (body !== undefined) {
  • L112: init.body = JSON.stringify(body);
  • L113: }
  • L114: ``
  • L115: if (isWriteMethod(method)) {
  • L116: await waitUntilOnline(signal);
  • L117: }
  • L118: ``
  • L119: const res = await fetch(${BASE_URL}${path}, init);
  • L120: ``
  • L155: export const apiClient = {
  • L156: get: <T>(path: string, schema: ZodType<T, ZodTypeDef, unknown>, signal?: AbortSignal): Promise<T> =>
  • L157: request(path, schema, { method: 'GET', signal }),
  • L158: post: <T, B = unknown>(path: string, body: B, schema: ZodType<T, ZodTypeDef, unknown>, signal?: AbortSignal): Promise<T> =>
  • L159: request(path, schema, { method: 'POST', body, signal }),
  • L160: put: <T, B = unknown>(path: string, body: B, schema: ZodType<T, ZodTypeDef, unknown>, signal?: AbortSignal): Promise<T> =>
  • L161: request(path, schema, { method: 'PUT', body, signal }),
  • L162: patch: <T, B = unknown>(path: string, body: B, schema: ZodType<T, ZodTypeDef, unknown>, signal?: AbortSignal): Promise<T> =>
  • L163: request(path, schema, { method: 'PATCH', body, signal }),
  • L164: delete: <T>(path: string, schema: ZodType<T, ZodTypeDef, unknown>, signal?: AbortSignal): Promise<T> =>
  • 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 providerapiClient 统一注入 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 等文件:

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 missingturbo: not found;补执行 /tmp/gaokao-frontend-gates-20260705.log 后,前端 gate 结果为 PASS

=== 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_TOKENpush/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, // 取 P75median
  • 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 容易混用

仓库内存在多个历史 reviewreports/REVIEW_REPORT_V10_FRONTEND_2026-07-05.mdREVIEW_REPORT_2026-07-02_SENIOR_DEVELOPER.mdSTRICT_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.tsapps/web/src/schemas/api-generated.tsany_count=0
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/H3React Admin 真实登录 + JWT 注入 + auth 状态测试。
  3. 修复 H4mypy 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. 本轮命令与证据索引

## 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 生产级验收闭环。
  • 线上真实支付/真实域名/真实用户流量验收完成。