feat(infospace): add L3 relation graph with VSM-aware triplets (S2.8)

Implements the L3 relation graph layer — a directed graph of (Subject,
Predicate, Object) triplets annotated with VSM channel codes and feedback
roles. Triplets are authored as markdown files under output/relations/,
parsed into RelationMeta dataclasses, and analysed with networkx.

New modules:
- markitect/infospace/relation_models.py — RelationMeta dataclass +
  RELATION_TYPES controlled vocabulary (15 relation classes → VSM codes)
- markitect/infospace/relation_parser.py — parse_relation_file() and
  parse_relations_directory()

New schema: examples/infospace-with-history/schemas/relation-schema-v1.0.md
  — file naming convention, required sections, controlled vocabulary table

15 seed relation files covering the three core WoN feedback loops:
  - Capital Accumulation loop (positive reinforcement, S1/S3)
  - Market Price Balancing loop (negative feedback, S2/S3)
  - Market Extent mutual dependency (S1/S2)
  Plus structural relations: wages regulation, rent residual, price
  decomposition, invisible hand coordination

CLI: markitect infospace relations [--entity SLUG] [--vsm FILTER]
     [--loops] [--stats]
  - Builds directed graph from parsed files
  - Detects feedback loops via nx.simple_cycles()
  - 6 loops found from 15 seed relations (3 intended + 3 emergent)
  - --stats aggregates by VSM system code (strips parentheticals)

Config: InfospaceConfig gains relations_dir (default output/relations)
infospace.yaml: schemas.relation references relation-schema-v1.0.md

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 06:04:28 +01:00
parent fa27572f43
commit 2d45425b25
21 changed files with 1058 additions and 0 deletions

View File

@@ -254,6 +254,7 @@ class InfospaceConfig:
entities_dir: str = "output/entities"
evaluations_dir: str = "output/evaluations"
metrics_dir: str = "output/metrics"
relations_dir: str = "output/relations"
def to_dict(self) -> Dict[str, Any]:
d: Dict[str, Any] = {"topic": self.topic.to_dict()}
@@ -276,6 +277,8 @@ class InfospaceConfig:
d["evaluations_dir"] = self.evaluations_dir
if self.metrics_dir != "output/metrics":
d["metrics_dir"] = self.metrics_dir
if self.relations_dir != "output/relations":
d["relations_dir"] = self.relations_dir
return d
@classmethod
@@ -301,6 +304,7 @@ class InfospaceConfig:
entities_dir=data.get("entities_dir", "output/entities"),
evaluations_dir=data.get("evaluations_dir", "output/evaluations"),
metrics_dir=data.get("metrics_dir", "output/metrics"),
relations_dir=data.get("relations_dir", "output/relations"),
)