tp/docs/retained-runtime-target-architecture.md
Kinneyzhang db5864141a update
2026-08-13 14:40:44 +08:00

206 lines
15 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# TP Retained/Reactive Text Runtime 目标架构
英文版见 [TP Retained/Reactive Text Runtime Target Architecture](retained-runtime-target-architecture-en.md)。
公共入口索引见 [API-REFERENCE.md](API-REFERENCE.md);语义合同见
[API-SEMANTICS.md](API-SEMANTICS.md)。
状态TP 1.0 已实现的架构合同。本文记录已经落地并由测试保护的目标边界;当前模块和公共行为事实分别以 [ARCHITECTURE.md](ARCHITECTURE.md) 与 [API-SEMANTICS.md](API-SEMANTICS.md) 为准。文件名保留 `target-architecture` 以维持既有链接稳定。
## 1. 产品定位
TP 1.0 是一个可独立安装、测试、发布和使用的 Emacs retained/reactive text runtime它把声明式属性、响应式数据和稳定文本对象投影到 string 与 buffer并拥有最终文本属性 diff、marker/index、事务和 buffer publication。
TP 不依赖 Ebox也不包含 Box、Flex、Grid、padding、layout owner 或 Ebox region 等空间布局概念。Ebox、ETAF、dashboard、diagnostics、交互列表和普通 buffer 标注都可以通过同一 TP public API 使用它。
TP 也不是 CSS 引擎。完整 stylesheet、selector、specificity、origin/importance、CSS cascade layer、CSS-wide value 和 custom property 由独立 ECSS 包负责ECSS 可以把最终 computed declarations 交给 TP 发布,但 TP 不 require、检测或命名 ECSS。
一句话边界是:
> TP 负责对象、依赖、位置、变化和提交;上层 consumer 负责这些对象的业务含义以及期望显示结果。
## 2. 设计不变量
- TP source、tests、examples 和 package metadata 不 require、检测或命名 Ebox。
- TP 不拥有 CSS stylesheet、selector matching、specificity 或 cascade winner这些能力属于独立 ECSS 包。
- live runtime 只有一套 property、binding、surface、mount、diff 和 transaction core不增加 embedded mode。
- TP 是唯一最终 buffer committerproducer 和 consumer 的 prepare 阶段只能产生候选值。
- 一个 live object 只有一个 TP identityconsumer 可以保存 opaque handle但不能建立需要长期同步的平行 identity。
- signal update 从 source 直达 binding再从 object 直达 marker-backed mount正常热路径不扫描 `buffer-list` 或按 `tp-name`/`tp-layers` 搜索文本。
- `tp-name`、`tp-layers`、`tp-meta` 不是 live runtime 数据库definition、provenance、identity、dependencies 和 mount metadata 都在 side state。
- capability 只有 `content``properties` 两种;它们共享同一计算和提交语义,只是写权限不同。
- 普通函数值是 literal只有显式 `tp-computed` value source 会执行并收集依赖。
- signal source、bindings、plans、markers/indexes 和 opaque client state 在 transaction 中使用同一 revision失败时共同回滚。
## 3. 独立数据流
```text
application state / theme / host edit
signals + bindings
│ exact dependency graph
TP prepare context
│ candidate objects/bindings/anchors
ordinary or client producer
│ object → direct property contributions
│ → generic keyed surface plan
reconcile + prepare diff
│ prepared text/property operations
atomic multi-surface publication
strings / Emacs buffers
```
prepare 只产生 candidate state。producer 返回普通 surface plan 和可选 opaque client state。TP 不解释 client state但把它与 plan、dependencies、mounts 和 revision 一起原子晋升。
## 4. 属性 contribution、合成与 value source
### 4.1 Property policy 与 contribution composition
TP 为它最终写入的 Emacs text property 维护通用 property policypresence、normalizer、validator、equality、merge 和 projector。policy 只回答多个 TP contribution 如何合成、值是否真的变化以及怎样形成最终 Emacs property它不定义 selector、stylesheet 或 CSS winner。
每个 properties mount 只贡献它声明的 property。TP 按稳定的 mount/contribution 顺序逐属性合成 host baseline 与所有 TP contribution严格区分显式 nil 和 absent并保存足够的 side state 以便撤销单个 contribution、检测外部冲突和执行事务回滚。这个过程是文本属性 ownership/composition不是 CSS cascade。
ECSS 是独立的纯样式计算包:输入 property schemas、subject tree/adapter 与 stylesheet输出 computed declarations 和 provenance不读写 buffer、不持有 marker也不依赖 TP 或 Ebox。Ebox 或普通文本 UI 若需要完整 CSS 能力,先调用 ECSS再把最终文本/属性计划交给 TP不需要 CSS 的调用者直接使用 TP。
### 4.2 Literal 与 computed
普通 Elisp value 永远是 literal包括 function object。`help-echo` function、keymap command 和 callback 不会被 TP 隐式调用。
需要求值的声明必须使用 `tp-computed` 包装 compute function。compute 在所属 object binding 的 prepare context 中运行;`tp-signal-read` 和 `tp-binding-read` 自动登记依赖。返回值随后按 property policy normalize/validate并按 literal 处理,不隐式调用第二次。错误终止整个 candidate transaction相等结果不产生 surface change。
上层 DSL 可以接受可求值 Elisp但必须在 constructor/compiler 边界明确区分 quoted list、literal function 和 computed source并把响应式计算编译到 `tp-computed`,不能建立第二套 watcher。
## 5. Stable object 与 prepare context
每个 object 的 identity 只在一个 surface 中有效,由 parent identity、sibling key 和 opaque kind 保留。显式 key 在同一 parent 下唯一unkeyed object 只能按 position+kind 保留handle 不能跨 surface 使用。
TP 在 mount/update prepare 开始时创建短生命周期 prepare context。producer 调用:
```elisp
(tp-object-ensure context parent key kind)
```
匹配的 live object 返回原 handle新增 object 只存在于 candidate state。duplicate key、stale parent、orphan object 或 cross-surface handle 在 prepare 中失败。只有成功 publication 才把 candidate identity 变成 live identity失败 handle 必须不可解析。
`tp-surface-materialize-string` 给 producer 提供相同语义的 ephemeral context。object/binding 只在这次纯计算中存在string 返回后全部释放,不进入 live registry。
`tp-object-resolve` 只读地按 surface 与 key path 解析 live handle用于公开选择/交互入口,不创建对象。
## 6. Binding 与真正的响应式依赖
definition 是可复用配方binding 是安装在一个 object 上的具体计算。`tp-bind` 以 object handle 加 caller-namespaced binding key 幂等安装,至少保存 compute、last successful value、dependencies、dirty state、revision 和 lifecycle policy。
`tp-binding-read` 读取另一个 binding 的 memoized value并登记 binding→binding dependency。因此 signal、computed property、measurement 或普通 derived value 可以组成真实依赖图,而不是在变量变化后扫描 buffer。
重新计算前撤销旧依赖成功后以本次实际读取集合替换。conditional branch 因而会自动断开不再使用的 source。transaction dirty queue 去重,同一 binding 每批最多重算一次;循环报告完整路径。
每次 prepare 记录 touched object/binding。没有可见字符但仍属于 candidate 的 logical object 必须显式 retain一个 logical object 可以通过 prepare-only attachment 对应多个不连续 plan fragmentattachment 不进入 plan。删除 object 时同步清理 bindings、subscriptions、anchors 和 mounts仍存在但本次未声明的 binding 默认删除,只有显式 lifecycle policy 才能保留。普通 binding 可返回任意 client value只有 designated producer binding 返回 plan/subplan。
## 7. Surface plan
surface plan 是不可变语义的纯数据,节点字段为:
| Field | Contract |
| --- | --- |
| `key` | sibling-local stable key |
| `kind` | opaque comparable discriminator |
| `text` | optional plain/propertized string leaf |
| `props` | final direct Emacs text properties |
| `children` | ordered child plans |
| `tags` | opaque side metadata indexed but not interpreted by TP |
| `capability` | `content` or `properties` |
plan 不携带 marker、buffer position、patch op、producer closure、binding closure 或 client continuation。合法的 keymap/help-echo/callback function 可以作为最终 property value 保留reconciler 永不调用它们。
public constructor 对 caller-owned plist/string 做 defensive copy 或提供 immutable semanticscommit 后修改原值不能改变已发布 surface。
## 8. Mount、range anchor 与属性所有权
### 8.1 Content capability
`content` mount 拥有其 span 的文本和属性,可以插入、删除、移动、替换文字以及修改属性。它适合 dashboard、menu、special UI buffer 和高级 renderer output。
### 8.2 Properties capability
`properties` mount 只能贡献声明的属性,不能修改 host text。`tp-range-anchor-create` 从 buffer、start/end 和 marker insertion policy 创建 opaque marker-backed anchorproducer 在 prepare 中调用 `tp-object-attach-range` 把 object 绑定到 anchor。plan 本身仍没有位置。
TP side state 为每个 anchor/property interval 保存 host baseline、各 TP contribution 和最后一次 published value。同一 surface 的重叠 contribution 通过 property policy 逐属性合成。
外部代码把同一 property 改成不同于 TP 最后发布值时,下一次 prepare 默认报 `tp-property-conflict`。TP 不覆盖外部值,也不恢复旧 snapshot调用者显式 `tp-range-rebase` 或 unmount。unmount 只撤销 TP contribution当前值仍等于 TP 最后发布值时恢复 baseline否则保留外部值并在 report 中记录 conflict。
host text 编辑由 live markers 跟随。跨越或删除 anchor 边界时按显式 shorten/remove/stale policy 处理TP 不扫描附近文本猜测新位置。
## 9. Reconcile 与 transaction
TP 按 parent/key reconcile object identity并对 old/new plan 计算 child insert/remove/reorder、text diff 和 property-run diff。normal update 从 object→mount index 直接取得一个或多个范围;公开查询只返回数值 range/tag snapshot不暴露 live marker。
transaction 顺序是:冻结 candidate signal writes建立 prepare contexts准备所有实际受影响 surfaces验证 capability/conflict/lifecycle生成 per-surface operations 与 inverse journals按稳定 surface id publish最后原子切换 source values、bindings、plans、indexes、client state 和 revisions全部成功后才运行 observers。
一个 global signal 可以触达多个 surfaces。TP 必须先全部 prepare再逐 surface publish任一 compute、buffer write、marker/index step 或 transaction participant 失败时,回滚已经发布的 surfaces并恢复 signal values、binding values/dependencies、dirty queues 和 revisions。observer failure 发生在成功 commit 后,只记录而不回滚。
若 publish 中 buffer 被 killkill-buffer teardown 是不可逆权威结果;其他 surface 与 source state 回滚TP 不复活 killed buffer。
## 10. Public API 层级
### 10.1 简单的一次性属性
```elisp
(tp-propertize string declarations)
(tp-apply buffer start end declarations)
```
二者使用相同的 direct-declaration validation/projection 和 mutation primitives不建立 identity、binding 或 live mount。现有 `tp-set`、`tp-reset`、`tp-add`、`tp-remove`、lookup/search APIs 继续复用这些底层语义。
### 10.2 响应式已有文本
```elisp
(tp-watch buffer start end compute)
```
`tp-watch` 创建 properties range anchor、surface object 和 bindingCOMPUTE 返回 declarations结果是可 update/unmount 的 opaque handle。普通用户不需要手工创建 object、binding 或 surface。
### 10.3 完整 retained UI
```elisp
(tp-surface-mount buffer plan-or-producer options)
(tp-surface-update surface plan-or-producer)
(tp-surface-update-scoped surface objects plan-or-producer options)
(tp-surface-unmount surface)
```
`tp-surface-update-scoped` 是一次事务内基于 retained object identity 的授权。TP 通过 mount index 解析对象,支持一个对象拥有多个离散 mount验证完整 candidate 不会修改授权范围之外的输出,然后在同一回滚边界内发布 text、direct properties、mount、index、client state 和 revision。scope 不写入文本,也不会形成第二套响应式 runtime。默认 mismatch 直接报错;调用者可显式选择 full-root fallback。
完整 API 还包括 `tp-computed`、signals、prepare/object、bindings、range anchors、transactions、materialize-string、at-point/inspect/report。TP 不增加含义重叠的 `tp-mount`/`tp-update` aliases也不公开 raw marker/patch/change-set、domain dirty kind 或 `:impact` APIs。
## 11. 独立验收场景
TP 1.0 在没有 Ebox load-path 时必须长期通过:
1. static string/buffer propertiesnested face、keymap、help-echo function、explicit nil
2. reactive status`tp-watch` 与 retained content producer、conditional/binding dependency、batch
3. retained dashboardkeyed add/remove/reorder、button、signal-driven theme declarations、rollback
4. existing-text diagnosticsrange anchor、host edit、overlap、external property conflict/rebase/unmount
5. two-buffer produceridentity/state 隔离与 global-signal multi-surface atomicity
6. performancesparse update 与实际 dependency closure 成正比equal value 零 buffer opunmount 后无 marker/subscription/weak-reference 泄漏。
## 12. 兼容、版本与删除
TP 1.0 是主版本切换。能直接映射到统一 core 的静态 public API 保留;依赖 inline `tp-layers`/`tp-meta` database 和扫描式 renderer 才能工作的 managed behavior 必须明确删除或替换,不保留 hidden compatibility engine。
当前没有 legacy import API调用者必须显式提供 plan 或 producer。TP 不会扫描历史 propertized text 来重建 surface identity。
当前实现已经删除 layer→buffer registry、scan-driven refresh hooks、旧 inline managed codec、重复 transaction 和旧 batch renderer。TP 1.0 package、tests、examples 和本文档均可在没有 Ebox repository 的环境中独立工作。
## 13. 已冻结并实现的合同
TP 1.0 的 executable contract tests 已确定并持续保护prepare-context/object 时序binding identity/lifecycleliteral/computed valuerange anchor 和 property conflictsingle/multi-surface rollback三层便利 APIerror taxonomyreport shapeexplicit nil/absenceread-only、undo、narrowing、indirect-buffer 和 kill-buffer 行为。
任何实现若要求 Ebox/ECSS-specific branch、CSS selector/cascade winner、post-commit identity scan、plan 中的 raw position/closure、第二套 renderer 或无法安全撤销 properties contribution应停止接入并重新评审 ownership model而不是增加 adapter mode。