+// failed logs the failed build of the node key with its cause err.
+func (m *Memory) failed(key NodeKey, err error) {
+ m.logger.Warn("summary failed", "level", key.level, "index", key.index, "err", err)
+}
+
+// ready tells if both children of the node key are built and the node is not.
+func (m *Memory) ready(key NodeKey) (bool, error) {
+ for _, index := range []int{2 * key.index, 2*key.index + 1} {
+ built, err := m.tree.Built(key.level-1, index)
+ if err != nil || !built {
+ return false, err
+ }
+ }
+
+ built, err := m.tree.Built(key.level, key.index)
+
+ return !built, err
+}
+
+func (m *Memory) context(level, index int) string {
+ end := index
+ if level > 0 {
+ end = nodeEnd(level, index)
+ }
+
+ return m.cview.Context(end)
+}
+
+// View returns the chat view as the model reads it.
+func (m *Memory) View() string {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ return m.chat.Render()
+}
+
+// Context returns the compaction view for the build of node (l, i). It ends
+// before the message of a leaf, or after the last message of a merge, and it
+// stops at the first line that is not built.
+func (m *Memory) Context(level, index int) string {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ return m.context(level, index)
+}
+
+// rebuildComplete checks the whole tree, not only the visible view. A compactor
+// can fail without an HTTP error, for example with an empty line. Such a failed
+// merge can be absent from the view and must prevent a swap.
+func (m *Memory) rebuildComplete() error {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if len(m.queue.leaves) != 0 || len(m.queue.queued) != 0 {
+ return errors.New("rebuild failed: summary tree has unfinished nodes")
+ }
+
+ return nil
+}
+
+// buildModelLines returns the sizes of the nodes that the model wrote, in tree
+// order. A node that fits its source with no model call is not one of them.
+func (m *Memory) buildModelLines() ([]int, error) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ keys, err := m.tree.Keys()