---
status-class: historical
design-state: superseded-unshipped-v1
historical-refs:
  - specs/agent-space/owner-private-agent-delivery.md
  - specs/agent-space/shared-spaces-and-agent-spaces.md
---
# Agent Space 01：Space Core Design

日期：2026-04-24
状态：superseded historical record。本文只记录未上线 Agent Space unified-space /
transcript 实现线，不能作为当前产品、schema、API、runtime 或用户可见 surface 的合同。
当前实现已经删除 Agent Space transcript writer/copy/linkage、publish /
auto-publish support tables、browser-facing API/realtime/panel，以及 agent-facing
`scope-execution:<id>` target；保留的 shipped 方向是 parent timeline + hidden
owner-private context、scope cursors、sparse targeted events 和 owner_quote source
facts。需要实现当前语义时请参考 `runtime-api-design.md`、Task 收敛 specs、storage
spec 与代码。
范围：统一 `channel` / `thread` / `dm` / `agent_space` 的 Space 底座；先落已经拍定的对象模型、核心表、身份/派生关系、ID/排序语义与迁移清单骨架。
非目标：mute/wake 细节、share pipeline 细节、UI 呈现细节、migration rollout 策略、API 兼容层细节。


## 0. 与当前 canonical specs 的关系

本篇定位为 **superseded historical record**。

这意味着：

- unified Space 底座曾在未上线 implementation line 中以 `scopes` / `scope_memberships` / `scope_messages` / `scope_channel_profiles` 等表落地；
- 本篇记录的是未上线实现曾采用的 Space Core 形态与迁移语义；
- 若本文后续保留早期 draft wording，它只说明已删除或待收敛的未上线实现，不覆盖当前 Task / Scope Execution / storage / runtime 语义。

该未上线 Agent Space transcript 主路径当时 supersede 至少以下文档中的对应段落：

- `specs/agent-space/owner-private-agent-delivery.md`（尤其是 owner quote / private reply 的 authored location 与 delivery object 定义）
- `specs/agent-space/shared-spaces-and-agent-spaces.md`（其中早期面向目标设计的 Space Core / Agent Space 对象模型段落已由 implemented-v1 wording 收敛）

legacy `owner-private-agent-delivery.md` 仍仅覆盖兼容入口；当前工作流以 Task / Scope Execution 收敛 specs、storage spec 与 runtime-api design 为准。

## 1. 目标

AgentTeam 现有实现对 `channel`、`thread`、`dm`、private/public agent 协作使用了分裂的数据模型与运行时路径。`Shared Spaces / Agent Spaces` 的目标是把产品模型与存储模型统一到同一套 Space 底座上：

- `channel`
- `thread`
- `dm`
- `agent_space`

都作为 `scopes` 的实例存在。

这次 `01-space-core` 先解决：

1. Space 是什么；
2. 哪些 parent→child 派生关系合法；
3. unified core tables 是什么；
4. 哪些旧表被新底座替代；
5. ID / 排序 / 分页的真相是什么。

## 2. 核心模型

### 2.1 逻辑核心表（logical core model）

unified core model 为 5 张逻辑核心表：

1. `scopes`
2. `scope_memberships`
3. `scope_messages`
4. `scope_message_sources`
5. `scope_channel_profiles`

### 2.2 物理辅助字段（physical helper）

物理辅助字段为：

- `scopes.host_channel_id`
- `scopes.archived_until_seq`

其中：

- `host_channel_id` 用于加速 thread-level agent space 的 roster / routing / archive / backfill 查询；
- `archived_until_seq` 用于承接现有冷热消息架构中的归档边界。

它们都属于 **denormalized helper / physical helper**，不是逻辑真相源；逻辑真相仍然分别在 parent chain 与 `(space_id, seq)` timeline 语义上。

### 2.3 支持表（support tables）

support tables 为：

1. `scope_channel_profiles`
2. `scope_notification_settings`

它们不进入“五张逻辑核心表”，但属于 unified Space 底座不可回避的周边支撑对象：

- `scope_channel_profiles` 负责 channel-specific governance / metadata；
- `scope_notification_settings` 负责 delivery / unread / inbox intent，与真实 participant 边界分开建模。

## 3. Space identity 与派生关系

### 3.1 `scopes.type`

`scopes.type` 只有四种合法取值：

- `channel`
- `thread`
- `dm`
- `agent_space`

### 3.2 合法 parent→child 关系

合法派生关系为：

- `channel -> thread`
- `dm -> thread`
- `channel -> agent_space`
- `thread -> agent_space`

显式不支持：

- `thread -> thread`
- `agent_space -> thread`
- `agent_space -> agent_space`
- `dm -> agent_space`

### 3.3 `parent_space_id`

`parent_space_id` 的含义如下：

- `channel` / `dm`：`NULL`
- `thread`：指向所属 `channel` 或 `dm`
- `agent_space`：指向其直接 parent（`channel` 或 `thread`）

### 3.4 `parent_message_id`

`parent_message_id` 只给 `thread` 使用：

- `thread.parent_message_id` 表示该 thread 是从 parent space 的哪条消息开出来的
- `agent_space` 不带 `parent_message_id`

原因：

- `thread` 的身份与某条 parent message 的 reply-stream 直接绑定
- `agent_space` 的身份由 `(owner_member_id, parent_space_id, type='agent_space')` 决定，而不是由某条 parent message 决定

### 3.5 `owner_member_id`

`owner_member_id` 只在 `agent_space` 上存在：

- `agent_space` 必须有且只有一个 owner
- `channel` 若存在治理上的 owner，不通过这列表达，而由 `scope_channel_profiles.channel_owner_member_id` 承载
- `thread` / `dm` 若未来需要 owner-like profile，再由各自的 type-specific sidecar 或后续治理设计承载

### 3.6 `host_channel_id`

`host_channel_id` 保留在 `scopes` 上，作为物理辅助字段。

它的定义是：

- channel 自己的 `host_channel_id = 自己`
- thread 的 `host_channel_id = 它所属的 channel`
- thread-level agent space 的 `host_channel_id = 它的 parent thread 所属 channel`
- channel-level agent space 的 `host_channel_id = 它的 parent channel`

该字段 **不作为 routing / lifecycle / roster 的权威来源**；如果它与 parent chain 不一致，应视 parent chain 为真相并修正 helper 值。

### 3.7 唯一性约束

锁定两条**逻辑不变量**，但不锁最终的物理约束形式：

1. **Agent Space active uniqueness**
   - 对同一个 `(parent_space_id, owner_member_id)`，任一时刻最多只能有一个 `active` agent space
   - `archived` 的旧 row 可以作为历史对象继续存在，因此同一个 `(parent, owner)` 可以有多代 archived Agent Spaces
   - 01 只锁这个逻辑不变量，不锁最终采用 partial unique、`generation/lifecycle_epoch` 还是其他 DDL 形式

2. **Thread uniqueness（subject to current thread semantics）**
   - 先继承产品上的直觉：同一个 `(parent_space_id, parent_message_id)` 应最多对应一个活跃 thread
   - 具体是否做成严格唯一约束，还是带 `archived_at IS NULL` / reopen 语义的条件唯一，取决于现有 AgentTeam thread 是否允许同一条 parent message 在归档后重新开出新 thread

> 这里的原则是：01 锁产品/逻辑不变量，不提前锁死最终物理约束形式；具体 DDL（例如 partial unique index / trigger / app-layer fallback / generation column）留给后续 schema/migration 细化。

## 4. 五张核心表与支持表的职责边界

### 4.1 `scopes`

`scopes` 承载所有空间对象本身，至少需要表达：

- `id`
- `workspace_id`
- `type`
- `parent_space_id`
- `parent_message_id`（仅 thread）
- `owner_member_id`（仅 agent_space）
- `creator_member_id`
- `host_channel_id`（helper）
- `archived_until_seq`（helper，冷热边界）
- `status`
- `archived_at`
- 通用时间字段（如 `created_at` / `updated_at`）

`scopes` 只回答：

- 这是什么 space
- 它和谁是 parent/child
- 它是否还处于 active / archived 等生命周期状态

### 4.2 `scope_memberships`

`scope_memberships` 承载**某个 space 的真实参与者**。

原则：

- `scope_memberships` 不带 `public/private`
- `public/private` 不是 participant 类型，而是 channel 级 agent entry 的概念
- human / agent / service 只要是某个具体 space 的真实参与者，都可以出现在 `scope_memberships`

还锁定：**read state 合并进 `scope_memberships`**。

因此，`scope_memberships` 至少同时承载：

- 参与关系本身
- 该 participant 在该 space 中的 read cursor（例如 `last_read_message_id` / `last_read_at`）
- 该 participant 在该 space 中的 dismiss / intent cursor（例如 `owner_context_dismissed_through_*` 一类字段）
- 该 participant 在该 space 中的 per-member archived / hidden state（例如 `archived_at`）

并且在最终态里：

- `channel_memberships` 与 `reply_thread_participants` 不再作为运行时 participant truth 存在；
- channel / thread / dm / reply-thread / agent_space 的真实参与者、read cursor 与 per-member archived state 都统一直接落在 `scope_memberships`。

同时明确一条排除边界：

- Parent Observer 默认可浏览 Agent Space transcript，但 **Parent Observer 不是 Agent Space participant**；
- 因此 observer 不在 `scope_memberships` 中 materialize，也不在 01 中为其单独持久化 read cursor。

### 4.3 `scope_messages`

`scope_messages` 承载所有 space 的**逻辑 timeline 消息模型**。

它至少要支撑：

- `space_id`
- `workspace_id`
- `seq`（per-space 单调递增）
- `workspace_global_seq`（workspace 级单调）
- `author_member_id`
- `message_type`（如 text / system / owner_quote 等）
- message body / metadata
- created / updated timestamps

`scope_messages` 的排序真相见第 6 节。

还锁一条物理实现边界：

- 01 里的 `scope_messages` 是逻辑 timeline 模型，不等于“必须存在一张永久不归档的大表”；
- 物理实现继续对齐现有冷热架构：hot message rows + archive objects；
- 统一后的冷热边界按 `(space_id, seq)` 工作，并由 `scopes.archived_until_seq` 一类 helper 参与定位。

### 4.4 `scope_message_sources`

`scope_message_sources` 是因为 **parent ↔ agent space 双向导航** 与 share provenance 被锁成产品能力，明确保留的结构化关系表。

它承载：

- 一个 `space_message` 引用了哪些 parent source messages
- source 来自哪个 parent space
- source 的关系类型（例如未来的 `auto_flow` / `share`）
- 一份 durable source identity，供导航 / provenance / archive 后解析使用

它存在的原因不是“转发必须严格引用”，而是：

- 需要支持双向导航
- 需要支持 provenance / trace
- 需要支持后续 folded rendering / debug / audit

01 只锁它的 **durable ref** 职责；snapshot / enrichment 字段是否存在，留给后续 share design。

### 4.5 `scope_channel_profiles`

`scope_channel_profiles` 承载 channel 级别的 agent 身份与可见性定义。

锁定：

- `public/private` 只存在这里
- private entry 是 **owner-scoped** 的；并且 private agent 本身只有一个 owner，同一个 agent 不允许被不同 owner 以 private 方式分别占有
- 一个 agent 在某个 channel 里是 public 还是 private，不由 `scope_memberships` 决定，而由这张表决定
- 同一个 `(host_channel, agent)` 不能同时存在 active public entry 与 active private entry
- public entry 必须满足 `owner_member_id IS NULL`
- private entry 必须满足 `owner_member_id IS NOT NULL`
- 新建 `channel_agent_entry` 的默认 `visibility` = `private`；只有显式请求 public 时才允许落 active public entry
- `#all` 对 agent 是特例：新建 agent 在 `#all` 中只能 materialize 为 **private entry**，`owner_member_id` 固定为创建该 agent 的 human workspace member；`#all` 不允许为 agent 建 active public entry

这张表至少需要表达：

- `host_channel_id`
- `owner_member_id`（public entry 为 `NULL`；private entry 填该 private agent 的唯一 owner，可作为 denormalized helper）
- `agent_member_id`
- `visibility`（`public|private`）
- `removed_at`（或等价 lifecycle 字段）
- public entry 与 private entry 的唯一性规则（例如二者都以 `(host_channel_id, agent_member_id)` 唯一；private entry 另外要求 `owner_member_id` 与该 agent 的唯一 owner 一致）

这张表回答的是：

- 这个 agent 在这个 channel 里的身份是什么
- 它是否进入 shared participant 集合（public）
- 它是否只进入某个 owner 的 agent space（private）
- 这个 private roster entry 属于哪个唯一 owner

### 4.5.1 v1 的 agent owner truth

v1 先显式对齐当前实现：spec 中提到的 **agent owner / manager-of-record**，默认都绑定到当前 agent runtime 的 creator truth（现实现侧等价于 `machines.created_by_user_id` 所代表的人类创建者 / manager-of-record）。

因此：

- private agent 的“唯一 owner”在 v1 默认按这条 creator truth 解析；
- public agent 若需要判断 DM owner gate，也同样按 creator / manager-of-record 解析；
- 这些文档暂不要求新增一张独立的 agent-owner 表或专门的 owner 列。

Future work 可以把 owner truth 提升为 dedicated owner column 或 workspace-installation ownership 模型，但那属于后续 identity/runtime 演进，不是本轮 spec 的前置条件。

> 这张表不继续拆成 `channel_public_agent_entries` / `channel_private_agent_entries`；先保持一个表，并用 `visibility=public|private` 表达 channel 级身份。

### 4.6 `scope_channel_profiles`（support sidecar）

`scope_channel_profiles` 是 channel-specific governance 的 sidecar，而不是 unified `scopes` 的跨类型核心字段集合。

它至少承载：

- `channel_space_id`（PK / FK -> `scopes`）
- `slug`
- `description`
- `visibility`
- `channel_owner_member_id`
- `is_all_channel`

这张表存在的原因是：

- channel 有自己的治理字段（owner / slug / visibility / `#all` 特例）
- 这些字段不适合回灌进所有 type 共用的 `scopes`
- `scopes` 保持跨类型最小共享字段，channel-specific metadata 走 sidecar

只锁 **channel** 有这张 sidecar；是否将来为 `dm` / `thread` 引入各自 profile，留给后续 design。

### 4.7 `scope_notification_settings`（support table）

`scope_notification_settings` 承载的是**delivery / unread / inbox intent**，而不是“我是不是这个 space 的真实参与者”。

它至少要表达：

- `space_id`
- `member_id`
- `subscription_state`（或等价表达，例如 `subscribed` / `muted`）
- 通用时间字段（如 `created_at` / `updated_at`）

这张表存在的原因是：

- “是这个 space 的 participant” 与 “要不要持续接收这个 space 的 delivery / unread” 不是同一个 intent
- 尤其在 thread 中，implicit subscribe + manual unsubscribe 这类生命周期与 participant lifecycle 不同
- 因此不把 subscription 状态揉进 `scope_memberships`

01 只锁这个边界与对象；具体枚举值、默认策略与 runtime 行为留给后续 delivery-wake design。

## 5. 核心不变量

### 5.1 `public/private` 是 channel 级概念，不是 participant 类型

- `scope_memberships` 不带 `public/private`
- `scope_channel_profiles` 才定义一个 agent 在某个 channel 的 public/private 身份
- 对同一个 `(host_channel, agent)`，不能同时存在 active public entry 与 active private entry

### 5.2 Private agent 不进入 parent channel/thread participants

对于 private agent：

- 它不是 parent channel 的 participant
- 它不是 parent thread 的 participant
- 它只在对应的 `agent_space` 中作为 participant 出现
- 这条规则同样适用于 `#all`：agent 即使默认挂在 `#all`，也只能以 owner-scoped private entry 的形式存在，不能进入 `#all` 的 `scope_memberships`
- 升级迁移时，历史 `visibility='public'` 的 agent entry 也要按 owner truth 改写成 private；如果无法从 agent 的 machine creator 回填有效 `owner_member_id`，则该 entry 必须被删除，而不是保留成无 owner 的 public 残留

### 5.3 `scope_memberships` 统一承载真实参与者

- channel / thread / dm 的真实参与者走 `scope_memberships`
- reply-thread 的真实参与者也走 `scope_memberships`
- agent_space 的真实参与者也走 `scope_memberships`
- 是否 public/private 不由 `scope_memberships` 表示

还锁定：

- Agent Space 的 participant rows 是 **materialized truth**；
- 其中 private-agent participant 的加入/移除仍然以 `scope_channel_profiles` 为 source of truth，并在实现上保持原子同步，避免 roster drift。
- channel-level Agent Space 的 private-agent participants 表达的是 owner 在该 host channel 下的**全部** active private roster，默认 subscription state = `no-wake`
- thread-level Agent Space 的 private-agent participants 表达的是 owner 在该 thread 中**显式 `@mention` 过** 的 private-agent subset；每次新的合法 `@mention` 只追加到同一个 `(owner, thread)` space，默认 subscription state = `auto-wake`
- `channel_memberships` / `reply_thread_participants` 只允许作为 migration / rollback seam 留存，不再作为运行时读写路径的 participant 真相来源。

### 5.4 Parent Observer 是派生浏览能力，不是 participant

- Parent Observer 来自 parent chain 上的默认浏览规则
- Parent Observer 不在 `scope_memberships` 中单独落表
- Parent Observer 也不在 01 中获得单独的 read cursor / unread / subscription 行

### 5.5 Thread-level private membership 是 host-channel private roster 的 mention 子集

thread-level agent space 的 private-agent membership **不能 bulk-sync owner 在 host channel 下的全部 private agents**。

它必须同时满足两层条件：

- host channel 的 `scope_channel_profiles` 定义 agent 是否仍然是该 owner 的有效 private entry；
- thread-level agent space 只 materialize **owner 在这个 thread 中显式 `@mention` 过** 的那些 private agents。

因此：

- host channel private roster 仍然是“这个 agent 是否有资格进入该 owner 的 thread-level Agent Space”的 source of truth；
- 但 thread-level agent space 自己维护的是一个 **mention-scoped subset**，而不是 host-channel 全量 private roster 的镜像；
- owner 后续在同一个 thread 中再 `@mention` 新的 private agent 时，应把该 agent 追加进同一个 `(owner, thread)` Agent Space；
- owner 之后只是把新的 private agent 加进 host channel，并不会 retroactively 把它同步进所有已存在的 thread-level Agent Spaces。

### 5.6 Read / dismiss cursor 与 participant 在同一行；subscription 单独建模

一个 member 只要是某个 space 的 participant，就天然有且只有一份该 space 的 read cursor。

因此：

- read state 锁定为并入 `scope_memberships`，而不再单独保留 `space_read_states` 作为核心表；
- dismiss / intent cursor 也与 participant 同行持久化；
- 但 delivery subscription 不再并进 `scope_memberships`，而是进入 `scope_notification_settings` support table。

### 5.7 Private agent reply routing 不变量

owner 在 parent channel / thread 中 `@mention` private agent 后，该 agent 的 reply **必须写入对应 `agent_space` 的 `scope_messages`**，而不是写回 parent shared timeline。

具体 routing 算法、首次 materialize 时机与是否附带 parent-side anchor 留到后续章节，但这一条落点结论已经锁定。

## 6. ID / 排序 / 分页真相

### 6.1 ID 类型

锁定：

- `space_id` / `space_message_id` / participant 相关 FK 继续沿用 AgentTeam 现有的 **opaque string id** 风格
- 01 不把自己绑定到 UUIDv7 / ULID 的具体生成器实现
- 设计层只要求：全局唯一、可迁移、与现有代码风格兼容

### 6.2 排序真相

`scope_messages` 保留两层序，并直接复用现有热层 transcript 的 per-space `seq` / workspace-scope `workspace_global_seq` 语义分工：

1. `seq`
   - per-space 单调递增
   - 作为 timeline / unread / pagination 的排序真相

2. `workspace_global_seq`
   - 在同一个 `workspace_id` 下单调递增
   - 用于 workspace-scope sync / 审计 / 全局事件流 / debug / 诊断

因此，所有 space 的 timeline 视图都以：

> `(space_id, seq)`

作为主排序真相，而不是 `created_at`；而增量同步 / workspace 级事件流则围绕 `(workspace_id, workspace_global_seq)` 工作。

### 6.3 Migration 友好性

01 不切换 ID 风格；迁移重点放在：

- 统一 object model
- 统一 FK 指向
- 统一 per-space seq 语义
- 统一 workspace-scope `workspace_global_seq` 语义

而不是引入第二套新的 ID 生成体系。

### 6.4 Space public identity 与旧 ID 复用策略

`#2` 的选择为：

- `channel space_id = previous channel scope id`
- `thread space_id = previous thread scope id`
- `dm space_id = previous DM scope id`
- `agent_space.id` 使用新生成的 opaque string id

这条选择的含义是：

- channel 继续沿用现有对外 object identity（slug / URL / governance / owner 都围绕 channel `scopes.id`）；
- thread / dm 延续现有 timeline object identity；
- channel 主 timeline 的previous thread `scopes.id` 变成 migration-only seam，用于把旧 message FK 重映射到新的 channel `space_id`，但它不是 01 core model 中新的 public identity。

## 7. 旧表去向

### 7.1 被 unified Space 底座直接替代的旧表

可以确定，会被新底座替代/吸收的旧表包括：

- channel scopes
- thread scopes
- DM scopes
- `channel_memberships`
- `scope_memberships`
- thread scopes
- 以及 reply-thread 相关的 stats / participants 附属结构

其中，旧 `reply_thread` 的语义在新模型中由 `scopes(type='thread', parent_space_id, parent_message_id)` 直接表达。

而旧的 `scope_notification_settings` 不再并入 `scope_memberships`，而是由后续的 `scope_notification_settings` support table 承接。

### 7.2 保留、但需要改 FK 的表

明确不会在 01 中继续硬砍，而是保留后续改 FK / projection 的表包括：

- `attachments`
- `message_reactions`
- `scope_message_search_docs`
- `scope_fanout_outbox`
- `reaction_agent_deliveries`
- `trigger_runs`
- task 家族相关表

这些表不是 Space 分裂导致的“结构重复”，而是消息能力层的附属表 / runtime 表 / projection 表。

## 8. Lifecycle state machine

### 8.1 生命周期状态

生命周期状态为两种：

- `active`
- `archived`

`agent_space` 在 01 中不引入 `deleted` / `inactive` 作为常规运行态。

- `active`：可继续接收 auto-flow / share / private agent replies 的运行态
- `archived`：只读历史态；保留 transcript，但停止继续参与 active collaboration

真正 destructive delete 如果未来需要，必须作为单独显式动作设计，而不是 remove / owner leave 的隐含副作用。

### 8.2 创建规则

#### Channel-level Agent Space

当 owner 在某个 channel 中加入**第一个** private agent entry 时：

- 创建 `(owner_member_id, parent_space_id=channel_id, type='agent_space')`
- 初始状态 = `active`
- 按产品 spec 的 backfill 规则，把 owner 之前在该 channel 中的历史发言纳入该 Agent Space 的初始 transcript

#### Thread-level Agent Space

当 owner 在某个 thread 中首次 `@mention` private agent 时：

- 创建 `(owner_member_id, parent_space_id=thread_id, type='agent_space')`
- 初始状态 = `active`
- 初始 private-agent participants = 这条 triggering message 中被 owner `@mention` 到的 private agents
- 这些 thread-level private-agent participants 的初始 subscription state = `auto-wake`
- 按产品 spec 的 backfill 规则，把 owner 之前在该 thread 中的历史发言纳入该 Agent Space 的初始 transcript

### 8.3 Active 条件

`agent_space.status='active'` 只表达两件事：

- 它的 parent space 仍然存在；
- 它尚未因为 lifecycle 事件转为 `archived`。

像“owner 仍在 host channel 范围内”或“host channel 里仍然有 private agent entry”这类条件，应被视为 **archive 触发条件**，而不是 active 状态定义本身。

> 这里刻意避免把状态定义和触发条件混写；具体 membership 检查在 lifecycle / routing 实现章节细化。

### 8.4 Archive 触发条件

archive 触发分为两类：

1. **Last private entry removed**
   - 当某个 `(owner, host_channel)` 的最后一个 private agent entry 被移除时：
     - 该 owner 在这个 host channel 下的 channel-level Agent Space 转为 `archived`
     - 该 owner 在这个 host channel 下所有已存在的 thread-level Agent Spaces 也一并转为 `archived`

2. **Owner leaves channel**
   - 当 owner 离开 host channel 时：
     - 效果等同于移除其在该 host channel 下的全部 private agent entries
     - 相关 channel-level 与 thread-level Agent Spaces 一并转为 `archived`

3. **Host parent is removed from active collaboration**
   - 当 host channel 本身被归档、删除或以其他方式退出 active collaboration surface 时：
     - 挂在该 host channel 上的 channel-level Agent Space 全部转为 `archived`
     - 该 host channel 下所有已存在的 thread-level Agent Spaces 也一并转为 `archived`
   - 当某个 host thread 被归档、删除或以其他方式退出 active collaboration surface 时：
     - 只挂在该 thread 上的 thread-level Agent Spaces 转为 `archived`
     - 同 host channel 下的 channel-level Agent Space 不因此被级联归档

还锁一条一致性不变量：

> 对任意 `(owner, host_channel)`，如果对应 `scope_channel_profiles` 中 `visibility='private' AND owner_member_id = owner` 的有效计数已经为 0，则该 owner 在这个 host channel 下的 channel-level Agent Space 必须已经处于 `archived`；相关 thread-level Agent Spaces 也必须处于 `archived`。

entry 删除与 Agent Space 归档在实现上应表现为同一事务边界内的状态收敛，避免出现“最后一个 private entry 已删，但 Agent Space 仍然保持 active”的可观察中间态。

host parent 的移除也遵守同一原则：

- 这里的级联结果是 **cascade archive**，不是 destructive delete；
- Agent Space transcript 保留为历史对象，继续遵守 `archived` 的只读 browse 合同；
- 只有当前仍对该 host parent 具备 read 权限的 viewer 才能继续读取这段归档 transcript；它不引入新的“历史 browse ACL”，也不能再把这段 transcript 恢复成 active collaboration target。

### 8.5 Archived 的行为边界

`archived` Agent Space 的行为：

- transcript 保留，可继续浏览；
- 仍然对该 host parent 保有 read 权限的 parent participants 可以继续 browse 这段 archived transcript；
- 不再接受新的 auto-flow；
- 不再接受新的 private-agent runtime replies；
- 不再继续作为 active collaboration target；
- archived space 不会被后续 re-add 直接 revive。

已锁定：`archived` 是**完全冻结的只读终态**。

因此：

- share / attach / explicit new message 写入一律关闭；
- archived Agent Space 只能被浏览，不能再作为协作输入目标；
- archived 与 active 之间不允许直接 revive。

换句话说：

> `archived` 是保留历史的只读终态，而不是可恢复的暂停态。

### 8.6 Recreate 语义

当 owner 之后重新 add private agent 时：

- channel-level Agent Space 按 channel-level 创建规则新建一条新的 `scopes` row；
- thread-level Agent Space 仍按 thread first-`@mention` 规则再 materialize；
- 新建的 Agent Space **不继承**之前 archived Agent Space 的 transcript；
- archived Agent Space 保持为历史对象，新 Agent Space 拥有新的 identity。

### 8.7 不做的 lifecycle 扩展

01 不引入：

- ownership transfer
- archive -> active revive
- soft-delete / tombstone 与 archive 并存的多态状态机
- join-aware archive 分叉逻辑（join 模型已从本轮 spec 中拿掉）

## 9. Routing invariants

### 9.1 Routing 的三个写入目标

在 unified Space 底座里，运行时最终只能把一条新消息写到三类 space 之一：

- parent `channel`
- parent `thread`
- `agent_space`

其中 private-agent collaboration 的关键结论已经锁定：

> private agent 的 reply 不写 parent shared timeline，而是写入对应 `agent_space` 的 `scope_messages`。

### 9.2 Channel-level private auto-flow

当 `(owner, channel)` 的 channel-level Agent Space 已经存在且处于 `active` 时：

- owner 在该 channel 中 authored 的后续 parent messages，会自动流入这个 channel-level Agent Space；
- 即使某条 channel main 消息因为 per-message routing modifier 被分流到一个新建的 thread-level Agent Space（例如“Also start a thread”），这条 owner-authored parent message 也仍然必须以 **no-wake durable copy** 的形式写入 channel-level Agent Space，作为 `(owner, agent, channel)` 的完整 ledger；真正承担本次 wake 的只应是 thread-level 那份 interaction copy；
- 这些 auto-flow rows 属于 `scope_messages(space_id = channel_level_agent_space_id)`；
- 它们不是 parent message 的“移动”，而是 Agent Space transcript 中的新 message objects；
- parent message 本身仍然保留在 parent channel 的 timeline 中。

本节只锁“会进入 Agent Space transcript”这层；它是复制一条独立 message、还是由 source-backed row 派生，留到 backfill/source semantics 章节细化。

`auto_flow` 消息是否也产生 `scope_message_sources` 关系，留到 source refs 章节统一决定。

### 9.3 Thread-level private auto-flow

当 `(owner, thread)` 的 thread-level Agent Space 已经存在且处于 `active` 时：

- owner 在该 thread 中 authored 的后续 parent replies，会自动流入这个 thread-level Agent Space；
- thread-level Agent Space 不在 owner 首次 reply 时创建，而是在 owner 首次 `@mention` private agent 时 materialize；
- owner 在该 thread 中后续又 `@mention` 到新的 private agent 时，会把该 agent 追加进同一个 thread-level Agent Space；
- 这类 thread-level 新加入的 private-agent participant，其初始 subscription state 仍然是 `auto-wake`；
- 一旦 materialize 完成，后续 owner-authored thread replies 的 auto-flow 与 channel-level 规则一致。

### 9.4 Non-owner parent messages 默认 inert

private agent 的默认输入边界在 Space Core 的 routing 结论上表现为：

- non-owner 在 parent channel / thread 里的原始消息，不会自动写入任何 `agent_space`；
- 即使该 non-owner 是 parent participant，这条默认规则也不变化；
- 想让 private agent 理解 non-owner 消息，必须走显式 share / attach 流程。

因此，在 core model 中：

> parent raw messages -> agent_space 的自动路由只对 owner-authored parent messages成立。

同一条 owner-scoped 边界也适用于 human ↔ agent 的 DM 能力：

- v1 不允许 non-owner 与别人拥有的 agent（private / public）建立新的 DM conversation；
- non-owner 也不能绕过 parent-mounted Agent Space 的 read-only gate，改为在 DM 中继续对别人的 private agent author；
- human-to-agent DM 仍然是 owner-scoped 能力；对 public agent，v1 的 owner 默认取其 creator / manager-of-record，与当前实现的 creator gate 对齐；若未来要放开，必须作为独立产品能力另行设计。

### 9.5 Private-agent mention 的 materialize / reply 落点

当 owner 在 parent channel / thread 中 `@mention` private agent 时：

- 如果对应 `agent_space` 尚不存在，则在这一刻 materialize；
- owner 这条 parent message 仍然是 parent space 中的普通 owner-authored message；
- 由这次 `@mention` 唤醒出来的 private-agent reply，必须写入对应 `agent_space` 的 `scope_messages`；
- 不会因为 private-agent reply 而额外写回 parent shared timeline。

还锁一条实现边界：首次 materialize 该 `agent_space` 与首条 private-agent reply 的写入，应当表现为同一原子单元完成，或通过幂等重试保证最终一致；不能让用户观察到“space 已建但 reply 丢失”的不一致终态。

落点规则：

- parent = `channel` -> reply 写入 `(owner, channel)` channel-level Agent Space
- parent = `thread` -> reply 写入 `(owner, thread)` thread-level Agent Space

### 9.6 Unauthorized private mention 永远 inert

在 parent channel / thread 中：

- non-owner 对 private agent 的 `@mention` 不会产生 routing side effect；
- 不会 materialize 新的 Agent Space；
- 不会把这条 parent message 写入任何 Agent Space；
- 也不会触发 private-agent runtime reply。

消息本身仍然作为普通 parent message 存在；只有那段 unauthorized private mention 是 inert 的。该消息上的其他合法 routing（例如 public agent mention、其他 participant 的正常 delivery）不受影响。

### 9.7 Archived Agent Space 的 routing 边界

一旦 `agent_space.status='archived'`：

- 不再接收新的 auto-flow；
- 不再接收新的 share / attach 写入；
- 不再接收新的 private-agent runtime replies；
- 若后续重新满足创建条件，应创建新的 Agent Space，而不是向 archived space 继续写入。

### 9.8 Public agent 仍走 parent shared routing

统一 Space 底座不会改变 public agent 的根本位置：

- public agent 仍然是 parent shared space 的 participant；
- 它的 message routing 仍以 parent `channel` / `thread` 为目标，而不是写入 `agent_space`；
- `agent_space` 只承载 private collaboration transcript，不承载 public shared-collaboration transcript。

> mute / wake 强度、delivery queue 以及 `@mention` 覆盖规则留到后续 design 处理；本节只锁写入目标与路由边界。

### 9.9 Owner direct authoring in Agent Space

owner 可以直接在 Agent Space surface 内 authored 新消息。

这类消息的 routing 结论是：

- 直接写入对应 `agent_space` 的 `scope_messages`；
- 它们不是 parent timeline 的 message，也不要求先经过 parent 再 auto-flow；
- 默认不附带 parent source；若未来需要引用 parent 上下文，应通过 source refs / share 机制显式表达。

这条规则保证 panel / inline 等 Agent Space surface 可以承载真正的 Agent Space composer，而不只是 parent timeline 的被动投影。

> mute / wake 强度、delivery queue 以及 `@mention` 覆盖规则留到后续 design 处理；本节只锁写入目标与路由边界。

## 10. Backfill model

### 10.1 Backfill 与 auto-flow 是两件事

先明确区分：

- **backfill**：Agent Space 在创建时，把 parent 中已经存在的、符合规则的历史消息写入 Agent Space transcript
- **auto-flow**：Agent Space 创建之后，后续新产生的 parent message 按 routing 规则继续写入 Agent Space transcript

backfill 只发生在 Agent Space 的创建时刻；它不是一个持续运行的背景同步机制。

### 10.2 Channel-level creation backfill

当 owner 在某个 channel 中加入第一个 private agent entry，从而创建 `(owner, channel)` Agent Space 时：

- backfill 的来源集合是：该 channel 中**creation cutoff 之前**的 owner-authored parent messages；
- non-owner authored 的 parent messages 不进入 backfill 集合；
- child thread scopes 中的 owner 消息也不属于 channel-level backfill；它们属于各自 thread-level Agent Space 的历史边界。
- 这一点不因为 child thread 或其 thread-level Agent Space 是否已经 archived 而变化。

这条规则直接继承产品 spec：channel-level Agent Space 只自动理解 owner 在该 channel 主 timeline 中说过的话，不自动理解别人说过的话，也不把 child thread 历史混进来。

### 10.3 Thread-level creation backfill

当 owner 在某个 thread 中首次 `@mention` private agent，从而创建 `(owner, thread)` Agent Space 时：

- backfill 的来源集合是：该 thread 中**triggering mention 那条消息之前（不含该触发消息）**的 owner-authored thread messages；
- non-owner authored 的 thread messages 不进入 backfill 集合；
- parent channel mainline 的 owner 消息也不属于 thread-level backfill。

还锁一条边界：

- 触发创建的那条 owner `@mention` message **不属于 backfill**；
- 它应当通过普通 owner-authored parent message 的 routing / auto-flow 路径进入新创建的 Agent Space，只出现一次。

### 10.4 Backfill 的落点

无论 channel-level 还是 thread-level，backfill 的产物都必须成为：

- 对应 `agent_space` 中的 `scope_messages`

也就是说，backfill 不是“懒读取 parent 历史”，而是把符合条件的历史消息真正纳入 Agent Space transcript。

本节锁这层结果；至于这些 backfill rows 在物理上是：

- 完整复制的独立 messages
- 带 source refs 的派生 messages
- 或其他 source-backed 表达

留到 source refs / implementation 章节细化；具体选择见下一节 `Source refs semantics`。

### 10.5 Recreate 时的 backfill

当一个 Agent Space 因 last private entry removed / owner leave 而 archived 之后，owner 再次满足创建条件时：

- 会创建一个**新的** Agent Space；
- 这个新 Agent Space 的 backfill 仍按创建规则重新执行；
- archived Agent Space 的 transcript 不会被直接复制或 revive 到新 space。

这意味着：

> recreate 的语义是“从当前 parent raw history 重新开始构建一个新的 Agent Space”，而不是“恢复旧 Agent Space 并补齐中间缺口”。

### 10.6 Backfill 的事务边界

创建 Agent Space 的那次操作，至少在逻辑上应被视为同一个原子单元：

- 创建 `scopes` row
- 计算并写入初始 backfill transcript
- 若当前操作本身伴随一条触发消息（如 thread first `@mention`），则把该触发消息按 routing 规则写入新 Agent Space

实现可以通过单事务、幂等批写、或补偿机制实现，但最终必须满足：

- 用户不会观察到“space 已经存在，但初始历史缺半截”的稳定错误状态；
- 触发创建的当前消息不会因为 backfill 流程被重复写入两次。

还锁一条逻辑去重要求：

> 对同一个 `(agent_space_id, source_space_id, source_seq)`，backfill 逻辑上最多只能产出一条对应的 Agent Space row。

具体 dedupe key 如何落地，取决于下一节 `Source refs semantics` 里选择的物理表达。

## 11. Source refs semantics

### 11.1 `scope_message_sources` 的职责

`scope_message_sources` 承载的是：

- `agent_space` transcript 中某条 `space_message` 与 parent source messages 之间的结构化关系；
- parent ↔ Agent Space 双向导航所需的可查询 provenance；
- folded rendering / explainability / audit 的统一真相源。

它不是第二份 timeline，也不是“原消息已经被搬运”的标记表；真正的 transcript 仍然只在 `scope_messages`。

最小字段集合至少包括：

- `space_message_id`（FK -> `scope_messages`）
- `source_space_id`（FK -> `scopes`）
- `source_seq`（该 source 在 `source_space_id` 内的 durable navigation key）
- `source_message_id`（可选的 durable convenience id；如果保留，也不假设它是指向 hot storage row 的 live cascading FK）
- `source_kind`（`auto_flow` / `share`）
- `ordinal`（同一条 Agent Space message 内多 source 的稳定顺序）

还锁一条真相边界：

> `scope_message_sources` 的导航 / provenance 真相是 durable source identity（至少 `source_space_id + source_seq`），而不是对 hot message row 的 live cascading FK。

这意味着 source resolve 可以经由：

- 当前仍在 hot storage 中的 message row
- 冷归档对象
- 未来的 permanent message index / tombstone layer

但 01 不锁这些 support object 的最终命名或物理形态。

还锁一条排除边界：

> `scope_message_sources` 只描述 **Agent Space transcript row -> parent source messages** 的 provenance / navigation 关系；它不承担 `publish_out` 这类 **parent published row -> Agent Space authoring row** 的反向 copy+link 关系。

也就是说：

- `scope_message_sources` 的方向仍然是 **Agent Space row 指向 parent source**；
- `source_kind` 仍只覆盖 `auto_flow` / `share`；
- publish-out 如需 durable relation，必须由独立的 `scope_share_cards` support table（或等价 publication relation object）承载，而不是复用进 `scope_message_sources`。

### 11.2 一条 Agent Space message 可以有 0..N 个 source refs

锁定：

- `scope_messages` 中的一条 message 可以有 **0 个、1 个或多个** source refs；
- source refs 自身需要保留稳定顺序（例如 `ordinal`），以支持多条 source 在 share quote 中的展示顺序。

因此：

- owner direct authoring in Agent Space：通常是 **0 source refs**
- private-agent runtime reply：通常是 **0 source refs**
- auto-flow message：通常是 **1 source ref**
- share / attach quote：可以是 **1..N source refs**

### 11.3 `source_kind`

`source_kind` 为两种：

- `auto_flow`
- `share`

语义分别是：

- `auto_flow`：这条 Agent Space message 来自 owner-authored parent message 的自动流入
- `share`：这条 Agent Space message 是 owner 显式 share / attach 产出的 quote message，其 source refs 指向被授权纳入的 parent messages

### 11.4 Auto-flow 也要产出 source refs

01 正式锁定：

> owner-authored parent message auto-flow 进 Agent Space 时，也必须产出对应的 `scope_message_sources` 关系。

原因：

- parent ↔ Agent Space 双向导航不能只覆盖 share，不覆盖 auto-flow；
- §10 backfill 的逻辑去重也需要稳定的 `(agent_space_id, source_space_id, source_seq)` 键；
- hover / context alignment 需要知道某条 Agent Space row 对应的是哪条 parent message。

因此，auto-flow rows 与 share rows 都进入同一套 source-ref 体系，只是 `source_kind` 不同。

### 11.5 Share 的产物模型

锁定：share / attach 的产物不是把 source message 直接塞进 private-agent delivery pipeline，而是：

- 在 `agent_space.scope_messages` 中 author 一条新的 **owner-authored quote message**；
- 在 `scope_message_sources` 中记录这条 quote message 引用了哪些 parent source messages；
- source message 本身仍留在 parent Space，不变成 Agent Space 中的原始 delivery object。

这条结论延续了“share 产物是 owner-authored quote message”这一抽象，但**不再沿用当前 canonical model 中的 authored location**。

也就是说：

- 当前 canonical model 把 owner quote 视为 parent timeline 中的 visible normal message；
- 本设计把 owner quote 视为 Agent Space 内的 owner-authored message，并在其中记录 source refs。

从 authored semantics 上看，这类 quote message 也属于 `§9.9 Owner direct authoring in Agent Space` 的一个特例：

- 它仍然是一条 owner 直接 authored 的 Agent Space message；
- 只是额外带上了 `share` 类型的 source refs。

01 **不**把 snapshot / enrichment 字段锁进 `scope_message_sources`：

- quote message 自身的 payload 已经承载 owner-authored 的 share 结果；
- `scope_message_sources` 只负责 durable provenance / navigation；
- 若后续确认需要额外的 source snapshot fields，应由后续 share design 单独补充。

### 11.6 Source scope 边界

已锁定 **祖先链（ancestor chain）** 规则：

- channel-level Agent Space 的 source 只能来自该 channel 主 timeline；
- thread-level Agent Space 的 source 可以来自该 thread，或它的 host channel 主 timeline；
- 不允许跨出这条祖先链去引用别的 channel、别的 thread、别的 DM。

因此：

- `parent_space_id` 仍然定义 Agent Space 的直接上下文；
- source scope 则允许 thread-level Agent Space 从自己的直接 parent thread，或其 host channel 祖先中显式 share / attach context；
- channel-level Agent Space 依然只消费 channel 主 timeline，不会反向吸入 child thread 历史。

这条规则的产品含义是：

> thread-level Agent Space 的默认工作上下文是 thread，但 owner 可以显式把 host channel 主 timeline 的上下文拉进来；它不是对“任何祖先之外的 Space”开放的全局引用能力。

### 11.7 Source 编辑 / 删除后的行为

锁定：Agent Space transcript 是**快照式历史**，不是对 parent source 的活引用视图。

因此：

- parent source message 后续被编辑，不会回写或重写已存在的 Agent Space message body；
- parent source message 后续被删除，也不会级联删除已存在的 Agent Space transcript；
- `scope_message_sources` 关系仍保留，用于说明“这条 Agent Space message 当时来源于哪条 parent message”；
- UI 可以在 render 时把 source 标记成 edited / deleted / unavailable，但不改变 transcript 事实。

还补一条 hot/cold 约束：

- source resolve 不依赖 live cascading FK；
- 若 hot message row 已经被归档或 rewind 清出 hot storage，应通过 durable source identity 去冷归档或 tombstone/index 层继续解析 provenance；
- 解析失败时，UI 至少仍然可以把这条 relation 表达为“来源消息不可用”，而不是破坏 Agent Space transcript。

对 surface 的最低回退合同再补一条：

- shared-context row / detail 中若 `Jump to source` 已无法解析，应保留 snapshot 本体可读；
- 导航 affordance 降级为 disabled state 或明确的 `Source unavailable` placeholder；
- 不允许因为 source resolve 失败而把整条 Agent Space transcript row 隐藏掉。

### 11.8 Source refs 是 trace 的真相源

parent message 上的轻量 trace（例如未来的 “used in Agent Space” 提示）如果需要存在，应从 `scope_message_sources` 推导，而不是再维护第二份并行状态。

这保证：

- provenance 只有一份真相源；
- 双向导航、审计、UI trace、explainability 都围绕同一组 source refs 工作；
- 不再引入额外的 source-delivery shadow object。

### 11.9 去重与唯一性

去重语义分两层：

1. **Auto-flow / backfill 去重**
   - 对同一个 `(agent_space_id, source_space_id, source_seq, source_kind='auto_flow')`，逻辑上最多只能有一条对应 transcript row。

2. **Share quote 去重**
   - v1 明确允许同一 source message 被 owner 多次显式 share；每次 share 都会产出新的 quote message 和新的 `share` source refs。
   - `share` 因此不要求全局唯一。
   - 但自动预填 / pending-candidate 这类体验如果需要“只推荐尚未用过的 source”，应仍以真实 sent quote messages 上的 source refs 为准，而不是额外维护平行状态。

## 12. Migration inventory

### 12.1 直接被 unified Space 底座替代的旧表

以下旧表或旧对象在 unified Space 底座落地后，应被新 core model 直接替代：

- channel scopes
- thread scopes
- DM scopes
- `channel_memberships`
- `scope_memberships`
- thread scopes
- reply-thread 相关的 summary / stats / participants 附属结构

替代关系如下：

- `channel` / `thread` / `dm` / `agent_space` 统一进入 `scopes`
- 参与关系、read cursor 与 dismiss cursor 统一进入 `scope_memberships`
- thread 级 delivery subscription 统一进入 `scope_notification_settings`
- 所有 timeline message 统一进入 `scope_messages`
- parent ↔ Agent Space 的 provenance / navigation 统一进入 `scope_message_sources`
- channel 级 agent 身份统一进入 `scope_channel_profiles`
- channel-specific governance 统一进入 `scope_channel_profiles`

### 12.2 保留但需要改 FK / projection 的表

以下表在 01 中不继续被硬砍，而是保留为消息能力层、runtime 层或子域层的附属表；它们后续需要把旧 `channel_id` / `thread_id` / `message_id` 关系迁到 unified Space 底座：

- `attachments`
- `message_reactions`
- `scope_message_search_docs`
- `scope_fanout_outbox`
- `reaction_agent_deliveries`
- `trigger_runs`
- task 家族相关表（`task_boards` / `tasks` / `task_review_requests` 等）
- 现有 read-state 旁路 cursor（例如 `owner_context_dismissed_through_global_seq`）需要在吸收 `scope_memberships` 时一并迁移或重表达

这些表的共同原则是：

- 它们不是旧 Space 分裂模型本身；
- 它们代表消息能力、runtime 投影、搜索投影或业务子域；
- 01 只需要列出它们受影响，不在 01 里把每张表都重设计一遍。

还补一条迁移时机约束：

- `attachments` / `message_reactions` / `scope_message_search_docs` 与 `scope_messages` 的 FK 改造属于 hot-path 消息能力层，应与 unified message 底座一起完成，不能长时间分叉；
- `scope_fanout_outbox` / `reaction_agent_deliveries` / `trigger_runs` 可以跟随后续 delivery-runtime / wake design 一起迁移；
- task 家族由于与 message 底座解耦更强，可以排在更后面。

具体 rollout 节奏仍留给 migration design 决定。

### 12.3 迁移清单的最小结论

01 只锁三类迁移动作：

1. **Core replacement**
   - 用 unified core tables 替代旧的 channel/thread/dm/message 底座

2. **FK remap**
   - 让附属表从旧 `channel_id` / `thread_id` / `message_id` 指向新 `space_id` / `space_message_id`

3. **Subdomain follow-up**
   - 对 task、trigger、搜索、delivery-runtime 等子域，仅在 01 中登记“受影响”，具体改造留给各自 design 或 implementation PR

还显式锁一条 migration 简化前提：

- 现有 channel `scopes.id` / thread `scopes.id` / DM `scopes.id` / `scope_messages.id` 都已经是 opaque string id；
- 因此 unified Space 迁移会**尽量复用现有 object/message id**，而不是额外生成一套新 id；
- 但复用不是“完全零映射”：其中 channel message 的previous thread-scope-id to channel-scope-id 关系需要在迁移时做 FK remap；
- 这意味着 migration 的主要复杂度来自 schema / FK 重定向与语义统一，而不是 deployment-wide old-id → new-id 对照表维护。

### 12.4 01 不在这里决定的迁移问题

01 不锁以下问题：

- dual-write / dual-read / big-bang cutover 的 rollout 策略
- 外部 API 是否保留旧 `thread_id` / `channel_id` compat projection
- 各附属表的最终物理索引 / 分区 / hot path 调优
- `scope_fanout_outbox` 与 `reaction_agent_deliveries` 是否在后续 delivery-wake design 中进一步合并

这些问题都属于“建立在 unified Space 底座之上的迁移与实现策略”，而不是 Space Core 本身的对象模型。

## 13. Out of scope 与后续 design 边界

`01-space-core` 到这里为止，只锁 **Space identity / core tables / invariants / lifecycle / routing / backfill / source refs / migration inventory**。

以下问题明确不在本篇继续拍细节，而交给后续 design：

### 13.1 Migration rollout strategy

本篇不决定：

- dual-write / dual-read / big-bang cutover 采用哪一种 rollout 策略；
- 是否需要 shadow read / backfill verification / rollback window；
- migration 的执行窗口、停机策略与回滚路径。

这些属于“如何把 unified Space 底座落到线上”的迁移策略问题，不属于 Space Core 对象模型本身。

### 13.2 API compatibility shape

本篇不决定：

- 外部 API 是否继续暴露 `channel_id` / `thread_id` 兼容字段；
- server / web / daemon 在过渡期是否通过 compat projection 屏蔽底层变化；
- 对外 response shape 是一次性切换还是阶段性兼容。

这些问题建立在 `scopes` / `scope_messages` 已经确定之后，再由 API / migration design 单独收口。

### 13.3 Mute / wake contract

本篇不继续展开：

- private/public agent 在 channel/thread/dm 中的默认 wake 强度；
- `@mention` 覆盖 quiet 的精确规则；
- delivery queue / runtime queue / unread 的最终关系。

本篇只锁：

- “消息写入哪个 space”
- “哪些消息进入哪个 transcript”
- subscription 是独立于 participant 的 support object（`scope_notification_settings`）

wake 强度与 runtime 行为留给后续 `02-delivery-wake` 设计。

### 13.4 Share pipeline 细节

本篇已经锁定：

- share 产物是 Agent Space 内 owner-authored quote message；
- source refs 是 provenance 真相源；
- thread-level source scope 允许祖先链。

但本篇不继续展开：

- multi-message share / range share / thread snapshot 的具体产品形态；
- folded rendering 的具体 UI 和 payload shape；
- share action 的 pending / draft / suggestion / prefill 体验。

这些属于后续 `03-share` 设计。

### 13.5 UI surfaces / context alignment rendering

本篇不决定：

- inline / panel 的开关位置与默认值；
- panel / inline / compare 的最终交互形态；
- parent ↔ Agent Space 导航在 UI 上是 hover、click、badge 还是其他触发方式；
- 窄屏 bottom sheet / 桌面 side panel 的具体实现。

本篇只保留对这些 UI 的**数据与语义支撑**：

- Agent Space transcript 是独立真实 timeline；
- source refs 支撑双向导航；
- parent inline 只是 render surface，不改变 authored location。

具体 surface 形态与交互留给后续 `04-surface` 设计。

### 13.6 子域跟进边界

以下子域在 01 中只登记“受影响”，不在这里展开：

- delivery-runtime（`scope_fanout_outbox` / `reaction_agent_deliveries`）
- search / indexing
- triggers
- tasks
- attachments / reactions 等消息附属能力

这些子域都应以本篇锁定的 unified Space 底座为前提，再分别做自己的设计或实现改造。

## 14. 结论（一句话）

`01-space-core` 已经锁定的方向是：

> 用 `scopes + scope_memberships + scope_messages + scope_message_sources + scope_channel_profiles` 作为 unified Space core model，并以 `scope_channel_profiles` / `scope_notification_settings` 作为 support tables；其中 `public/private` 只属于 channel 级 agent entry，真实 participant 与 read/dismiss cursor 进入 `scope_memberships`，observer 不落表，delivery subscription 独立建模，`workspace_id` 是 core 字段，消息排序以 `(space_id, seq)` 为真相、以 `(workspace_id, workspace_global_seq)` 承接 workspace 级同步，而物理存储继续沿用 hot/cold message 架构。
