125 lines
3.8 KiB
Python
125 lines
3.8 KiB
Python
import unittest
|
|
from pathlib import Path
|
|
|
|
|
|
ROOT = Path(__file__).resolve().parents[1]
|
|
SKILL_PATH = ROOT / ".codex" / "skills" / "fesa-fem-specification" / "SKILL.md"
|
|
|
|
|
|
def read_skill():
|
|
return SKILL_PATH.read_text(encoding="utf-8")
|
|
|
|
|
|
def parse_frontmatter(text):
|
|
lines = text.splitlines()
|
|
if not lines or lines[0] != "---":
|
|
raise AssertionError("SKILL.md must start with YAML frontmatter")
|
|
|
|
fields = {}
|
|
for line in lines[1:]:
|
|
if line == "---":
|
|
return fields
|
|
key, sep, value = line.partition(":")
|
|
if not sep:
|
|
raise AssertionError(f"Invalid frontmatter line: {line}")
|
|
fields[key.strip()] = value.strip()
|
|
|
|
raise AssertionError("SKILL.md frontmatter must be closed")
|
|
|
|
|
|
class FesaFemSpecificationSkillTests(unittest.TestCase):
|
|
def test_skill_file_exists_with_required_frontmatter(self):
|
|
self.assertTrue(SKILL_PATH.exists(), "fesa-fem-specification SKILL.md is missing")
|
|
|
|
fields = parse_frontmatter(read_skill())
|
|
|
|
self.assertEqual(set(fields), {"name", "description"})
|
|
self.assertEqual(fields["name"], "fesa-fem-specification")
|
|
self.assertIn("Use when", fields["description"])
|
|
self.assertIn("FESA FEM formulations", fields["description"])
|
|
self.assertIn("numerical review", fields["description"])
|
|
self.assertIn("Abaqus .inp", fields["description"])
|
|
self.assertIn("CSV schemas", fields["description"])
|
|
self.assertIn("implementation-planning handoffs", fields["description"])
|
|
|
|
def test_skill_body_defines_workflow_and_inputs(self):
|
|
body = read_skill()
|
|
|
|
for required_text in (
|
|
"## Inputs",
|
|
"## Workflow",
|
|
"INPUT CHECK",
|
|
"FORMULATION SPEC",
|
|
"NUMERICAL REVIEW CHECK",
|
|
"I/O CONTRACT",
|
|
"HANDOFF",
|
|
"## Quality Gate",
|
|
"docs/SOLVER_SKILL_DESIGN.md",
|
|
"docs/SOLVER_AGENT_DESIGN.md",
|
|
"AGENTS.md",
|
|
"docs/requirements/<feature-id>.md",
|
|
"docs/research/<feature-id>-research.md",
|
|
):
|
|
self.assertIn(required_text, body)
|
|
|
|
def test_skill_body_defines_formulation_contract(self):
|
|
body = read_skill()
|
|
|
|
for required_text in (
|
|
"Strong Form",
|
|
"Weak or Variational Form",
|
|
"Discretization",
|
|
"Kinematics",
|
|
"Element Equations",
|
|
"Mapping and Numerical Integration",
|
|
"Output Recovery",
|
|
"Numerical Risks",
|
|
"partition of unity",
|
|
"Kronecker delta",
|
|
"Jacobian",
|
|
"derivative transform",
|
|
):
|
|
self.assertIn(required_text, body)
|
|
|
|
def test_skill_body_defines_io_contract(self):
|
|
body = read_skill()
|
|
|
|
for required_text in (
|
|
"Abaqus Input Scope",
|
|
"Model Data Mapping",
|
|
"History Data Mapping",
|
|
"Internal Model Contract",
|
|
"Output and CSV Schemas",
|
|
"*NODE",
|
|
"*ELEMENT",
|
|
"*MATERIAL",
|
|
"*ELASTIC",
|
|
"*BOUNDARY",
|
|
"*STEP",
|
|
"*OUTPUT",
|
|
"*NODE OUTPUT",
|
|
"*ELEMENT OUTPUT",
|
|
"displacements.csv",
|
|
"reactions.csv",
|
|
"element_forces.csv",
|
|
"stresses.csv",
|
|
):
|
|
self.assertIn(required_text, body)
|
|
|
|
def test_skill_body_enforces_scope_boundaries(self):
|
|
body = read_skill()
|
|
|
|
for required_text in (
|
|
"Do not implement C++ code.",
|
|
"Do not design C++ APIs.",
|
|
"Do not implement parsers.",
|
|
"Do not run Abaqus, Nastran, or any reference solver.",
|
|
"Do not generate reference CSVs.",
|
|
"Do not approve release readiness.",
|
|
):
|
|
self.assertIn(required_text, body)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|