modify documents
This commit is contained in:
@@ -3,6 +3,10 @@ from pathlib import Path
|
||||
import re
|
||||
import tomllib
|
||||
|
||||
import pytest
|
||||
|
||||
from test_execute import step_runtime
|
||||
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[1]
|
||||
AGENT_DIR = ROOT / ".codex" / "agents"
|
||||
@@ -24,14 +28,14 @@ EXPECTED_AGENTS = {
|
||||
|
||||
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",
|
||||
"fem-implementation-tdd",
|
||||
"fem-formulation-spec",
|
||||
"fem-io-contract",
|
||||
"fem-numerical-review",
|
||||
"fem-physics-review",
|
||||
"fem-release-readiness",
|
||||
"fem-requirements-baseline",
|
||||
"fem-research-evidence",
|
||||
}
|
||||
|
||||
LEGACY_OUTPUT_DIRS = {
|
||||
@@ -94,22 +98,27 @@ AGENT_STAGE_GUIDANCE_FILES = (
|
||||
)
|
||||
|
||||
EXPECTED_AGENT_STAGE_MAPPING = (
|
||||
("1. 요구조건", "requirement-agent", "requirements.md"),
|
||||
("2. 연구", "research-agent", "research.md"),
|
||||
("3. 정식화", "formulation-agent", "formulation.md"),
|
||||
("requirements", "requirement-agent", ("requirements.md",)),
|
||||
("research", "research-agent", ("research.md",)),
|
||||
("formulation", "formulation-agent", ("formulation.md",)),
|
||||
(
|
||||
"4. 수치 검토 + reference model 계약",
|
||||
"numerical-review",
|
||||
"numerical-review-agent",
|
||||
"numerical-review.md, reference-model.md",
|
||||
("numerical-review.md", "reference-model.md"),
|
||||
),
|
||||
("5. I/O 정의", "io-definition-agent", "io.md"),
|
||||
("io-definition", "io-definition-agent", ("io.md",)),
|
||||
(
|
||||
"6. 구현 계획 + C++ 구현 + build/test + reference comparison",
|
||||
"implementation-planning-agent, implementation-agent",
|
||||
"implementation-plan.md, implementation-report.md, build-test.md, reference-comparison.md",
|
||||
"implementation-planning",
|
||||
"implementation-planning-agent",
|
||||
("implementation-plan.md",),
|
||||
),
|
||||
("7. 물리 검토", "physics-evaluation-agent", "physics-evaluation.md"),
|
||||
("8. 배포 준비", "release-agent", "release.md"),
|
||||
(
|
||||
"implementation",
|
||||
"implementation-agent",
|
||||
("implementation-report.md", "build-test.md", "reference-comparison.md"),
|
||||
),
|
||||
("physics-evaluation", "physics-evaluation-agent", ("physics-evaluation.md",)),
|
||||
("release-readiness", "release-agent", ("release.md",)),
|
||||
)
|
||||
|
||||
CANONICAL_FEATURE_DIRS = (
|
||||
@@ -173,12 +182,24 @@ def live_contract_text() -> str:
|
||||
return "\n".join(read(path) for path in paths)
|
||||
|
||||
|
||||
def markdown_stage_mapping(path: Path) -> tuple[tuple[str, str, str], ...]:
|
||||
def markdown_stage_mapping(path: Path) -> tuple[tuple[str, str, tuple[str, ...]], ...]:
|
||||
heading = (
|
||||
"## Agent/Skill Workflow"
|
||||
if path.name == "AGENTS.md"
|
||||
else "## 4. 단계와 소유권"
|
||||
)
|
||||
section = read(path).split(heading, maxsplit=1)[1].split("\n## ", maxsplit=1)[0]
|
||||
rows = []
|
||||
for line in read(path).splitlines():
|
||||
for line in section.splitlines():
|
||||
if not line.lstrip().startswith("|"):
|
||||
continue
|
||||
columns = [column.strip().replace("`", "") for column in line.strip().split("|")[1:-1]]
|
||||
if len(columns) >= 4 and re.fullmatch(r"[1-8]\. .+", columns[0]):
|
||||
rows.append((columns[0], columns[1], columns[3]))
|
||||
if len(columns) < 3:
|
||||
continue
|
||||
stage = re.sub(r"^\d+\.\s+", "", columns[0])
|
||||
if re.fullmatch(r"[a-z]+(?:-[a-z]+)*", stage):
|
||||
reports = tuple(re.findall(r"\b[\w-]+\.md\b", columns[-1]))
|
||||
rows.append((stage, columns[1], reports))
|
||||
return tuple(rows)
|
||||
|
||||
|
||||
@@ -254,84 +275,68 @@ def test_existing_feature_artifacts_are_bundled():
|
||||
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
|
||||
# Preserve historical artifacts while allowing Feature management and linked details.
|
||||
assert expected_files <= actual_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
|
||||
text = read(SKILL_DIR / "fem-numerical-review" / "SKILL.md")
|
||||
outputs = set(re.findall(r"docs/<feature-id>/([\w-]+\.md)", text))
|
||||
assert {"numerical-review.md", "reference-model.md"} <= outputs
|
||||
|
||||
|
||||
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 (
|
||||
text = read(SKILL_DIR / "fem-implementation-tdd" / "SKILL.md")
|
||||
outputs = set(re.findall(r"docs/<feature-id>/([\w-]+\.md)", text))
|
||||
assert {
|
||||
"implementation-plan.md",
|
||||
"implementation-report.md",
|
||||
"build-test.md",
|
||||
"reference-comparison.md",
|
||||
"artifact check -> compare -> classify -> report",
|
||||
"pass-for-physics-evaluation",
|
||||
):
|
||||
assert marker in text
|
||||
"corrections.md",
|
||||
} <= outputs
|
||||
|
||||
|
||||
def test_implementation_steps_keep_final_comparison_in_a_final_gate():
|
||||
paths = (
|
||||
AGENT_DIR / "implementation-agent.toml",
|
||||
SKILL_DIR / "fesa-cpp-msvc-tdd" / "SKILL.md",
|
||||
)
|
||||
for path in paths:
|
||||
text = read(path).lower()
|
||||
assert "final implementation-owned verification step/gate" in text, path
|
||||
assert "non-final step" in text, path
|
||||
assert "artifact check -> compare -> classify -> report" in text, path
|
||||
assert "results.h5" in text, path
|
||||
assert "reference-comparison.md" in text, path
|
||||
def test_implementation_verification_reference_is_resolvable():
|
||||
skill = SKILL_DIR / "fem-implementation-tdd" / "SKILL.md"
|
||||
links = re.findall(r"\[[^\]]+\]\(([^)]+)\)", read(skill))
|
||||
assert "references/toolchain-and-verification.md" in links
|
||||
for link in links:
|
||||
assert (skill.parent / link).is_file(), link
|
||||
|
||||
|
||||
def test_p_agent_001_implementation_agent_reads_coding_style_before_cpp_steps():
|
||||
def test_p_agent_001_implementation_guidance_resolves_coding_style_contract():
|
||||
parsed = tomllib.loads(read(AGENT_DIR / "implementation-agent.toml"))
|
||||
instructions = parsed["developer_instructions"]
|
||||
section_match = re.search(
|
||||
r"Mandatory global input:\n(?P<section>.*?)(?=\n[A-Z][^\n]+:\n)",
|
||||
instructions,
|
||||
re.DOTALL,
|
||||
)
|
||||
|
||||
assert section_match is not None
|
||||
section = section_match.group("section")
|
||||
assert "Before every C++ implementation Step" in section
|
||||
assert "read docs/CODINGSTYLE.md" in section
|
||||
assert "apply it to production and test code" in section
|
||||
assert "Doxygen coverage applies only to production code" in section
|
||||
assert "AGENTS.md" in instructions
|
||||
project_links = re.findall(r"\[[^\]]+\]\(([^)]+)\)", read(ROOT / "AGENTS.md"))
|
||||
assert "docs/CODINGSTYLE.md" in project_links
|
||||
assert (ROOT / "docs/CODINGSTYLE.md").is_file()
|
||||
|
||||
|
||||
def test_agent_hierarchy_is_explicit():
|
||||
coordinator = read(AGENT_DIR / "coordinator-agent.toml").lower()
|
||||
assert "main agent" in coordinator
|
||||
assert "docs/<feature-id>/coordination.md" in coordinator
|
||||
assert "worklist" in coordinator
|
||||
assert "sub-agent dispatch" in coordinator
|
||||
|
||||
def test_agent_profiles_reference_shared_authority_and_feature_context():
|
||||
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
|
||||
instructions = tomllib.loads(read(path))["developer_instructions"]
|
||||
for document in (
|
||||
"AGENTS.md",
|
||||
"docs/SOLVER_AGENT_DESIGN.md",
|
||||
"docs/FEATURE_DOCUMENTATION.md",
|
||||
"FEATURED_AGENTS.md",
|
||||
"PRD.md",
|
||||
"ADR.md",
|
||||
):
|
||||
assert document in instructions, (path.name, document)
|
||||
for skill_name in re.findall(r"\$([a-z0-9-]+)", instructions):
|
||||
candidates = (
|
||||
SKILL_DIR / skill_name / "SKILL.md",
|
||||
ROOT / ".agents/skills" / skill_name / "SKILL.md",
|
||||
)
|
||||
assert any(candidate.is_file() for candidate in candidates), (path, skill_name)
|
||||
|
||||
|
||||
def test_live_contracts_have_no_retired_names_or_paths():
|
||||
@@ -341,10 +346,9 @@ def test_live_contracts_have_no_retired_names_or_paths():
|
||||
assert token not in text, token
|
||||
|
||||
|
||||
def test_live_guidance_declares_exact_ordered_eight_stage_workflow():
|
||||
def test_live_guidance_declares_consistent_ordered_nine_stage_workflow():
|
||||
for path in LIVE_CONTRACT_FILES:
|
||||
text = read(path)
|
||||
assert "8단계" in text, path
|
||||
assert "docs/<feature-id>/" in text, path
|
||||
|
||||
for path in AGENT_STAGE_GUIDANCE_FILES:
|
||||
@@ -389,7 +393,9 @@ def test_agent_toml_and_limited_skill_yaml_schemas_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"
|
||||
assert parsed["model_reasoning_effort"] == "xhigh"
|
||||
assert parsed["sandbox_mode"] == "workspace-write"
|
||||
assert parsed["developer_instructions"].strip(), path
|
||||
|
||||
for skill_name in EXPECTED_SKILLS:
|
||||
skill_dir = SKILL_DIR / skill_name
|
||||
@@ -397,3 +403,20 @@ def test_agent_toml_and_limited_skill_yaml_schemas_are_parseable():
|
||||
interface = parse_skill_interface(skill_dir / "agents" / "openai.yaml")
|
||||
assert frontmatter["name"] == skill_name
|
||||
assert f"${skill_name}" in interface["default_prompt"]
|
||||
|
||||
|
||||
@pytest.mark.parametrize("document", [".agents/skills/harness/SKILL.md", "docs/HARNESS_WORKFLOW.md"])
|
||||
def test_documented_phase_example_passes_executor_preflight(step_runtime, document):
|
||||
"""Exercise the documented schema with its real consumer, not prose matching."""
|
||||
from scripts import execute
|
||||
|
||||
examples = [json.loads(block) for block in re.findall(r"```json\s*\n(.*?)\n```", read(ROOT / document), re.DOTALL)]
|
||||
phase = next(example for example in examples if "steps" in example)
|
||||
phase["project"] = "Harness Tests"
|
||||
phase["phase"] = "runtime"
|
||||
phase["steps"][0]["name"] = "runtime"
|
||||
runtime = step_runtime
|
||||
runtime.executor._write_json(runtime.index_file, phase)
|
||||
executor = execute.StepExecutor("runtime")
|
||||
executor._preflight_repository()
|
||||
assert runtime.calls == []
|
||||
|
||||
Reference in New Issue
Block a user