Pinned Messages and Objects
This content is not available in your language yet.
0. 规范语言
本文中的规范关键字(MUST / SHOULD / MAY 等)按 conformance/normative-language.md 解释;仅大写形式具有规范约束力。
1. 范围
Shared pin 是进入 Realm reducer 的共享投影事实,用于把 Message、Strand、Morph、Relation 或其它可引用对象固定在某个共享范围中。个人保存 / 收藏不使用 shared pin;它们由 personal-productivity.md 的 account data 表达。
架构决策(normative boundary):Pin 有意不建模为 Relation.kind=pinned。Relation 表达对象之间可查询、可参与图遍历的语义边;Pin 表达某个 projection home 的有序 UI roster,pin_scope 不是关系端点或安全边界,且 reorder 是高频 CAS 排序操作。把 Pin 放入 Relation 会让 UI 排序边进入通用关系图、改变 graph query / relation-kind registry 语义并混淆 scope。实现 MUST 使用本文件的 pin typed current result 与三类 event,MUST NOT 以 Relation 代替 shared Pin。
实现声明 ak.profile.pinned_items.v1 时,MUST 支持 ak.pin.add、ak.pin.remove 和 ak.pin.reorder。Realm effective owner MAY 在自己的 Realm resource 上逐字授予这三项 action,唯一上界来源是 capabilities.md §3.2 与 compiled ak.realm.owner.grant_authority_rule.include_actions[];该 grant authority 不赋予 owner 直接 pin 权限,也不绕过本文件 §3 的 scope safety。
2. Pin Scope
Pin payload 使用 pin_scope,MUST NOT 使用裸 scope 或 scope-reference 字段。pin_scope 的形态为 { kind, id },kind 取 strand、realm、circle 或 space。
pin_scope 是 projection home,不是安全边界。kind=space 时,reducer MUST 解析 Space metadata 的 effective scope;Space 不因此获得独立 membership、policy、history visibility 或 MLS boundary。
该 typed current result 的 family 名是 pin(current-result-registry.json 的登记行,value schema 见 typed-current-result.schema.json#/$defs/pin_result)。它的 result_selector.kind 固定为 composite,唯一分量是 {"kind":"canonical_json","field":"payload.pin_scope"}——整个封闭对象按 RFC 8785 canonical JSON 取字节,因此 special form 渲染为 pin:<pin_scope.kind>:<pin_scope.id>,kind 在前、id 在后,从该字符串可逆还原出 {kind, id}。MUST NOT 只取 .id:pin_scope 是四分支 oneOf,丢掉 kind 既不可逆,也会让不同 kind 下同名的 id 撞成同一个 subject。producer、reducer、Current publisher 与 selector validator MUST 从同一 registry row 得到完全相同的 typed current result id。v1 不注册 pin 专用 typed id——strand_position 同样没有,这不影响可逆性,因为分量本身是自带 kind 的封闭对象。
3. Scope Safety
目标对象必须落在 resolved effective scope 内,或在该 scope 内可见。Public Space 不得 pin Circle-private object;Realm-wide pin 不得泄露 Circle-scoped Message 的存在性。若目标不可见或跨 scope 不合法,reducer MUST fail closed,并对调用方返回与不可见对象一致的 not_found;错误形态不得向无权 actor 泄露目标是否存在。内部审计 MAY 记录更具体的 scope mismatch 诊断。
Pin note 若存在 MUST 使用 EncryptedPayload 加密;v1 不提供 plaintext-visible note 分支。需要公开说明时应创建普通 Message / Morph 并 pin 该对象,不得把 Pin 元数据变成额外明文通道。
4. Events
ak.pin.add 添加或更新一个 (pin_scope, target_ref) pin entry,携带 rank 和可选 note。ak.pin.remove tombstone 同一 entry。ak.pin.reorder 只更新 rank;不得改变 target 或 pin scope。
4.1 收敛(normative)
pin:<pin_scope.id> 的 domain reducer 是 keyed-set projection——该取值属于
common-fields.md §2 的封闭枚举,元素的稳定 tag 是
event-and-patch.md §2.4.2 的 canonical Event dot
<event_id>:<write_index>;join 由本节下文给出。其元素是 pin 断言:ak.pin.add、ak.pin.remove 与 ak.pin.reorder
各精确投影一个 {"kind":"keyed_set_add","tag":{"dot":true},"value":{"field":"payload"}}。
remove 与 reorder 同样是往集合里加一条断言,而不是 explicit revocation。
一个 pin scope 下的全部 pin 共用这一个 typed current result,因此 entry 身份分两层:pin_scope 由 typed current result subject
承载,target_ref 是元素值上的字段。三个 kind 的元素值都是各自完整 payload,投影不拼装、
改名或裁剪字段(event-and-patch.md §2.4.2)。
断言者与极性不是元素字段(normative):某个元素属于 add、remove 还是 reorder,以及是谁断言的,reader MUST 从该元素 dot 所指 Event 的签名 envelope(kind 与 actor_id)读出,MUST NOT 从元素值推断。这不是风格选择而是必要条件:pin_add_payload 与 pin_reorder_payload 的必填成员同为 {pin_scope, target_ref, rank},两条都不带可选成员时元素值逐字节相同,故元素 schema 是 anyOf 而不是可判别的 oneOf。下文 roster 折叠对三种断言的区分完全依赖这一条。
为什么 remove 不用 keyed_set_remove_observed:无 match 的形态会移除同 scope 下全部
target 的 pin;带 match 的形态只移除冻结前态下存活的 add dot,与该 remove 并发的 add
不在其中,于是并发 (add, remove) 会静默收敛为 add;下一段要求这类互斥并发显式暴露而非任选一边,故 remove 必须是断言。
Roster 投影(默认视图):对每个 target_ref,取该 (pin_scope, target_ref) 下因果最晚
的断言集;恰有一个 head 时它是 effective 断言。存在互不可达 heads 时,该 target 进入冲突投影,
默认 roster 不投影 active pin,并向有权 reader 暴露完整 heads;后续断言必须在 causal basis 覆盖完整
current head set 才能收敛。
effective 断言来自 ak.pin.remove 时该 entry 不出现在 roster;来自 ak.pin.add 时 entry 为
该 payload;来自 ak.pin.reorder 时 rank 取该 reorder 的 rank,note 与其余 entry 字段
继承自同一 (pin_scope, target_ref) 下因果最晚的存活 ak.pin.add 断言——reorder
「只更新 rank」即由此保证,reorder MUST NOT 清除 note。
没有可继承 ak.pin.add 时的 reorder(normative):若该 (pin_scope, target_ref) 下不存在
因果更早且存活的 ak.pin.add 断言(从未 add,或 effective 断言是 ak.pin.remove),reducer
MUST 以 failed_precondition(reason=pin_target_not_pinned)拒绝该 ak.pin.reorder,
MUST NOT 用只有 rank 的合成 entry 把目标重新放回 roster。目标对象尚未在本地物化时按
common-fields.md §5.1 的「未知对象 pending / replay」保留待重放。
该 keyed-set projection 的 join 由 common-fields.md §2 定义(dot 集合并,可交换、可结合、幂等),本节 MUST NOT 另行定义它。审计视图保留全部断言;领域投影可报告冲突,但该诊断不是新的 typed current result 状态或授权拒绝。
重排必须保持稳定:不同 target 按 (rank, target_ref) 的 ASCII bytewise lexicographic ascending 排序;相同 rank 不构成互斥冲突。ak.pin.remove.expected_rank 与 ak.pin.reorder.expected_rank 是可选 CAS 前置;存在时 MUST 与无冲突的 current materialized rank 逐字节相等,否则 failed_precondition 且不得修改 entry。单一 target 的互不可达 reorder/add/remove 按上一段进入冲突视图,不得用 digest、HLC、actor id 或接收顺序选边。该冲突视图没有也不得有 reason_code:本节上文已定它既不是新的 typed current result 状态、也不是授权拒绝,而 error-code-registry.json 只登记可拒绝的诊断;本域真正会拒绝的两条是 pin_target_not_pinned 与 expected_rank 不匹配时的 failed_precondition,两者均已登记。writer SHOULD 使用 rank rebalance 避免长期 rank 碰撞。
5. Interactions
Pin 不覆盖 Message expiry、redaction、history visibility、moderation 或 capability。目标过期、redacted、quarantined 或对 viewer 不可见时,pin projection MUST 降级为最小 stub 或省略;不得因为 pin 而恢复 plaintext。