from pathlib import Path import tomllib ROOT = Path(__file__).resolve().parents[1] AGENT_DIR = ROOT / ".codex" / "agents" SKILL_DIR = ROOT / ".codex" / "skills" EXPECTED_AGENTS = { "coordinator-agent", "correction-agent", "formulation-agent", "implementation-agent", "implementation-planning-agent", "io-definition-agent", "numerical-review-agent", "physics-evaluation-agent", "release-agent", "requirement-agent", "research-agent", } EXPECTED_SKILLS = { "fem-theory-query", "fesa-cpp-msvc-tdd", "fesa-formulation-spec", "fesa-io-contract", "fesa-numerical-review", "fesa-physics-sanity", "fesa-release-readiness", "fesa-requirements-baseline", "fesa-research-evidence", } LEGACY_OUTPUT_DIRS = { "coordination", "requirements", "research", "formulations", "numerical-reviews", "io-definitions", "reference-models", "implementation-plans", "build-test-reports", "corrections", "reference-verifications", "physics-evaluations", "releases", } FEATURE_FILES = { "linear-static-3d-euler-beam": { "coordination.md", "requirements.md", "research.md", "formulation.md", "numerical-review.md", "reference-model.md", "io.md", "implementation-plan.md", "implementation-report.md", "build-test.md", "reference-comparison.md", "physics-evaluation.md", "release.md", }, "linear-static-mitc4-shell": { "coordination.md", "requirements.md", "research.md", "formulation.md", "numerical-review.md", "reference-model.md", "io.md", "implementation-plan.md", "build-test.md", "reference-comparison.md", "physics-evaluation.md", "release.md", }, } LIVE_CONTRACT_FILES = ( ROOT / "AGENTS.md", ROOT / "docs" / "SOLVER_AGENT_DESIGN.md", ROOT / "docs" / "SOLVER_SKILL_DESIGN.md", ) RETIRED_TOKENS = ( "reference-model-agent", "reference model agent", "build-test-executor-agent", "build/test executor agent", "reference-verification-agent", "reference verification agent", "fesa-reference-models", "fesa-reference-comparison", "docs/coordination/", "docs/requirements/", "docs/research/", "docs/formulations/", "docs/numerical-reviews/", "docs/io-definitions/", "docs/reference-models/", "docs/implementation-plans/", "docs/build-test-reports/", "docs/corrections/", "docs/reference-verifications/", "docs/physics-evaluations/", "docs/releases/", ) def read(path: Path) -> str: return path.read_text(encoding="utf-8") def live_contract_text() -> str: paths = list(LIVE_CONTRACT_FILES) paths.extend(sorted(AGENT_DIR.glob("*.toml"))) paths.extend(sorted(SKILL_DIR.glob("*/SKILL.md"))) return "\n".join(read(path) for path in paths) def test_agent_inventory_is_consolidated(): actual = {path.stem for path in AGENT_DIR.glob("*.toml")} assert actual == EXPECTED_AGENTS def test_project_skill_inventory_is_consolidated(): actual = { path.name for path in SKILL_DIR.iterdir() if path.is_dir() and (path / "SKILL.md").is_file() } assert actual == EXPECTED_SKILLS def test_existing_feature_artifacts_are_bundled(): docs_dir = ROOT / "docs" for legacy_dir in LEGACY_OUTPUT_DIRS: assert not (docs_dir / legacy_dir).exists(), legacy_dir for feature_id, expected_files in FEATURE_FILES.items(): feature_dir = docs_dir / feature_id actual_files = {path.name for path in feature_dir.glob("*.md")} assert actual_files == expected_files def test_numerical_review_absorbs_reference_model_contract(): assert not (AGENT_DIR / "reference-model-agent.toml").exists() assert not (SKILL_DIR / "fesa-reference-models").exists() text = read(SKILL_DIR / "fesa-numerical-review" / "SKILL.md").lower() for marker in ( "numerical-review.md", "reference-model.md", "reference case inventory", "source identity", "row precheck", "tolerance", "pass-for-io-definition", ): assert marker in text def test_implementation_absorbs_build_test_and_reference_comparison(): assert not (AGENT_DIR / "build-test-executor-agent.toml").exists() assert not (AGENT_DIR / "reference-verification-agent.toml").exists() assert not (SKILL_DIR / "fesa-reference-comparison").exists() text = read(SKILL_DIR / "fesa-cpp-msvc-tdd" / "SKILL.md").lower() for marker in ( "implementation-report.md", "build-test.md", "reference-comparison.md", "artifact check -> compare -> classify -> report", "pass-for-physics-evaluation", ): assert marker in text def test_agent_hierarchy_is_explicit(): coordinator = read(AGENT_DIR / "coordinator-agent.toml").lower() assert "main agent" in coordinator assert "docs//coordination.md" in coordinator assert "worklist" in coordinator assert "sub-agent dispatch" in coordinator for path in sorted(AGENT_DIR.glob("*.toml")): if path.name == "coordinator-agent.toml": continue text = read(path).lower() assert "sub-agent" in text, path.name assert "coordinator agent" in text, path.name def test_live_contracts_have_no_retired_names_or_paths(): text = live_contract_text().lower() assert "docs//" in text for token in RETIRED_TOKENS: assert token not in text, token def test_live_guidance_declares_eight_stage_workflow(): for path in LIVE_CONTRACT_FILES: text = read(path) assert "8단계" in text, path assert "docs//" in text, path def test_mitc4_live_authority_links_use_feature_bundle(): text = read(ROOT / "docs" / "MITC4_SUPP.md") authority_section = text.split("라인 참조", maxsplit=1)[0] for path in ( "docs/linear-static-mitc4-shell/requirements.md", "docs/linear-static-mitc4-shell/formulation.md", "docs/linear-static-mitc4-shell/io.md", "docs/linear-static-mitc4-shell/reference-model.md", "docs/linear-static-mitc4-shell/numerical-review.md", "docs/linear-static-mitc4-shell/release.md", ): assert path in authority_section for legacy_path in ( "docs/requirements/linear-static-mitc4-shell.md", "docs/formulations/mitc4-shell-formulation.md", "docs/io-definitions/linear-static-mitc4-shell-io.md", "docs/reference-models/linear-static-mitc4-shell-reference-models.md", "docs/numerical-reviews/linear-static-mitc4-shell-review.md", "docs/releases/linear-static-mitc4-shell-release.md", ): assert legacy_path not in authority_section def test_agent_toml_and_skill_ui_metadata_are_parseable(): for path in sorted(AGENT_DIR.glob("*.toml")): parsed = tomllib.loads(read(path)) assert parsed["name"] == path.stem assert parsed["model_reasoning_effort"] == "extra high" for skill_name in EXPECTED_SKILLS: skill_dir = SKILL_DIR / skill_name skill_text = read(skill_dir / "SKILL.md") ui_text = read(skill_dir / "agents" / "openai.yaml") assert f"name: {skill_name}" in skill_text assert f"${skill_name}" in ui_text