Skip to content

Phase 01 · Linear intake

The simplest slice of The Archivist: wire a dispatcher, register its nodes and DAGs in dependency order, execute one visitor query, and read the lifecycle result. The full runner is below — this is the real code.

Flow

Code

The #linear-run region covers the dispatcher construction, molecular deep-DAG registration, and the execute call that drives the full flow:

ts
// ── Dispatcher ───────────────────────────────────────────────────────────
const dispatcher = new Dagonizer<ArchivistState, ArchivistServices>({ services });

// ── Deep-DAG node registration (molecular pattern) ───────────────────────
// Each deep-DAG module exports a registerXxxNodes helper that registers
// the nodes it needs. Call it before registerDAG so the validator can
// resolve all node references when the DAG is registered.
registerBookSearchFanoutNodes(dispatcher);
dispatcher.registerDAG(BookSearchFanoutDAG);

registerComposeRetryLoopNodes(dispatcher);
dispatcher.registerDAG(ComposeRetryLoopDAG);

// ── Parent-DAG-only nodes (not used by deep-DAGs) ────────────────────────
for (const node of [
  recallContext,
  classifyIntent,
  // Inlined branch nodes (reviews + describe) — not in the deep-DAGs
  extractQuery,
  decideTools,
  webSearchScout,
  openLibraryScout,
  googleBooksScout,
  subjectScout,
  wikipediaScout,
  rankByRating,
  pickBestMatch,
  mergeCandidates,
  recordFindings,
  hasCitationsGate,
  groupByYear,
  recallPastVisits,
  recommendSimilar,
  // recall-memories branch
  recallMemories,
  composeMemoryResponse,
  respondToVisitor,
  declineOffTopic,
  declineEmpty,
  // empty-result LLM response branch
  composeEmptyResponse,
]) {
  dispatcher.registerNode(node);
}

dispatcher.registerDAG(archivistDAG);

// ── Demo run ─────────────────────────────────────────────────────────────
const visitor = new ArchivistState();
visitor.query = "I'm looking for a book about a strange house and a library";

const result = await dispatcher.execute('the-archivist', visitor);

logger.result(`intent=${result.state.intent}`);
logger.result(`shortlist=${String(result.state.shortlist.length)}`);
logger.result(`draft=${result.state.draft}`);
logger.result(`lifecycle=${result.state.lifecycle.kind}`);
logger.result(`triples=${String(services.memory.size)} written`);

What it demonstrates

  • Molecular registration order — deep-DAG nodes must be registered before their DAG is registered (registerBookSearchFanoutNodesdispatcher.registerDAG(BookSearchFanoutDAG)), and both deep-DAGs before the parent archivistDAG. The dispatcher validates all node references at registration time.
  • Single execute calldispatcher.execute('the-archivist', visitor) drives the entire multi-branch flow. The caller sees one ExecutionResult<ArchivistState> containing the final state and lifecycle.
  • Lifecycle resultresult.state.lifecycle.kind is 'completed', 'cancelled', or 'timed_out'. Nodes never throw; the dispatcher always returns.
  • Services bag — every node receives context.services (LLM, search tools, memory, logger). Nodes never construct their own clients.

See this in action in the Archivist live demo.

Watched over by the Order of Dagon.