⚠️ 自动镜像 · 此页由
docs-site/scripts/mirror-changelog.mjs从docs/changelogs/0.8.x.md生成,请勿直接编辑此处;改源文件后pnpm docs:build会自动同步。
Changelog — 0.8.x
9 个版本:v0.8.0(文档细化与补全)→ v0.8.1(治理 / 合规收口)→ v0.8.2(文档深度优化)→ v0.8.3(治理 / 测试基建闭环)→ v0.8.4 + 4-hotfix(效率看板 / 人员绩效 epic)→ v0.8.5(fabric 清理 / 直方图 / 单测 25%)→ v0.8.6(v0.9.x 准备版)→ v0.8.7(防机器人 / 指标深化 / 工作台 UX 收口)→ v0.8.8(0.9.x 前最后清债版 Sparkling Tome)。
[0.8.8] - 2026-05-07
0.9.x 前最后一个清债版(Sparkling Tome)。 把 ROADMAP 中所有 P3 杂项 + 几个体量适中的 P2 一次性收齐,让 v0.9.x Grounded-SAM-2 主轴启动时不再被外围小问题打断。一次推进 12 件互相独立的事:① HTTPS / HSTS / CSP middleware(production-only,宽松基线 CSP 兼容 Vite shim + Cloudflare Turnstile,ADR-0010);②
_task_with_url改TaskOut.model_validate(schema 漂移自动跟随 v0.8.7skip_reason类增量字段,不再漏注入);③ 失败预测「永久放弃」全链路(failed_predictions.dismissed_at+POST /admin/failed-predictions/{id}/dismiss|restore+ admin 列表 toggle / 紫色 dismiss 按钮 / 灰色已放弃行);④/auth/refresh+ WebSocket 鉴权过期重连(7 天 grace、jti 黑名单 + gen + is_active 三段闭环、useNotificationSocketonclose 1008/4001 自动 refresh,长会话标注员不再被踢,ADR-0011);⑤ OpenAPI snapshot pre-commit hook(schemas/api 路径变更自动重生成 snapshot 并 git add,CI 不再因忘 export 红灯);⑥ Sentry DSN 缺失 production 启动 WARN(lifespan 检测 environment+DSN,error tracking 失踪不再悄悄发生);⑦ reviewer 端 skip badge + reject modal 预填(task.skip_reason 非空时紫色 SKIP badge + 顶部说明条 + RejectReasonModal 默认选「其他」预填「标注员跳过:xxx」);⑧ CommentsPanel keyset 分页(base64-urlsafe cursor,useAnnotationCommentsInfinite+ 「加载更早评论」按钮,单标注 100+ 评论不再初始化卡顿);⑨ Polygon 顶点 hit-test 改进(视觉 radius 5→6、命中 hitStrokeWidth 9,z-order 顺势按绘制顺序优先选最后落点);⑩ 前端工程化:bundle size budget(.size-limit.json+pnpm size+ CI 进 vitest 后阻断;当前 main 549/600KB / vendor-konva 283/320 / vendor-markdown 123/150)+ viteenvDir收口仓库根 .env SoT;⑪ 可观测性 stack(docker-composemonitoringprofile 加 prometheus + grafana service,infra/grafana/dashboards/anno-overview.json5 panel 覆盖 HTTP / ML / Celery,新增docs-site/dev/monitoring.md);⑫ 前端单测从 22.04% 推回 25.17%(5 个新 test 文件 ~35 case:turnstile / useCanvasDraftPersistence / RejectReasonModal / FailedPredictionsPage / useNotificationSocket / AnnotationHistoryTimeline),加review-approve-loop.spec.tsE2E 闭环(reviewer approve → annotator notification)。
Added
- production 安全响应头 middleware(
apps/api/app/middleware/security_headers.py):HSTSmax-age=31536000; includeSubDomains+ X-Content-Type-Options=nosniff + X-Frame-Options=DENY + Referrer-Policy=strict-origin-when-cross-origin + 宽松基线 CSP(兼容现有 inline style + Cloudflare Turnstile)。if settings.environment == "production"包裹注册,dev/staging 零影响。/metrics由独立 ASGI 子应用挂载,不经过本中间件——内网 scrape 无需 HSTS/CSP。详见docs/adr/0010-security-headers-middleware.md。 POST /auth/refresh(apps/api/app/api/v1/auth.py:284-380):用即将 / 已过期的 token 换新 token,7 天 grace。校验链:jwt.decode(verify_exp=False)→now > exp + 7d返 401 grace_expired → jti 黑名单返 401 token_revoked →user.is_active=False返 401 user_inactive → gen 不匹配返 401 generation_outdated → 通过则换发新 token + 写 auditauth.token_refresh(detail 含 old_jti / expired_seconds_ago)+ 更新 last_seen_at。slowapi 5/min/IP 限流。详见docs/adr/0011-websocket-token-reauth.md。- WebSocket 鉴权过期重连(
apps/web/src/hooks/useNotificationSocket.ts):onclose 检测 close code 1008/4001 →authApi.refresh()→ 写 authStore + 用新 token scheduleRetry;refresh 失败 → ApiError 401 → client.ts 自动 logout → 路由跳/login。refreshingflag 防止单次过期触发多次 refresh 打 5/min 限流。 - 失败预测 dismiss / restore 端点(
apps/api/app/api/v1/predictions.py):POST /admin/failed-predictions/{id}/dismiss写dismissed_at = now()+ auditfailed_prediction.dismissed;/restore清空 dismissed_at;列表?include_dismissed=false(默认隐藏)。dismiss 后 retry 返 409 防误用。前端FailedPredictionsPage加 toggle / 紫色「放弃」按钮(confirm 二次确认)+ 已放弃行灰色背景 + 「恢复」按钮。 - reviewer 端 skip 反馈(
apps/web/src/pages/Review/ReviewWorkbench.tsx+RejectReasonModal.tsx):task.skip_reason 非空时 ReviewWorkbench 顶部紫色说明条 + display_id 旁紫色SKIPbadge;ReviewPage 单任务退回时透传 skipReasonHint 到 RejectReasonModal,自动选「其他」并预填「标注员跳过:{reason}」。 - CommentsPanel keyset 分页:后端
GET /annotations/{id}/comments/page?cursor&limit=50(base64-urlsafe cursor,DESC(created_at, id))+ schemaAnnotationCommentListPage;前端useAnnotationCommentsInfinite+ 「加载更早评论」按钮;保留旧/comments端点向后兼容;mutation 同步 invalidate 新 / 旧 query key。 - Polygon 顶点 hit-test(
apps/web/src/pages/Workbench/stage/ImageStage.tsx):编辑态顶点 Circle radius 5→6 + 显式 hitStrokeWidth 9(命中区域比视觉大),顶点重叠时 Konva 按 z-order 自然选最后绘制者,与 PolygonTool 落点顺序一致。 - OpenAPI pre-commit hook(
.pre-commit-config.yaml):apps/api/app/api/**/*.py或apps/api/app/schemas/**/*.py变更时自动cd apps/api && uv run python ../../scripts/export_openapi.py+git add openapi.snapshot.json docs-site/api/openapi.json。 - Sentry DSN 启动 WARN(
apps/api/app/main.pylifespan):environment=="production" && !sentry_dsn时 logger.warning 不阻断启动,运维忘配置不再悄悄丢线上错误。 - bundle size budget(
apps/web/.size-limit.json+apps/web/scripts/check-bundle-size.mjs+pnpm size):纯 Node std lib 无第三方依赖,main 600KB / vendor-konva 320KB / vendor-markdown 150KB 三档预算。CI vitest 之后跑pnpm size,超阈值非零退出阻断 PR。当前实际 main 549 KB / vendor-konva 283 KB / vendor-markdown 123 KB。 - 可观测性 stack(docker-compose
monitoringprofile +infra/{prometheus,grafana}/):默认不启动避免 dev 多吃 ~200MB;docker compose --profile monitoring up -d prometheus grafana→ http://localhost:3001 admin/admin。Grafana 自动 provision datasourcePrometheus+ dashboard 文件夹Anno,anno-overview.json5 panel 覆盖 HTTP rate / HTTP p95 / ML p50/p95/p99 / Celery queue / Worker heartbeat。新增docs-site/dev/monitoring.md+ sidebar 入口。 - 前端单测推到 25.17%(
apps/web/vitest.config.ts阈值 22→25):5 个新 test 文件、~35 case:turnstile.test.ts(5)、useCanvasDraftPersistence.test.ts(8)、RejectReasonModal.test.tsx(4)、FailedPredictionsPage.test.tsx(9)、useNotificationSocket.test.tsx(6,含 ws reauth 关键路径用 MockWebSocket + fakeTimers + auth refresh mock)、AnnotationHistoryTimeline.test.tsx(9);vitest config 加scripts/**排除(build-time 脚本不算覆盖率分母)。 review-approve-loop.spec.tsE2E:reviewer 通过 UI approve → 后端校验 task.status=completed + annotator 视角/notifications取到task.approved通知,与 v0.8.7 reject feedback loop 镜像。- 新增 ADR:
0010-security-headers-middleware.md(HSTS/CSP 折衷与 follow-up)+0011-websocket-token-reauth.md(refresh grace 设计与攻击面分析)。 - 新增后端测试 ~14 case:
test_auth_refresh.py(6,含 happy path / grace_expired / inactive / generation_outdated / malformed / 速率限制 limiter.reset 兼容) +test_failed_predictions.py加 5(dismiss / dismiss_blocks_retry / restore / 幂等 / RBAC) +test_annotation_comments_paged.py(2,cursor 串联 + invalid_cursor 400)。
Changed
_task_with_url改用TaskOut.model_validate(task, from_attributes=True)(apps/api/app/api/v1/tasks.py:1025-1080):手写 30 字段 dict 改为 ORM 直读 + 4 个动态字段(file_url / thumbnail_url / image_dim+blurhash / assignee+reviewer brief)显式 patch。schema 加新字段(如 v0.8.7skip_reason/skipped_at)只需改TaskOut,本 helper 自动跟随,不再漏字段。apps/web/vite.config.ts加envDir: "../../":仓库根.env收口为前后端共用 SoT,避免apps/web/.env与根.env漂移。apps/web/.env.example已删(v0.8.7 落地)。apps/web/package.jsonversion 0.7.6 → 0.8.8:v0.8.x 起前端版本号同步主仓 release(之前漂移到 0.7.6)。apps/api/app/main.pyFastAPI version "0.8.3" → "0.8.8":同上。
Database
0047_failed_prediction_dismissed_at—failed_predictions.dismissed_at TIMESTAMPTZ NULL+ix_failed_predictions_dismissed_at(部分索引 WHERE NOT NULL,「显示已放弃」筛选快速过滤)
Fixed
- CommentsPanel 单标注 100+ 评论时初始化卡顿:旧
/annotations/{id}/comments全量返回,CommentsPanelmaxHeight: 240 + overflowY: auto仅控制视觉,不限制 DOM 节点数。改 keyset 分页后首屏只渲 50 条,按需追加。
Notes
- v0.8.8 完成后 0.9.x Grounded-SAM-2 主轴可专注 GPU backend 容器化,不被「P3 杂项 / 小动作」打断。剩余推迟项:截图自动化 14 张实跑回填、首次登录 onboarding tooltip、LoginPage progressive CAPTCHA、邮箱验证 / OAuth2 / 系统设置 admin UI、API 密钥模型、批次状态机 admin-locked 实施。
- security headers CSP 当前为「宽松基线版」(允许 'unsafe-inline');下一步 nonce-based 收紧留作 v0.10.x 与 ProjectSettingsPage 重构同窗口做。
/auth/refresh7 天 grace 是「出差一周回来 token 过期但身份还在」的体感线;超过强制重登。攻击者 stoles 一个未过期 token 后能 7 天内不停 refresh ——is_active=False+ 主动 logout 仍可斩断。- bundle main 当前 549 KB(接近 600 KB 上限):v0.9.x 接 SAM 工具时若主包再涨需要拆 vendor 子 chunk;记录在 ROADMAP P3。
- v0.8.8 是 v0.9.x(Grounded-SAM-2 主轴 ~5 周)启动前的最后一个准备版。
[0.8.7] - 2026-05-07
防机器人 / 指标深化 / E2E 续作 / 截图自动化 / 工作台 UX 收口。 一次性收 8 件可独立推进、互不依赖的事,让 v0.9.x Grounded-SAM-2 主轴启动前的最后一个准备版彻底干净:① CAPTCHA / Cloudflare Turnstile(注册 + 忘记密码两路防分布式刷号;TURNSTILE_ENABLED=False 时 service 层 short-circuit,dev/CI 透传不阻断);② Celery / ML Backend 指标(Prometheus 加
ml_backend_request_duration_secondsHistogram +celery_queue_length/celery_worker_heartbeat_secondsGauge;/health/celery扩 response 含 queues + workers 心跳;prediction_cost_stats 加 P50/P95/P99 PERCENTILE_CONT;AdminDashboard mini-stat 显示 P95);③ E2E review 反馈环 spec(reviewer 通过 UI reject → 后端校验 task.status/reject_reason;annotation.spec 升级为监听 POST /annotations 网络断言);④ Playwright 截图自动化(pnpm screenshots跑 14 场景写到 docs-site/user-guide/images,scenes.ts 配置驱动;keypoint 两张延后;不进 CI 避免 flaky);⑤ C.2 收口:阈值控件 Topbar[ ]主控统一 + AIInspectorPanel 改 read-only 显示 + ReviewerMiniPanel(今日通过/退回/平均耗时 3 mini-stat,20s 自动 refetch);⑥ C.3 Shift 锁纵横比 / Alt 中心 resize(applyResize 加 modifiers,6 例单测);⑦ C.3 任务跳过与原因(Task.skip_reason + skipped_at + POST /tasks/{id}/skip + Topbar「跳过」按钮 + SkipTaskModal 4 项预设原因);⑧ C.3 History 持久化(useAnnotationHistory 加 sessionStorage 5min TTL persist/restore,刷新后撤销栈不丢)。
Added
- Cloudflare Turnstile CAPTCHA 全栈(
apps/api/app/services/captcha_service.py、apps/web/src/lib/turnstile.ts、apps/web/src/components/Captcha.tsx):后端 verify_turnstile_token 3s 超时、fail-closed;前端动态注入官方 api.js + 受控组件;TURNSTILE_ENABLED=False时 short-circuit 通过,dev/CI 不阻断;启用后注册(/auth/register-open)与忘记密码(/auth/forgot-password)必须携带 captcha_token,校验失败 400captcha_failed。 - Prometheus 指标三件(
apps/api/app/observability/metrics.py):ml_backend_request_duration_seconds(Histogram,labels=backend_id+outcome;MLBackendClient.predict / predict_interactive 计时 observe)+celery_queue_length(Gauge,labels=queue)+celery_worker_heartbeat_seconds(Gauge,labels=worker);/health/celery同步暴露 queues + workers 心跳明细。 - 预测延迟分位数:
/dashboard/admin/prediction-cost-statsresponse 加p50/p95/p99_inference_time_ms(PostgreSQLPERCENTILE_CONT(0.5/0.95/0.99) WITHIN GROUP (ORDER BY prediction_metas.inference_time_ms));AdminDashboardMLBackendsAndCostCard在「平均耗时」mini-stat 下方显示 P95 hint。 GET /dashboard/reviewer/today-mini(apps/api/app/api/v1/dashboard.py):返当日 approved_today / rejected_today / avg_review_seconds 3 个数(基于 Task.reviewer_id == me + reviewed_at >= today UTC + reject_reason 区分通过/退回);前端ReviewerMiniPanel在 ReviewWorkbench 右侧栏顶部渲染,useReviewerTodayMini20s 自动 refetch。POST /tasks/{id}/skip(apps/api/app/api/v1/tasks.py):标注员跳过任务接口,body{reason: "image_corrupt"|"no_target"|"unclear"|"other", note?: str};状态 pending/in_progress → review,其他 409;非法 reason 422;同时落 audit_logstask.skip+ 设 task.skip_reason + skipped_at;前端SkipTaskModal4 项预设原因 + 可选 note,Topbar 提交按钮旁加「跳过」入口(仅非 review/completed 状态显示)。- applyResize 修饰键(
apps/web/src/pages/Workbench/stage/ResizeHandles.tsx):{shiftKey, altKey}入参;shift 锁起始 aspect ratio(newW/newH = origW/origH,按 |dx| 与 |dy*aspect| 较大轴选主导方向);alt 以 bbox 中心为 anchor 反向 mirror(一边变化 dw → 总宽度变 2dw);两键叠加。ImageStageresize handler 透传e.shiftKey/e.altKey。 - History 持久化(
apps/web/src/pages/Workbench/state/useAnnotationHistory.ts):sessionStorage keywb:hist:{taskId}存{undo, redo, ts},TTL 5 分钟(与 prediction id 替换窗口对齐);taskId 切换 / 初始化时尝试 restore;写时机 throttle 50ms;JSON 损坏 / quota 异常静默忽略,不破坏内存 history。 - Playwright 截图自动化(
apps/web/e2e/screenshots/):scenes.ts14 场景配置(getting-started login/forgot-password、bbox toolbar/iou/bulk-edit、polygon vertex-edit/close-hint、projects create-entry/wizard-steps、review workbench/reject-form、export format-select/progress;keypoint 两张延后);screenshots.spec.ts主入口固定 1440×900 视口、动画 disable、networkidle 等待;pnpm --filter web screenshots触发,playwright.config.tstestIgnore 排除让test:e2e不跑。 - 新增前端测试 4 个文件 / ~20 case:
Captcha.test.tsx(3)、SkipTaskModal.test.tsx(4)、ResizeHandles.test.ts(6)、ReviewerMiniPanel.test.tsx(1)+ useAnnotationHistory.test.ts 加 sessionStorage 6 case;前端单测从 277 → 297。 - 新增后端测试 4 个文件 / 17 case:
test_captcha_service.py(7,含 disabled / no-secret / 网络异常 / siteverify 200/200-fail / 超时 / 503)、test_metrics_celery.py(3,含 queue 聚合 / no-workers 503 / Prometheus Gauge 写入)、test_ml_client_metrics.py(3,含 success/error Histogram observe + backend 自报值优先 wall-clock fallback)、test_dashboard_reviewer_mini.py(4,含权限 / 多 reviewer 隔离 / today only)+test_open_registration.py加 2(CAPTCHA 缺失 400 / token 通过)+test_prediction_cost_stats.py加 1(PERCENTILE_CONT 100 样本断言)+test_task_skip.py(5,含合法/非法 reason / 状态 409 / audit / 自分派)。 .env.example/apps/web/.env.example:新增TURNSTILE_ENABLED / SITE_KEY / SECRET_KEY+VITE_TURNSTILE_SITE_KEY占位 + 注释指向 Cloudflare 控制台。
Changed
- AIInspectorPanel 阈值控件改为 read-only 数值(
apps/web/src/pages/Workbench/shell/AIInspectorPanel.tsx):原生<input type="range">slider 移除,改为「在工具栏使用[/]调整」提示文案 + 滚轮微调 fallback;阈值百分比与刻度尺保留作视觉反馈。Topbar[/]仍是主控(行为零变化)。 /health/celeryresponse shape 升级(apps/api/app/api/health.py):workers从list[str]升级为list[{name, last_heartbeat_seconds_ago, pool_max}];新增queues: list[{name, length}];旧测试桩(仅 mockping())通过try/except降级为空 active/reserved/stats 兼容。- vitest coverage thresholds 临时降到 22:v0.8.7 引入 8 个新组件/hook,分母增长大于新单测覆盖(实测 22.04%,297 case);下一版优先补 ProjectSettingsPage / AuditPage / WorkbenchShell hook,目标推回 ≥ 25 → 30。
Database
0046_task_skip_reason—tasks.skip_reason VARCHAR(50)+tasks.skipped_at TIMESTAMPTZ(NULL 默认;现有数据无需迁移)
Fixed
pydantic-settings2.13 默认 extra=forbid 导致本地启动炸(apps/api/app/config.py):.env中含VITE_*前端变量与 Settings 字段不匹配,pydantic 2.13 起拒绝;显式class Config: extra = "ignore"让前后端共用 .env。
Notes
- v0.8.7 是 v0.9.x(Grounded-SAM-2 接入)启动前最后一个准备版;CAPTCHA 与指标体系到位后 v0.9.x M0/M1 可专注 GPU backend 容器化。
- Turnstile 占位 sitekey
1x00000000000000000000AA(永远成功)/2x00000000000000000000AB(永远失败)官方测试用,便于 production 部署前自查链路。 - E2E
review-feedback-loop.spec.ts与annotation.spec.tsbbox 落库网络断言均依赖完整启动栈(docker + api + dev),CI workers=1 + fullyParallel=false 串行跑无并发风险。 - 截图自动化 14 张为基线,部分场景(iou.png 双框 / progress.png 真实 50%)需 maintainer 准备数据后人工抓帧覆盖;keypoint 两张随非 image-det 工作台 epic 一起补。
[0.8.6] - 2026-05-07
v0.9.x 准备版。 Grounded-SAM-2 主轴(v0.9.0 起 ~5 周)启动前,把不依赖 GPU 的 6 件事提前做完:① 协议 §2.2
context.type扩text(schema docstring + 文档同步,exemplar留给 v0.10.x SAM 3);② ML Backend 周期健康检查(Celery beat 每 60s,串行 + 0-3s 抖动错峰,新增ml_backends.last_checked_at);③Project.ml_backend_id真实绑定 + GeneralSection / CreateWizard 改造(ai_model保留作 display hint,绑定 backend 时自动用backend.name覆盖;ON DELETE SET NULL);④ AdminDashboard 预测成本卡片(/admin/prediction-cost-stats?range=7d|30d+ 4 mini-stat:调用数 / 平均耗时 / 失败率 / 总成本,by_backend 维度);⑤apps/_shared/mask_utils/共享 Python 包骨架(mask_to_polygoncv2.findContours + shapely.simplify + 顶点归一化 + 单测,v0.9.0 grounded-sam2-backend 与 v0.10.x sam3-backend 共用);⑥ 失败预测重试管理页(Celery 异步 + WebSocket 推failed_prediction.retry.{started,succeeded,failed}进度,单条 max 3 次,超过返 409)。一次性收 ROADMAP §A 四条 + 提前完成 v0.9.5 M5 三件,让 v0.9.0 一开张直接做 GPU backend 容器化主线,不被外围杂事打断。
Added
- 协议 §2.2
context.type扩text(docs-site/dev/ml-backend-protocol.md):v0.9.x Grounded-SAM-2 文本批量 prompt 入口;InteractiveRequest.context仍为开放 dict 不锁死,docstring 列出 4 种 type 与示例。 - ML Backend 周期健康检查 Celery task(
apps/api/app/workers/ml_health.py):check_ml_backends_health每 60s 触发,串行扫所有 backend,每个调用前random.uniform(0, 3)s 抖动错峰,避免同 GPU 节点 CUDA 上下文 contention。复用MLBackendService.check_health写state+last_checked_at。MLBackendOut.last_checked_at暴露给前端。 Project.ml_backend_id外键绑定:Alembic0044_project_ml_backend_id(FKml_backends.idON DELETE SET NULL + 部分索引 WHERE NOT NULL);ProjectCreate/Update/Outschema 同步;MLBackendService.get_project_backend(project_id)优先返回显式绑定,否则 fallback 到is_interactive=True && state=connected任选;POST/PATCH/projectshandler_apply_backend_display_hint用backend.name覆盖ai_model。- GeneralSection MLBackend 下拉(
apps/web/src/pages/Projects/sections/GeneralSection.tsx):开启 AI 后展示「实际 ML Backend」下拉,列出当前项目已注册 backends + 在线状态徽标 + 是否交互式;选「未绑定」也允许(仅显示ai_modelhint)。CreateProjectWizard 步骤 3 加引导文案「项目创建后到 ML 模型 tab 注册 backend 再回基本信息绑定」。 - AdminDashboard 预测成本卡片:
GET /admin/prediction-cost-stats?range=7d|30d端点基于predictions × prediction_metas × failed_predictions × ml_backends聚合;前端MLBackendsAndCostCard替换原「ML 后端状态」单卡,4 mini-stat(调用数 / 平均耗时 / 失败率 / 总成本)+ Range toggle;异常时降级返回零值避免 Dashboard 黑屏。 apps/_shared/mask_utils/Python 包:pyproject.toml(anno-mask-utils@0.1.0, py>=3.10, numpy + opencv-python-headless + shapely)、src/mask_utils/{polygon,normalize}.py、tests/test_polygon.py(圆 / 方 / 空 mask × IoU≥0.95)。本期apps/api不依赖此包;为 v0.9.0 M0/M3 grounded-sam2-backend 与 v0.10.x sam3-backend 共用而提前就位。- 失败预测重试管理页
/admin/failed-predictions(apps/web/src/pages/Admin/FailedPredictionsPage.tsx):表格列 项目 / 任务 / Backend / 错误类型 / 消息 / 重试 N/3 / 时间 / 重试按钮。后端新路由apps/api/app/api/v1/predictions.py:GET /admin/failed-predictions?page&page_size(分页 + 关联 task / project / backend)+POST /admin/failed-predictions/{id}/retry(202 投递 Celery task)。PageKey加admin-failed-predictions;super_admin / project_admin 可见。 retry_failed_predictionCelery task(apps/api/app/workers/predictions_retry.py,路由ml队列):读 failed_prediction → 推 wsfailed_prediction.retry.started→ 调 backend.predict → 成功则插 predictions + 删 failed + 推succeeded;失败则retry_count += 1+last_retry_at+ 推failed。max=3 软上限由路由层判断(HTTP 409)。- WebSocket retry 事件 invalidate(
apps/web/src/hooks/useNotificationSocket.ts):消息type以failed_prediction.retry.起头时同时 invalidate["admin", "failed-predictions"]query,列表自动刷新无需轮询。 - AdminDashboard 失败预测入口卡:跳转
/admin/failed-predictions。 - 20 个新单测:
test_ml_backend_schemas(6)、test_ml_health_worker(6,含 worker 异常隔离)、test_projects_ml_backend_binding(6,含 ON DELETE SET NULL)、test_prediction_cost_stats(5)、test_failed_predictions(6,含 max 3 次返 409)、mask_utils 独立包test_polygon(7)。
Changed
Project.ai_model语义重定义为 display hint:保留字段不删除,绑定 backend 时自动用backend.name覆盖;未绑定时仍可手填作为 Badge 显示(5 处 Dashboard / ProjectGrid 渲染逻辑零改动)。MLBackendService.check_health写last_checked_at:除原有的 state 更新外,每次调用都打上时间戳,便于 UI 显示心跳新鲜度。
Database
0043_ml_backend_last_checked_at—ml_backends.last_checked_at TIMESTAMPTZ(周期健康检查时间戳)0044_project_ml_backend_id—projects.ml_backend_id UUID FK ml_backends(id) ON DELETE SET NULL+ix_projects_ml_backend_id部分索引0045_failed_prediction_retry_count—failed_predictions.retry_count INT NOT NULL DEFAULT 0+last_retry_at TIMESTAMPTZ+extra JSONB
Notes
- v0.8.6 提前完成的 v0.9.5 M5 任务(
ROADMAP/[archived]0.9.x.md同步标记):周期健康检查、失败预测重试 UI、预测成本卡片。M5 工时从 3d 降到 1.5d,仅剩 backend 显存监控 + ADR-0010/0011 + deploy 文档。 - mask_utils 包 ~50MB cv2 依赖暂不进
apps/api镜像;v0.9.x backend 镜像本就有 cv2 可复用。 - 30d 内
predictions数据量级 < 10K,predictions.created_at暂不加 BRIN 索引;CHANGELOG 留 TODO「数据量上 10 万后再加」。
[0.8.5] - 2026-05-07
fabric 清理 / 24-bar 专注时段直方图 / 单测 25% 硬阻断 / E2E 写实化。 收口 v0.7.x ~ v0.8.4 三个尾巴:① 清掉
fabric@^6.5.0dead dep(apps/web/src/全量零引用,仅App.tsx:22注释提到,ADR-0004 评估通过);② v0.8.4 已造好的<Histogram>组件接到个人 dashboard,标注员可看到当日 0-23 时的标注分钟数节律;③ 前端单测覆盖率从 v0.8.3 的 10.88% 推到 25.28%,CI 阈值同步上调到 25 严格阻断回退;④ E2E annotation/batch-flow 从「最小 happy path」升级为带 bbox 拖框 + 多角色串联的写实场景,工作台引入 4 处 data-testid 提供稳定 selector,后端补_test_seed.advance_task辅助端点跳过 UI 链路串联多角色。
Added
- AnnotatorDashboard「今日专注时段分布」24-bar 直方图:后端
/dashboard/annotator新增hour_buckets: list[int]字段,按EXTRACT(hour, TaskEvent.started_at)GROUP BY 聚合duration_ms / 60_000;前端复用 v0.8.4 的<Histogram>组件,xLabels=["00:00", "23:00"]。 - 工作台 data-testid(4 处):
tool-btn-{id}(ToolDock 工具按钮,含 box / hand / polygon / canvas)、workbench-stage(ImageStage 容器)、workbench-submit(Topbar 提交质检按钮)、settings-tab-{key}(ProjectSettingsPage 8 个 section tab)。 POST /api/v1/__test/seed/advance_task测试辅助端点:直接置 task 到目标状态(pending / annotating / submitted / review / completed / rejected),可同时绑定 annotator / reviewer,绕过 UI 链路。E2E batch-flow.spec 用此跳过画框 / 提交流程,专注验证多角色交接。- 9 个新前端测试文件(277 case):Histogram / AnnotatorDashboard / ReviewerDashboard / AdminDashboard / ViewerDashboard / LoginPage / RegisterPage / PasswordPages(Forgot + Reset)/ InviteUserModal / useDashboard。
- vitest setup localStorage / sessionStorage polyfill:jsdom 在 about:blank(opaque origin)下不提供 storage,导致 zustand persist 在 setState 时炸
storage.setItem is not a function,统一注入内存 storage 兜底,afterEach清理。
Changed
apps/web/vite.config.tscoverage thresholds:lines / statements10 → 25,functions / branches 维持。pnpm test:coverage实测 lines=25.28%(277 case,0.28pp 容差)。- E2E annotation.spec:保留 smoke「路由可达」case;新增 bbox 拖框 case(getByTestId
tool-btn-box→boundingBox()计算坐标 →mouse.move/down/up模拟拖框 →seed.advanceTask(submitted)模拟提交结果 → 断言 URL 未崩溃)。 - E2E batch-flow.spec:保留 smoke「项目设置页 200」case;新增三角色串联(annotator submit → reviewer 看到 /review → admin 切到 batches tab)。
apps/web/package.json:删fabric@^6.5.0(lockfile 重生);apps/web/src/App.tsx:22注释里konva / fabric→konva。
Fixed
- vitest jsdom 环境 zustand persist 崩溃:v0.8.3 起 11 个 case(usePermissions / stores)依赖 zustand persist 写
localStorage,但 jsdom 默认 opaque origin 抛SecurityError: localStorage is not available,导致storage.setItem is not a function。setup 注入内存 storage polyfill 解除。 tests/factory.create_projectclasses 字段类型不匹配:写的是[{"name": c}]但ProjectOut.classes: list[str],导致 GET/projects/:id在 ResponseValidationError 上 500。E2E 写实化暴露的预先存在 bug。修为list(classes or ["car", "person"])。_test_seed.seed_reset非特权用户工作台空任务:seed 创建的 task 无batch_id,非特权用户在list_tasks受batch_visibility_clause过滤后看不到孤儿任务,工作台显示「该项目暂无任务」。reset 现在创建status='annotating'+annotator_id/reviewer_id双绑定的 default batch,并把 5 个 task 全归入;同时补 ProjectMember 行(annotator + reviewer),否则RequireProjectMember在/projects/:id/annotate拦回 dashboard。- playwright
fullyParallelseed/reset 互相覆盖:seed/reset 是数据库 TRUNCATE 全局操作,多 spec 并发会让 fixture 拿到对方的造数结果而 500。playwright.config.ts改为fullyParallel: false, workers: 1。
Removed
fabric@^6.5.0:v0.7.x ~ v0.8.0 ADR-0004 评估时已确认apps/web/src/全量零引用,本期清理。bundle 减少 ~150KB / 一项 supply-chain 风险面。
[0.8.4 hotfix] - 2026-05-06
接通 v0.8.4 的活跃维度占位字段。 v0.8.4 与 v0.8.3 并行开发,落地时心跳基座尚未合并,因此
active_minutes_today / streak_days / activity_score三处保持None / 50占位待下一版接通;v0.8.3 合并后即解除阻塞,本热修复直接基于task_events把三处真实化,前端组件不动(schema 字段类型不变)。
变更
GET /dashboard/annotator:active_minutes_today←SUM(task_events.duration_ms) / 60000WHEREstarted_at >= today UTC。streak_days← 从今天倒推(started_at AT TIME ZONE 'UTC')::datedistinct 连续天数(30 天上限),口径与mv_user_perf_daily.day一致。
GET /dashboard/admin/people:activity_score← 7d 窗口SUM(task_events.duration_ms)按用户分组后的团队百分位(沿用_percentile_rank),替换固定 50 占位。直查 task_events 而非 mv 视图,避免 1h 刷新 lag 让活跃用户被打回中位。
- 测试:
tests/test_task_events_batch.py新增 2 例(共 8 例):annotator 接通后active_minutes_today / streak_days数值正确;admin/people 活跃用户activity_score严格 > 非活跃用户。
修复
apps/api/alembic版本号漂移:开发环境出现"relation "task_events" already exists",因 0.8.3/0.8.4 并行 worktree 中已 apply 0039–0042 但主仓alembic_version停留在 0038。运行时无影响,需alembic stamp head同步(schema 已正确)。
[0.8.4] - 2026-05-06
效率看板 / 人员绩效 epic。 一次性把 ROADMAP P1「Layer 1 数据沉淀 + Layer 2 个人 dashboard 强化 + Layer 3 管理员人员看板」三层落地:标注员/审核员能自查产能/质量/投入;管理员有
/admin/people卡片网格 + 抽屉下钻看全员效率。
数据沉淀:
Task.assigned_at写入点全量改派(_cascade_task_assignee/task.submit兜底 / 用户注销改派);新表task_events(工作台useSessionStats每 20 条 flush);物化视图mv_user_perf_daily每小时 refresh。
L2/L3 占位字段:
active_minutes_today / streak_days / activity_score当前后端返None / 50。v0.8.3 已落last_seen_at心跳基座,下一版只需切 dashboard 端点的占位逻辑即可点亮,前端组件无需改动。
新增
- 迁移 0039-0042(4 个,链在 v0.8.3 的
0038_user_last_seen_at之后):0039_task_assigned_at:tasks.assigned_at TIMESTAMPTZ+ 部分索引(assignee_id, assigned_at DESC) WHERE assigned_at IS NOT NULL。idempotent(ADD COLUMN IF NOT EXISTS)。0040_task_events:新表task_events(id PK, task_id, user_id, project_id, kind ENUM(annotate|review), started_at, ended_at, duration_ms, annotation_count, was_rejected)+ 三个时序索引 + CHECK 约束。0041_mv_user_perf_daily:物化视图(user_id, project_id, kind, day) → throughput / median_duration_ms / p95_duration_ms / rejected_n / active_minutes,UNIQUE 索引支持 CONCURRENTLY refresh。0042_user_weekly_target:users.weekly_target_default+project_members.weekly_target,替换 AnnotatorDashboard.tsxweeklyTarget = 200硬编码。
- 后端端点(4 个):
POST /auth/me/task-events:batch:单批 ≤ 200 条,user_id 强制覆盖为登录用户(防伪造);走 Celery 异步队列app.workers.task_events.persist_task_events_batch,broker 不可用 → sync fallback。GET /dashboard/admin/people?role=&period=&sort=&q=:super_admin only,返回每人{main_metric, throughput_score, quality_score, activity_score, sparkline_7d, alerts},告警 chip:被退回率 > 15% / 周环比降 > 30%。GET /dashboard/admin/people/{user_id}?period=4w:详情 — 4 周趋势 / 项目分布 / 耗时直方图(10 桶 + p50/p95)/ 最近 50 条 timeline。GET /dashboard/annotator//reviewer扩展:annotator 增加median_duration_ms / rejected_rate / reopened_avg / weekly_compare_pct / weekly_target;reviewer 增加median_review_duration_ms / reopen_after_approve_rate / weekly_compare_pct / daily_review_counts。
- Celery beat hourly refresh:
refresh_user_perf_mvcrontabminute=5,REFRESH MATERIALIZED VIEW CONCURRENTLY mv_user_perf_daily;端点优先读视图,当日窗口直查 task_events 兜底。 - 前端原子组件:
<SectionDivider>/<RadialProgress>/<Histogram>(仿 RegistrationSourceCard SVG bar 风格,不引图表库)。 - AnnotatorDashboard 5 卡 → 9 卡三段:产能(待标 / 今日 / 本周 + 周环比 + sparkline / 单题中位耗时)+ 质量(原创比例 / 退回率 / 重审次数 avg)+ 投入(活跃时长 / streak / 累计),心跳依赖项暂显
—。 - ReviewerDashboard 5 卡 → 6 卡两段:产能(待审 / 今日 + 周环比 + sparkline / 平均审核耗时 / 累计)+ 质量(24h 通过率 / 历史通过率 / 二次返修率)。
- AdminPeoplePage(
/admin/people):sticky 筛选栏(角色 / 时间 / 排序 / 搜索 → URL search params 同步)+ 响应式卡片网格(auto-fill minmax 280px)+ 右侧抽屉个人详情(4 hero KPI + 4 周趋势双 sparkline + 耗时直方图 + 项目分布 + timeline)。super_admin RBAC 双重门(路由 + endpoint)。 - AdminDashboard 入口卡:顶部 4 总量 StatCard 下方加「成员绩效 →」可点击 Card,跳
/admin/people。 - 工作台埋点:
useSessionStats(currentTaskId, projectId, kind)现支持pendingEvents缓冲,每 20 条或 unmount/pagehide 时 flush 到meApi.submitTaskEvents。失败静默丢弃避免雪崩。 - ADR-0009:
docs/adr/0009-task-events-table-and-partition.md记录 task_events 月分区两阶段方案:Stage 1(本期)普通表 + 时序索引;Stage 2(行数 > 1M 或单月 INSERT > 100k 触发)按started_atRANGE 分区,参考 ADR-0006 / ADR-0008 模式。 - 测试:
tests/test_task_events_batch.py(6 例)覆盖 sync fallback INSERT 落库、ended_at < started_at422、/admin/people403 / 200 / 404 / detail 200。
变更
- alembic env.py:
config.set_main_option("sqlalchemy.url", ...)改为只在调用方未注入 URL 时设置,否则 conftest 注入的 test DB URL 会被覆盖到 dev DB。 app/services/batch.py:_cascade_task_assignee:user_id非空时assigned_at = func.now(),否则置 NULL。app/api/v1/tasks.py:548/app/api/v1/users.py:587:兜底分派 / 注销改派路径同步写assigned_at。app/api/v1/dashboard.py:annotator/reviewer 端点新增字段(详见上文)。- OpenAPI snapshot 同步:新增 7 个 schema(AdminPeopleList / AdminPersonItem / AdminPersonDetail / TaskEventIn / TaskEventBatchIn / TaskEventBatchOut + reviewer/annotator 字段)。
推迟(下一版收口)
- 接通活跃时长 / streak / activity_score:
占位→ 已在 v0.8.4.1 hotfix 接通,口径切到None / 50task_events而非last_seen_at(埋点累计耗时比心跳点位更贴合"投入维度"语义)。 - L3 leaderboard Tab:
GET /dashboard/admin/people/leaderboard端点占位未实现,UI 也未暴露。
[0.8.3] - 2026-05-06
治理 / 测试基建闭环。 把 ROADMAP 中四块 P1/P2 一次性收齐:在线状态心跳机制(替代旧的 status==online 偏差)、审计不可变 trigger 测试覆盖(兜底
security.md的可靠声明)、前端单测推到稳定 baseline 并切硬阻断(v0.7.6 8.68% → 10.88%)、E2E 三 spec 写实 + 摘continue-on-error。后续效率看板 / SAM 接入等 P1 大件不再被基建空缺挡路。
新增
- 在线状态心跳机制(A · UsersPage):迁移 0038 给
users表加last_seen_at列(带索引);POST /auth/me/heartbeat端点(前端 30s 周期触发,仅 visible tab 跑);Celery beat 任务mark_inactive_offline(每 2 分钟扫描,把超OFFLINE_THRESHOLD_MINUTES=5min 未活跃的 online 用户置 offline);GET /users/stats返回{total, online, weekly_active},weekly_active基于last_seen_at >= now-7d。前端useHeartbeathook 挂在 AppShell + FullScreenWorkbench;UsersPage 顶部「本周活跃」改读后端聚合。修复了「关浏览器永远在线」的偏差,同时为下一版「效率看板 P1.投入 维度」打底。 - 审计日志不可变 trigger 测试覆盖(B · 治理合规):
tests/test_audit_immutability.py5 条 case 覆盖 ① UPDATE 阻断 ② DELETE 阻断 ③SET LOCAL "app.allow_audit_update" = 'true'豁免后允许 ④ 豁免不跨 SAVEPOINT 泄漏 ⑤ COPY 路径(pg_restore 用)走 BEFORE ROW trigger 不触发。security.md已声称可靠,本版补测试兜底,防止后续分区 / trigger 重写时回归。 - E2E 基础设施(B · 测试/DX):
apps/api/tests/factory.py(create_user/create_project/create_task/create_batch,幂等可重入)+apps/api/app/api/v1/_test_seed.py(仅environment != 'production'挂载,POST /api/v1/__test/seed/resettruncate + 重建 admin/annotator/reviewer + 1 项目 + 5 task;POST /__test/seed/login跳密码发 JWT)+apps/web/e2e/fixtures/seed.ts(Playwright fixture,提供seed.reset()/seed.injectToken(page, email)/seed.loginViaUI(page, email, pwd))。 - 前端单测 8 个新测试文件 + 切硬阻断:
useSessionStats.test.ts(ring buffer 边界 + dt 过滤)useHeartbeat.test.ts(visible / hidden 切换 + 401 静默)usePermissions.test.ts(5 角色权限分支)auditLabels.test.ts(业务动作过滤 / 中文标签 fallback)iou.test.ts+polygonGeom.test.ts(几何工具)transforms.test.ts(bbox / polygon / prediction 映射)useAnnotationHistory.hook.test.ts(栈状态机 + 切任务清栈 + tmpId 替换)stores.test.ts(authStore / appStore / bugDrawerStore 三个 zustand store)
变更
apps/api/app/api/v1/auth.py:99-104:login成功时同步写user.last_seen_at = now,与last_login_at同步。logout/logout_all 保持仅切status='offline'(last_seen_at 不重置,登出仍是活跃)。apps/api/app/api/v1/me.py:新增POST /heartbeat(无 body,204 返回,刷新 last_seen_at + status='online')。apps/api/app/workers/celery_app.py:beat_schedule 新增mark-inactive-offline(crontab(minute="*/2"));include加app.workers.presence。apps/api/app/config.py:新增offline_threshold_minutes: int = 5配置(30s 心跳 × 10 容差)。apps/web/src/pages/Users/UsersPage.tsx:150:「本周活跃」从本地filter(status==="online")改读useUsersStats().weekly_active。apps/web/src/App.tsx:AppShell 与 FullScreenWorkbench 各调一次useHeartbeat(),覆盖工作台与主壳两条路径。apps/web/vite.config.ts:coverage 配置加 thresholds(lines/statements 10%、branches 60%、functions 30%)+ exclude(types / mock data / Konva 工具 / bugReportCapture 等不可单元测部分)。低于阈值时 vitest 退出非 0。codecov.yml切硬阻断:backend.informational: false(target 60%,已稳定)+frontend.informational: false(target 10%,实测 10.88% 留 0.88pp 容差)。下一版逐步推 frontend 向 ROADMAP P2 列出的 25%。.github/workflows/ci.ymle2e job:摘掉continue-on-error: true;env 加ENVIRONMENT=development暴露_test_seedrouter,加PLAYWRIGHT_API_BASE让 fixture 知道后端地址。apps/web/e2e/tests/{auth,annotation,batch-flow}.spec.ts三个 spec 全部写实:auth.spec.ts:登录 → dashboard / 错密码留页 + 错误条 / 未登录访问 /dashboard 跳 /loginannotation.spec.ts:annotator 跳登录后 /annotate 路由可达batch-flow.spec.ts:admin 跳登录后项目设置页可达
推迟(明确不在本期)
- 效率看板 Layer 1/2/3:依赖本版心跳但工作量独立(2-3 + 1-2 + 3-4 天),留给 v0.8.4+
- ADR-0008 admin-locked 实施:v0.8.2 ADR 仍 Proposed,scheduler 测试覆盖未补
- OAuth / CAPTCHA / 邮箱验证:开放注册基座已稳,加固延后
- fabric.js dead dep 清理:非心跳 / 测试主题
- 前端单测推到 25%:8 个新测试 + 167 case 把 baseline 从 8.68% 推到 10.88%;25% 需再 6000 行覆盖,留给 v0.8.4+
[0.8.2] - 2026-05-06
文档深度优化。 把 v0.8.0 / v0.8.1 留下的四处文档机制缝隙以自动化方式补齐:ADR 不再孤悬 GitHub、
pnpm docs:build进 PR gate、how-to 与源码漂移即报错、ML Backend 协议有可跑样板。后续文档随代码自然漂移即被 CI 拦下,免人工巡检。
新增
pnpm docs:build进 CI gate:.github/workflows/ci.yml新增docs-buildjob,所有 PR 都跑(不带paths过滤),约 5s 成本;dead-link / hotkeys SoT 漂移 / snippet 不一致即时阻断。原docs.yml保留为 GitHub Pages 发布触发,无重复构建影响。docs-site/scripts/check-doc-snippets.mjs+ snippet 标记机制:扫.md中的<!-- snippet:PATH:START-END -->...<!-- /snippet -->块,逐行比对源文件区间与代码块内容,不一致即打印 diff 并 exit 1。docs-site/package.json的prebuild链中追加;pnpm check:snippets顶层别名可直接调用。add-api-endpoint.md加 snippet 标记:logout 代码块绑定apps/api/app/api/v1/auth.py:239-266;同时把 v0.8.0 写入时已漂移的内容(漏current_user.status = "offline"、AuditService.log 压缩单行)对齐到当前真实源码。后续 logout 函数任一字符变化 prebuild 即报。docs-site/scripts/mirror-adr.mjs+ ADR 接入 sidebar:把docs/adr/*.md镜像到docs-site/dev/adr/让 VitePress 渲染,文件头注入"自动镜像"警告条;同时输出sidebar.generated.json。docs-site/.vitepress/config.ts顶部读取该 JSON,在/dev/侧边栏底部新增「ADR(架构决策)」可折叠组(默认 collapsed)。.gitignore排除docs-site/dev/adr/,避免 mirror 产物入库。docs-site/dev/examples/echo-ml-backend/可执行样板(5 文件):main.py(协议 §1-3 四端点完整 FastAPI 实现)+requirements.txt+Dockerfile(python:3.11-slim + uvicorn)+test.sh(curl 三连击 health/setup/predict)+README.md(uvicorn / docker 两种启动方式 + 接入平台步骤)。ml-backend-protocol.md §8改为<!-- snippet -->引用main.py:1-63,inline 示例与样板永远同步。- ADR-0008(Proposed)批次 admin-locked 字段:
docs/adr/0008-batch-admin-locked-status.md把 ROADMAP A §批次状态机二阶段「annotating → active暂停」难点(scheduler 死锁)文字化。决定引入正交admin_locked: bool字段(独立于 7 态枚举),check_auto_transitions 起始处短路返回;包含表迁移 SQL、API 端点设计(lock/unlock)、状态机 mermaid 图、3 种被拒绝方案(PAUSED 枚举 / task 级锁 / 借 ARCHIVED)的取舍理由。仅设计,实现推迟到 v0.9 评估窗口。
变更
docs-site/package.jsonprebuild 链:从sync-openapi && generate-hotkeys扩为sync-openapi && mirror-adr && generate-hotkeys && check-doc-snippets;新增check:snippets/mirror:adr顶层 script。.vitepress/config.ts:/dev/侧边栏底部新增ADR(架构决策)折叠组,items 由 mirror-adr 输出的 sidebar.generated.json 注入;缺文件时降级为空数组让 VitePress 仍可启动。
推迟(明确不在本期)
- 截图自动化(Playwright + IMAGE_CHECKLIST 16 处):与 E2E spec 写实共建 fixture,1-2 天深活,本期窗口不足
- getting-started 3 张 GIF 录屏:等截图自动化方案落地后批量产出
- fabric.js dead dep 清理:非文档主题,留给下次依赖清理 PR
[0.8.1] - 2026-05-06
治理 / 合规向收口 epic。 一次性把 ROADMAP 中「系统设置可编辑、注册统计、自助注销、管理员重置密码、审计分区归档、数据导出审计」6 项硬残缺收齐。
新增
- 系统设置 DB 化(PR 1):新表
system_settings(迁移 0034)+SystemSettingsService(30s LRU)+PATCH /settings/system(super_admin only)。白名单字段allow_open_registration / invitation_ttl_days / frontend_base_url / smtp_*;黑名单字段(SECRET_KEY / DATABASE_URL / ENVIRONMENT等)永不可在 UI 编辑。smtp_password在 GET 响应掩码(password_set: bool),audit_log detail 仅记录{changed: True}不含明文。 - SMTP 测试发送:
POST /settings/system/test-smtp(3/min限流),用stdlib smtplib即时给当前管理员发一封测试邮件,无新依赖。 - SettingsPage 系统设置区改可编辑:受控表单 + 「立即生效 / 需新会话生效」标注 + SMTP 密码独立「设置 / 更换」按钮 + 「发送测试邮件到我」按钮。
- 注册统计仪表卡(PR 2A):
/dashboard/admin响应增加registration_by_day(30 天,邀请 vs 开放注册分别聚合audit_logs.detail_json.method/invitation_id)。AdminDashboard 新增「30 天注册来源」双柱条形图(沿用现有<StatusBar>风格,不引入图表库)。 - 管理员重置低等级用户密码(PR 2B):
POST /users/{id}/admin-reset-password(super_admin / project_admin,3/min限流,角色等级校验 + project_admin 项目内限制)。生成 16 字符强临时密码(大小写 + 数字 + 符号),audit_log detail 不记录密码本身。User 模型新增password_admin_reset_at(迁移 0035),用户首次登录后自助change_password成功时清空。UsersPage 新增「重置密码」按钮 + 二次确认 + 临时密码展示 Modal(带复制按钮)。 - 账号自助注销冷静期(PR 3):
POST/DELETE /auth/me/deactivation-request(迁移 0036 加deactivation_requested_at / reason / scheduled_at)。提交时通知所有 super_admin,给 7 天处理窗口(如转交任务);冷静期内可撤销。Celery beatprocess_deactivation_requests(每日 04:00 UTC)扫描到期用户,复用 GDPR 软删路径(is_active=False+audit_logs脱敏)。SettingsPage<DangerZoneCard>:未申请态「申请注销」/ 已申请态「已于 X 申请,将于 Y 自动生效 + 撤销」。 - audit_logs 月分区表(PR 4,迁移 0037):
PARTITION BY RANGE (created_at),PK 改为(id, created_at)。复用 v0.7.8 不可变 trigger,挂在分区父表上(PG13+ 自动级联到所有子分区)。覆盖[min(legacy.created_at), now+3m]子分区,原数据零丢失迁移。原 7 个索引(含 GIN on detail_json)按相同名重建为分区局部索引。详见 ADR-0007(已从「延期」更新为「已实施」+ 实施记录)。 - 审计冷数据归档(PR 4):Celery beat
ensure_future_audit_partitions(每月 25 日提前建未来 3 月分区)+archive_old_audit_partitions(每月 2 日把 >AUDIT_RETENTION_MONTHS的子分区 stream-gzip 上传 MinIOaudit-archive/{YYYY}/{MM}.jsonl.gz,成功后DROP TABLE)。新 audit actionaudit.archive,detail 含分区名 + 行数 + S3 key。 - 数据导出审计强化(PR 4):4 个导出端点(projects / batches / audit-logs / users)的
AuditService.log调用统一走新 helperexport_detail(),detail_json 增actor_email / ip / request_id / filter_criteria。CSV 文件首部插入# Exported by / Exported at / Request ID注释行;JSON 文件包装_export_meta顶层字段(pandas/Excelcomment='#'自动跳过)。 AUDIT_RETENTION_MONTHSenv:.env.example新增,默认 12 个月。
变更
- 邀请流程(
InvitationService.create / resend+ 用户/邀请管理路由)改读SystemSettingsService.get(...),env 仅作启动 fallback。 /auth/forgot-password重置链接生成同样改走SystemSettingsService,便于同 PATCH 即时切换前端域名。tests/conftest.pydb_sessionfixture 在 teardown 时清SystemSettingsService模块级缓存,防跨测试 PATCH 值泄漏。tests/test_alembic_drift.py豁免audit_logs_y*分区子表(不在 ORM metadata 中)。
[0.8.0] - 2026-05-06
文档细化与补全。 v0.7.x 收口的特性都齐了,本期把文档站从「骨架完整、内容大纲为主」推进到「可作为新人 onboarding 与运维交付物」。
新增
docs-site/dev/deploy.md(部署指南):拓扑图、必填/推荐 env 一览、nginx 反代示例(含 WS 长连接 +proxy_read_timeout)、首次bootstrap_admin步骤、备份恢复(pg_dump + MinIO sync + Redis 不需备份说明)、升级 runbook、健康检查端点表、常见 production 启动错误。docs-site/dev/security.md(安全模型):威胁模型表 + 缓解一览、5 级 RBAC + 全局能力矩阵、JWT 生命周期 +jti/gen黑名单时序图、邀请流程 mermaid、审计日志字段释义 + 不可变 trigger 说明、CORS 收紧维度对比。docs-site/dev/ml-backend-protocol.md(ML Backend 协议契约):4 个端点 schema(/health//predict同步+交互式 //setup//versions)、鉴权约定、is_interactive语义、错误格式、prediction_metastoken/cost 透传字段表、50 行 FastAPI echo backend 最小可跑示例。docs-site/dev/ws-protocol.md(WebSocket 协议):两个频道(/ws/notificationsJWT query +/ws/projects/{pid}/preannotatecookie)、消息格式(NotificationOut + ping 心跳 + 进度 payload)、断线兜底、前端指数退避策略、Redis ConnectionPool 上限、扩展新频道的 4 步 how-to。- ADR 0002-0005 回填:
0002-backend-stack-fastapi-sqlalchemy-alembic.md— FastAPI + SQLAlchemy 2.0 async + Alembic 选型,对比 DRF / Tortoise / Node Prisma0003-openapi-client-codegen.md—@hey-api/openapi-ts选型,对比 orval / swagger-typescript-api / openapi-generator-cli0004-canvas-stack-konva.md— Konva 4 Layer(v0.6.4 起 5 Layer)选型,对比 Fabric.js / 原生 Canvas / PixiJS / SVG;含package.json仍含 deadfabric依赖的备注0005-task-lock-and-review-matrix.md— 5min TTL + 60s 心跳 + 接管阈值 TTL/2 的取值理由 + 5 状态机 + 角色权限矩阵
docs-site/scripts/generate-hotkeys.mjs:从apps/web/src/pages/Workbench/state/hotkeys.tsregex 解析HOTKEYS+GROUP_LABEL,生成docs-site/user-guide/workbench/hotkeys.generated.md;predev/prebuild自动跑,新增pnpm docs:hotkeys顶层别名。docs-site/user-guide/IMAGE_CHECKLIST.md:截图回填清单 + 拍摄约定(分辨率、脱敏、红框规范);汇总 16 处<!-- TODO(0.8.1) -->占位。
改进
docs-site/dev/architecture/data-flow.md:4 个 mermaid 序列图全部加上代码路径标注(apps/api/...:行号),读者可点 GitHub 跳具体函数;新增「实时通知」详细序列图(含 30s 心跳)。docs-site/dev/how-to/add-api-endpoint.md:从 widgets 占位例改成 v0.7.8 真实落地的POST /auth/logout全链路(路由 + token_blacklist service + 测试覆盖正常路径/错误路径/副作用断言 + snapshot + 前端 wrapper + PR checklist)。docs-site/user-guide/workbench/index.md:删除手抄快捷键表(与代码漂移:W/R/Ctrl+Enter 都不存在),改为<!--@include: ./hotkeys.generated.md-->,与hotkeys.ts38 条 SoT 对齐。docs-site/user-guide/getting-started.md:基础快捷键 6 项与代码 SoT 对齐(B/P/V/Space+drag/Ctrl+Z/E);加 3 处截图占位(登录、忘记密码、端到端 GIF)。- bbox / polygon / keypoint / projects / review / export 页:合计 13 处截图占位,均含拍摄要求注释,配合 IMAGE_CHECKLIST 回填。
配置
docs-site/.vitepress/config.ts:/dev/侧边栏新增「部署与协议」分组(4 项),位于「架构」与「How-to」之间。docs-site/package.json:predev/prebuild改为sync-openapi && generate-hotkeys;新增hotkeys脚本。package.json:新增pnpm docs:hotkeys顶层别名。
占位(不阻断发布)
- 16 处用户手册截图(
docs-site/user-guide/images/<page>/*.png|gif)目前为 1×1 透明 PNG,build 通过;真实图按 IMAGE_CHECKLIST 在 0.8.1 回填。