Skip to content

Example 10: Shared State

What It Is

Shared State is for data that belongs to a session or application boundary, not to one edge in the DAG. The Archivist uses a session MemoryStore so parent nodes, embedded search DAGs, and resume logic can read and write the same memory graph.

Use this when stateMapping or gather would force every node to pass around a growing structure that is really a shared service: memory, audit trails, caches, ranked stores, or provenance indexes.

How It Works

A MemoryStore is passed into each node's constructor. Parent and child nodes append entries to the same store without passing values through inputs or gather. Checkpoint.capture snapshots the store alongside parent state; Checkpoint.load and restoreStores restore it on resume. The code below is the real Archivist browser/CLI memory path.

The graph remains pure topology. The store is an injected dependency, so reusable DAGs can share application state without smuggling it through every placement.

Diagrams, Examples, and Outputs

DAG registration and diagram

The graph is the production-shaped pattern in The Archivist: parent placements and embedded search/compose sub-DAGs share the session MemoryStore through injected services while the topology stays pure JSON-LD.

The Archivist parent DAG

55 placements
DAG JSON-LD registered with the dispatcher
{
  "@context": {
    "@version": 1.1,
    "name": {
      "@id": "https://noocodec.dev/ontology/dag/name"
    },
    "version": {
      "@id": "https://noocodec.dev/ontology/dag/version"
    },
    "entrypoints": {
      "@id": "https://noocodec.dev/ontology/dag/entrypoints",
      "@container": "@index"
    },
    "nodes": {
      "@id": "https://noocodec.dev/ontology/dag/nodes",
      "@container": "@set"
    },
    "outputs": {
      "@id": "https://noocodec.dev/ontology/dag/outputs"
    },
    "node": {
      "@id": "https://noocodec.dev/ontology/dag/node"
    },
    "dag": {
      "@id": "https://noocodec.dev/ontology/dag/dag"
    },
    "body": {
      "@id": "https://noocodec.dev/ontology/dag/body"
    },
    "source": {
      "@id": "https://noocodec.dev/ontology/dag/source"
    },
    "sources": {
      "@id": "https://noocodec.dev/ontology/dag/sources",
      "@container": "@index"
    },
    "itemKey": {
      "@id": "https://noocodec.dev/ontology/dag/itemKey"
    },
    "execution": {
      "@id": "https://noocodec.dev/ontology/dag/execution"
    },
    "concurrency": {
      "@id": "https://noocodec.dev/ontology/dag/concurrency"
    },
    "throttle": {
      "@id": "https://noocodec.dev/ontology/dag/throttle"
    },
    "reservoir": {
      "@id": "https://noocodec.dev/ontology/dag/reservoir"
    },
    "gather": {
      "@id": "https://noocodec.dev/ontology/dag/gather"
    },
    "dagReference": {
      "@id": "https://noocodec.dev/ontology/dag/dagReference",
      "@type": "@id"
    },
    "DagReference": {
      "@id": "https://noocodec.dev/ontology/dag/DagReference"
    },
    "from": {
      "@id": "https://noocodec.dev/ontology/dag/from"
    },
    "path": {
      "@id": "https://noocodec.dev/ontology/dag/path"
    },
    "candidates": {
      "@id": "https://noocodec.dev/ontology/dag/candidates",
      "@container": "@set"
    },
    "candidateDag": {
      "@id": "https://noocodec.dev/ontology/dag/candidateDag",
      "@type": "@id"
    },
    "selectedDag": {
      "@id": "https://noocodec.dev/ontology/dag/selectedDag",
      "@type": "@id"
    },
    "resultField": {
      "@id": "https://noocodec.dev/ontology/dag/resultField"
    },
    "policy": {
      "@id": "https://noocodec.dev/ontology/dag/policy"
    },
    "reducer": {
      "@id": "https://noocodec.dev/ontology/dag/reducer"
    },
    "outcome": {
      "@id": "https://noocodec.dev/ontology/dag/outcome"
    },
    "phase": {
      "@id": "https://noocodec.dev/ontology/dag/phase"
    },
    "stateMapping": {
      "@id": "https://noocodec.dev/ontology/dag/stateMapping"
    },
    "container": {
      "@id": "https://noocodec.dev/ontology/dag/container"
    },
    "DAG": {
      "@id": "https://noocodec.dev/ontology/dag/DAG"
    },
    "Placement": {
      "@id": "https://noocodec.dev/ontology/dag/Placement"
    },
    "SingleNode": {
      "@id": "https://noocodec.dev/ontology/dag/SingleNode"
    },
    "ScatterNode": {
      "@id": "https://noocodec.dev/ontology/dag/ScatterNode"
    },
    "EmbeddedDAGNode": {
      "@id": "https://noocodec.dev/ontology/dag/EmbeddedDAGNode"
    },
    "GatherNode": {
      "@id": "https://noocodec.dev/ontology/dag/GatherNode"
    },
    "TerminalNode": {
      "@id": "https://noocodec.dev/ontology/dag/TerminalNode"
    },
    "PhaseNode": {
      "@id": "https://noocodec.dev/ontology/dag/PhaseNode"
    }
  },
  "@id": "urn:noocodec:dag:the-archivist",
  "@type": "DAG",
  "name": "the-archivist",
  "version": "6.0",
  "entrypoints": {
    "main": "urn:noocodec:dag:the-archivist/node/park-for-input"
  },
  "nodes": [
    {
      "@id": "urn:noocodec:dag:the-archivist/node/setup",
      "@type": "PhaseNode",
      "name": "setup",
      "node": "urn:noocodec:node:pre-run-setup",
      "phase": "pre"
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/park-for-input",
      "@type": "SingleNode",
      "name": "park-for-input",
      "node": "urn:noocodec:node:park-for-input",
      "outputs": {
        "parked": "urn:noocodec:dag:the-archivist/node/park-for-input",
        "resumed": "urn:noocodec:dag:the-archivist/node/recall-context"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recall-context",
      "@type": "SingleNode",
      "name": "recall-context",
      "node": "urn:noocodec:node:recall-context",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/classify-intent"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/classify-intent",
      "@type": "SingleNode",
      "name": "classify-intent",
      "node": "urn:noocodec:node:classify-intent",
      "outputs": {
        "lookup-author": "urn:noocodec:dag:the-archivist/node/author-search",
        "find-reviews": "urn:noocodec:dag:the-archivist/node/reviews-extract",
        "describe-book": "urn:noocodec:dag:the-archivist/node/describe-extract",
        "recommend-similar": "urn:noocodec:dag:the-archivist/node/recommend-similar",
        "recall-memories": "urn:noocodec:dag:the-archivist/node/memory-recall",
        "on-topic": "urn:noocodec:dag:the-archivist/node/on-topic-search",
        "recommend-top-rated": "urn:noocodec:dag:the-archivist/node/recommend-extract",
        "off-topic": "urn:noocodec:dag:the-archivist/node/decline-off-topic",
        "retry": "urn:noocodec:dag:the-archivist/node/classify-intent",
        "salvage": "urn:noocodec:dag:the-archivist/node/classify-intent-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/classify-intent-salvage",
      "@type": "SingleNode",
      "name": "classify-intent-salvage",
      "node": "urn:noocodec:node:classify-intent-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/on-topic-search"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/on-topic-search",
      "@type": "EmbeddedDAGNode",
      "name": "on-topic-search",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/compose-loop",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:book-search-scatter",
      "stateMapping": {
        "output": {
          "terms": "terms",
          "toolPlan": "toolPlan",
          "candidates": "candidates",
          "shortlist": "shortlist",
          "priorContext": "priorContext",
          "failureCause": "failureCause"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/author-search",
      "@type": "EmbeddedDAGNode",
      "name": "author-search",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/group-by-year",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:book-search-scatter",
      "stateMapping": {
        "output": {
          "terms": "terms",
          "toolPlan": "toolPlan",
          "candidates": "candidates",
          "shortlist": "shortlist",
          "priorContext": "priorContext",
          "failureCause": "failureCause"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/group-by-year",
      "@type": "SingleNode",
      "name": "group-by-year",
      "node": "urn:noocodec:node:group-by-year",
      "outputs": {
        "ordered": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-extract",
      "@type": "SingleNode",
      "name": "reviews-extract",
      "node": "urn:noocodec:node:extract-query",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools",
        "retry": "urn:noocodec:dag:the-archivist/node/reviews-extract",
        "salvage": "urn:noocodec:dag:the-archivist/node/reviews-extract-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-extract-salvage",
      "@type": "SingleNode",
      "name": "reviews-extract-salvage",
      "node": "urn:noocodec:node:extract-query-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools",
      "@type": "SingleNode",
      "name": "reviews-decide-tools",
      "node": "urn:noocodec:node:decide-tools",
      "outputs": {
        "tools": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets",
        "no-tools": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets",
        "retry": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools",
        "salvage": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-decide-tools-salvage",
      "@type": "SingleNode",
      "name": "reviews-decide-tools-salvage",
      "node": "urn:noocodec:node:decide-tools-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-build-worksets",
      "@type": "SingleNode",
      "name": "reviews-build-worksets",
      "node": "urn:noocodec:node:build-book-worksets",
      "outputs": {
        "ready": "urn:noocodec:dag:the-archivist/node/reviews-scatter"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-scatter",
      "@type": "ScatterNode",
      "name": "reviews-scatter",
      "source": "bookWorksets",
      "body": {
        "dag": {
          "@type": "DagReference",
          "from": "item",
          "path": "dagIri",
          "candidates": [
            "urn:noocodec:tool:web_search_books",
            "urn:noocodec:tool:google_books_search",
            "urn:noocodec:tool:subject_search",
            "urn:noocodec:tool:wikipedia_summary"
          ]
        }
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/reviews-gather",
        "error": "urn:noocodec:dag:the-archivist/node/reviews-gather",
        "empty": "urn:noocodec:dag:the-archivist/node/reviews-rank"
      },
      "itemKey": "currentItem",
      "reducer": "any-success",
      "execution": {
        "mode": "item",
        "concurrency": 4
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-gather",
      "@type": "GatherNode",
      "name": "reviews-gather",
      "sources": {
        "urn:noocodec:dag:the-archivist/node/reviews-scatter": {}
      },
      "gather": {
        "strategy": "tool-candidate-merge"
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/reviews-rank",
        "error": "urn:noocodec:dag:the-archivist/node/reviews-rank",
        "empty": "urn:noocodec:dag:the-archivist/node/reviews-rank"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-rank",
      "@type": "SingleNode",
      "name": "reviews-rank",
      "node": "urn:noocodec:node:rank-by-rating",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/reviews-merge"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-merge",
      "@type": "SingleNode",
      "name": "reviews-merge",
      "node": "urn:noocodec:node:merge-candidates",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/reviews-record",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-record",
      "@type": "SingleNode",
      "name": "reviews-record",
      "node": "urn:noocodec:node:record-findings",
      "outputs": {
        "recorded": "urn:noocodec:dag:the-archivist/node/reviews-gate"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-gate",
      "@type": "SingleNode",
      "name": "reviews-gate",
      "node": "urn:noocodec:node:has-citations-gate",
      "outputs": {
        "pass": "urn:noocodec:dag:the-archivist/node/reviews-recall",
        "fail": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/reviews-recall",
      "@type": "SingleNode",
      "name": "reviews-recall",
      "node": "urn:noocodec:node:recall-past-visits",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-extract",
      "@type": "SingleNode",
      "name": "recommend-extract",
      "node": "urn:noocodec:node:extract-query",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools",
        "retry": "urn:noocodec:dag:the-archivist/node/recommend-extract",
        "salvage": "urn:noocodec:dag:the-archivist/node/recommend-extract-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-extract-salvage",
      "@type": "SingleNode",
      "name": "recommend-extract-salvage",
      "node": "urn:noocodec:node:extract-query-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools",
      "@type": "SingleNode",
      "name": "recommend-decide-tools",
      "node": "urn:noocodec:node:decide-tools",
      "outputs": {
        "tools": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets",
        "no-tools": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets",
        "retry": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools",
        "salvage": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-decide-tools-salvage",
      "@type": "SingleNode",
      "name": "recommend-decide-tools-salvage",
      "node": "urn:noocodec:node:decide-tools-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-build-worksets",
      "@type": "SingleNode",
      "name": "recommend-build-worksets",
      "node": "urn:noocodec:node:build-book-worksets",
      "outputs": {
        "ready": "urn:noocodec:dag:the-archivist/node/recommend-scatter"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-scatter",
      "@type": "ScatterNode",
      "name": "recommend-scatter",
      "source": "bookWorksets",
      "body": {
        "dag": {
          "@type": "DagReference",
          "from": "item",
          "path": "dagIri",
          "candidates": [
            "urn:noocodec:tool:web_search_books",
            "urn:noocodec:tool:google_books_search",
            "urn:noocodec:tool:subject_search",
            "urn:noocodec:tool:wikipedia_summary"
          ]
        }
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/recommend-gather",
        "error": "urn:noocodec:dag:the-archivist/node/recommend-gather",
        "empty": "urn:noocodec:dag:the-archivist/node/recommend-rank"
      },
      "itemKey": "currentItem",
      "reducer": "any-success",
      "execution": {
        "mode": "item",
        "concurrency": 4
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-gather",
      "@type": "GatherNode",
      "name": "recommend-gather",
      "sources": {
        "urn:noocodec:dag:the-archivist/node/recommend-scatter": {}
      },
      "gather": {
        "strategy": "tool-candidate-merge"
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/recommend-rank",
        "error": "urn:noocodec:dag:the-archivist/node/recommend-rank",
        "empty": "urn:noocodec:dag:the-archivist/node/recommend-rank"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-rank",
      "@type": "SingleNode",
      "name": "recommend-rank",
      "node": "urn:noocodec:node:rank-by-rating",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/recommend-merge"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-merge",
      "@type": "SingleNode",
      "name": "recommend-merge",
      "node": "urn:noocodec:node:merge-candidates",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/recommend-record",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-record",
      "@type": "SingleNode",
      "name": "recommend-record",
      "node": "urn:noocodec:node:record-findings",
      "outputs": {
        "recorded": "urn:noocodec:dag:the-archivist/node/recommend-gate"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-gate",
      "@type": "SingleNode",
      "name": "recommend-gate",
      "node": "urn:noocodec:node:has-citations-gate",
      "outputs": {
        "pass": "urn:noocodec:dag:the-archivist/node/recommend-recall",
        "fail": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-recall",
      "@type": "SingleNode",
      "name": "recommend-recall",
      "node": "urn:noocodec:node:recall-past-visits",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-extract",
      "@type": "SingleNode",
      "name": "describe-extract",
      "node": "urn:noocodec:node:extract-query",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/describe-decide-tools",
        "retry": "urn:noocodec:dag:the-archivist/node/describe-extract",
        "salvage": "urn:noocodec:dag:the-archivist/node/describe-extract-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-extract-salvage",
      "@type": "SingleNode",
      "name": "describe-extract-salvage",
      "node": "urn:noocodec:node:extract-query-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/describe-decide-tools"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-decide-tools",
      "@type": "SingleNode",
      "name": "describe-decide-tools",
      "node": "urn:noocodec:node:decide-tools",
      "outputs": {
        "tools": "urn:noocodec:dag:the-archivist/node/describe-build-worksets",
        "no-tools": "urn:noocodec:dag:the-archivist/node/describe-build-worksets",
        "retry": "urn:noocodec:dag:the-archivist/node/describe-decide-tools",
        "salvage": "urn:noocodec:dag:the-archivist/node/describe-decide-tools-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-decide-tools-salvage",
      "@type": "SingleNode",
      "name": "describe-decide-tools-salvage",
      "node": "urn:noocodec:node:decide-tools-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/describe-build-worksets"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-build-worksets",
      "@type": "SingleNode",
      "name": "describe-build-worksets",
      "node": "urn:noocodec:node:build-book-worksets",
      "outputs": {
        "ready": "urn:noocodec:dag:the-archivist/node/describe-scatter"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-scatter",
      "@type": "ScatterNode",
      "name": "describe-scatter",
      "source": "bookWorksets",
      "body": {
        "dag": {
          "@type": "DagReference",
          "from": "item",
          "path": "dagIri",
          "candidates": [
            "urn:noocodec:tool:web_search_books",
            "urn:noocodec:tool:google_books_search",
            "urn:noocodec:tool:subject_search",
            "urn:noocodec:tool:wikipedia_summary"
          ]
        }
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/describe-gather",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "itemKey": "currentItem",
      "reducer": "any-success",
      "execution": {
        "mode": "item",
        "concurrency": 4
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-gather",
      "@type": "GatherNode",
      "name": "describe-gather",
      "sources": {
        "urn:noocodec:dag:the-archivist/node/describe-scatter": {}
      },
      "gather": {
        "strategy": "tool-candidate-merge"
      },
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/describe-pick",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-pick",
      "@type": "SingleNode",
      "name": "describe-pick",
      "node": "urn:noocodec:node:pick-best-match",
      "outputs": {
        "picked": "urn:noocodec:dag:the-archivist/node/describe-merge"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-merge",
      "@type": "SingleNode",
      "name": "describe-merge",
      "node": "urn:noocodec:node:merge-candidates",
      "outputs": {
        "ranked": "urn:noocodec:dag:the-archivist/node/describe-record",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-record",
      "@type": "SingleNode",
      "name": "describe-record",
      "node": "urn:noocodec:node:record-findings",
      "outputs": {
        "recorded": "urn:noocodec:dag:the-archivist/node/describe-gate"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-gate",
      "@type": "SingleNode",
      "name": "describe-gate",
      "node": "urn:noocodec:node:has-citations-gate",
      "outputs": {
        "pass": "urn:noocodec:dag:the-archivist/node/describe-recall",
        "fail": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/describe-recall",
      "@type": "SingleNode",
      "name": "describe-recall",
      "node": "urn:noocodec:node:recall-past-visits",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-loop"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/recommend-similar",
      "@type": "SingleNode",
      "name": "recommend-similar",
      "node": "urn:noocodec:node:recommend-similar",
      "outputs": {
        "seeded": "urn:noocodec:dag:the-archivist/node/similar-search",
        "empty": "urn:noocodec:dag:the-archivist/node/compose-empty"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/similar-search",
      "@type": "EmbeddedDAGNode",
      "name": "similar-search",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/compose-loop",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:book-search-scatter",
      "stateMapping": {
        "output": {
          "terms": "terms",
          "toolPlan": "toolPlan",
          "candidates": "candidates",
          "shortlist": "shortlist",
          "priorContext": "priorContext",
          "failureCause": "failureCause"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-loop",
      "@type": "EmbeddedDAGNode",
      "name": "compose-loop",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
        "error": "urn:noocodec:dag:the-archivist/node/compose-empty"
      },
      "dag": "urn:noocodec:dag:compose-retry-loop",
      "stateMapping": {
        "output": {
          "draft": "draft",
          "approvalState": "approvalState"
        }
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
      "@type": "SingleNode",
      "name": "respond-to-visitor",
      "node": "urn:noocodec:node:respond-to-visitor",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/end"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/memory-recall",
      "@type": "SingleNode",
      "name": "memory-recall",
      "node": "urn:noocodec:node:recall-memories",
      "outputs": {
        "recalled": "urn:noocodec:dag:the-archivist/node/compose-memory-recall"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-memory-recall",
      "@type": "SingleNode",
      "name": "compose-memory-recall",
      "node": "urn:noocodec:node:compose-memory-response",
      "outputs": {
        "drafted": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
        "retry": "urn:noocodec:dag:the-archivist/node/compose-memory-recall",
        "salvage": "urn:noocodec:dag:the-archivist/node/compose-memory-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-memory-salvage",
      "@type": "SingleNode",
      "name": "compose-memory-salvage",
      "node": "urn:noocodec:node:compose-memory-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/respond-to-visitor"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/decline-off-topic",
      "@type": "SingleNode",
      "name": "decline-off-topic",
      "node": "urn:noocodec:node:decline-off-topic",
      "outputs": {
        "success": "urn:noocodec:dag:the-archivist/node/end"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-empty",
      "@type": "SingleNode",
      "name": "compose-empty",
      "node": "urn:noocodec:node:compose-empty",
      "outputs": {
        "drafted": "urn:noocodec:dag:the-archivist/node/respond-to-visitor",
        "retry": "urn:noocodec:dag:the-archivist/node/compose-empty",
        "salvage": "urn:noocodec:dag:the-archivist/node/compose-empty-salvage"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/compose-empty-salvage",
      "@type": "SingleNode",
      "name": "compose-empty-salvage",
      "node": "urn:noocodec:node:compose-empty-salvage",
      "outputs": {
        "done": "urn:noocodec:dag:the-archivist/node/respond-to-visitor"
      }
    },
    {
      "@id": "urn:noocodec:dag:the-archivist/node/end",
      "@type": "TerminalNode",
      "name": "end",
      "outcome": "completed"
    }
  ]
}
Mermaid generated from the same DAG
Mermaid source
%%{init: {"flowchart":{"nodeSpacing":92,"rankSpacing":104,"padding":28}}}%%
flowchart TB
  %% the-archivist (v6.0)
  entry_main(["main"])
  entry_main --> urn_noocodec_dag_the-archivist/node/park-for-input
  urn_noocodec_dag_the-archivist/node/setup(["setup (pre)"])
  urn_noocodec_dag_the-archivist/node/park-for-input["park-for-input"]
  urn_noocodec_dag_the-archivist/node/park-for-input -->|parked| urn_noocodec_dag_the-archivist/node/park-for-input
  urn_noocodec_dag_the-archivist/node/park-for-input -->|resumed| urn_noocodec_dag_the-archivist/node/recall-context
  urn_noocodec_dag_the-archivist/node/recall-context["recall-context"]
  urn_noocodec_dag_the-archivist/node/recall-context -->|recalled| urn_noocodec_dag_the-archivist/node/classify-intent
  urn_noocodec_dag_the-archivist/node/classify-intent["classify-intent"]
  urn_noocodec_dag_the-archivist/node/classify-intent -->|lookup-author| urn_noocodec_dag_the-archivist/node/author-search
  urn_noocodec_dag_the-archivist/node/classify-intent -->|find-reviews| urn_noocodec_dag_the-archivist/node/reviews-extract
  urn_noocodec_dag_the-archivist/node/classify-intent -->|describe-book| urn_noocodec_dag_the-archivist/node/describe-extract
  urn_noocodec_dag_the-archivist/node/classify-intent -->|recommend-similar| urn_noocodec_dag_the-archivist/node/recommend-similar
  urn_noocodec_dag_the-archivist/node/classify-intent -->|recall-memories| urn_noocodec_dag_the-archivist/node/memory-recall
  urn_noocodec_dag_the-archivist/node/classify-intent -->|on-topic| urn_noocodec_dag_the-archivist/node/on-topic-search
  urn_noocodec_dag_the-archivist/node/classify-intent -->|recommend-top-rated| urn_noocodec_dag_the-archivist/node/recommend-extract
  urn_noocodec_dag_the-archivist/node/classify-intent -->|off-topic| urn_noocodec_dag_the-archivist/node/decline-off-topic
  urn_noocodec_dag_the-archivist/node/classify-intent -->|retry| urn_noocodec_dag_the-archivist/node/classify-intent
  urn_noocodec_dag_the-archivist/node/classify-intent -->|salvage| urn_noocodec_dag_the-archivist/node/classify-intent-salvage
  urn_noocodec_dag_the-archivist/node/classify-intent-salvage["classify-intent-salvage"]
  urn_noocodec_dag_the-archivist/node/classify-intent-salvage -->|done| urn_noocodec_dag_the-archivist/node/on-topic-search
  urn_noocodec_dag_the-archivist/node/on-topic-search[["on-topic-search"]]
  urn_noocodec_dag_the-archivist/node/on-topic-search -->|success| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/on-topic-search -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/author-search[["author-search"]]
  urn_noocodec_dag_the-archivist/node/author-search -->|success| urn_noocodec_dag_the-archivist/node/group-by-year
  urn_noocodec_dag_the-archivist/node/author-search -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/group-by-year["group-by-year"]
  urn_noocodec_dag_the-archivist/node/group-by-year -->|ordered| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/reviews-extract["reviews-extract"]
  urn_noocodec_dag_the-archivist/node/reviews-extract -->|success| urn_noocodec_dag_the-archivist/node/reviews-decide-tools
  urn_noocodec_dag_the-archivist/node/reviews-extract -->|retry| urn_noocodec_dag_the-archivist/node/reviews-extract
  urn_noocodec_dag_the-archivist/node/reviews-extract -->|salvage| urn_noocodec_dag_the-archivist/node/reviews-extract-salvage
  urn_noocodec_dag_the-archivist/node/reviews-extract-salvage["reviews-extract-salvage"]
  urn_noocodec_dag_the-archivist/node/reviews-extract-salvage -->|done| urn_noocodec_dag_the-archivist/node/reviews-decide-tools
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools["reviews-decide-tools"]
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|tools| urn_noocodec_dag_the-archivist/node/reviews-build-worksets
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|no-tools| urn_noocodec_dag_the-archivist/node/reviews-build-worksets
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|retry| urn_noocodec_dag_the-archivist/node/reviews-decide-tools
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools -->|salvage| urn_noocodec_dag_the-archivist/node/reviews-decide-tools-salvage
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools-salvage["reviews-decide-tools-salvage"]
  urn_noocodec_dag_the-archivist/node/reviews-decide-tools-salvage -->|done| urn_noocodec_dag_the-archivist/node/reviews-build-worksets
  urn_noocodec_dag_the-archivist/node/reviews-build-worksets["reviews-build-worksets"]
  urn_noocodec_dag_the-archivist/node/reviews-build-worksets -->|ready| urn_noocodec_dag_the-archivist/node/reviews-scatter
  urn_noocodec_dag_the-archivist/node/reviews-scatter[/"reviews-scatter"/]
  urn_noocodec_dag_the-archivist/node/reviews-scatter -->|success| urn_noocodec_dag_the-archivist/node/reviews-gather
  urn_noocodec_dag_the-archivist/node/reviews-scatter -->|error| urn_noocodec_dag_the-archivist/node/reviews-gather
  urn_noocodec_dag_the-archivist/node/reviews-scatter -->|empty| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-gather{"reviews-gather"}
  urn_noocodec_dag_the-archivist/node/reviews-gather -->|success| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-gather -->|error| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-gather -->|empty| urn_noocodec_dag_the-archivist/node/reviews-rank
  urn_noocodec_dag_the-archivist/node/reviews-rank["reviews-rank"]
  urn_noocodec_dag_the-archivist/node/reviews-rank -->|ranked| urn_noocodec_dag_the-archivist/node/reviews-merge
  urn_noocodec_dag_the-archivist/node/reviews-merge["reviews-merge"]
  urn_noocodec_dag_the-archivist/node/reviews-merge -->|ranked| urn_noocodec_dag_the-archivist/node/reviews-record
  urn_noocodec_dag_the-archivist/node/reviews-merge -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/reviews-record["reviews-record"]
  urn_noocodec_dag_the-archivist/node/reviews-record -->|recorded| urn_noocodec_dag_the-archivist/node/reviews-gate
  urn_noocodec_dag_the-archivist/node/reviews-gate["reviews-gate"]
  urn_noocodec_dag_the-archivist/node/reviews-gate -->|pass| urn_noocodec_dag_the-archivist/node/reviews-recall
  urn_noocodec_dag_the-archivist/node/reviews-gate -->|fail| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/reviews-recall["reviews-recall"]
  urn_noocodec_dag_the-archivist/node/reviews-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/recommend-extract["recommend-extract"]
  urn_noocodec_dag_the-archivist/node/recommend-extract -->|success| urn_noocodec_dag_the-archivist/node/recommend-decide-tools
  urn_noocodec_dag_the-archivist/node/recommend-extract -->|retry| urn_noocodec_dag_the-archivist/node/recommend-extract
  urn_noocodec_dag_the-archivist/node/recommend-extract -->|salvage| urn_noocodec_dag_the-archivist/node/recommend-extract-salvage
  urn_noocodec_dag_the-archivist/node/recommend-extract-salvage["recommend-extract-salvage"]
  urn_noocodec_dag_the-archivist/node/recommend-extract-salvage -->|done| urn_noocodec_dag_the-archivist/node/recommend-decide-tools
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools["recommend-decide-tools"]
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|tools| urn_noocodec_dag_the-archivist/node/recommend-build-worksets
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|no-tools| urn_noocodec_dag_the-archivist/node/recommend-build-worksets
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|retry| urn_noocodec_dag_the-archivist/node/recommend-decide-tools
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools -->|salvage| urn_noocodec_dag_the-archivist/node/recommend-decide-tools-salvage
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools-salvage["recommend-decide-tools-salvage"]
  urn_noocodec_dag_the-archivist/node/recommend-decide-tools-salvage -->|done| urn_noocodec_dag_the-archivist/node/recommend-build-worksets
  urn_noocodec_dag_the-archivist/node/recommend-build-worksets["recommend-build-worksets"]
  urn_noocodec_dag_the-archivist/node/recommend-build-worksets -->|ready| urn_noocodec_dag_the-archivist/node/recommend-scatter
  urn_noocodec_dag_the-archivist/node/recommend-scatter[/"recommend-scatter"/]
  urn_noocodec_dag_the-archivist/node/recommend-scatter -->|success| urn_noocodec_dag_the-archivist/node/recommend-gather
  urn_noocodec_dag_the-archivist/node/recommend-scatter -->|error| urn_noocodec_dag_the-archivist/node/recommend-gather
  urn_noocodec_dag_the-archivist/node/recommend-scatter -->|empty| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-gather{"recommend-gather"}
  urn_noocodec_dag_the-archivist/node/recommend-gather -->|success| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-gather -->|error| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-gather -->|empty| urn_noocodec_dag_the-archivist/node/recommend-rank
  urn_noocodec_dag_the-archivist/node/recommend-rank["recommend-rank"]
  urn_noocodec_dag_the-archivist/node/recommend-rank -->|ranked| urn_noocodec_dag_the-archivist/node/recommend-merge
  urn_noocodec_dag_the-archivist/node/recommend-merge["recommend-merge"]
  urn_noocodec_dag_the-archivist/node/recommend-merge -->|ranked| urn_noocodec_dag_the-archivist/node/recommend-record
  urn_noocodec_dag_the-archivist/node/recommend-merge -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/recommend-record["recommend-record"]
  urn_noocodec_dag_the-archivist/node/recommend-record -->|recorded| urn_noocodec_dag_the-archivist/node/recommend-gate
  urn_noocodec_dag_the-archivist/node/recommend-gate["recommend-gate"]
  urn_noocodec_dag_the-archivist/node/recommend-gate -->|pass| urn_noocodec_dag_the-archivist/node/recommend-recall
  urn_noocodec_dag_the-archivist/node/recommend-gate -->|fail| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/recommend-recall["recommend-recall"]
  urn_noocodec_dag_the-archivist/node/recommend-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/describe-extract["describe-extract"]
  urn_noocodec_dag_the-archivist/node/describe-extract -->|success| urn_noocodec_dag_the-archivist/node/describe-decide-tools
  urn_noocodec_dag_the-archivist/node/describe-extract -->|retry| urn_noocodec_dag_the-archivist/node/describe-extract
  urn_noocodec_dag_the-archivist/node/describe-extract -->|salvage| urn_noocodec_dag_the-archivist/node/describe-extract-salvage
  urn_noocodec_dag_the-archivist/node/describe-extract-salvage["describe-extract-salvage"]
  urn_noocodec_dag_the-archivist/node/describe-extract-salvage -->|done| urn_noocodec_dag_the-archivist/node/describe-decide-tools
  urn_noocodec_dag_the-archivist/node/describe-decide-tools["describe-decide-tools"]
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|tools| urn_noocodec_dag_the-archivist/node/describe-build-worksets
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|no-tools| urn_noocodec_dag_the-archivist/node/describe-build-worksets
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|retry| urn_noocodec_dag_the-archivist/node/describe-decide-tools
  urn_noocodec_dag_the-archivist/node/describe-decide-tools -->|salvage| urn_noocodec_dag_the-archivist/node/describe-decide-tools-salvage
  urn_noocodec_dag_the-archivist/node/describe-decide-tools-salvage["describe-decide-tools-salvage"]
  urn_noocodec_dag_the-archivist/node/describe-decide-tools-salvage -->|done| urn_noocodec_dag_the-archivist/node/describe-build-worksets
  urn_noocodec_dag_the-archivist/node/describe-build-worksets["describe-build-worksets"]
  urn_noocodec_dag_the-archivist/node/describe-build-worksets -->|ready| urn_noocodec_dag_the-archivist/node/describe-scatter
  urn_noocodec_dag_the-archivist/node/describe-scatter[/"describe-scatter"/]
  urn_noocodec_dag_the-archivist/node/describe-scatter -->|success| urn_noocodec_dag_the-archivist/node/describe-gather
  urn_noocodec_dag_the-archivist/node/describe-scatter -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-scatter -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-gather{"describe-gather"}
  urn_noocodec_dag_the-archivist/node/describe-gather -->|success| urn_noocodec_dag_the-archivist/node/describe-pick
  urn_noocodec_dag_the-archivist/node/describe-gather -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-gather -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-pick["describe-pick"]
  urn_noocodec_dag_the-archivist/node/describe-pick -->|picked| urn_noocodec_dag_the-archivist/node/describe-merge
  urn_noocodec_dag_the-archivist/node/describe-merge["describe-merge"]
  urn_noocodec_dag_the-archivist/node/describe-merge -->|ranked| urn_noocodec_dag_the-archivist/node/describe-record
  urn_noocodec_dag_the-archivist/node/describe-merge -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-record["describe-record"]
  urn_noocodec_dag_the-archivist/node/describe-record -->|recorded| urn_noocodec_dag_the-archivist/node/describe-gate
  urn_noocodec_dag_the-archivist/node/describe-gate["describe-gate"]
  urn_noocodec_dag_the-archivist/node/describe-gate -->|pass| urn_noocodec_dag_the-archivist/node/describe-recall
  urn_noocodec_dag_the-archivist/node/describe-gate -->|fail| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/describe-recall["describe-recall"]
  urn_noocodec_dag_the-archivist/node/describe-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/recommend-similar["recommend-similar"]
  urn_noocodec_dag_the-archivist/node/recommend-similar -->|seeded| urn_noocodec_dag_the-archivist/node/similar-search
  urn_noocodec_dag_the-archivist/node/recommend-similar -->|empty| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/similar-search[["similar-search"]]
  urn_noocodec_dag_the-archivist/node/similar-search -->|success| urn_noocodec_dag_the-archivist/node/compose-loop
  urn_noocodec_dag_the-archivist/node/similar-search -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/compose-loop[["compose-loop"]]
  urn_noocodec_dag_the-archivist/node/compose-loop -->|success| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/compose-loop -->|error| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/respond-to-visitor["respond-to-visitor"]
  urn_noocodec_dag_the-archivist/node/respond-to-visitor -->|success| urn_noocodec_dag_the-archivist/node/end
  urn_noocodec_dag_the-archivist/node/memory-recall["memory-recall"]
  urn_noocodec_dag_the-archivist/node/memory-recall -->|recalled| urn_noocodec_dag_the-archivist/node/compose-memory-recall
  urn_noocodec_dag_the-archivist/node/compose-memory-recall["compose-memory-recall"]
  urn_noocodec_dag_the-archivist/node/compose-memory-recall -->|drafted| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/compose-memory-recall -->|retry| urn_noocodec_dag_the-archivist/node/compose-memory-recall
  urn_noocodec_dag_the-archivist/node/compose-memory-recall -->|salvage| urn_noocodec_dag_the-archivist/node/compose-memory-salvage
  urn_noocodec_dag_the-archivist/node/compose-memory-salvage["compose-memory-salvage"]
  urn_noocodec_dag_the-archivist/node/compose-memory-salvage -->|done| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/decline-off-topic["decline-off-topic"]
  urn_noocodec_dag_the-archivist/node/decline-off-topic -->|success| urn_noocodec_dag_the-archivist/node/end
  urn_noocodec_dag_the-archivist/node/compose-empty["compose-empty"]
  urn_noocodec_dag_the-archivist/node/compose-empty -->|drafted| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/compose-empty -->|retry| urn_noocodec_dag_the-archivist/node/compose-empty
  urn_noocodec_dag_the-archivist/node/compose-empty -->|salvage| urn_noocodec_dag_the-archivist/node/compose-empty-salvage
  urn_noocodec_dag_the-archivist/node/compose-empty-salvage["compose-empty-salvage"]
  urn_noocodec_dag_the-archivist/node/compose-empty-salvage -->|done| urn_noocodec_dag_the-archivist/node/respond-to-visitor
  urn_noocodec_dag_the-archivist/node/end((("end")))

Run

bash
npm run docs:dev

What It Lets You Do

Shared state lets applications accumulate data across parent DAGs, embedded DAGs, and scatter clones without threading every value through stateMapping or gather. Use it for memory graphs, caches, audit logs, ranked stores, and other structures that many nodes read or write over time.

For an application, this keeps the DAG readable while still supporting long-lived domain state. The parent graph says what runs next; the store owns the durable structure multiple nodes collaborate on.

Code Samples

Read the snippets with the diagrams nearby so the TypeScript behavior, JSON-LD graph shape, and runtime output line up as one contract.

Store implementation

The Archivist MemoryStore is the session memory graph shared across turns and nodes:

ts
/**
 * MemoryStore: browser-runnable RDF quad store for the Archivist.
 *
 * Wraps `n3.Store` (pure JS, ~30KB gzipped, identical surface on Node
 * and in the browser) and exposes a named-graph-aware surface:
 *
 *   assert(s, p, o, graph?)              write one quad
 *   ask({ s?, p?, o?, graph? })          boolean existence check
 *   select({ s?, p?, o?, graph? })       list bound rows (vars start with ?)
 *   triplesIn(graph)                     iterate quads in one graph
 *   triples()                            iterate every quad
 *
 * Four named graphs are reserved by convention:
 *
 *   urn:dagonizer:ontology              TBox schema (classes, properties, domains, ranges);
 *                                          loaded once on mount via loadOntology()
 *   urn:dagonizer:memory                persistent cross-run facts
 *                                          (books, sources, scores; survives reloads)
 *   urn:dagonizer:state:<runId>         per-run typed-state mirror
 *                                          (ArchivistState fields → triples on every node end)
 *   urn:dagonizer:prov:<runId>          PROV-O activity log
 *                                          (which node did what when, attributed to which agent)
 *
 * Pattern surface intentionally mirrors SPARQL's basic graph pattern
 * (`{ ?s <pred> ?o }`) without a full SPARQL engine. For richer query
 * shapes (UNION, FILTER, paths) swap in `@comunica/query-sparql`.
 */

import { DataFactory, Parser, Store, Writer } from 'n3';
import type { Literal, NamedNode, Quad, Quad_Graph, Quad_Object, Quad_Predicate, Quad_Subject, Term } from 'n3';

import type { SnapshottableInterface, StoreSnapshotEntryType, StoreSnapshotType } from '@studnicky/dagonizer/contracts';

const { namedNode, literal, quad, defaultGraph } = DataFactory;

/** Stable identifier + version for `MemoryStore` snapshots; resume refuses anything else. */
const MEMORY_SNAPSHOT_TYPE = 'archivist-memory-v1';
const MEMORY_SNAPSHOT_VERSION = 1;
/** Single snapshot entry key: the whole quad store serialised as N-Quads. */
const MEMORY_SNAPSHOT_KEY = 'nquads';

export const DAG_NS = 'https://noocodec.dev/ontology/dagonizer/';
export const BOOK_NS = 'urn:dagonizer:book:';
export const RUN_NS  = 'urn:dagonizer:run:';

/** Named-graph IRIs reserved by the Archivist demo. */
export const GRAPH_ONTOLOGY = namedNode('urn:dagonizer:ontology');
export const GRAPH_MEMORY   = namedNode('urn:dagonizer:memory');
export const STATE_GRAPH_PREFIX = 'urn:dagonizer:state:';
export const PROV_GRAPH_PREFIX  = 'urn:dagonizer:prov:';

/**
 * One bound row from `select()`. Keys are pattern variable names without
 * the leading `?`. Values are the raw n3 terms (NamedNode | Literal | …).
 */
export type Binding = Readonly<Record<string, Term>>;

interface SlotPattern {
  readonly subject?:   Term | string;
  readonly predicate?: Term | string;
  readonly object?:    Term | string;
  readonly graph?:     Term | string;
}

export class MemoryStore implements SnapshottableInterface {
  readonly #store = new Store();

  /** Total quad count; useful for the live UI counter. */
  get size(): number { return this.#store.size; }

  /** Pre-bake a named-node IRI for the `dag:` vocabulary. */
  static dagIri(local: string): NamedNode { return namedNode(`${DAG_NS}${local}`); }
  /** Pre-bake a named-node IRI for a candidate book by ISBN. */
  static bookIri(isbn: string): NamedNode { return namedNode(`${BOOK_NS}${isbn}`); }
  /** Per-run subject IRI. */
  static runIri(id: string):   NamedNode { return namedNode(`${RUN_NS}${id}`); }
  /** Make any IRI. */
  static iri(value: string):   NamedNode { return namedNode(value); }
  /** Named-graph IRI for the per-run typed-state mirror graph. */
  static stateGraphIri(runId: string): NamedNode { return namedNode(`${STATE_GRAPH_PREFIX}${runId}`); }
  /** Named-graph IRI for the per-run PROV-O activity log. */
  static provGraphIri(runId: string):  NamedNode { return namedNode(`${PROV_GRAPH_PREFIX}${runId}`); }

  /** Literal helpers: typed XSD where it matters for SPARQL FILTER. */
  static lit = {
    str(value: string):   Literal { return literal(value); },
    num(value: number):   Literal { return literal(String(value), namedNode('http://www.w3.org/2001/XMLSchema#double')); },
    int(value: number):   Literal { return literal(String(value), namedNode('http://www.w3.org/2001/XMLSchema#integer')); },
    bool(value: boolean): Literal { return literal(String(value), namedNode('http://www.w3.org/2001/XMLSchema#boolean')); },
    dateTime(value: Date): Literal { return literal(value.toISOString(), namedNode('http://www.w3.org/2001/XMLSchema#dateTime')); },
  };

  /**
   * Load the TBox ontology into `urn:dagonizer:ontology`.
   *
   * Accepts the `ONTOLOGY_NTRIPLES` array from `ArchivistOntology.ts`.
   * Idempotent: clears the graph before writing so repeated calls on
   * mount are safe.  The `typeof` guard lets tests supply any string[].
   */
  loadOntology(ntriples: readonly string[]): void {
    this.#store.removeQuads(this.#store.getQuads(null, null, null, GRAPH_ONTOLOGY));
    const parser = new Parser({ 'format': 'N-Triples' });
    const joined = ntriples.join('\n');
    const parsed = parser.parse(joined);
    for (const q of parsed) {
      this.#store.addQuad(
        quad(q.subject, q.predicate, q.object, GRAPH_ONTOLOGY),
      );
    }
  }

  /** Write one quad. `graph` defaults to the default graph. */
  assert(s: Quad_Subject, p: Quad_Predicate, o: Quad_Object, graph?: Quad_Graph): void {
    this.#store.addQuad(quad(s, p, o, graph ?? defaultGraph()));
  }

  /** Write many quads. Each quad carries its own graph. */
  assertAll(quads: readonly Quad[]): void {
    for (const q of quads) this.#store.addQuad(q);
  }

  /** ASK: true when at least one quad matches the pattern. */
  ask(pattern: SlotPattern): boolean {
    return this.#store.getQuads(
      MemoryStore.asTerm(pattern.subject)   ?? null,
      MemoryStore.asTerm(pattern.predicate) ?? null,
      MemoryStore.asTerm(pattern.object)    ?? null,
      MemoryStore.asTerm(pattern.graph)     ?? null,
    ).length > 0;
  }

  /**
   * SELECT: list bound rows. Variables: pass a string `?name` in any
   * slot and it becomes a binding key; concrete terms filter.
   */
  select(pattern: SlotPattern): Binding[] {
    const subject   = MemoryStore.asTerm(pattern.subject)   ?? null;
    const predicate = MemoryStore.asTerm(pattern.predicate) ?? null;
    const object    = MemoryStore.asTerm(pattern.object)    ?? null;
    const graph     = MemoryStore.asTerm(pattern.graph)     ?? null;
    const quads = this.#store.getQuads(subject, predicate, object, graph);
    return quads.map((q) => {
      const row: Record<string, Term> = {};
      if (MemoryStore.isVar(pattern.subject))   row[MemoryStore.stripQuestion(pattern.subject)]   = q.subject;
      if (MemoryStore.isVar(pattern.predicate)) row[MemoryStore.stripQuestion(pattern.predicate)] = q.predicate;
      if (MemoryStore.isVar(pattern.object))    row[MemoryStore.stripQuestion(pattern.object)]    = q.object;
      if (MemoryStore.isVar(pattern.graph))     row[MemoryStore.stripQuestion(pattern.graph)]     = q.graph;
      return row;
    });
  }

  /** Count matching quads. */
  count(pattern: SlotPattern): number {
    return this.#store.getQuads(
      MemoryStore.asTerm(pattern.subject)   ?? null,
      MemoryStore.asTerm(pattern.predicate) ?? null,
      MemoryStore.asTerm(pattern.object)    ?? null,
      MemoryStore.asTerm(pattern.graph)     ?? null,
    ).length;
  }

  /** Empty the entire store. */
  clear(): void {
    this.#store.removeQuads(this.#store.getQuads(null, null, null, null));
  }

  /** Drop every quad in one named graph (useful when a run resets). */
  clearGraph(graph: Term): void {
    this.#store.removeQuads(this.#store.getQuads(null, null, null, graph));
  }

  /**
   * Drop every quad in `urn:dagonizer:memory` whose subject is typed as
   * `dag:Book` (i.e. has a `rdf:type dag:Book` triple). Safe to call
   * before re-seeding so the library stays idempotent across reloads.
   */
  clearBooks(): void {
    const rdfType = namedNode('http://www.w3.org/1999/02/22-rdf-syntax-ns#type');
    const dagBook = namedNode(`${DAG_NS}Book`);
    // Collect all book subject IRIs in GRAPH_MEMORY.
    const bookSubjects = this.#store
      .getQuads(null, rdfType, dagBook, GRAPH_MEMORY)
      .map((q) => q.subject.value);
    // Remove every quad whose subject is one of those book IRIs.
    for (const subjectValue of bookSubjects) {
      const subject = namedNode(subjectValue);
      this.#store.removeQuads(this.#store.getQuads(subject, null, null, GRAPH_MEMORY));
    }
  }

  /**
   * Capture the entire quad store (all named graphs) as a `StoreSnapshotType`.
   *
   * Satisfies the `SnapshottableInterface` contract so the store can ride along in
   * `Checkpoint.capture(dag, result, { stores: { memory } })`. The whole
   * store serialises to one N-Quads string entry; N-Quads carries the
   * graph term per quad, so ontology / memory / per-run graphs all round-trip.
   */
  // #region snapshottable-impl
  async snapshot(): Promise<StoreSnapshotType> {
    const nquads = await this.#serializeNquads();
    return {
      'version': MEMORY_SNAPSHOT_VERSION,
      'type':    MEMORY_SNAPSHOT_TYPE,
      'entries': [{ 'key': MEMORY_SNAPSHOT_KEY, 'value': nquads }],
    };
  }

  /**
   * Repopulate from a `StoreSnapshotType` produced by `snapshot()`. Clears the
   * current store first so restore is a full replace, not a merge. Refuses a
   * snapshot whose `type` / `version` doesn't match this store's format.
   */
  async restore(snapshot: StoreSnapshotType): Promise<void> {
    if (snapshot.type !== MEMORY_SNAPSHOT_TYPE) {
      throw new Error(`MemoryStore.restore: incompatible snapshot type '${snapshot.type}' (expected '${MEMORY_SNAPSHOT_TYPE}')`);
    }
    if (snapshot.version !== MEMORY_SNAPSHOT_VERSION) {
      throw new Error(`MemoryStore.restore: incompatible snapshot version ${String(snapshot.version)} (expected ${String(MEMORY_SNAPSHOT_VERSION)})`);
    }
    const entry = snapshot.entries.find((e) => e.key === MEMORY_SNAPSHOT_KEY);
    const nquads = typeof entry?.value === 'string' ? entry.value : '';

    this.#store.removeQuads(this.#store.getQuads(null, null, null, null));
    if (nquads.length > 0) {
      const parser = new Parser({ 'format': 'N-Quads' });
      for (const q of parser.parse(nquads)) this.#store.addQuad(q);
    }
  }
  /**
   * Stream the entire quad store as a sequence of `StoreSnapshotEntryType` values.
   * Emits exactly one entry containing the full N-Quads serialization.
   */
  async *snapshotStream(): AsyncIterable<StoreSnapshotEntryType> {
    const nquads = await this.#serializeNquads();
    yield { 'key': MEMORY_SNAPSHOT_KEY, 'value': nquads };
  }

  /**
   * Restore state from a stream of `StoreSnapshotEntryType` values.
   * Clears the current store and repopulates from the N-Quads entry.
   */
  async restoreStream(entries: AsyncIterable<StoreSnapshotEntryType>): Promise<void> {
    this.#store.removeQuads(this.#store.getQuads(null, null, null, null));
    for await (const entry of entries) {
      if (entry.key === MEMORY_SNAPSHOT_KEY && typeof entry.value === 'string' && entry.value.length > 0) {
        const parser = new Parser({ 'format': 'N-Quads' });
        for (const q of parser.parse(entry.value)) this.#store.addQuad(q);
      }
    }
  }
  // #endregion snapshottable-impl

  /** Serialise every quad in every graph to an N-Quads string. Promisified `Writer.end`. */
  #serializeNquads(): Promise<string> {
    return new Promise<string>((resolve, reject) => {
      const writer = new Writer({ 'format': 'N-Quads' });
      writer.addQuads(this.#store.getQuads(null, null, null, null));
      writer.end((err, result) => {
        if (err === null || err === undefined) resolve(result);
        else reject(err instanceof Error ? err : new Error(String(err)));
      });
    });
  }

  /** Iterate every quad in every graph. */
  *triples(): IterableIterator<Quad> {
    for (const q of this.#store.getQuads(null, null, null, null)) yield q;
  }

  /** Iterate every quad in a single named graph. */
  *triplesIn(graph: Term): IterableIterator<Quad> {
    for (const q of this.#store.getQuads(null, null, null, graph)) yield q;
  }

  /** Distinct graph IRIs the store currently knows about. */
  graphs(): readonly Term[] {
    const seen = new Map<string, Term>();
    for (const q of this.#store.getQuads(null, null, null, null)) {
      if (q.graph.termType === 'DefaultGraph') continue;
      if (!seen.has(q.graph.value)) seen.set(q.graph.value, q.graph);
    }
    return [...seen.values()];
  }

  private static isVar(slot: Term | string | undefined): slot is string {
    return typeof slot === 'string' && slot.startsWith('?');
  }

  private static stripQuestion(name: string): string {
    return name.startsWith('?') ? name.slice(1) : name;
  }

  private static asTerm(slot: Term | string | undefined): Term | null {
    if (slot === undefined) return null;
    if (MemoryStore.isVar(slot)) return null;
    if (typeof slot === 'string') return null;
    return slot;
  }
}

Store in service state

The shared store is part of the ArchivistServices record injected into node constructors:

ts
export interface ArchivistServices {
  readonly webSearch: WebSearchTool;
  readonly googleBooks: GoogleBooksToolContract;
  readonly wikipediaSummary: WikipediaSummaryToolContract;
  readonly subjectSearch: SubjectSearchToolContract;
  readonly llm: LlmClientInterface;
  /**
   * RDF triple store (n3.js in-memory). Per-run scratchpad: memory
   * nodes write findings; gate nodes ASK the store; the live UI panel
   * mirrors the triples so the visitor can watch the graph grow.
   */
  readonly memory: MemoryStore;
  /**
   * Optional embedder service for cosine-similarity recall and hybrid
   * ranking. Resolved at runtime via `EmbedderCascade.select()`. Set to
   * `null` when no embedder is reachable (browser without Ollama, no
   * API keys, etc.); every consumer is required to handle this
   * gracefully and use deterministic Jaccard / heuristics.
   * Explicit-null sentinel (not optional) keeps V8 hidden-class stability.
   */
  readonly embedder: EmbedderInterface | null;
  /**
   * Per-placement deadline overrides in milliseconds, keyed by node/placement
   * name (`context.nodeName`). Empty map ⇒ every node uses its built-in
   * default. The live demo wires this from the TimeoutPane sliders.
   */
  readonly nodeTimeouts: Readonly<Record<string, number>>;
}

Checkpoint capture with stores

The browser session captures the same memory store when a run parks for HITL:

ts
const ckpt = await Checkpoint.capture(event.dagName, event.execution, { 'stores': { 'memory': this.store } });
await ckpt.persist(this.#ckptStore, event.execution.parked.correlationKey);

Checkpoint restore with stores

On resume, the browser restores the memory store before calling back into the dispatcher:

ts
const recalled = await Checkpoint.recall(this.#ckptStore, pendingKey);
if (recalled === null) {
  this.appendErrorLine(`Checkpoint '${pendingKey}' not found in store.`);
  return;
}
await recalled.restoreStores({ 'memory': this.store });
const { dagName, state, cursor } = recalled.restoreState(
  CheckpointRestoreAdapter.wrap((snap) => ArchivistState.restore(snap)),
);
state.query = humanText;
this.#dom.hitlInput.value = '';
ConversationView.pushVisitor(this.#dom.conversationEl, humanText);
await this.resumeRun(humanText, dagName, state, cursor);

Details for Nerds

  • Constructor/service injection. Nodes receive ArchivistServices, which carries the shared MemoryStore.
  • Single store, many writers. Recall, record, provenance, and projection paths read/write one session memory graph.
  • Embedded DAGs share services. Embedded placements receive mapped state while their nodes still use the same service record.
  • Checkpoint.capture({ stores }). Capturing a checkpoint with the stores option snapshots memory alongside state.
  • restoreStores({ memory }). Resume restores the memory graph before the parked DAG continues.

See Shared state for the decision matrix between inputs/gather (point-to-point transfer) and Store (accumulating shared structure), and the concurrency contract for write-write races across concurrent scatter clones.

Watched over by the Order of Dagon.