import json
import os
import subprocess
import sys
import zipfile
from pathlib import Path
import yaml
from infospace_bench.generator import (
init_generation_infospace,
run_generation,
status_generation,
)
PROFILE_DIR = Path("src/infospace_bench/profiles/trading-literature")
def _fixture_responses(path: Path) -> None:
data = {
"responses": [
{
"stage_id": "summarize-source",
"input_artifact_id": "*",
"markdown": "# Source Summary\n\nThe chapter introduces a bucket-shop apprenticeship.\n",
},
{
"stage_id": "extract-entities",
"input_artifact_id": "*",
"markdown": (
"# Tape Reading\n\n"
"## Category\n\nstrategy\n\n"
"## Definition\n\n"
"Inferring price intent from the ticker tape rather than fundamentals.\n\n"
"## Context\n\nFramed as a learnable pattern skill in the chapter.\n\n"
"# Bucket Shop\n\n"
"## Category\n\ninstitution\n\n"
"## Definition\n\n"
"A 1900s retail brokerage that took the other side of customer tape bets.\n\n"
),
},
{
"stage_id": "extract-relations",
"input_artifact_id": "*",
"markdown": (
"# Tape Reading Reduces Tip Following\n\n"
"## Subject\n\nTape Reading\n\n"
"## Predicate\n\nreduces\n\n"
"## Object\n\nTip Following\n\n"
"## Relation Type\n\nrisk_mitigation\n\n"
"## Evidence\n\nThe narrator's profits track tape behaviour, not rumour.\n"
),
},
{
"stage_id": "evaluate-entity",
"input_artifact_id": "*",
"markdown": (
"---\n"
"artifact_id: entity/tape-reading.md\n"
"evaluator: fixture\n"
"evaluated_at: '2026-05-17T00:00:00'\n"
"scores:\n"
" - name: groundedness\n value: 4.0\n max_value: 5.0\n"
" - name: lesson_clarity\n value: 4.0\n max_value: 5.0\n"
" - name: historical_context\n value: 4.0\n max_value: 5.0\n"
" - name: overgeneralization_risk\n value: 4.0\n max_value: 5.0\n"
"---\n\n"
"# Evaluation: entity/tape-reading.md\n"
),
},
]
}
path.write_text(yaml.safe_dump(data, sort_keys=False), encoding="utf-8")
CONTAINER_XML = """
The narrator tries tape reading at a bucket shop.
", ) archive.writestr( "OEBPS/ch2.xhtml", "He learns the cost of acting on rumours.
", ) def test_trading_profile_declares_required_categories_and_criteria() -> None: data = yaml.safe_load((PROFILE_DIR / "profile.yaml").read_text(encoding="utf-8")) assert data["id"] == "trading-literature" assert set(data["entity_categories"]) == { "traders", "markets", "strategies", "errors", "psychological_patterns", "institutions", "instruments", "evidence_bearing_claims", } assert set(data["relation_categories"]) == { "cause_effect", "lesson_evidence", "risk_mitigation", "actor_venue", "strategy_outcome", } assert data["evaluation_criteria"] == [ "groundedness", "lesson_clarity", "historical_context", "overgeneralization_risk", ] def test_trading_profile_evaluate_template_mentions_all_criteria() -> None: template = (PROFILE_DIR / "templates" / "evaluate-entity.md").read_text(encoding="utf-8") for criterion in ( "groundedness", "lesson_clarity", "historical_context", "overgeneralization_risk", ): assert criterion in template, f"evaluate template should reference {criterion}" def test_trading_profile_relation_template_lists_required_relation_types() -> None: template = (PROFILE_DIR / "templates" / "extract-relations.md").read_text(encoding="utf-8") for relation_type in ( "cause_effect", "lesson_evidence", "risk_mitigation", "actor_venue", "strategy_outcome", ): assert relation_type in template, f"relation template should reference {relation_type}" def test_trading_profile_contracts_present() -> None: contracts_dir = PROFILE_DIR / "contracts" expected = {"entity.contract.md", "relation.contract.md", "evaluation.contract.md", "summary.contract.md"} actual = {path.name for path in contracts_dir.glob("*.md")} assert expected.issubset(actual) def test_trading_profile_runs_end_to_end_with_fixture(tmp_path: Path) -> None: book = tmp_path / "book.epub" _write_two_chapter_epub(book) fixture = tmp_path / "responses.yaml" _fixture_responses(fixture) infospace = init_generation_infospace( tmp_path, book, "trading-fixture", name="Trading Fixture", profile="trading-literature", ) result = run_generation(infospace.root, fixture_responses=fixture) status = status_generation(infospace.root) assert result.status == "completed" assert status["profile"] == "trading-literature" assert status["source_chunk_count"] == 2 assert status["entity_count"] >= 1 assert status["relation_count"] >= 1 assert status["evaluation_count"] >= 1 # Installed profile should have copied templates and contracts into the infospace. assert (infospace.root / "profiles" / "trading-literature" / "templates" / "evaluate-entity.md").is_file() assert ( infospace.root / "profiles" / "trading-literature" / "contracts" / "entity.contract.md" ).is_file() def test_trading_profile_selectable_via_cli(tmp_path: Path) -> None: book = tmp_path / "book.epub" _write_two_chapter_epub(book) fixture = tmp_path / "responses.yaml" _fixture_responses(fixture) env = os.environ.copy() env["PYTHONPATH"] = "src:/home/worsch/markitect-tool/src" result = subprocess.run( [ sys.executable, "-m", "infospace_bench", "generate", "from-source", str(book), "--workspace", str(tmp_path), "--slug", "trading-cli", "--name", "Trading CLI", "--profile", "trading-literature", "--fixture-responses", str(fixture), "--apply", ], check=False, env=env, text=True, capture_output=True, ) assert result.returncode == 0, result.stderr payload = json.loads(result.stdout) assert payload["status"] == "completed" assert "trading-cli" in payload["root"]