Skip to content

fix(r20): wave2 Phase1收尾+Phase2A API打底(P1-02/P1-04/P1-08 + R19-18/19/21/22) - #13

Merged
mycyg merged 8 commits into
mainfrom
r20/wave2
Jul 17, 2026
Merged

fix(r20): wave2 Phase1收尾+Phase2A API打底(P1-02/P1-04/P1-08 + R19-18/19/21/22)#13
mycyg merged 8 commits into
mainfrom
r20/wave2

Conversation

@mycyg

@mycyg mycyg commented Jul 17, 2026

Copy link
Copy Markdown
Owner

R20 修复流水线 wave2:Phase 1 收尾 + Phase 2A API 打底,四条产线各自独立复验(根因测试修复前红/后绿),交叉合入后整仓集成终验全绿(pnpm -r typecheck + 全包测试 0 fail + cargo 117 passed)。

Phase 1 收尾

  • P1-04(REL-6):第二实例带 workbench 深链不再丢链——正常深链与 single-instance 分支收敛到共享的 create-if-missing 落地路径(trait 注入可单测,cargo 新增 4 测试)。
  • P1-02(REL-5):password/hybrid 模式桌面获得完整登录链路——凭据登录门(密码只走请求体)→ 复用既有 /api/auth/login 建会话 → desktop-bootstrap 在密码模式改为「凭会话 exchange 设备 token」(无会话仍 404,SameSite=Lax 兜 CSRF);与 SEC-1 组合正确:被移出者 exchange 撞墓碑 403。

Phase 2A API 打底(全 additive,消费端切换在后续 2B/2C)

  • P1-08 后端半边:GET /api/workspace/roster——workspace-scoped 花名册,membership join 严格按 actor.workspaceId 隔离,分页不再硬截断 200,total 为真实成员数,非成员 403(修「全局 /api/users 被当花名册」的正确替代品)。
  • R19-19:POST /api/projects/:id/archive 与 /delete(管理员或项目所有者,CAS 只命中活跃行)。
  • R19-18:POST /api/workitems/:id/assign(被指派人须本工作区 active 成员,防越租户)与 /claim(复用 CAS,落空 409)。
  • R19-22:GET/POST /api/workitems/:id/comments(canView 门控,author 取 actor 不可伪造)。
  • R19-21:GET /api/workspace/audit(仅管理员,workspace 硬取自认证身份,时间倒序+分页)。
  • OpenAPI lockstep 门同步(8 条新路径),契约 zod schema 齐备,零迁移。

验证

整仓 typecheck 全包 Done;全包测试 16/16 包 0 fail;cargo 117 passed(含 REL-6 新增);api 1796 / db 439 / desktop-webview 1395 / contracts 168 / api-client 25。

🤖 Generated with Claude Code

mycyg and others added 8 commits July 17, 2026 12:32
REL-6:第二实例(应用已运行时通过 OS/命令行再次打开 workhub://workbench/... 深链)
此前直接执行窗口控制,跳过了正常 deep-link 分支才有的"workbench 窗按需建"步骤——
workbench 窗 tauri.conf.json 里 create:false,若用户从未点开过工作台,
execute_window_control 会报 "workbench window is not available",深链目标丢失。

抽出共享落地路径 apply_deep_link_plan(经 DeepLinkWindowHost trait 注入,便于脱离
真实 AppHandle 单测)+ handle_deep_link_plan,让 handle_deep_link_url(冷启动/运行时
深链)与 handle_single_instance_launch(第二实例)两条入口收敛到同一份判断逻辑,
不再各自维护一份。

新增 4 个单测(main.rs tests 模块):
- skipping_the_create_step_loses_a_workbench_deep_link_when_window_is_missing:
  复现旧 bug(跳过建窗直接控制窗口 → "window is not available")
- apply_deep_link_plan_creates_the_missing_workbench_window_before_navigating:
  验证新路径先建窗再控制
- apply_deep_link_plan_does_not_create_the_always_present_main_window:
  非 workbench 目标不多走一次建窗
- single_instance_workbench_deep_link_creates_the_window_via_shared_apply_path:
  串联 single_instance 的纯规划(argv → deep_links)与共享落地路径,证明第二实例
  入口不再丢链

验证方式:临时把 apply_deep_link_plan 改回旧行为(跳过 create 直接 control)复现
两个新测试 FAILED("workbench window is not available"),确认是根因测试后再还原。
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
/api/users 是全局用户目录(跨租户泄露 + 全局昵称排序 + 硬 .limit(200) 截断),却被消费端误当
工作区花名册用(成员数、加人选择器)。本单纯 additive 补齐正确数据源,不动 /api/users 本身:

- 新增 GET /api/workspace/roster:任意工作区 active 成员可读本工作区花名册;按 membership join
  严格隔离到 actor.workspaceId;limit/offset 分页(无硬 200 截断,深 offset 可翻至全量成员);
  回工作区成员总数 total(修「计数错」)+ 头像/在线态占位。非成员 403、未登录 401。
- 新增 memberships.listActiveRosterPageByWorkspace(OPTIONAL 契约,仿 UserRepository.listActiveRefs):
  join users、绑定 workspaceId + 排除 membership/user 双软删、按 lower(nickname) 稳定序、limit/offset。
- 契约:workspaceRosterQuerySchema(coerce + 越界回退)/ workspaceRosterMemberVmSchema /
  workspaceRosterResultVmSchema 加在 packages/contracts/src/auth.ts。
- openapi.ts 补 GET /api/workspace/roster 文档(过 runtime↔openapi lockstep 门)。
- 测试基建:query-recorder 补 offset 透传。

消费端迁移(成员数/加人选择器改用本端点)属 Phase 2B,不在本单。

测试:工作区隔离、分页、超 200 经 offset 可达、非成员 403 全绿。
contracts 173/173、db 434 pass/0 fail、api 1758 pass/0 fail、pnpm -r typecheck 全绿。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
补齐 R19 确认缺失的四组 additive 后端端点,全部纯新增、不改既有端点/仓库方法行为:

R19-19 项目归档/删除:
- POST /api/projects/:id/archive、/api/projects/:id/delete
- ProjectRepository.archiveProject/softDeleteProject(CAS 只命中活跃/未删行)
- 门=管理员或项目所有者(比 canManageProjectDrive 更严),复用 ProjectServiceError

R19-18 指派/认领:
- POST /api/workitems/:id/assign(写 work_item_assignments,门=canManageWorkItemAssignees
  + 被指派人须为本工作区成员)、/api/workitems/:id/claim(复用 claimOwnerlessWorkItem,
  门=canClaimWorkItem)
- 新仓库 work-item-assignments.ts(upsert on (work_item,user) 唯一索引)

R19-22 工作项评论:
- GET/POST /api/workitems/:id/comments(门=canViewWorkItemRecord;author_nickname 取 actor)
- 新仓库 comments.ts(list 升序 + 上限 / insert)

R19-21 工作区审计列表:
- GET /api/workspace/audit(仅管理员,工作区硬隔离,actor/action/时间范围过滤+分页,时间倒序)
- audit.ts 新增 WorkspaceAuditLogRepository(独立类型/工厂,不动 AuditLogRepository)

契约进 packages/contracts,OpenAPI 七条路径补齐(lockstep 门绿),app.ts 挂载 workspace-audit。
测试:db 查询形状 8 + api 服务单测/路由联调 43;pnpm -r typecheck 全绿;
db 439 / contracts 168 / api 1796 全通过。

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
修复 codex P1-02:AUTH_MODE=password|hybrid 下桌面/CU 完全没有可用登录链路——
desktop-bootstrap 无条件 404,桌面只有 nickname 自动 bootstrap,用户被锁在门外。

- api routes/auth.ts(仅 desktop-bootstrap 段):密码模式从无条件 404 改为
  「凭有效会话换设备令牌」的 exchange(复用 resolveOptionalCurrentUser,不新造鉴权);
  无会话仍 404、垃圾 client token 403 fail-closed。不碰 SEC-1 的
  resolveHumanActor/ensureDefaultWorkspaceMembership/墓碑逻辑与 /api/users 段。
- api-client:补 login(POST /api/auth/login) typed 方法,密码只走请求体,绝不进 URL。
- desktop-webview:新增 desktop-login.ts 凭据登录门(邮箱+密码表单,错误可见可重试,
  参照 SEC-2 logout 状态机风格);browser.ts/workbench/boot.ts 的 bootstrap 探测
  404=需要凭据 → 渲登录门,登录成功 → 换令牌落 workhub_client_token 后同 nickname 流;
  登出态按探得的模式提示选门,昵称模式行为不变。
- 测试:API exchange 三态(有会话 201/无会话 404/坏 token 403)+ api-client login
  往返/401 + desktop-login 模块 8 条;web/desktop main.test.ts 假客户端 stub 补
  login 成员(共享接口新增所需)。

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

# Conflicts:
#	packages/db/src/test-query-recorder.ts
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@mycyg
mycyg merged commit a19eff9 into main Jul 17, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant