1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
//! `Grouper<R>` — kind-agnostic timeline grouping. Given a stream of
//! `KernelEvent`s and a `ParentResolver`, emits a `Vec<TimelineBlock>` where
//! reply chains collapse into Twitter-style modules.
//!
//! ## Algorithm sketch
//!
//! On each event insert:
//! 1. Ignore if already known.
//! 2. Resolve parent via the per-NIP `ParentResolver`.
//! 3. If parent is an `Event` already in store AND occupies the leaf of an
//! existing block, splice the new event onto that block (promoting
//! Standalone → Module if needed) up to `policy.max_module_size`.
//! 4. Otherwise walk ancestors up to `policy.max_ancestor_hops`, picking
//! up `Event` ids that are in the store and not yet `seen`. `Address`
//! / `External` parents terminate the walk and become the module's
//! `root` pointer.
//! 5. Wrap the chain in a `TimelineBlock`; `blocks` is kept sorted by
//! newest event timestamp, regardless of relay arrival order.
//! 6. If the parent is unknown locally, buffer the child in `orphans`
//! keyed by the missing parent id. Parent arrival replays children.
//!
//! Adjacent-block collapse runs after every mutation: two `Module` blocks
//! sharing the same `root` pointer merge if `policy.collapse_adjacent_same_
//! root` is set and the merged length would fit `max_module_size`.
//!
//! ## Why no dynamic dependency injection
//!
//! A view's `dependencies` is a pure function of its spec. There is no API
//! to re-publish dependencies with `pending_ancestor_ids` learned at
//! runtime. `ThreadView` lives with the same constraint and relies on the
//! surrounding planner subscription (broad `("e", target)` tag-ref) to
//! surface ancestors. Wrappers around this grouper inherit that contract;
//! `pending_ancestor_ids` is kept as internal diagnostic state.
//!
//! ## Module layout
//!
//! The algorithm is split by phase across submodules; this file is the
//! spine (state + construction + read accessors) that the phases share:
//! - [`lifecycle`] — insert/remove/replace entry points, orphan replay,
//! supersession bookkeeping.
//! - [`placement`] — splicing an event onto an existing block or walking
//! ancestors to build a fresh chain.
//! - [`ordering`] — block recency ordering and delta-index resolution.
//! - [`collapse`] — adjacent same-root module merging.
use ;
use ;
use ;
use crateTimelineBlock;
use crateModulePolicy;
use crateParentResolver;
/// Delta surface for the grouper. Wrappers map this into their own
/// view-module `Delta` type (typically a 1:1 forward).
/// Owning state for the algorithm. One instance per open view.