From 918cdbfa270f6b1e4b85878ee3146fb0c9cf44fd Mon Sep 17 00:00:00 2001 From: Codex Bot Date: Sat, 24 Oct 2026 02:18:53 +0800 Subject: [PATCH] docs: describe precise property binding invalidation and evaluation counts --- README.md | 2 ++ src/README.md | 2 ++ src/api.rs | 4 ++-- 3 files changed, 6 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 4ebcbeb..30c3b74 100644 --- a/README.md +++ b/README.md @@ -582,3 +582,5 @@ Foreground REPL/events now retain a BeforeInput request while an existing idle l Source layout/paint now validate name-level read observations from Aster: each globals/builtins lookup records its text key and observed presence/value, while dictionary kind checks remain separate. Updating the REPL result `_` or another unread global no longer invalidates these frames solely because the whole globals dictionary version changed. Reading `_` still observes its value; adding a previously missing global correctly invalidates a builtin fallback. Ordinary native dictionary iteration/content reads retain whole-object version dependencies. Measurement cache hits replay their recorded observations into the outer solve; they do not drop dependencies merely because source execution was skipped. Cache observations contain borrowed handles (including object-valued names), add no GC roots, and never write comparison snapshots back into language objects. Invalid-name diagnostics report only identity/key, not captured values. General Block binding and other legacy readers keep their existing conservative object dependencies. See examples/global-dependencies.endor. + +Property bind computations also use precise global-name observations. Unrelated REPL/global changes keep valid cached outputs; changing an observed name or business object recalculates the binding, including hidden retained Blocks. A direct overwrite of a bound property is repaired by its compute function. ui.stats().bindingEvaluations counts computations in completed binding refreshes over this VM's lifetime, including initial computations; querying it does not itself force valid bindings to recompute. General dictionary-content reads still conservatively observe the dictionary. diff --git a/src/README.md b/src/README.md index 2ab3e2c..a736cd5 100644 --- a/src/README.md +++ b/src/README.md @@ -250,3 +250,5 @@ Source layout/paint and measurement caches exclude every presentation's private The gc-lifecycle example and endor_host regression combine a mounted Page/Block cycle, source measure/paint dependencies and an owner-scoped coroutine suspended on sleep. Closing the tab preserves a referenced Block's workflow; releasing the external Page/Task holders lets normal collection reclaim the cycle before the wait completes. Regression assertions check both the same-program object baseline and weak identities. The API worker logs the submission identity once when a newly accepted input enters BeforeInput waiting. It omits source and runtime values, and resume quanta do not repeat it. This distinguishes cancellation of an accepted pre-input wait from cancellation of an unpublished candidate or a request still in the transport queue. + +Property binding caches now consume synchronous precise ReadSet dependencies, replay validation through ObjectStore and exclude the same private presentation desktops as source layout/paint. Cached output equality still repairs a direct overwrite even when its compute dependencies are unchanged. ui.stats().bindingEvaluations counts computations in completed binding refreshes for this VM, including initial/hidden bindings, so unrelated REPL inputs can be checked without adding effects to read-only source compute functions. diff --git a/src/api.rs b/src/api.rs index b7de1a7..a26370d 100644 --- a/src/api.rs +++ b/src/api.rs @@ -1584,7 +1584,7 @@ Application exit now uses a private VM-worker control channel, independent of HT Foreground REPL/events now retain a BeforeInput request while an existing idle layout/paint refresh advances one stage/quantum per worker turn. They execute their source only after that refresh settles, so mutations never enter its read-only scope. New task callbacks are not delivered ahead of this accepted input. Shutdown can cancel the waiting request with 503, and superseded submissions still return 409. Reset cancels/discards the old VM instead of synchronously draining its unpublished refresh. Source parsing/compilation and ordinary synchronous source/native calls remain atomic boundaries; this change removes the worker's synchronous pre-input refresh drain. Native stderr records the submission once when a request first waits for an active refresh; it does not log source text or repeat the line for each quantum. -Source layout/paint now validate name-level read observations from Aster: each globals/builtins lookup records its text key and observed presence/value, while dictionary kind checks remain separate. Updating the REPL result `_` or another unread global no longer invalidates these frames solely because the whole globals dictionary version changed. Reading `_` still observes its value; adding a previously missing global correctly invalidates a builtin fallback. Ordinary native dictionary iteration/content reads retain whole-object version dependencies. Measurement cache hits replay their recorded observations into the outer solve; they do not drop dependencies merely because source execution was skipped. Cache observations contain borrowed handles (including object-valued names), add no GC roots, and never write comparison snapshots back into language objects. Invalid-name diagnostics report only identity/key, not captured values. General Block binding and other legacy readers keep their existing conservative object dependencies. See examples/global-dependencies.endor. +Source layout/paint now validate name-level read observations from Aster: each globals/builtins lookup records its text key and observed presence/value, while dictionary kind checks remain separate. Updating the REPL result `_` or another unread global no longer invalidates these frames solely because the whole globals dictionary version changed. Reading `_` still observes its value; adding a previously missing global correctly invalidates a builtin fallback. Ordinary native dictionary iteration/content reads retain whole-object version dependencies. Measurement cache hits replay their recorded observations into the outer solve; they do not drop dependencies merely because source execution was skipped. Cache observations contain borrowed handles (including object-valued names), add no GC roots, and never write comparison snapshots back into language objects. Invalid-name diagnostics report only identity/key, not captured values. Block binding uses the same precise observations through the synchronous read-only API; other legacy callers of the object-list API remain conservative. See examples/global-dependencies.endor. @@ -1620,7 +1620,7 @@ Trait definitions Visual, EventTarget, Children and Presentable are managed nomi For a plain object explicitly implementing Presentable, present(self, window: Window): Block is invoked by ui.present(value) or ui.open(value), using the current managed Window and the same VM budget. The result must be an actual Block. Blocks pass through directly; unregistered values retain the summary fallback. Source views retain their business object via managed fields/callbacks/bindings, as in examples/presentable.endor. Viewing function definitions does not itself execute them. Rendering methods still run read-only with nominal result checks. -Block.bind(property, compute) binds an existing managed Block field to a zero-argument function and returns Subscription (disconnect/isConnected). This includes source-defined fields and native TextBlock.text/size/color, RectBlock.width/height/radius/color, Spacer.size and container padding/gap. Native fields can also be assigned directly. The target content owns compute/captures; tokens and host registry are weak. Rebinding disconnects the old connection; disconnect keeps the last value. Actual read dependencies are tracked at object granularity, including conservative global reads; no explicit source-list parameter is required. Hidden retained Blocks update at successful input/event stabilization boundaries before all windows publish. Computes are read-only for existing objects and may allocate results. They have 100000 instructions/128 frames each, 128 active bindings per Block and a 10000-computation refresh limit that errors rather than truncates propagation. Binding failure stops the live VM and preserves published pictures. Non-Block targets, explicit dependency lists and general type/effect checking remain pending. See examples/property-bindings.endor. +Block.bind(property, compute) binds an existing managed Block field to a zero-argument function and returns Subscription (disconnect/isConnected). This includes source-defined fields and native TextBlock.text/size/color, RectBlock.width/height/radius/color, Spacer.size and container padding/gap. Native fields can also be assigned directly. The target content owns compute/captures; tokens and host registry are weak. Rebinding disconnects the old connection; disconnect keeps the last value. Actual reads track global-name presence/value separately from whole-object reads; ordinary dictionary content access remains conservative. No explicit source-list parameter is required. Unrelated global updates reuse valid compute results, while direct output overwrites still trigger repair. ui.stats().bindingEvaluations counts computations in completed refreshes over this VM lifetime, including initial and hidden bindings. Hidden retained Blocks update at successful input/event stabilization boundaries before all windows publish. Computes are read-only for existing objects and may allocate results. They have 100000 instructions/128 frames each, 128 active bindings per Block and a 10000-computation refresh limit that errors rather than truncates propagation. Binding failure stops the live VM and preserves published pictures. Non-Block targets, explicit dependency lists and general type/effect checking remain pending. See examples/property-bindings.endor. Native input POST /events is a transport envelope, not Endor source. Each frame and input carries an opaque runtime window identity; omitted input window defaults to 1 for existing clients. GET /code exposes X-Tessera-Window. A window/session/layout identity mismatch is rejected before invoking code. Tab actions use kind tab-activate/tab-close/tab-detach/tab-drop and the mount wire field carries the opaque tab ID from frame.tabs; point/press_point must be omitted. tab-drop additionally requires tab_drop={{window,layout_version,index}}, where index is a published destination gap from 0 through its tab count (at most 64). Both source and destination orders must still match their published frames. tab-detach alone accepts optional screen_position=[x,y] as signed integer physical desktop coordinates within +/-1000000; other kinds must omit it. All spatial/tab actions validate session and layout_version before dispatch. Resource window-close uses mount=0 and no points; it validates the live window/session rather than its old layout version. The window sends current session, layout_version and Mount identity; stale positions fail without calling a callback. Pointer clicks also include point and press_point [x,y] in logical page coordinates. Both targets are resolved through read-only hitTest, with fallback behind a rejected candidate; a click is dispatched only when both resolve to the same Mount. Keyboard/code clicks omit coordinates and remain semantic activation. GET /code also exposes X-Tessera-Session and X-Tessera-Layout-Version headers for transport clients. Native mouse, Tab, Enter and Space delegate to the same