feat(transaction): expose structured participant registration

This commit is contained in:
Kinneyzhang 2026-09-01 15:52:05 +08:00
parent 469bdff17d
commit 25cb66c040
8 changed files with 63 additions and 4 deletions

View File

@ -13,6 +13,8 @@ All notable changes to the tp library are documented here.
- `tp-runtime-manifest`, advertising `tp-transaction-protocol-v1+v2` without removing the v1 participant route. - `tp-runtime-manifest`, advertising `tp-transaction-protocol-v1+v2` without removing the v1 participant route.
- Supported `tp-transaction-execution-route` kill switch with `structured` as - Supported `tp-transaction-execution-route` kill switch with `structured` as
the default live authority and `v1` as the complete legacy rollback route. the default live authority and `v1` as the complete legacy rollback route.
- Public `tp-transaction-participate-v2` registration for cross-package
structured participants, with the v1 call-shape adapter retained unchanged.
- Native property policies and contribution composition with explicit nil/absence, normalization, validation, equality, merge, projection, named direct styles, and explicit `tp-computed` value sources. - Native property policies and contribution composition with explicit nil/absence, normalization, validation, equality, merge, projection, named direct styles, and explicit `tp-computed` value sources.
- `tp-propertize`, `tp-apply`, and `tp-watch` as the one-shot string, one-shot buffer-range, and reactive existing-text conveniences over the same direct property/surface core. - `tp-propertize`, `tp-apply`, and `tp-watch` as the one-shot string, one-shot buffer-range, and reactive existing-text conveniences over the same direct property/surface core.
- Retained logical objects with `tp-object-retain` and `tp-object-attach-fragment`, allowing one object to own multiple disjoint physical fragments without placing handles or positions in plans. - Retained logical objects with `tp-object-retain` and `tp-object-attach-fragment`, allowing one object to own multiple disjoint physical fragments without placing handles or positions in plans.

View File

@ -187,7 +187,7 @@ cycles instead of spinning.
| Core inspection and debug | `tp-debug-*`, `tp-intervals`, `tp-intervals-map`, `tp-plist`, `tp-text-snapshot`, `tp-empty-p` | | Core inspection and debug | `tp-debug-*`, `tp-intervals`, `tp-intervals-map`, `tp-plist`, `tp-text-snapshot`, `tp-empty-p` |
| Property policy and declarations | `tp-define-property-policy`, `tp-register-text-property`, `tp-text-declarations`, `tp-computed`, `tp-resolve-value`, `tp-merge-declarations`, `tp-define-style`, `tp-style-declarations` | | Property policy and declarations | `tp-define-property-policy`, `tp-register-text-property`, `tp-text-declarations`, `tp-computed`, `tp-resolve-value`, `tp-merge-declarations`, `tp-define-style`, `tp-style-declarations` |
| Static recipes | `define-tp`/`tp-define-layer`, `define-tps`/`define-tp-group`/`tp-define-group`, layer/group queries, undefine/reset/describe | | Static recipes | `define-tp`/`tp-define-layer`, `define-tps`/`define-tp-group`/`tp-define-group`, layer/group queries, undefine/reset/describe |
| Signals and bindings | signal create/read/peek/set/dispose, binding install/read/dispose, `tp-variable-signal`, `tp-with-transaction`, `tp-transaction-participate`, read-only `tp-transaction-active-p`, `tp-runtime-manifest`, counters/reset | | Signals and bindings | signal create/read/peek/set/dispose, binding install/read/dispose, `tp-variable-signal`, `tp-with-transaction`, `tp-transaction-participate`, `tp-transaction-participate-v2`, read-only `tp-transaction-active-p`, `tp-runtime-manifest`, counters/reset |
| Objects and plans | plan/result constructors, `tp-object-ensure`, retain/reuse, fragment/content-range attachment, resolve, mounted/mounts | | Objects and plans | plan/result constructors, `tp-object-ensure`, retain/reuse, fragment/content-range attachment, resolve, mounted/mounts |
| Host ranges | `tp-range-anchor-create`, `tp-range-anchor-live-p`, `tp-object-attach-range`, `tp-range-rebase` | | Host ranges | `tp-range-anchor-create`, `tp-range-anchor-live-p`, `tp-object-attach-range`, `tp-range-rebase` |
| Surfaces | mount/update/scoped update, materialize, live/revision/client-state, at-point, report/report-summary/inspect, unmount | | Surfaces | mount/update/scoped update, materialize, live/revision/client-state, at-point, report/report-summary/inspect, unmount |

View File

@ -182,7 +182,7 @@ effect 的 input/version tuple重复或超过图规模上限时停止循环
| Core inspection 与 debug | `tp-debug-*`、`tp-intervals`、`tp-intervals-map`、`tp-plist`、`tp-text-snapshot`、`tp-empty-p` | | Core inspection 与 debug | `tp-debug-*`、`tp-intervals`、`tp-intervals-map`、`tp-plist`、`tp-text-snapshot`、`tp-empty-p` |
| Property policy 与 declaration | `tp-define-property-policy`、`tp-register-text-property`、`tp-text-declarations`、`tp-computed`、`tp-resolve-value`、`tp-merge-declarations`、`tp-define-style`、`tp-style-declarations` | | Property policy 与 declaration | `tp-define-property-policy`、`tp-register-text-property`、`tp-text-declarations`、`tp-computed`、`tp-resolve-value`、`tp-merge-declarations`、`tp-define-style`、`tp-style-declarations` |
| 静态 recipe | `define-tp`/`tp-define-layer`、`define-tps`/`define-tp-group`/`tp-define-group`、layer/group 查询、undefine/reset/describe | | 静态 recipe | `define-tp`/`tp-define-layer`、`define-tps`/`define-tp-group`/`tp-define-group`、layer/group 查询、undefine/reset/describe |
| Signal 与 binding | signal create/read/peek/set/dispose、binding install/read/dispose、`tp-variable-signal`、`tp-with-transaction`、`tp-transaction-participate`、只读 `tp-transaction-active-p`、`tp-runtime-manifest`、counter/reset | | Signal 与 binding | signal create/read/peek/set/dispose、binding install/read/dispose、`tp-variable-signal`、`tp-with-transaction`、`tp-transaction-participate`、`tp-transaction-participate-v2`、只读 `tp-transaction-active-p`、`tp-runtime-manifest`、counter/reset |
| Object 与 plan | plan/result constructor、`tp-object-ensure`、retain/reuse、fragment/content-range attach、resolve、mounted/mounts | | Object 与 plan | plan/result constructor、`tp-object-ensure`、retain/reuse、fragment/content-range attach、resolve、mounted/mounts |
| Host range | `tp-range-anchor-create`、`tp-range-anchor-live-p`、`tp-object-attach-range`、`tp-range-rebase` | | Host range | `tp-range-anchor-create`、`tp-range-anchor-live-p`、`tp-object-attach-range`、`tp-range-rebase` |
| Surface | mount/update/scoped update、materialize、live/revision/client-state、at-point、report/report-summary/inspect、unmount | | Surface | mount/update/scoped update、materialize、live/revision/client-state、at-point、report/report-summary/inspect、unmount |

View File

@ -231,6 +231,11 @@ tp-binding-dispose 释放单个 binding。
(lambda () (my-publish)) (lambda () (my-publish))
(lambda () (my-rollback))) (lambda () (my-rollback)))
(tp-transaction-participate-v2
:key 'my-structured-state
:stage (lambda () (my-stage))
:rollback (lambda () (my-rollback)))
(tp-transaction-active-p) (tp-transaction-active-p)
(setq tp-transaction-execution-route 'structured) ; structured 或 v1 (setq tp-transaction-execution-route 'structured) ; structured 或 v1
@ -247,7 +252,9 @@ tp-binding-dispose 释放单个 binding。
tp-with-transaction 将 signal、binding、surface 和注册的 transaction tp-with-transaction 将 signal、binding、surface 和注册的 transaction
participant 一起原子处理。participant 必须在 active transaction 内注册; participant 一起原子处理。participant 必须在 active transaction 内注册;
publish 在 surface publication 后、source commit 前运行,失败时按逆序 publish 在 surface publication 后、source commit 前运行,失败时按逆序
rollback。tp-variable-signal 用 Emacs variable watcher 适配全局或指定 rollback。跨包 structured consumer 使用 `tp-transaction-participate-v2`;它
返回 key不暴露内部 participant 对象。tp-variable-signal 用 Emacs variable
watcher 适配全局或指定
Buffer 的变量,不是旧的 $variable API。 Buffer 的变量,不是旧的 $variable API。
tp-transaction-active-p 是只读边界查询:只在当前 dynamic extent 已进入 tp-transaction-active-p 是只读边界查询:只在当前 dynamic extent 已进入
@ -268,7 +275,9 @@ tp-runtime-manifest 返回防御性 capability snapshot本版本的
`:execution-routes``(structured v1)`、`:route-option` 为 `:execution-routes``(structured v1)`、`:route-option` 为
`tp-transaction-execution-route`,兼容字段 `:execution-route` `tp-transaction-execution-route`,兼容字段 `:execution-route`
`:batch-execution` 也均为 `structured``:batch-execute` 为 non-nil。公开 `:batch-execution` 也均为 `structured``:batch-execute` 为 non-nil。公开
`tp-transaction-participate` 仍是 v1 adaptermanifest 以 `:v1-adapter` structured registration 由 `:structured-participant-api` 指向
`tp-transaction-participate-v2``tp-transaction-participate` 仍是 v1 adapter
manifest 以 `:v1-adapter`
`:v1-rollback-route` 明确声明 adapter 与完整旧 route 均保留; `:v1-rollback-route` 明确声明 adapter 与完整旧 route 均保留;
`:single-live-writer` 明确声明 route 不会双写; `:single-live-writer` 明确声明 route 不会双写;
`:batch-artifacts``:shadow-proof` 也保持 non-nil。 `:batch-artifacts``:shadow-proof` 也保持 non-nil。

View File

@ -211,6 +211,11 @@ TP 为每个 interval 保存:
`tp-transaction-participate` 允许 client side state 在 surfaces 发布后、source commit 前加入同一 rollback boundary。它保留公共 v1 调用形状,但在默认 structured route 中适配成同一个 participant record 的 `stage` capability只执行一次。participant key 在一个 outer transaction 中必须唯一。它不是 observer失败会回滚 transaction。Observer failure 只记录,不回滚已提交结果。 `tp-transaction-participate` 允许 client side state 在 surfaces 发布后、source commit 前加入同一 rollback boundary。它保留公共 v1 调用形状,但在默认 structured route 中适配成同一个 participant record 的 `stage` capability只执行一次。participant key 在一个 outer transaction 中必须唯一。它不是 observer失败会回滚 transaction。Observer failure 只记录,不回滚已提交结果。
跨包 consumer 使用公开 `tp-transaction-participate-v2` 直接注册 structured
`stage`/`rollback` pair返回值仍是 key内部 participant identity、state 与
journal 不暴露。完整 v1 execution route 也能执行该同一 v2 participant object
因此 Ebox 可以独立 cutover而不要求同步改变 TP live route。
publication batch、structured participant、final marker 与 tagged outcome 共享 publication batch、structured participant、final marker 与 tagged outcome 共享
现有 journal/change-group不复制第二份 live state。默认 `structured` route 是 现有 journal/change-group不复制第二份 live state。默认 `structured` route 是
唯一 live writersurface 从 candidate entries 执行participant 从 candidate 唯一 live writersurface 从 candidate entries 执行participant 从 candidate

View File

@ -17,6 +17,7 @@
(require 'tp-surface) (require 'tp-surface)
(declare-function tp--transaction-participate-v2 "tp-reactive" (&rest args)) (declare-function tp--transaction-participate-v2 "tp-reactive" (&rest args))
(declare-function tp-transaction-participate-v2 "tp-reactive" (&rest args))
(declare-function tp--transaction-participant-protocol "tp-reactive" (value)) (declare-function tp--transaction-participant-protocol "tp-reactive" (value))
(declare-function tp--transaction-participant-state "tp-reactive" (value)) (declare-function tp--transaction-participant-state "tp-reactive" (value))
(declare-function tp--transaction-participant-journal "tp-reactive" (value)) (declare-function tp--transaction-participant-journal "tp-reactive" (value))
@ -388,6 +389,32 @@
'(:owner old-state))) '(:owner old-state)))
(should (eq (tp--transaction-participant-state participant) 'committed)))) (should (eq (tp--transaction-participant-state participant) 'committed))))
(ert-deftest tp-transaction-test-public-v2-participant-api-spans-both-routes ()
"The public v2 API registers one v2 object under structured and v1 writers."
(dolist (route '(structured v1))
(let ((tp-transaction-execution-route route)
(tp-transaction-test--trace nil)
participant)
(should
(eq
(tp-with-transaction
(should
(eq
(tp-transaction-participate-v2
:key 'public-v2
:stage (lambda () (push 'stage tp-transaction-test--trace))
:rollback (lambda () (push 'rollback tp-transaction-test--trace))
:journal '(:owner public))
'public-v2))
(setq participant (car tp--transaction-participants))
'body-result)
'body-result))
(should (equal tp-transaction-test--trace '(stage)))
(should (eq (tp--transaction-participant-protocol participant) 'v2))
(should (equal (tp--transaction-participant-journal participant)
'(:owner public)))
(should (eq (tp--transaction-participant-state participant) 'committed)))))
(ert-deftest tp-transaction-test-v2-participant-stage-fault-rolls-back-prior-only () (ert-deftest tp-transaction-test-v2-participant-stage-fault-rolls-back-prior-only ()
"A structured stage fault reverses only participants that entered staged." "A structured stage fault reverses only participants that entered staged."
(let ((tp-transaction-test--trace nil) (let ((tp-transaction-test--trace nil)
@ -1604,6 +1631,8 @@
'tp-transaction-execution-route)) 'tp-transaction-execution-route))
(should (eq (plist-get manifest :batch-execution) 'structured)) (should (eq (plist-get manifest :batch-execution) 'structured))
(should (plist-get manifest :batch-execute)) (should (plist-get manifest :batch-execute))
(should (eq (plist-get manifest :structured-participant-api)
'tp-transaction-participate-v2))
(should (plist-get manifest :v1-adapter)) (should (plist-get manifest :v1-adapter))
(should (plist-get manifest :v1-rollback-route)) (should (plist-get manifest :v1-rollback-route))
(should (plist-get manifest :single-live-writer)) (should (plist-get manifest :single-live-writer))

View File

@ -744,6 +744,19 @@ participant's opaque owner-local state."
(tp--transaction-make-participant (tp--transaction-make-participant
key stage rollback 'v2 precommit after-commit journal))) key stage rollback 'v2 precommit after-commit journal)))
;;;###autoload
(cl-defun tp-transaction-participate-v2
(&key key stage rollback precommit after-commit journal)
"Register a structured rollback-capable participant under KEY.
STAGE and ROLLBACK are required no-argument functions. PRECOMMIT may be one
declared package-owned validator; AFTER-COMMIT is contained work queued only
after final accept. JOURNAL is opaque owner-local rollback state. Return KEY
without exposing TP's internal participant object."
(tp--transaction-participate-v2
:key key :stage stage :rollback rollback :precommit precommit
:after-commit after-commit :journal journal)
key)
(defun tp--transaction-participants-in-registration-order () (defun tp--transaction-participants-in-registration-order ()
"Return the authoritative participants in deterministic declaration order." "Return the authoritative participants in deterministic declaration order."
(reverse tp--transaction-participants)) (reverse tp--transaction-participants))

1
tp.el
View File

@ -75,6 +75,7 @@
:route-option tp-transaction-execution-route :route-option tp-transaction-execution-route
:batch-execution structured :batch-execution structured
:batch-execute t :batch-execute t
:structured-participant-api tp-transaction-participate-v2
:v1-adapter t :v1-adapter t
:v1-rollback-route t :v1-rollback-route t
:single-live-writer t :single-live-writer t