diff --git a/docs/records/2026-10-14-rdm-settings-completion-contract.md b/docs/records/2026-10-14-rdm-settings-completion-contract.md new file mode 100644 index 0000000..78aed17 --- /dev/null +++ b/docs/records/2026-10-14-rdm-settings-completion-contract.md @@ -0,0 +1,57 @@ +# 项目设置完成性修复:接口与验证记录 r0.1 + +本记录承接已批准的 `../proposals/2026-10-13-rdm-settings-members-duties-perms-increment.md` r1;不改变 P1–P8 和 R1–R5 的交付范围。当前是阶段 6 实施候选,正式验收、发布、部署与清理均不由本记录代签。 + +当前权限边界遵循已合入的 `../proposals/2026-10-13-iam-i1-permission-contract.md`:系统管理身份不代替业务成员、动作、资源或文档职责授权;停用角色不回落为无绑定默认。 + +## 恢复与完成性差距 + +恢复前共享检出只有 Documents/Guidelines 的已保存提交漂移。核对内部无未提交文件、detached HEAD、当前提交已在登记历史保存之后,恢复登记 gitlink、快进本任务收尾分支并物化新 Documents。随后 Dev 启动检查返回 READY,独立任务为 `feature/rdm-settings-completion`;初始主干 R0 是 `add097d06137c4912a4ed9279e98ca353e4e4cf1`。RDM 独立任务同名,初始 R0 是 `6fcb256205c2384abed35d3b6ab96ca7216e9297`。 + +旧任务的归档与通过叙述保留为历史。当前源码核对发现角色过滤、文档搜索/树、共享配置和模型/密钥其他覆盖入口缺项;旧接受脚本场景 9 没有执行检查、场景 10a 只比较 UI 默认值。这些内容不能证明原方案完整完成,新的验证必须检查真实动作和结果,不修改旧收据。 + +## P2 基座公开扩展申请与候选合同 + +场景:项目成员应读取同一业务配置;只有目标项目的合法配置者可以修改。个人在项目中的偏好仍须私有。 + +现有 `ResourceAuthorizerV2` 的公开合同只覆盖读取,不能将其复用为项目配置写授权。`ProjectLifecycle.Work` 是成员工作资格,不是配置资格。新增可选 `ProjectSettingsRegistry.RegisterProjectSettingsAuthorizer`;回调别名为 `func(context.Context, actor, projectID string, write bool) error`。主体和宿主项目 ID 由执行端解析,不能由请求自报;提供者必须与项目生命周期提供者一致,缺失或拒绝时失败关闭。 + +这是一项向后兼容的可选公开能力,候选扩展面为 `2.13.0-experimental`。RDM 以窄接口消费,不修改 `pins/`,不导入 Backend 私有包,不用反射或缓存的他人 Host 身份恢复授权。新基座发版与组合采用仍是完成条件;发版双钥匙由用户执行。 + +设置描述的归属词汇为 `system`、`user`、`project`、`project-personal`。空的旧插件 scope 仅保留个人兼容,不能推断共享策略资格。模型、运行参数、系统工具与插件目录属于系统;交付物默认样式等项目业务配置共享;主题、个人指令与跨会话个人开关不变成全员共享。 + +公共接口候选: + +| 接口 | 合同 | +|---|---| +| `GET /api/settings` | 仅个人 schema/default/user | +| `GET /api/settings?project=<宿主ID>` | 共享 schema/default/project、projectRevision、projectCanConfigure;个人项目偏好另列 personalSchema/personalDefaults/personalUser/personalProject | +| `PUT /api/settings/project/<宿主ID>` | `{values,expectedRevision,unset?}`;只更新明确变更的项目叶子,旧版本 409 | +| `PUT /api/settings/project/<宿主ID>/personal` | 私有合并补丁;声明的非秘密叶子 null 表示移除覆盖 | +| `GET /api/settings/system` | 系统 schema/default/system、systemRevision;管理员保护 | +| `PUT /api/settings/system` | `{values,expectedRevision,unset?}`;管理员保护与版本比较 | +| `PUT /api/settings/user` | 个人合并补丁;声明的非秘密叶子 null 表示移除覆盖 | + +`unset` 只接受当前作用域已声明的非秘密叶子;父路径、未知路径、内部策略、重复或与 values 重叠的路径拒绝。未修改的掩码不提交;秘密撤销显式写空字符串,不能通过移除让旧部署凭据恢复。画像模型路由、个人服务密钥、环境 AuthRef 与所有运行解析同样受系统边界约束。 + +## P3/P4/P6 领域合同 + +人员检索共用 query/match/roleId/projectRole/cursor/limit;姓名缺失回退账号,重名使用稳定账号区分,过滤后分页。`rdm.role.options` 仅返回有资格使用的角色名称和 ID,不下发全部绑定账号或权限上限。 + +成员目录返回当前成员的姓名、有效性、专业角色及请求者的实际管理能力。缺失账号目录时有效性是未知,不伪造有效。职责资格还须对应动作的上限;专业角色筛选不授予身份或职责。 + +文档目录/树共用同一受授权裁剪的检索结果;仅文档节点可提交 docId。当前职责与冻结名单分别返回。文档、章节和批量写入均要求 expectedDutyVersion,实际作废在途审签还要求 confirmInvalidation;无版本、缺账号校验或不合资格的旧命令同样拒绝。变更、作废、事件全成或全败,历史名单保留。 + +成员移除先读取 `rdm.project.member.impact`,只给该成员职责关系的必要元数据;没有正文或职责目录权限不因此获得全项目目录。实际移除重新检查影响与最后负责人,并与成员删除/事件写入同事务。 + +项目权限只返回项目动作;系统创建权限不混入项目矩阵或项目覆盖。解释返回 defaultAllowed/capAllowed/override 与可读来源/原因;控制组件消费请求者真实能力,不从被查询成员身份推算。项目动作允许不代表任意文档读取或签字资格。 + +## P8 迁移与验证边界 + +迁移清单只返回来源、路径与状态,不返回秘密值或摘要。旧表没有逐键更新时间,不能伪造“最近写入”权威;冲突必须明确裁定。显式预览选择生成受保护、绑定主体与项目的观察记录,应用时重新鉴权、核对来源与目标版本、保留旧行、原子写目标与结果;相同观察重试幂等。系统迁移只采用管理员明确提供的新值,不自动提升他人的私有凭据。 + +配置来源/目标在同一存储事务中检查;跨数据库的身份与业务授权回调在进入操作时独立核验,不声称它们属于同一个 SQL 事务。模板的实际使用仍重验实时资源授权。 + +验证包含领域/公共契约红测、SQLite/PostgreSQL 事务和竞争、真实 React 浏览器机制、隔离实例完整行为、Core/UI/Frontend/Backend/RDM 精确组合。mock/接口 fixture 只证明相应机制,不能替代真实组合、模型任务级证据、正式验收或当前 HEAD CI。 + +实施与自检的实际对象、命令、输出及剩余项维护在 Dev/RDM 同名任务卡和源码外原始收据;本记录不手抄命令输出、不提升 SELF 为 REPLAY/GATE。未执行的正式验证保持 NOT_RUN。