+ built, err := m.tree.Built(key.level, key.index)
+
+ return !built, err
+}
+
+// children reads the two children of the node key.
+func (m *Memory) children(key nodeKey) (Node, Node, error) {
+ var children [2]Node
+ for n := range children {
+ child, built, err := m.tree.Node(key.level-1, 2*key.index+n)
+ if err != nil {
+ return Node{}, Node{}, err
+ }
+
+ if !built {
+ return Node{}, Node{}, fmt.Errorf("node (%d, %d) is not built", key.level-1, 2*key.index+n)
+ }
+
+ children[n] = child
+ }
+
+ return children[0], children[1], nil
+}
+
+// dispatch starts the builds that are ready, up to the limit. A build whose
+// input cannot be read fails and waits for the next message.
+func (m *Memory) dispatch() {
+ for m.ctx.Err() == nil {
+ key, ready := m.queue.Next()
+ if !ready {
+ return
+ }
+
+ task, err := m.task(key)
+ if err != nil {
+ m.queue.Fail(key)
+ continue
+ }
+
+ m.workers.Add(1)
+ go m.run(key, task)
+ }
+}
+
+// task returns the build task of the node key, with its input read from the log
+// or the tree.
+func (m *Memory) task(key nodeKey) (Task, error) {
+ task := Task{
+ Level: key.level,
+ Index: key.index,
+ Context: m.context(key.level, key.index),
+ }
+
+ if key.level == 0 {
+ message, err := m.log.Message(key.index)
+ if err != nil {
+ return Task{}, err
+ }
+
+ task.Input = []string{leafSource(message)}
+
+ return task, nil
+ }
+
+ left, right, err := m.children(key)
+ if err != nil {
+ return Task{}, err
+ }
+
+ for _, child := range []Node{left, right} {
+ task.Input = append(task.Input, strings.ReplaceAll(child.Text, "\n", " "))
+ }
+
+ return task, nil
+}
+
+// run builds one node with the summarizer. A failed build stays in the queue
+// until the next message.
+func (m *Memory) run(key nodeKey, task Task) {
+ defer m.workers.Done()
+
+ text, err := m.summarizer.Summarize(m.ctx, task)
+
+ m.mutex.Lock()
+ defer m.mutex.Unlock()
+
+ if err == nil {
+ err = m.build(Node{
+ Level: key.level,
+ Index: key.index,
+ Text: text,
+ Size: len(text),
+ })
+ }
+
+ if err != nil {
+ m.queue.Fail(key)
+ } else {
+ m.queue.Done(key)
+ }
+
+ close(m.changed)
+ m.changed = make(chan struct{})
+ m.dispatch()
+}
+
+// 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 {