feat(example): migrate to infospace config with tooling integration (S3.1)
Add infospace.yaml declaring topic, disciplines, schemas, viability thresholds. Integrate infospace tooling into process_chapters.py with --infospace-status, --infospace-check, and --infospace-viability flags. Initial check: 85 entities, 4/5 viable (coverage 0.36 < 0.50 — only 7/35 chapters processed so far). Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
51
examples/infospace-with-history/infospace.yaml
Normal file
51
examples/infospace-with-history/infospace.yaml
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
# Infospace: The Wealth of Nations through the Viable System Model
|
||||||
|
#
|
||||||
|
# This configuration declares the infospace built by processing
|
||||||
|
# Adam Smith's "The Wealth of Nations" (1776) through the lens of
|
||||||
|
# Stafford Beer's Viable System Model (VSM).
|
||||||
|
|
||||||
|
topic:
|
||||||
|
name: "The Wealth of Nations"
|
||||||
|
domain: "Classical Economics"
|
||||||
|
sources: artifacts/sources/
|
||||||
|
|
||||||
|
disciplines:
|
||||||
|
- name: "Viable System Model"
|
||||||
|
path: artifacts/vsm-reference/
|
||||||
|
|
||||||
|
schemas:
|
||||||
|
entity: schemas/economic-entity-schema-v1.0.md
|
||||||
|
mapping: schemas/vsm-mapping-schema-v1.0.md
|
||||||
|
analysis: schemas/chapter-analysis-schema-v1.0.md
|
||||||
|
|
||||||
|
competency_questions: |
|
||||||
|
1. How does Smith's division of labour map to VSM System 1 operations?
|
||||||
|
2. What mechanisms in WoN correspond to VSM coordination (System 2)?
|
||||||
|
3. Where does Smith describe self-organising regulation (System 3)?
|
||||||
|
4. What role does the "invisible hand" play as a System 4 mechanism?
|
||||||
|
5. How do Smith's views on government map to System 5 policy?
|
||||||
|
6. Is the WoN entity set viable as an explanatory framework?
|
||||||
|
|
||||||
|
viability:
|
||||||
|
redundancy_ratio:
|
||||||
|
max: 0.10
|
||||||
|
coverage_ratio:
|
||||||
|
min: 0.50
|
||||||
|
coherence_components:
|
||||||
|
max: 3
|
||||||
|
consistency_cycles:
|
||||||
|
max: 0
|
||||||
|
granularity_entropy:
|
||||||
|
min: 1.0
|
||||||
|
|
||||||
|
pipeline:
|
||||||
|
stages:
|
||||||
|
- name: extract-entities
|
||||||
|
template: templates/extract-entities.md
|
||||||
|
- name: map-to-vsm
|
||||||
|
template: templates/map-to-vsm.md
|
||||||
|
- name: synthesize-analysis
|
||||||
|
template: templates/synthesize-analysis.md
|
||||||
|
post_batch:
|
||||||
|
- name: assess-metrics
|
||||||
|
template: templates/assess-metrics.md
|
||||||
@@ -856,6 +856,125 @@ class ChapterProcessor:
|
|||||||
print(f" (No data yet: {e})")
|
print(f" (No data yet: {e})")
|
||||||
|
|
||||||
|
|
||||||
|
# ── Infospace tooling integration ─────────────────────────────────
|
||||||
|
|
||||||
|
|
||||||
|
def _load_infospace(example_dir: Path):
|
||||||
|
"""Load infospace config and entities from the example directory."""
|
||||||
|
from markitect.infospace.config import load_infospace_config
|
||||||
|
from markitect.infospace.entity_parser import parse_entity_directory
|
||||||
|
|
||||||
|
config_path = example_dir / "infospace.yaml"
|
||||||
|
if not config_path.is_file():
|
||||||
|
print("Error: No infospace.yaml found. Create one first.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
config = load_infospace_config(config_path)
|
||||||
|
entities_dir = example_dir / config.entities_dir
|
||||||
|
entities = parse_entity_directory(entities_dir) if entities_dir.is_dir() else []
|
||||||
|
return config, config_path, entities
|
||||||
|
|
||||||
|
|
||||||
|
def _run_infospace_status(example_dir: Path):
|
||||||
|
"""Show infospace status using the tooling layer."""
|
||||||
|
from markitect.infospace.state import build_state
|
||||||
|
|
||||||
|
config, config_path, entities = _load_infospace(example_dir)
|
||||||
|
state = build_state(config, entities=entities)
|
||||||
|
|
||||||
|
print(f"Infospace: {state.topic_name}")
|
||||||
|
print(f"Domain: {config.topic.domain}")
|
||||||
|
print(f"Entities: {state.entity_count}")
|
||||||
|
if state.domains:
|
||||||
|
print(f"Domains: {', '.join(state.domains)}")
|
||||||
|
if config.disciplines:
|
||||||
|
names = [d.name for d in config.disciplines]
|
||||||
|
print(f"Disciplines: {', '.join(names)}")
|
||||||
|
|
||||||
|
# Show processing progress
|
||||||
|
sources_dir = example_dir / "artifacts" / "sources"
|
||||||
|
total_chapters = len(list(sources_dir.glob("*.md")))
|
||||||
|
processed = len(list((example_dir / "output" / "analyses").glob("*-analysis.md")))
|
||||||
|
print(f"Chapters: {processed}/{total_chapters} processed")
|
||||||
|
|
||||||
|
|
||||||
|
def _run_infospace_check(example_dir: Path):
|
||||||
|
"""Run collection-level quality checks."""
|
||||||
|
from markitect.infospace.checks import run_all_checks
|
||||||
|
from markitect.infospace.history import record_check_results
|
||||||
|
|
||||||
|
config, config_path, entities = _load_infospace(example_dir)
|
||||||
|
|
||||||
|
if not entities:
|
||||||
|
print("No entities to check.")
|
||||||
|
return
|
||||||
|
|
||||||
|
print(f"Running collection checks on {len(entities)} entities...\n")
|
||||||
|
report = run_all_checks(entities=entities)
|
||||||
|
|
||||||
|
d = report.to_dict()
|
||||||
|
for concern_name, concern_data in d.items():
|
||||||
|
label = concern_data.get("concern", concern_name.upper())
|
||||||
|
print(f" {label} — {concern_name}")
|
||||||
|
for k, v in concern_data.items():
|
||||||
|
if k == "concern":
|
||||||
|
continue
|
||||||
|
print(f" {k}: {v}")
|
||||||
|
print()
|
||||||
|
|
||||||
|
m = report.metrics()
|
||||||
|
if m:
|
||||||
|
print("Metrics summary:")
|
||||||
|
for k, v in sorted(m.items()):
|
||||||
|
print(f" {k}: {v:.4f}")
|
||||||
|
snap = record_check_results(report, config, example_dir, entity_count=len(entities))
|
||||||
|
print(f"\nRecorded snapshot {snap.snapshot_id}")
|
||||||
|
|
||||||
|
|
||||||
|
def _run_infospace_viability(example_dir: Path):
|
||||||
|
"""Show viability dashboard."""
|
||||||
|
from markitect.infospace.history import read_metrics_file
|
||||||
|
from markitect.infospace.state import build_state
|
||||||
|
|
||||||
|
config, config_path, entities = _load_infospace(example_dir)
|
||||||
|
|
||||||
|
if not config.viability:
|
||||||
|
print("No viability thresholds configured.")
|
||||||
|
return
|
||||||
|
|
||||||
|
metrics = read_metrics_file(example_dir / config.metrics_dir / "metrics.yaml")
|
||||||
|
if not metrics:
|
||||||
|
print("No metrics available. Run --infospace-check first.")
|
||||||
|
print("\nConfigured thresholds:")
|
||||||
|
for name, t in config.viability.items():
|
||||||
|
bounds = []
|
||||||
|
if t.min is not None:
|
||||||
|
bounds.append(f"min={t.min}")
|
||||||
|
if t.max is not None:
|
||||||
|
bounds.append(f"max={t.max}")
|
||||||
|
print(f" {name}: {', '.join(bounds)}")
|
||||||
|
return
|
||||||
|
|
||||||
|
state = build_state(config, entities=entities, metrics=metrics)
|
||||||
|
|
||||||
|
print(f"{'Metric':<30} {'Value':>8} {'Threshold':>15} {'Status':>8}")
|
||||||
|
print("-" * 63)
|
||||||
|
for r in state.viability_results:
|
||||||
|
bounds = []
|
||||||
|
if r.threshold.min is not None:
|
||||||
|
bounds.append(f"min={r.threshold.min}")
|
||||||
|
if r.threshold.max is not None:
|
||||||
|
bounds.append(f"max={r.threshold.max}")
|
||||||
|
status_str = "PASS" if r.passed else "FAIL"
|
||||||
|
print(f"{r.metric:<30} {r.value:>8.4f} {', '.join(bounds):>15} {status_str:>8}")
|
||||||
|
|
||||||
|
print()
|
||||||
|
if state.is_viable:
|
||||||
|
print(f"Viable: YES ({state.viability_pass_count}/{state.viability_total_count} thresholds met)")
|
||||||
|
else:
|
||||||
|
print(f"Viable: NO ({state.viability_pass_count}/{state.viability_total_count} thresholds met)")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(
|
parser = argparse.ArgumentParser(
|
||||||
description="Process Wealth of Nations chapters through VSM analysis pipeline"
|
description="Process Wealth of Nations chapters through VSM analysis pipeline"
|
||||||
@@ -869,6 +988,12 @@ def main():
|
|||||||
group.add_argument("--stats", action="store_true", help="Show dependency statistics")
|
group.add_argument("--stats", action="store_true", help="Show dependency statistics")
|
||||||
group.add_argument("--archive-entity", type=str, metavar="SLUG",
|
group.add_argument("--archive-entity", type=str, metavar="SLUG",
|
||||||
help="Archive an entity (move to archive/ with reason)")
|
help="Archive an entity (move to archive/ with reason)")
|
||||||
|
group.add_argument("--infospace-status", action="store_true",
|
||||||
|
help="Show infospace status via infospace tooling")
|
||||||
|
group.add_argument("--infospace-check", action="store_true",
|
||||||
|
help="Run collection-level quality checks (C1-C5)")
|
||||||
|
group.add_argument("--infospace-viability", action="store_true",
|
||||||
|
help="Show viability dashboard")
|
||||||
|
|
||||||
parser.add_argument("--reason", type=str, default=None,
|
parser.add_argument("--reason", type=str, default=None,
|
||||||
help="Reason for archiving (used with --archive-entity)")
|
help="Reason for archiving (used with --archive-entity)")
|
||||||
@@ -930,6 +1055,15 @@ def main():
|
|||||||
for ch in chapters:
|
for ch in chapters:
|
||||||
processor.process_chapter(ch, auto_commit=not args.no_commit)
|
processor.process_chapter(ch, auto_commit=not args.no_commit)
|
||||||
print()
|
print()
|
||||||
|
elif args.infospace_status:
|
||||||
|
_run_infospace_status(example_dir)
|
||||||
|
return
|
||||||
|
elif args.infospace_check:
|
||||||
|
_run_infospace_check(example_dir)
|
||||||
|
return
|
||||||
|
elif args.infospace_viability:
|
||||||
|
_run_infospace_viability(example_dir)
|
||||||
|
return
|
||||||
|
|
||||||
processor.show_stats()
|
processor.show_stats()
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user