A health check on a real personal soul ran soul repair --rebuild-graph. The rebuilt graph has 1881 nodes and zero edges. This is on current dev-level code with a fresh rebuild, so it is a code bug, not a stale graph.
What the rebuilt graph looks like
The nodes are not entities. They are single capitalized tokens, acronyms, and code identifiers lifted out of memory text. A sample:
MERGED, SEPARATE, Literal, PRs, M3, M2a, Nov-2025, CI-green
POCKETPAW_POCKET_REFRESH_SCHEDULER_ENABLED, CloudLifecycleHook, TaskSpec, HOME_POCKET_PROMPT
Nearly every node is typed person. Only a handful (pocketpaw, Claude, soul-protocol) come out as tool. With zero edges, graph traversal and graph-augmented recall have nothing to walk.
This is the same state #190 described before its planned overhaul: "64 entities, 0 edges, entity extraction is regex token-splitting on capitalised words, default type is person." #190 Phase 1 was supposed to replace that. The enum and provenance plumbing landed; the extractor did not.
Where the bug is
Extraction is regex token-splitting, not noun-phrase NER
MemoryManager.extract_entities() at src/soul_protocol/runtime/memory/manager.py:1893:
- Line 1906 splits the text into word tokens with
re.findall(r"[\w][\w\-]*", combined).
- Lines 1916-1935 walk each sentence and, for every token after the first that starts with a capital letter and is not a stopword, create an entity.
tokens[1:] is the whole sentence minus the first word, so MERGED, CI-green, TaskSpec, and POCKETPAW_POCKET_REFRESH_SCHEDULER_ENABLED all qualify. Compound names like "Soul Protocol" are split into two separate one-token entities.
- There is no noun-phrase chunking and no NER. The
_TOPIC_PATTERNS pass only catches a few first-person speech templates ("I am learning X"), which do not fire on memory content written by the soul-sync hook.
Typing defaults to person
The capitalized-token branch hard-codes "type": "person" (manager.py:1932). The third-person relation pass does the same for any new subject or object (lines 1992, 2004). The only non-person path is the KNOWN_TECH lookup (lines 1908-1914), a fixed set of technology names. translate_to_ontology() (manager.py:311) runs afterward but only maps legacy synonym strings (technology to tool, user to person); it cannot infer a type. So anything not in KNOWN_TECH lands as person regardless of what it is.
The rebuild path drops relationships before they reach the graph
extract_entities does build edges in the third-person relation pass (manager.py:1973-2024) and returns them under each entity's relationships key. But repair --rebuild-graph discards them. src/soul_protocol/cli/main.py:3692-3698 builds the per-entity dict with only name and entity_type:
graph_ent = {
"name": ent["name"],
"entity_type": ent.get("type", "unknown"),
}
ent["relationships"] is never copied. update_graph() (manager.py:2063) reads entity.get("relationships", []), gets an empty list, and adds no edges. Even when extraction finds edges, the rebuild throws them away. That is the direct cause of zero edges after a rebuild.
Correct behavior
- Extract real noun-phrase entities. Keep compound names like "Soul Protocol" intact instead of shredding them into single tokens. Drop bare verbs, acronyms, and code identifiers, or route them to a
concept type rather than person.
- Type entities through the ontology. A capitalized token is not evidence of a person.
person should not be the catch-all default.
- Carry relationships through
repair --rebuild-graph. Forward relationships (and edge_metadata for provenance) into the dict passed to update_graph() so a rebuilt graph has edges.
References
A health check on a real personal soul ran
soul repair --rebuild-graph. The rebuilt graph has 1881 nodes and zero edges. This is on current dev-level code with a fresh rebuild, so it is a code bug, not a stale graph.What the rebuilt graph looks like
The nodes are not entities. They are single capitalized tokens, acronyms, and code identifiers lifted out of memory text. A sample:
MERGED,SEPARATE,Literal,PRs,M3,M2a,Nov-2025,CI-greenPOCKETPAW_POCKET_REFRESH_SCHEDULER_ENABLED,CloudLifecycleHook,TaskSpec,HOME_POCKET_PROMPTNearly every node is typed
person. Only a handful (pocketpaw,Claude,soul-protocol) come out astool. With zero edges, graph traversal and graph-augmented recall have nothing to walk.This is the same state #190 described before its planned overhaul: "64 entities, 0 edges, entity extraction is regex token-splitting on capitalised words, default type is
person." #190 Phase 1 was supposed to replace that. The enum and provenance plumbing landed; the extractor did not.Where the bug is
Extraction is regex token-splitting, not noun-phrase NER
MemoryManager.extract_entities()atsrc/soul_protocol/runtime/memory/manager.py:1893:re.findall(r"[\w][\w\-]*", combined).tokens[1:]is the whole sentence minus the first word, soMERGED,CI-green,TaskSpec, andPOCKETPAW_POCKET_REFRESH_SCHEDULER_ENABLEDall qualify. Compound names like "Soul Protocol" are split into two separate one-token entities._TOPIC_PATTERNSpass only catches a few first-person speech templates ("I am learning X"), which do not fire on memory content written by the soul-sync hook.Typing defaults to
personThe capitalized-token branch hard-codes
"type": "person"(manager.py:1932). The third-person relation pass does the same for any new subject or object (lines 1992, 2004). The only non-personpath is theKNOWN_TECHlookup (lines 1908-1914), a fixed set of technology names.translate_to_ontology()(manager.py:311) runs afterward but only maps legacy synonym strings (technologytotool,usertoperson); it cannot infer a type. So anything not inKNOWN_TECHlands aspersonregardless of what it is.The rebuild path drops relationships before they reach the graph
extract_entitiesdoes build edges in the third-person relation pass (manager.py:1973-2024) and returns them under each entity'srelationshipskey. Butrepair --rebuild-graphdiscards them.src/soul_protocol/cli/main.py:3692-3698builds the per-entity dict with onlynameandentity_type:ent["relationships"]is never copied.update_graph()(manager.py:2063) readsentity.get("relationships", []), gets an empty list, and adds no edges. Even when extraction finds edges, the rebuild throws them away. That is the direct cause of zero edges after a rebuild.Correct behavior
concepttype rather thanperson.personshould not be the catch-all default.repair --rebuild-graph. Forwardrelationships(andedge_metadatafor provenance) into the dict passed toupdate_graph()so a rebuilt graph has edges.References