burnmydays's picture
Initial commit: Commitment Conservation Framework
2a64ad4
#!/usr/bin/env python3
"""
Commitment Conservation Harness CLI
Runs the operational harness via a single command so users don't need to
navigate internal modules. Outputs JSON/CSV receipts.
"""
import os
import json
import argparse
from datetime import datetime
# Force non-GUI plotting backend (prevents macOS blocking)
os.environ.setdefault("MPLBACKEND", "Agg")
def _now_iso() -> str:
return datetime.utcnow().replace(microsecond=0).isoformat() + "Z"
def simple_extraction(text: str, quiet: bool = False, as_json: bool = False) -> int:
"""Simple commitment extraction (default mode)."""
try:
from src.extraction import extract_hard_commitments
import spacy
except ImportError as e:
print(f"Import error: {e}", file=__import__('sys').stderr)
return 1
try:
nlp = spacy.load("en_core_web_sm")
except OSError:
print("Error: spaCy model 'en_core_web_sm' not found.", file=__import__('sys').stderr)
print("Install with: python -m spacy download en_core_web_sm", file=__import__('sys').stderr)
return 1
commitments = extract_hard_commitments(text, nlp)
if as_json:
import json
print(json.dumps({"input": text, "commitments": sorted(list(commitments))}, indent=2))
elif quiet:
for c in sorted(commitments):
print(c)
else:
print(f"Extracted {len(commitments)} commitment(s) from: \"{text[:60]}{'...' if len(text) > 60 else ''}\"")
if commitments:
for i, c in enumerate(sorted(commitments), 1):
print(f" {i}. {c}")
else:
print(" (none)")
return 0
def main() -> int:
import sys
# Check if this is a 'run' subcommand or simple extraction
if len(sys.argv) > 1 and sys.argv[1] == "run":
# Experimental mode
return run_experiment()
else:
# Simple extraction mode
return run_simple_extraction()
def run_simple_extraction() -> int:
"""Simple extraction CLI."""
import sys
p = argparse.ArgumentParser(
prog="commitment-harness",
description="Extract commitments from text.",
epilog="For full experiments, use: python analyze.py run {compression|recursion|full}"
)
p.add_argument("text", help="Text to analyze")
p.add_argument("--quiet", "-q", action="store_true", help="Output only commitments (no headers)")
p.add_argument("--json", action="store_true", help="Output as JSON")
args = p.parse_args()
return simple_extraction(args.text, quiet=args.quiet, as_json=args.json)
def run_experiment() -> int:
"""Experimental harness CLI."""
import sys
p = argparse.ArgumentParser(
prog="commitment-harness run",
description="Run commitment conservation experiments and export receipts."
)
sub = p.add_subparsers(dest="experiment", required=True)
# compression experiment
pc = sub.add_parser("compression", help="Run compression sweep on a signal.")
pc.add_argument("--signal", required=True, help="Input signal text.")
pc.add_argument("--out", default="outputs/compression_receipt.json", help="Output receipt path (json).")
# recursion experiment
pr = sub.add_parser("recursion", help="Run recursion test on a signal.")
pr.add_argument("--signal", required=True, help="Input signal text.")
pr.add_argument("--depth", type=int, default=8, help="Recursion depth.")
pr.add_argument("--enforced", action="store_true", help="Use enforcement mode.")
pr.add_argument("--out", default="outputs/recursion_receipt.json", help="Output receipt path (json).")
# full pipeline
pf = sub.add_parser("full", help="Run the deterministic pipeline (if available).")
pf.add_argument("--out", default="outputs/full_receipt.json", help="Output receipt path (json).")
# Remove 'run' from argv so argparse sees the subcommand correctly
sys.argv.pop(1)
args = p.parse_args()
os.makedirs(os.path.dirname(args.out), exist_ok=True)
# Import from your harness implementation
try:
from src.deterministic_pipeline import compression_sweep, recursion_test, deterministic_pipeline
except ImportError:
# Fallback to test_harness if deterministic_pipeline doesn't exist
try:
from src.test_harness import compression_sweep, recursion_test
deterministic_pipeline = None
except Exception as e:
raise SystemExit(f"Import error: {e}\n(Verify you run this from the harness/ directory.)")
receipt = {
"timestamp_utc": _now_iso(),
"experiment": args.experiment,
"python": {
"mpl_backend": os.environ.get("MPLBACKEND"),
},
}
if args.experiment == "compression":
sigma_vals, fid_vals = compression_sweep(args.signal)
receipt.update({
"input_signal": args.signal,
"n": len(fid_vals),
"sigma_values": sigma_vals,
"fidelities": fid_vals,
})
elif args.experiment == "recursion":
deltas = recursion_test(args.signal, depth=args.depth, enforced=args.enforced) if hasattr(recursion_test, '__code__') and 'enforced' in recursion_test.__code__.co_varnames else recursion_test(args.signal, depth=args.depth)
receipt.update({
"input_signal": args.signal,
"depth": args.depth,
"enforced": args.enforced if hasattr(recursion_test, '__code__') and 'enforced' in recursion_test.__code__.co_varnames else False,
"deltas": deltas,
})
elif args.experiment == "full":
if deterministic_pipeline is None:
raise SystemExit("deterministic_pipeline not available. (Missing src/deterministic_pipeline.py import.)")
result = deterministic_pipeline()
receipt.update({"result": result})
with open(args.out, "w", encoding="utf-8") as f:
json.dump(receipt, f, indent=2, ensure_ascii=False)
print(f"✓ Wrote receipt: {args.out}")
return 0
if __name__ == "__main__":
raise SystemExit(main())