add skills

This commit is contained in:
김경종
2026-06-04 15:03:03 +09:00
parent 1daf68afb9
commit 9b31adfd15
42 changed files with 1401 additions and 398 deletions
+75
View File
@@ -0,0 +1,75 @@
import unittest
from pathlib import Path
try:
import tomllib
except ModuleNotFoundError: # pragma: no cover
import tomli as tomllib
ROOT = Path(__file__).resolve().parents[1]
AGENTS_ROOT = ROOT / ".codex" / "agents"
AGENT_SKILL_REFERENCES = {
"coordinator-agent.toml": (
"fesa-requirements-baseline",
"fesa-reference-models",
"fesa-release-readiness",
),
"requirement-agent.toml": ("fesa-requirements-baseline",),
"research-agent.toml": (
"fesa-research-evidence",
"fem-theory-query",
),
"formulation-agent.toml": (
"fesa-formulation-spec",
"fem-theory-query",
),
"numerical-review-agent.toml": (
"fesa-numerical-review",
"fem-theory-query",
),
"io-definition-agent.toml": (
"fesa-io-contract",
"fem-theory-query",
),
"reference-model-agent.toml": (
"fesa-reference-models",
"fem-theory-query",
),
"implementation-planning-agent.toml": (
"fesa-formulation-spec",
"fesa-reference-models",
"fesa-cpp-msvc-tdd",
"fem-theory-query",
),
"implementation-agent.toml": ("fesa-cpp-msvc-tdd",),
"build-test-executor-agent.toml": ("fesa-cpp-msvc-tdd",),
"correction-agent.toml": ("fesa-cpp-msvc-tdd",),
"reference-verification-agent.toml": (
"fesa-reference-comparison",
"fesa-io-contract",
),
"physics-evaluation-agent.toml": (
"fesa-physics-sanity",
"fem-theory-query",
),
"release-agent.toml": ("fesa-release-readiness",),
}
class AgentSkillReferenceTests(unittest.TestCase):
def test_agents_reference_their_solver_skills(self):
for agent_file, skill_names in AGENT_SKILL_REFERENCES.items():
with self.subTest(agent=agent_file):
text = (AGENTS_ROOT / agent_file).read_text(encoding="utf-8")
data = tomllib.loads(text)
instructions = data["developer_instructions"]
self.assertIn("Skill references:", instructions)
for skill_name in skill_names:
self.assertIn(f"${skill_name}", instructions)
if __name__ == "__main__":
unittest.main()
@@ -1,92 +0,0 @@
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SKILL_PATH = ROOT / ".codex" / "skills" / "fesa-feature-definition" / "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 FesaFeatureDefinitionSkillTests(unittest.TestCase):
def test_skill_file_exists_with_required_frontmatter(self):
self.assertTrue(SKILL_PATH.exists(), "fesa-feature-definition SKILL.md is missing")
fields = parse_frontmatter(read_skill())
self.assertEqual(set(fields), {"name", "description"})
self.assertEqual(fields["name"], "fesa-feature-definition")
self.assertIn("Use when", fields["description"])
self.assertIn("FESA solver feature requests", fields["description"])
self.assertIn("requirements", fields["description"])
self.assertIn("research questions", fields["description"])
self.assertIn("acceptance criteria", fields["description"])
self.assertIn("verification matrices", fields["description"])
def test_skill_body_defines_core_workflow_and_inputs(self):
body = read_skill()
for required_text in (
"## Inputs",
"## Workflow",
"## Output Contract",
"## Boundaries",
"## 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_requirement_and_verification_contract(self):
body = read_skill()
for required_text in (
"Feature Definition Packet",
"shall",
"Requirement Verification Matrix",
"Research Questions",
"Reference Artifact Needs",
"Tolerance Decisions",
"Downstream Handoff",
"FESA-REQ-<FEATURE>-###",
"needs-user-decision",
):
self.assertIn(required_text, body)
def test_skill_body_enforces_scope_boundaries(self):
body = read_skill()
for required_text in (
"Do not finalize FEM formulations.",
"Do not implement C++ code.",
"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()
@@ -1,124 +0,0 @@
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()
+283
View File
@@ -0,0 +1,283 @@
import unittest
from pathlib import Path
ROOT = Path(__file__).resolve().parents[1]
SKILLS_ROOT = ROOT / ".codex" / "skills"
COMMON_SECTIONS = (
"## Inputs",
"## Workflow",
"## Output Contract",
"## Boundaries",
"## Quality Gate",
"## Handoff",
)
SKILLS = {
"fesa-requirements-baseline": {
"description_terms": (
"Use when",
"FESA solver",
"requirements",
"acceptance criteria",
"verification matrix",
),
"body_terms": (
"docs/requirements/<feature-id>.md",
"Requirement Verification Matrix",
"shall",
"FESA-REQ-<FEATURE>-###",
"Verification Quantities",
"Tolerance Policy",
"Reference Artifact Requirements",
"Do not implement C++ code.",
),
},
"fesa-research-evidence": {
"description_terms": (
"Use when",
"FESA solver",
"research",
"FEM theory",
"benchmarks",
),
"body_terms": (
"docs/research/<feature-id>-research.md",
"Source Inventory",
"Source Reliability Tier",
"Candidate Benchmarks",
"Verification Relevance",
"Applicability Limits",
"Separate verified facts from inference.",
),
},
"fesa-formulation-spec": {
"description_terms": (
"Use when",
"FESA FEM",
"formulation",
"element equations",
"output recovery",
),
"body_terms": (
"docs/formulations/<feature-id>-formulation.md",
"Strong Form",
"Weak or Variational Form",
"Discretization",
"Kinematics",
"Element Equations",
"Jacobian",
"Output Recovery",
"Do not design C++ APIs.",
),
},
"fesa-numerical-review": {
"description_terms": (
"Use when",
"FESA FEM",
"numerical review",
"stability",
"implementation planning",
),
"body_terms": (
"docs/numerical-reviews/<feature-id>-review.md",
"pass-for-implementation-planning",
"rigid body modes",
"patch test",
"hourglass",
"locking",
"Jacobian",
"Do not edit formulations directly.",
),
},
"fesa-io-contract": {
"description_terms": (
"Use when",
"FESA solver",
"Abaqus .inp",
"CSV schemas",
"I/O",
),
"body_terms": (
"docs/io-definitions/<feature-id>-io.md",
"Abaqus Input Scope",
"Internal Model Contract",
"Output and CSV Schemas",
"*NODE",
"*ELEMENT",
"*MATERIAL",
"*BOUNDARY",
"*STEP",
"Do not implement parsers.",
),
},
"fesa-reference-models": {
"description_terms": (
"Use when",
"FESA",
"reference model",
"Abaqus input",
"artifact",
),
"body_terms": (
"docs/reference-models/<feature-id>-reference-models.md",
"references/<feature-id>/<model-id>/",
"model.inp",
"metadata.json",
"displacements.csv",
"reactions.csv",
"element_forces.csv",
"stresses.csv",
"Coverage Matrix",
"Do not generate reference CSVs.",
),
},
"fesa-cpp-msvc-tdd": {
"description_terms": (
"Use when",
"FESA solver",
"C++",
"MSVC",
"TDD",
),
"body_terms": (
"docs/implementation-plans/<feature-id>-implementation-plan.md",
"RED -> GREEN -> VERIFY",
"python -m unittest discover -s scripts -p \"test_*.py\"",
"python scripts/validate_workspace.py",
"ctest",
"configure | compile | link | test | reference-comparison",
"Do not change requirements.",
),
},
"fesa-reference-comparison": {
"description_terms": (
"Use when",
"FESA solver",
"reference CSV",
"tolerance",
"comparison",
),
"body_terms": (
"docs/reference-verifications/<feature-id>-reference-verification.md",
"ARTIFACT CHECK -> COMPARE -> CLASSIFY -> REPORT",
"max absolute error",
"max relative error",
"RMS error",
"missing rows",
"extra rows",
"pass-for-physics-evaluation",
"Do not change tolerance policies.",
),
},
"fesa-physics-sanity": {
"description_terms": (
"Use when",
"FESA solver",
"physical plausibility",
"equilibrium",
"physics",
),
"body_terms": (
"docs/physics-evaluations/<feature-id>-physics-evaluation.md",
"global equilibrium",
"reaction consistency",
"displacement direction",
"symmetry",
"element force balance",
"model coverage",
"pass-for-release-agent",
"Do not approve release readiness.",
),
},
"fesa-release-readiness": {
"description_terms": (
"Use when",
"FESA solver",
"release readiness",
"release notes",
"known limitations",
),
"body_terms": (
"docs/releases/<feature-id>-release.md",
"GATE AUDIT -> TRACEABILITY CHECK -> RELEASE DOCUMENTATION -> RELEASE VERDICT",
"ready-for-release",
"Known Limitations",
"Release Notes Draft",
"pass-for-reference-verification",
"pass-for-physics-evaluation",
"pass-for-release-agent",
"Do not publish, deploy, package, tag, commit, or externally release anything unless the user explicitly asks.",
),
},
}
def read_skill(skill_name):
return (SKILLS_ROOT / skill_name / "SKILL.md").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 FesaSolverSkillTests(unittest.TestCase):
def test_all_solver_skill_files_exist_with_required_frontmatter(self):
for skill_name, spec in SKILLS.items():
with self.subTest(skill=skill_name):
skill_path = SKILLS_ROOT / skill_name / "SKILL.md"
self.assertTrue(skill_path.exists(), f"{skill_name} SKILL.md is missing")
fields = parse_frontmatter(read_skill(skill_name))
self.assertEqual(set(fields), {"name", "description"})
self.assertEqual(fields["name"], skill_name)
for term in spec["description_terms"]:
self.assertIn(term, fields["description"])
def test_all_solver_skills_define_common_contract_sections(self):
for skill_name in SKILLS:
with self.subTest(skill=skill_name):
body = read_skill(skill_name)
for section in COMMON_SECTIONS:
self.assertIn(section, body)
self.assertIn("AGENTS.md", body)
self.assertIn("docs/SOLVER_AGENT_DESIGN.md", body)
self.assertNotIn("docs/SOLVER_SKILL_DESIGN.md", body)
def test_solver_skills_define_skill_specific_contracts(self):
for skill_name, spec in SKILLS.items():
with self.subTest(skill=skill_name):
body = read_skill(skill_name)
for term in spec["body_terms"]:
self.assertIn(term, body)
def test_solver_skills_have_openai_ui_metadata(self):
for skill_name in SKILLS:
with self.subTest(skill=skill_name):
metadata = SKILLS_ROOT / skill_name / "agents" / "openai.yaml"
self.assertTrue(metadata.exists(), f"{skill_name} openai.yaml is missing")
text = metadata.read_text(encoding="utf-8")
self.assertIn("interface:", text)
self.assertIn("display_name:", text)
self.assertIn("short_description:", text)
self.assertIn("default_prompt:", text)
self.assertIn(f"${skill_name}", text)
if __name__ == "__main__":
unittest.main()