feat(dom): B1.2d-i — MutationObserver transient registered observers (DOM §4.3)#413
Conversation
B1.2d-i = MutationObserver transient registered observers (DOM §4.3): creation (§4.2.3 remove step 15) hooked in notify_one (eager/synchronous per record); clearing (§4.3 notify step 6.3) in deliver_pending_mutation_records via one ECS query. Transient = a `transient:bool`-marked MutationObservedBy component entry (ECS-native, participates in the existing notify-walk by construction). Layers: VM + elidex-api-observers only (no script-session/dom-api change). Decomposes the handoff's "B1.2d = Range + transient + live-range": - live-range fixup = ALREADY DONE (LiveRangeBridge), not in scope. - B1.2d-i (this) = transient observers (foundational chokepoint feature). - B1.2d-ii (carved, own plan-review) = Range mutation records → apply_*. /elidex-plan-review 5-agent: 0C / 2I (both fixed) / ~6 MIN (fixed) / 14 FP. Approach validated (layering-clean, ECS-native, decomposition sound, algorithm spec-accurate). IMPs caught: (1) my timing-model misdescription — notify_one is EAGER/synchronous per-mutation, not a deferred microtask drain (the §7 FIFO race evaporates; spec "during remove" by construction); (2) §4.3.1→§4.3 def citation drift. Preflight + propagation re-verified. Closes #11-mutation-transient-observers at landing. IMPL PENDING (next session): implement per §5 → pre-push 6-stage → /external-converge. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…(DOM §4.3) Implements §4.2.3 "remove" step-15 transient registered-observer creation + §4.3 "notify mutation observers" step-6.3 clearing, so a node removed from a subtree observed by an ancestor's subtree:true observer keeps delivering records for mutations in the detached subtree until the next microtask. - api-observers: MutationObservation gains `transient`; free fns add_transient_observers / clear_transient_observers — transients live on the per-node MutationObservedBy component (ECS-native; registry owns only record queues), cleared via one retain-query (shared retain_observations helper). observe() re-observe clears the observer's transients (step 7.1, source→ observer-id collapse). - notify refactored to the §4.3.2 interestedObservers per-observer map-collapse (one record per observer) — required so a removed node holding a permanent + transient (or multi-ancestor) registration for one observer collapses to a single record; also fixes a pre-existing multi-ancestor duplicate-record bug. - VM: notify_one creates transients for ChildList removals (eager, during the remove, in JS-execution order); deliver loop clears per-observer interleaved (spec step 6.3, between 6.2 emptying the queue and 6.4 the callback). Closes #11-mutation-transient-observers. Carves #11-mutation-transient-source-fidelity (observe() step-7.1 over-clear in the contrived multi-ancestor corner; within the sanctioned observer-id collapse). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- §8: ship-time registration commitment for the carved #11-mutation-transient-source-fidelity slot (register in project_open-defer-slots.md SoT at landing, alongside the #11-mutation-transient-observers close). - §9: record the mutation.rs 1000-line crossing (618→1179, source-only 494; the inline test module is the standalone-split candidate, carried as a follow-up per the tree.rs-1019 precedent, not bundled here). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Covers the §4.3.2 step-3.2.3 invariant (a non-requesting match never resets a value a requesting registration set) — the one documented behavioral claim in the notify map-collapse with no prior direct coverage (/review nit). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 57e1af1625
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
F1 (P2, mutation_observer.rs:512) — the §4.3 notifySet was derived from record-queue non-emptiness (`observers_with_records`), so `mo.takeRecords()` before the microtask dropped the observer and its step-6.3 transient clear never ran (transient leaked → detached-subtree mutations kept notifying the stale observer). Add the spec's "pending mutation observers" set (`pending`, appended in queue-a-record step 4.3, drained by `take_pending_observers` = notify step 2 clone + step 3 empty). Both VM and boa delivery switch to it; the now-dead `observers_with_records` is removed. F3 (P2, mutation.rs:164) — the re-observe step-7.1 predicate counted a *transient* entry as an existing registration, so `observe(firstRemoved)` before the microtask fired the whole-tree transient clear and discarded an unrelated removed subtree's transients. Gate the predicate (and the ensure-permanent find) on `!transient`: only a permanent registration is a step-7.1 source under the source→observer-id collapse. Regression tests: pending_observer_survives_take_records, observe_on_transient_only_node_keeps_other_transients (api-observers) + take_records_before_microtask_still_clears_transient (VM integration). F2 (P3, test-module split) deferred to the standalone split task per CLAUDE.md "split は単独 commit" + the #409 tree.rs-1019 precedent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex R1 — 3 findings, all addressed (commit
@codex review |
|
Codex Review: Didn't find any major issues. Can't wait for the next one! Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ac7cd3fda3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ind + source-keyed observe) G1 (P2, vm_api.rs unbind) — clear_pending_records scrubbed only registry state, leaving transient MutationObservedBy entries in the DOM; a same-DOM rebind could deliver future detached-subtree mutations through a stale transient. Added clear_all_transient_observers(dom), called in the bound Vm::unbind block before dom_ptr is zeroed (permanent registrations untouched — they despawn with the outgoing world or legitimately persist for a same-DOM rebind). G4 (P2, mutation.rs observe step 7.1) — the observe-time clear was observer-keyed, over-clearing transients sourced from a different inclusive-ancestor registration of the same observer. Added `source: Option<Entity>` (the ancestor entity that spawned each transient) + clear_transient_observers_by_source (source == target); the notify clear (step 6.3) stays observer-keyed, clear_all (unbind) drops all — three spec-distinct clears via the shared retain_observations. CLOSES the former #11-mutation-transient-source-fidelity carve (the source key is the ancestor entity, available at creation — no per-registration identity needed). G3 (P2, boa observers.rs) — boa delivery lacks the transient lifecycle; scoped out (boa deletion-bound, transients become the sole path at boa removal). G2 (P3, test split) — deferred to the standalone split task (task_b3cd18da). Regression tests: reobserve_preserves_other_source_transient, clear_all_transient_observers_scrubs_transients_keeps_permanent. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex R3 — 4 findings addressed (commit
A cumulative design re-gate over the R-loop delta (R1+R3) confirmed the lifecycle is coherent (one @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 52d7a00df3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…pend-order delivery H1 (P2, observe step 7.1) — the ancestor-entity `source` key could not distinguish multiple registrations of one observer on a single node (a permanent + a chained transient), so re-observing that node over-cleared a transient sourced from a *different* registration (e.g. a grandparent-derived one on a doubly-removed descendant). Replaced with the spec's registered-observer identity: `MutationObservation` gains a `reg_id` (allocated from a registry counter), `source` becomes `Option<u64>` (the originating registration's reg_id), and observe step 7.1 clears by source reg_id. `add_transient_observers` is now a registry method (it allocates reg_ids); it walks permanent AND transient ancestor registrations (spec chained transients) tagging each transient with its source. H2 (P2, pending delivery order) — `pending` was a `BTreeSet` delivering callbacks in observer-id order; WHATWG DOM §4.3 iterates the pending **ordered set** in append order (the order each observer first received a record this cycle). Made `pending` an append-ordered `Vec` (dedup on insert); also made notify's §4.3.2 `interestedObservers` an insertion-ordered Vec (walk order) so the per- record append order is spec-faithful end-to-end (sibling-sweep — preempts the intra-record ordering facet). Preserves B1/#213's determinism goal with the spec-correct order. Closes the source-fidelity theme (the former #11-mutation-transient-source-fidelity is now fully resolved, not carved). Regression tests: reobserve_distinguishes_chained_transient_sources_on_one_node, pending_observers_preserve_append_order_not_id_order. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…pend-order contract The cumulative design re-gate flagged take_pending_observers_is_id_sorted's name/comment as stale after R4 made delivery append-order (it passed only coincidentally for a single target). Rename to ..._drains_in_append_order and assert registration/append order; the id-vs-append divergence stays covered by pending_observers_preserve_append_order_not_id_order. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex R4 — 2 findings, both fixed at the root (commit
A cumulative design re-gate over the full R-loop delta (R1–R4) confirmed the reg_id/source/pending/interested model is the spec's literal data model expressed ECS-native (fields on the component, no reverse index) — coherent, not an edifice. @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 573df55337
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…model The §0.OUT(d)/§8/§9 as-built notes still described the R3 ancestor-entity `source: Option<Entity>` / `source == target` model, which R4 superseded with per-registration identity (`reg_id` + `source: Option<u64>`). Since this plan is the B1.2 handoff, the stale notes could send follow-up observer/Range work back toward the over-clear design R4 removed. Corrected §0.OUT(d), the closed-slot note, and §9 (delta 9 now flags itself superseded; new deltas 11/12/13 record R4-H1 reg_id, R4-H2 append-order, R5). R5-I2 (test-module split, P3) = the recurring split MIN, deferred to task_b3cd18da. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex R5 — 2 findings addressed (commit
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 200e92b559
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… source model R5-I1 fixed §0/§8/§9 but left the pre-impl §3 spec-coverage table + §4 Design "Data" bullet describing the superseded observer-id-collapse / "no source field" model — which the B1.2d-ii handoff would follow back into the over-clear bug. Swept the whole plan: §3 table rows (52/53) + §4 Data (65, marked SUPERSEDED with the as-built reg_id model) now point at per-registration identity; also cross-referenced §9-delta-1's BTreeMap→Vec change (R4-H2). Whole-doc sweep rather than per-section patch to end the doc-staleness recurrence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex R6 — 1 finding fixed (commit
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: bf8df0812a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…er (§9 authoritative) R5-I1/R6-J1/R7-K1/K2 are the same recurring shape: a pre-implementation design section (§4 Design / §5 Implementation) still describes the original model the R-loop superseded. Patching bullet-by-bullet (§0→§3→§4→§5.1→§5.2) is #374 corner-hopping. Structural fix: a top-of-doc banner + §4/§5 banners declaring §9 "As-built deltas" the single authoritative record for the B1.2d-ii handoff, plus inline as-built pointers on the two flagged §5.1/§5.2 bullets (reg_id source / pending notifySet / interleaved step-6.3 clear / mandatory unbind scrub). Where §0–§8 conflicts with §9, §9 wins — so follow-up work cannot re-derive the over-clear / takeRecords-leak / stale-unbind models from the pre-impl prose. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex R7 — 2 findings (K1 §5.2, K2 §5.1), fixed structurally (commit Both are the same recurring shape as R5-I1/R6-J1 — a pre-implementation plan section (§4 Design / §5 Implementation) still describing the original model the R-loop superseded. Rather than patch the prose bullet-by-bullet (the §0→§3→§4→§5 corner-hopping pattern), I neutralized the generating layer: a top-of-doc banner + §4/§5 banners declare §9 "As-built deltas" the single authoritative record for the B1.2d-ii / Range handoff (reg_id source / @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: b10b6b1c5a
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ransient registrations)
L1 (P2, observe step 7/8) — the `!o.transient` predicate (R1-F3) treated a node
holding ONLY a transient registration for the observer as "not registered", so
step 8 added a spurious PERMANENT registration that outlived the next microtask's
step-6.3 transient clear (over-observation). Per webref, §4.3.1 step 7 matches
EVERY registration for `this` (a transient registered observer is a registered
observer); step 8 ("Otherwise") adds a permanent only if none matched. Rewrote
observe() to literal step 7/8: collect all matching reg_ids on target, clear by
each (step 7.1), set options on each (step 7.2), add permanent only if none
matched. Removes the `!transient` approximation entirely. R4-H1's two-ANCESTOR
case is unaffected (the re-observed node holds only a permanent → matches it,
preserves the other-ancestor-sourced transient); the chained permanent+transient
test now clears both (literal step-7 matches the transient too) — updated.
L2 (P2, doc) — §9-delta-2 still said add_transient_observers is a free fn;
corrected to the registry `&mut self` method (allocates reg_ids, R4).
Regression tests updated: observe_on_transient_only_node_keeps_other_transients
(no spurious permanent; transient updated in place) +
reobserve_matching_both_registrations_clears_both_chained_sources (renamed).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
Codex R8 — 2 findings fixed (commit
observe() is now literal §4.3.1 step 7/8 — the source-fidelity / re-observe area should be exhausted. @codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 63b0823a52
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Codex R9 — 0 new findings (both already-dispositioned repeats):
No new real findings → dry round (1/2 toward TERMINAL). @codex review |
|
Codex R9 disposition (both = genuine cross-PR scope boundaries, no in-PR change):
Resolving both threads on these dispositions. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 63b0823a52
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…ep-7.1 scope-equivalence note The post-Codex TERMINAL fix-delta /elidex-review pass (5-agent, over `dfc07b76..HEAD`) caught two doc-only gaps the Codex R9/R10 dry rounds missed (mirrors #409 where the TERMINAL fix-delta caught a citation drift): - F1 (IMP, Axis 5): plan §9 "as-built deltas" (the declared SINGLE AUTHORITATIVE handoff record) stopped at delta 13 and left delta 7's `!transient`-gating claim stale — R8 reversed exactly that. Added delta 14 (R8-L1 literal §4.3.1 step 7/8) and annotated delta 7 as superseded (mirrors the deltas 9→11 supersession). No code change. - F2 (MIN, Axis 4): noted in the step-7.1 comment that the tree-wide `clear_transient_observers_by_source` sweep is equivalent to the spec's "for each node of this's node list" scope by construction (any node carrying a transient with `source == reg_id` necessarily carries a registration for this observer → it is in the query-derived node list). Comment-only; webref-verified observe() step 7.1 + notify step 6.3 are both node-list-scoped and equivalent here. Axis 1/2/3 clean (0/0/0). All §4.3.1 step-7/8 + notify-6.3 citations webref-verified correct (no #409-style drift). elidex-api-observers 52 tests green; elidex-js compiles. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 22545c90fb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 22545c90fb
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Codex R12 disposition — convergence reached (2 consecutive dry rounds on head `22545c90`, R11 + R12). Both remaining findings are pre-existing, already-seen genuine cross-PR scope boundaries (not new gaps):
R1–R8 spec-fidelity findings were all fixed at root; the post-Codex TERMINAL /elidex-review fix-delta additionally reconciled the §9 as-built record (added delta 14) and a step-7.1 scope-equivalence note. Ready to merge pending maintainer approval. |
…nsient}.rs (#417) Standalone touch-time 1000-line-debt split (CLAUDE.md discipline). mutation.rs (1629 lines) → directory module with source + two test files by scenario seam: - mutation/mod.rs (610 lines): source body - mutation/tests_core.rs (354 lines): notify/observe/disconnect/subtree/pending tests - mutation/tests_transient.rs (687 lines): transient registered observers suite (DOM §4.3) Mirrors script-session mutation/tests.rs + tests_replace_all.rs sibling pattern. Pure split — 52/52 tests pass, clippy clean, CI green (3 OS). Surfaced by /elidex-review Axis 5 on PR #413 (B1.2d-i); kept as standalone per split discipline.
B1.2d-i — MutationObserver transient registered observers (DOM §4.3)
Per-PR slice of the B1.2 umbrella (Program B / F3 mutation-path), plan-reviewed clean. Implements the §4.3 transient-registered-observer machinery so a node removed from a subtree observed by an ancestor's
subtree:trueobserver keeps delivering records for mutations in the now-detached subtree until the next microtask delivery clears it.What
elidex-api-observers(mutation.rs):MutationObservationgainstransient; free fnsadd_transient_observers(§4.2.3 "remove" step 15 — walk parent's inclusive ancestors, append transient copies ofsubtree:trueobservers to removed nodes) andclear_transient_observers(§4.3 "notify mutation observers" step 6.3), both over the per-nodeMutationObservedByECS component (registry owns only record queues — ECS-native, no observer→nodes reverse index; sharedretain_observationsscaffold withdisconnect).observe()re-observe clears transients (§4.3.1 step 7.1,source→observer-id collapse).notifyrefactored to the §4.3.2interestedObserversper-observer map-collapse (one record per observer). Required for this PR's own correctness — a removed node can hold a permanent + transient (or multi-ancestor) registration for one observer, which the old per-entry push would deliver as duplicate records. Incidentally fixes a pre-existing multi-ancestor duplicate-record bug.vm/host/mutation_observer.rs):notify_onecreates transients forChildListremovals (eager, during the remove, in JS-execution order); the deliver loop clears per-observer interleaved (spec step 6.3, between 6.2 emptying the queue and 6.4 the callback).Layering / ECS-native
Algorithm lives entirely in the engine-independent crate; the VM host only orchestrates at the record-drain (api-observers depends on script-session, so creation can't live in
apply_*). Transients are marked component entries participating in the existing notify ancestor-walk — zero walk-structure change.Tests
46 api-observers unit tests (8 transient + collapse/old-value-union/deep-descendant) + 3 VM integration tests (headline detached-subtree delivery + clear, move-adopt, re-observe) + full 6024-test elidex-js suite green; boa compiles.
Slots
Closes
#11-mutation-transient-observers. Carves#11-mutation-transient-source-fidelity(observe() step-7.1 over-clear in the contrived multi-ancestor corner; within the sanctioned observer-id collapse). Standalone follow-up: splitmutation.rsinline test module (file crossed 1000 via tests; source-only 494).Pre-push gate
6-stage gate green (fmt → CI → /code-review high → /simplify → /review → /elidex-review). elidex-review: Axes 1–4 clean, Axis 5 IMP+2MIN addressed → 0 CRIT / 0 IMP. As-built spec re-verification deltas documented in plan §9 (notify collapse, free-fn placement, interleaved clear, citation-scope correction).
🤖 Generated with Claude Code