Mapping the Social Graph

Silhouette of person looking at glowing interconnected network nodes in dark industrial setting

In our previous post, we built the Knowledge Archive, a durable, atomic vault for historical data. We moved from “strings to things” by adopting the Schema.org vocabulary. But even with a vault full of people, the Scribe was still seeing individuals in isolation.

History isn’t just a list of names; it’s a web of Relationships. Today, we move from the Archive to the Social Graph.

Beyond the Nuclear Family

The 1880 Census is a social map. In a single dwelling in Salem, Oregon, you might find a Head of Household, his wife, four children, two boarders (often young laborers), and a live-in servant.

To capture this, the Digital Scribe needs to move beyond simple genealogy. We’ve engineered a Relational Linker that recognizes the “connective tissue” of a household:

  • Nuclear Ties: Mapping “Wife” and “Husband” to schema:spouse and “Son/Daughter” to schema:parent.
  • Extended Ties: Mapping “Boarder,” “Servant,” and “Cook” to a memberOfHousehold relationship.

The scribe uses a specialized ‘Idempotent Appender.’ This function is a surgical tool: it checks if a relationship already exists before adding it. If it finds a bare string ID from an older version of the archive, it ‘promotes’ it to a structured dictionary, ensuring the archive’s schema remains perfectly consistent over time.

# The Idempotent Relation Appender
def _add_to_relation(entity: dict, property_name: str, value: dict) -> bool:
    target_id = value.get("@id")
    existing = entity.get(property_name)

    # Promotion logic: Convert bare strings to structured dictionaries
    if isinstance(existing, str):
        if existing == target_id: return False
        entity[property_name] = [{"@id": existing}, value]
        return True

    # ... handles lists and deduplication to prevent redundant data ...
graph LR
    %% --- Central Entity ---
    Head[Head of Household]

    %% --- Nuclear Family ---
    subgraph Nuclear ["Nuclear Unit"]
        Spouse[Spouse: Wife or Husband]
        Child[Son or Daughter]
    end

    %% --- Extended Household ---
    subgraph Extended ["Extended Household"]
        Boarder[Boarder]
        Servant[Servant]
    end

    %% --- Relationship Edges ---
    %% Symmetric Spouse Link
    Head ---|spouse| Spouse
    Spouse ---|spouse| Head

    %% Parent/Child Links
    Child -->|parent| Head
    Head -.->|knows| Child

    %% Extended links
    Boarder -->|memberOfHousehold| Head
    Servant -->|memberOfHousehold| Head

    Head -.->|knows| Boarder
    Head -.->|knows| Servant

    %% --- Styles ---
    style Head fill:#f9f,stroke:#333,stroke-width:2px
    style Spouse fill:#bbf,stroke:#333
    style Child fill:#bbf,stroke:#333
    style Boarder fill:#eee,stroke:#999
    style Servant fill:#eee,stroke:#999

This graph highlights the multi-modal nature of historical memory. By using solid lines for core family ties and dashed lines for the “extended” household (Boarders and Servants), we maintain the distinction between biological lineage and social proximity. Note the bidirectional “spouse” arrows; in our graph, no one is a silent attribute—everyone is a first-class node capable of pointing back to their connections.

The Engineering of Symmetry

In a true Knowledge Graph, relationships must be Symmetric. If the Scribe identifies a “Wife,” it shouldn’t just point her to the Head of Household; the Head must also point back to her.

We implemented a Symmetric Linking Pipeline that ensures the graph is balanced. When the Scribe “forges” a link, it updates both entities simultaneously within a single atomic transaction.

To build a graph that actually works, the Scribe must be unbiased. It doesn’t just link a ‘Wife’ to a ‘Head’; it links them both as equals in a spouse relationship. This ensures that no matter which person the AI ‘looks’ at first, it can find the other.

# The Scribe's Symmetric Linking Pipeline
if rel_lower in ("wife", "husband"):
    # Mirroring the census reality: Both partners are linked symmetrically
    spouse_link = {"@id": head_id, "relationshipDescription": rel_raw}
    spouse_back = {"@id": member_id, "relationshipDescription": rel_raw}

    # Update Member -> Head
    if _add_to_relation(entity, "spouse", spouse_link):
        links_created += 1
    # Update Head -> Member (Symmetry)
    if _add_to_relation(head, "spouse", spouse_back):
        links_created += 1

Mapping the Block: The Dwelling as a Container

One of the most powerful tools we’ve added is search_by_dwelling. By treating the physical house as a container, the Scribe can now “Map the Block.” We can ask the Scribe to show us everyone living at Dwelling #10, revealing multi-family boarding houses and the complex social hierarchies within.

By combining this search with our Dry Run feature, the Scribe can “imagine” the social links of an entire neighborhood before committing them to the permanent archive.

# Mapping a multi-family dwelling via the MCP tool
dwelling_data = mcp.call_tool("search_by_dwelling", {"dwelling_number": 10})

# Logic: Dwelling 10 contains everyone in the building
# including Boarders and different Family Numbers.
# Output Count: 11 residents
# - Family 12: The Smith Family
# - Family 13: The Miller Family
# - Unaffiliated: John Doe (Boarder)

Because search_by_dwelling returns a structured list of all residents, the agent can iterate through multiple family units in a single pass, applying the graph-linking logic to the entire physical structure at once.

graph TD
    %% --- The Physical Container (Dwelling 10) ---
    subgraph Building ["Dwelling 10: The Physical Building"]

        %% --- Household A ---
        subgraph Family12 ["Family Number 12"]
            A1(Head: Farmer)
            A2(Wife)
            A3(Son)
        end

        %% --- Household B ---
        subgraph Family13 ["Family Number 13"]
            B1(Head: Blacksmith)
            B2(Son)
            B3(Daughter)
        end

        %% --- Unaffiliated ---
        C1[Servant]
        C2[Boarder]
    end

    %% --- Inter-Dwelling Links ---
    A1 <--> B1
    A1 <--> C1
    B1 <--> C2

    %% --- Styles ---
    style Building fill:#eef,stroke:#333,stroke-width:2px
    style Family12 fill:#f9f,stroke:#333
    style Family13 fill:#bbf,stroke:#333
    style A1,B1 fill:#f9f,stroke:#333,stroke-width:1px

Viewing history through the “Dwelling” lens reveals a different kind of truth. By treating the physical building as a parent container, the Scribe can group disparate family units (like Family 12 and 13 above) who shared the same roof. This “Mapping the Block” strategy allows an agent to infer social influence—how a Boarder’s trade might influence the children of the family they live with, or how neighborhoods clustered by occupation.

Why the Graph is the Future

By building a Social Graph, we’ve given the Digital Scribe a form of “Inference.” It no longer just knows who people are; it understands how they belong.

This is the final foundation stone for the Digital Scribe. We have mastered Capture, Persistence, and Connectivity.

What’s Next?

The Digital Scribe is now a fully realized Knowledge Graph. We have mastered Capture, Persistence, and Connectivity. But a Sovereign system shouldn’t just live in the past. In our next entry, we’re going to take a ‘Brain Break’ to look at how this exact same architecture can be handed over to a modern challenge: helping a farmer pivot their harvest when the market shifts.

The 1880 Archive was the training ground; the 2026 Vineyard is the mission.

Facebooktwitterredditlinkedinmail

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.