oh-my-muse/docs/agent-specs/2026-06-22-handoff-P2-agent放宽-review.md
lili 6616bf1022 docs(handoff): 回写 P2 agent 兑现进度(总账 + 总览 + review 实施状态)
- 进度总账:加 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>
2026-06-22 13:13:00 -07:00

14 KiB
Raw Permalink Blame History

跨空间 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)已端到端真后端闭环,作为照搬形态参照。


一、结论先行

  1. P2 的本质 = 让 requireVisibleActiveSourceAgent(MuseAgentSlotServiceImpl:221-239,现强制 agentType∈{system,user}+ownerUserId 一致)对 market 来源放行——但仅当 precheck 携带"已被 market 后端 verify 通过的一次性 handoff token";非 handoff 路径(现有同空间 system/user 流)一字不改

  2. 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 迁移。
  3. P2 风险在一点上低于 P1:agent-slot bind 是 updateById(更新已存在 slot binding),且 muse_agent_slot_binding 无 (work_id,slot_key) 唯一约束(只有 command_id 唯一)→ 不复现 P1 那次 uk_..._work_kb 冲突坑

  4. 三个拍板点必须人类先定(见 §九),其中头号决定"放宽是否真的必要":market 上架的 agent 资产物化成 muse_agent 的哪种 agentType。若物化为买家自己的 user agent(owner=买家),原校验天然通过、P2 根本不需放宽(只需 verify/consume token 确认买家有权);若物化为独立 market 类型,才需本评审的放宽。这是数据语义决策,非工程可自决。

  5. 边界门无新破损风险(已实证):BcBoundaryArchTest 允许 ai 依赖 muse-module-market-api(禁 .dal/.application);handoff 消费逻辑在 application service 层,不触 AiGrantRuntimeBoundaryArchTest 约束的 runtime 执行链路(P1 knowledge 同构,已绿)。


二、背景:agent-slot 现状(真实代码)

2.1 放宽点 —— 来源可见性校验

MuseAgentSlotServiceImpl:221-239requireVisibleActiveSourceAgent(ownerUserId, sourceAgentId, sourceAgentVersion):

  • system agent:对所有用户可见(不校验 owner);
  • user agent:仅 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(须人类拍板,拍板前不出执行版)

  1. 【头号】market→agent 资产物化模型:market 上架的 agent 资产,兑现时物化成 muse_agent 的哪种 agentType?

    • (a) 独立 market 类型(owner 为平台/卖家)→ 需本评审的放宽。推荐:保留跨空间 handoff 安全语义(token 是唯一合法入口)。
    • (b) 买家自己的 user agent(owner=买家)→ 原 user 分支天然通过,P2 无须放宽,只需 verify/consume 确认购买权——但这把"绑定"降级为买家私有资源操作,失去 handoff 的跨空间一次性凭据语义。
    • (c) system 类型 → 全员可见、绕过 token,违红线,排除。
    • ⚠️ 此项决定 P2 是"放宽校验"还是"仅接 token",直接改变执行版范围
  2. 放宽触发判据:用显式 sourceType=="market_agent"(推荐,与 knowledge market_kb 对称、判定清晰)还是"handoffToken 非空"(隐式,易误判)?

  3. 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"(与 knowledge market_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 之外的独立维度,P1P3 全缺。
  • P2 范围拍板 = 方案 A(最小验证放宽红线,与 P1 同口径):muse_agentsource_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 纵切。