+ return children[0], children[1], nil
+}
+
+// Wait returns when no build runs. The failed builds wait for the next message.
+func (m *Memory) Wait(ctx context.Context) error {
+ for {
+ m.mutex.Lock()
+ running := m.queue.Running()
+ changed := m.changed
+ m.mutex.Unlock()
+
+ if running == 0 {
+ return nil
+ }
+
+ select {
+ case <-changed:
+ case <-ctx.Done():
+ return ctx.Err()
+ }
+ }
+}
+
+// Flush returns when every write before the call is durable.
+func (m *Memory) Flush(ctx context.Context) error {
+ return m.syncer.Flush(ctx)
+}
+
+// ViewLines returns the lines of the chat view, each with its newline, without
+// the leaf lines of the messages in skip.
+func (m *Memory) ViewLines(skip map[int]bool) []string {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ return m.chat.LinesWithout(skip)
+}
+
+// Len returns the number of messages in the log.
+func (m *Memory) Len() int {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ return m.log.Len()
+}
+
+// Message reads the message with the given ID.
+func (m *Memory) Message(id int) (Message, error) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ return m.log.Message(id)
+}
+
+// Node reads the node (l, i) if it is built.
+func (m *Memory) Node(level, index int) (Node, bool, error) {
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ return m.tree.Node(level, index)
+}
+
+// load reads the saved views.
+func (m *Memory) load() error {
+ data, err := os.ReadFile(m.viewPath())
+ if errors.Is(err, os.ErrNotExist) && m.log.Len() == 0 {
+ m.chat = newView(viewChatHigh, viewChatLow)
+ m.cview = newView(viewCompactionHigh, viewCompactionLow)
+
+ return nil
+ }
+
+ if errors.Is(err, os.ErrNotExist) {
+ return errMemoryNoView
+ }
+
+ if err != nil {
+ return fmt.Errorf("read view: %w", err)
+ }
+
+ var file MemoryViewFile
+ err = json.Unmarshal(data, &file)
+ if err != nil {
+ return fmt.Errorf("decode view: %w", err)
+ }
+
+ m.chat, err = restoreView(file.Chat, viewChatHigh, viewChatLow, m.tree)
+ if err != nil {
+ return fmt.Errorf("chat view: %w", err)
+ }
+
+ m.cview, err = restoreView(file.Compaction, viewCompactionHigh, viewCompactionLow, m.tree)
+ if err != nil {
+ return fmt.Errorf("compaction view: %w", err)
+ }
+
+ if m.cview.end != m.chat.end {
+ return fmt.Errorf("compaction view covers %d messages, but the chat view %d", m.cview.end, m.chat.end)
+ }
+
+ if m.chat.end > m.log.Len() {
+ return fmt.Errorf("view covers %d messages, but the log has %d", m.chat.end, m.log.Len())
+ }