- 进度总账:加 2026-06-22 P2 时间线条目(后端放宽+verify/consume+V27、前端落地组件、
真后端 e2e 2/2、单测 25/0、ArchUnit 3/0、vitest 97、全量 e2e 50;诚实标注 market→agent
物化未接、e2e 用 seed fixture 替代、来源侧发起暂不做)
- 总览:handoff 令牌消费端 knowledge(P1)→ +agent(P2)红线闭环已接,剩 content(P3)
- P2 review:加实施状态行(✅ 已按方案 A 实施并真后端验证)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
14 KiB
跨空间 Handoff —— P2(agent owner)安全放宽评审版
版本:v0.1(P2 单阶段评审) · 日期:2026-06-22 · 目标读者:架构/安全拍板人 + 实现 agent · 类型:评审版(结论+拍板点+blast radius+验收,不预写代码细节) 上游:handoff 顶层评审 · 执行版 §六.3(明确"P2 须单独评审") 依据:本评审全部现状基于真实代码实证(2 路只读调研 agent,file:line 锚点见正文),非 summary 推断。 实施状态(2026-06-22):✅ 已按方案 A 实施并真后端验证(后端放宽 + verify/consume + V27 迁移、前端 AgentHandoffLanding 落地组件、handoff-agent.spec 真后端 e2e 2/2、MuseAgentSlotServiceTest 25/0、Bc/AiGrant ArchUnit 3/0、vitest 97、全量 e2e 50);详见进度总账 2026-06-22 P2 条目。⚠️ market→agent 资产物化未接(e2e 用 seed market agent fixture 替代),列独立后续主线。
〇、本版定位
执行版把 P2 标为最高风险:它要放宽 AI 槽位绑定的来源校验以接纳 market 来源 agent,而该校验是"AI 授权隔离"的一道闸。本评审聚焦放宽的精确边界与前置拍板点,确认后再出 P2 执行版。P1(knowledge)已端到端真后端闭环,作为照搬形态参照。
一、结论先行
-
P2 的本质 = 让
requireVisibleActiveSourceAgent(MuseAgentSlotServiceImpl:221-239,现强制agentType∈{system,user}+ownerUserId 一致)对 market 来源放行——但仅当 precheck 携带"已被 market 后端 verify 通过的一次性 handoff token";非 handoff 路径(现有同空间 system/user 流)一字不改。 -
P2 比 P1 多两处工作:
- ① 该校验在 precheck 与 bind 两处都调用(防历史 precheck 跨用户消费)。token 明文只在 precheck 持有(与 knowledge 一致),故 bind 阶段无 token、无法重新 verify → 必须让 precheck 落库时打 sourceOwner 标记,bind 凭
precheck.sourceOwner识别 market 路径。 - ② 因此 precheck DO 与表
muse_ai_agent_slot_precheck(V12)须加列source_owner+handoff_hash(knowledge 已有handoff_hash,P2 对齐)——比 P1 多一次 schema 迁移。
- ① 该校验在 precheck 与 bind 两处都调用(防历史 precheck 跨用户消费)。token 明文只在 precheck 持有(与 knowledge 一致),故 bind 阶段无 token、无法重新 verify → 必须让 precheck 落库时打 sourceOwner 标记,bind 凭
-
P2 风险在一点上低于 P1:agent-slot bind 是
updateById(更新已存在 slot binding),且muse_agent_slot_binding无 (work_id,slot_key) 唯一约束(只有 command_id 唯一)→ 不复现 P1 那次uk_..._work_kb冲突坑。 -
三个拍板点必须人类先定(见 §九),其中头号决定"放宽是否真的必要":market 上架的 agent 资产物化成 muse_agent 的哪种 agentType。若物化为买家自己的
useragent(owner=买家),原校验天然通过、P2 根本不需放宽(只需 verify/consume token 确认买家有权);若物化为独立market类型,才需本评审的放宽。这是数据语义决策,非工程可自决。 -
边界门无新破损风险(已实证):
BcBoundaryArchTest允许 ai 依赖muse-module-market-api(禁.dal/.application);handoff 消费逻辑在 application service 层,不触AiGrantRuntimeBoundaryArchTest约束的 runtime 执行链路(P1 knowledge 同构,已绿)。
二、背景:agent-slot 现状(真实代码)
2.1 放宽点 —— 来源可见性校验
MuseAgentSlotServiceImpl:221-239 的 requireVisibleActiveSourceAgent(ownerUserId, sourceAgentId, sourceAgentVersion):
systemagent:对所有用户可见(不校验 owner);useragent:仅ownerUserId == agent.ownerUserId可见;- 其他类型直接
AI_AGENT_SCOPE_FORBIDDEN(market 来源在此被拒); - 另校验 agent.status=active + version.status=active。
调用点两处:precheck(:90)、bind(:150,二次校验防历史 precheck 被跨用户消费)。
2.2 校验链(两入口)
- precheck(
:74-119,@Transactional):requireWorkOwner → rejectProtectedSlot → requireSlotBinding → requireRevision → rejectBlockedSource → requireVisibleActiveSourceAgent → 落MuseAgentSlotPrecheckDO(status=pending,10 分钟过期)。 - bind(
:122-170,@Transactional):requireWorkOwner → rejectProtectedSlot → 悲观锁读 pending precheck(校验 owner/work/slot 三维 + 未过期)→ requirePrecheckMatchesBindRequest → rejectBlockedSource → requireVisibleActiveSourceAgent → 悲观锁读 slot binding + revision 校验 →updateById更新绑定(agentId/version/snapshot/revision+1/status=active)。
2.3 保护不变式(放宽不可触碰)
- 保护节点:
slotKey.startsWith("protected:")→AI_AGENT_SLOT_PROTECTED(:241-249,precheck+bind 两次拒)。 - work 属主:requireWorkOwner(作品须属当前用户)。
- agent/version active:停用资源不可绑。
- AI 授权隔离:
AiGrantRuntimeBoundaryArchTest(runtime 执行侧不碰 ToolGrant 写模型)。
2.4 数据契约缺口(vs knowledge P1)
| 维度 | knowledge P1(已落地) | agent P2(待补) | 锚点 |
|---|---|---|---|
| Precheck ReqVO | 有 sourceType/sourceId/handoffToken/authorizationSummaryId | 无 handoff 字段 | AgentSlotPrecheckReqVO |
| Bind ReqVO | 凭 precheckId(无 token) | 凭 agentSlotPrecheckId(对齐,无须加 token) | AgentSlotBindReqVO |
| Precheck DO/表 | 有 handoff_hash/source_snapshot_id |
无 handoff/source* 列 | MuseAgentSlotPrecheckDO / muse_ai_agent_slot_precheck(V12) |
| pom | 已依赖 market-api | 无 market-api | muse-module-ai-server/pom.xml |
| 消费逻辑 | precheck 阶段 verify+consume | 无 | MuseKnowledgeBindingService:273-284 |
2.5 复用端口(无须新建)
MarketHandoffTokenApi(进程内 Bean):verify(HandoffVerifyReqDTO{token, expectedTargetOwner, expectedTargetAction})→HandoffVerifyRespDTO{valid,status,...}(脱敏、不回 token 明文);consume(HandoffConsumeReqDTO{token, commandId, apiVersion, expectedStatus, targetWorkId, bindingRef})(CAS pending→completed,沿用调用方事务)。expectedTargetOwner 已预留 "agent"。
三、范围与非目标
范围:market→agent 槽位跨空间 handoff 兑现侧(后端 verify/consume + 精确放宽 + precheck DO 加列 + 前端 agent 落地组件)。
非目标:
- 不改同空间 slot bind 既有语义(system/user 流);
- 不放宽保护节点 / work 属主 / agent-version active;
- 不动 market 发起侧 4 端点与 token 派生/属主/CAS 机制(已 P1r 验证);
- 不在本评审做 market→agent 资产物化建模(那是上游产品/数据决策,见 §九.1)。
四、推荐方案数据流
sequenceDiagram
participant U as 用户(studio)
participant M as market-server
participant A as ai-server(agent owner)
participant API as MarketHandoffTokenApi(进程内)
U->>M: bind-precheck(asset) 拿 authorizationSummaryId
U->>M: POST /handoffs(targetOwner=agent,action=bind) 拿一次性 token
Note over U: 落地页 replaceState 清 URL token(红线)
U->>A: agent precheck(slotKey, sourceType=market_agent, handoffToken, ...)
A->>API: verify(token, expectedTargetOwner=agent, action=bind)
API-->>A: valid + 授权摘要(脱敏,无 token 明文)
A->>API: consume(token, CAS pending→completed)
Note over A: 落 precheck DO: source_owner=market + handoff_hash, 放宽接纳 market agent
A-->>U: agentSlotPrecheckId
U->>A: bind(agentSlotPrecheckId)
Note over A: 读 precheck.source_owner=market → 走放宽路径(token 已核销,凭 precheckId 信任)
A->>A: updateById slot binding(active)
A-->>U: 绑定完成 → 返回来源
五、精确放宽设计(核心,待拍板后细化为执行版)
原则一句话:市场来源(且 token 已 verify 通过)才放宽 agentType 校验;非 handoff 路径维持原 requireVisibleActiveSourceAgent 不变。
- precheck 阶段:判 market 来源(显式
sourceType=="market_agent",见 §九.2)→verify(expectedTargetOwner="agent")valid 才继续 →consume(precheck 阶段核销,与 knowledge 一致,token 明文只此持有)→ 落 precheck DO 标source_owner="market"+handoff_hash;放宽只跳过 agentType∈{system,user} 这一条,仍校验 agent active + version active。非 market 来源:原校验一字不变。 - bind 阶段(关键):读
precheck.source_owner。=="market"→ 走放宽路径:token 已在 precheck 核销,bind 不重复 verify/consume,凭agentSlotPrecheckId(悲观锁 + owner/work/slot 三维 + 未过期)信任来源,仍校验 agent/version active + 保护节点;非 market → 维持原requireVisibleActiveSourceAgent。
这样放宽的最小性:只在"已 verify 通过的 market handoff"这一条缝放行,其余闸门(保护节点、work 属主、active 态、AI 授权隔离、非 handoff 原校验)全保持。
六、Blast Radius / 兼容 / 回滚
改动面(预估,执行版细化):
muse-module-ai-server/pom.xml:+muse-module-market-api;AgentSlotPrecheckReqVO:+sourceType/handoffToken/sourceId/authorizationSummaryId(market 来源必填,service 层校验);MuseAgentSlotPrecheckDO+ V-migration:+source_owner/handoff_hash(nullable,旧行 null→走原路径);MuseAgentSlotServiceImpl:precheck/bind 两处放宽 + consume 接入;ErrorCodeConstants:+handoff 错误码或复用AI_RESOURCE_FORBIDDEN/AI_SOURCE_BLOCKED;- 前端:agent 落地组件(照搬
KnowledgeHandoffLanding形态)+ market 详情发起按钮(targetOwner=agent)。
兼容:同空间 system/user 流走"非 market"分支、行为不变;新增列 nullable,旧 precheck 无 source_owner→原路径,向后兼容。
回滚:阶段独立 commit/revert;V-migration 仅加可空列,回滚不破旧数据(对齐执行版 §九 阶段回滚粒度)。
七、风险与缓解
| 风险 | 缓解 |
|---|---|
| 放宽过宽(market 路径绕过 token)——头号 | 放宽仅在 verify valid 之后;针对性单测:无 token/伪造/过期 token 走 market 分支→必拒;非 market 路径维持原校验回归测 |
bind 阶段信任 precheck.source_owner |
precheck 已 verify+consume,bind 凭 precheckId(悲观锁 + owner/work/slot 三维 + 未过期),不可伪造;source_owner 由后端落库非客户端可控 |
| market agent 物化模型未定 → 放宽形态错配 | 见 §九.1,拍板前不进执行版 |
| consume 时机错置 | 锁定 precheck 阶段 consume(token 明文只此持有),与 knowledge P1 一致 |
八、验收标准(P2 执行版完成时)
BcBoundaryArchTest+AiGrantRuntimeBoundaryArchTest绿(0 违例);- 单测:① 无 token/伪造/过期 token 走 market 分支→拒;② 非 market(system/user)路径校验完全不变(回归);③ 保护节点拒;④ market valid token→接纳 + consume CAS;
- 真后端 e2e:market agent→作品槽位正路闭环 + 负路伪造 token 被 verify 拒(对齐 P1 反假绿口径);
- 全量 e2e/vitest 不低于 P2 起基线。
九、Open Items(须人类拍板,拍板前不出执行版)
-
【头号】market→agent 资产物化模型:market 上架的 agent 资产,兑现时物化成
muse_agent的哪种agentType?- (a) 独立
market类型(owner 为平台/卖家)→ 需本评审的放宽。推荐:保留跨空间 handoff 安全语义(token 是唯一合法入口)。 - (b) 买家自己的
useragent(owner=买家)→ 原user分支天然通过,P2 无须放宽,只需 verify/consume 确认购买权——但这把"绑定"降级为买家私有资源操作,失去 handoff 的跨空间一次性凭据语义。 - (c)
system类型 → 全员可见、绕过 token,违红线,排除。 - ⚠️ 此项决定 P2 是"放宽校验"还是"仅接 token",直接改变执行版范围。
- (a) 独立
-
放宽触发判据:用显式
sourceType=="market_agent"(推荐,与 knowledgemarket_kb对称、判定清晰)还是"handoffToken 非空"(隐式,易误判)? -
bind 阶段信任模型:bind 凭
precheck.source_owner=="market"跳过 agentType 校验(token 已在 precheck 核销、bind 不重复核验)——是否接受?(与 knowledge P1 同构:bind 凭 precheckId 信任,token 用完即弃。)
十、拍板结论(2026-06-22)
- §九.1:选 (a) 独立 market 类型 → P2 走精确放宽。
- §九.2:采用显式
sourceType="market_agent"(与 knowledgemarket_kb对称)。 - §九.3:bind 凭
precheck.source_owner信任(与 knowledge P1 同构,token 用完即弃)。 - ⚠️ 连带发现(反假绿)——资产物化维度全缺:
requireVisibleActiveSourceAgent兑现时agentMapper.selectById(market-agent-id)要求 market agent 已物化进muse_agent,但实证 market→agent 物化路径不存在(muse_agent 无source_market_asset_id字段 + 0 物化代码;market install 只落 license 投影不建实体,MarketInstallServiceImpl:63)。且 knowledge 同样未接物化(P1 e2e 靠 asset1→kb_id1 fixture 巧合绕过)。物化是 handoff 之外的独立维度,P1–P3 全缺。 - P2 范围拍板 = 方案 A(最小验证放宽红线,与 P1 同口径):
muse_agent加source_market_asset_id+ 支持agent_type='market'+ seed 一个 market 类型 agent fixture + 放宽 + verify/consume + 真后端 e2e(正路放宽接纳 + 负路伪造 token 拒)。market install 自动物化 agent 列为独立后续主线,执行版与 e2e 须诚实标注"agent 为预置 fixture、install 物化未接"。
下一步:据 §十 拍板出 P2 执行版(muse_agent 加列迁移 + agent_type=market 支持 + market agent seed fixture + precheck DO 加 source_owner/handoff_hash 列 + ReqVO 契约 + 两处放宽精确逻辑 + consume 接入点 + 前端 agent 落地适配器 + 单测/e2e 验收),再实现 P2 纵切。