feat: enforce Korean prose gate in pipeline fixtures

This commit is contained in:
DongHyeonka
2026-07-29 18:36:17 +09:00
parent d333e265b9
commit 67c9867e58
4 changed files with 143 additions and 43 deletions
+83 -2
View File
@@ -5,11 +5,43 @@ import json
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch
from claridoc.models import PipelineConfig
from claridoc.models import Brief, PipelineConfig
from claridoc.pipeline import _mock_provider_warning, run_pipeline
from claridoc.providers.base import ProviderRequest, ProviderResponse
from claridoc.providers.mock import MockProvider
from claridoc.templates import mock_pipeline_config
from tests.helpers import make_brief, make_sources
from tests.helpers import brief_dict, make_brief, make_sources
def _korean_experience_brief() -> Brief:
data = brief_dict()
data.update(
{
"title": "기술적 선택을 경험과 근거로 설명하기",
"language": "ko-KR",
"reader_goal": "기술적 선택의 이유와 검증 방법을 이해한다",
"core_message": "선택의 배경과 대안, 비용, 검증을 경험의 흐름으로 연결해야 합니다.",
"scope": ["하나의 기술적 선택"],
"non_scope": ["근거가 없는 일반화"],
"required_topics": ["문제", "대안", "선택 이유", "검증"],
}
)
data["constraints"]["style_profile"] = "auto"
return Brief.from_dict(data)
class _PlainEndingWriter(MockProvider):
def generate(self, request: ProviderRequest) -> ProviderResponse:
response = super().generate(request)
if request.stage == "draft":
response.text = response.text.replace(
"처음에는 작은 구현 선택 하나만 고치면 된다고 생각했습니다.",
"처음에는 작은 구현 선택 하나만 고치면 된다고 생각했다.",
1,
)
return response
class PipelineTests(unittest.TestCase):
@@ -69,6 +101,55 @@ class PipelineTests(unittest.TestCase):
self.assertEqual(len(result.rounds), 2)
self.assertTrue((output / "rounds" / "round-01" / "revision.raw.txt").is_file())
def test_korean_mock_run_records_reader_prose_contract(self) -> None:
with tempfile.TemporaryDirectory() as temp:
output = Path(temp) / "run"
result = run_pipeline(
_korean_experience_brief(),
make_sources(),
PipelineConfig.from_dict(mock_pipeline_config()),
output,
)
self.assertTrue(result.passed)
self.assertEqual(
result.rounds[-1].lint_report.metrics["style_contract"],
"korean_first_person_experience_v1",
)
report_text = result.report_path.read_text(encoding="utf-8")
self.assertIn("Reader-prose contract", report_text)
self.assertIn("korean_first_person_experience_v1", report_text)
def test_style_blocker_cannot_be_hidden_by_permissive_error_limit(self) -> None:
with tempfile.TemporaryDirectory() as temp:
output = Path(temp) / "run"
config_data = mock_pipeline_config()
config_data["quality_gate"].update(
{
"minimum_score": 0,
"max_errors": 99,
"max_revisions": 0,
}
)
with patch(
"claridoc.pipeline.create_provider",
side_effect=lambda spec: _PlainEndingWriter(spec),
):
result = run_pipeline(
_korean_experience_brief(),
make_sources(),
PipelineConfig.from_dict(config_data),
output,
)
self.assertFalse(result.passed)
self.assertGreater(result.rounds[-1].blocker_count, 0)
self.assertIn(
"STYLE002",
{issue.code for issue in result.rounds[-1].lint_report.issues},
)
def test_reviewer_role_cannot_escape_artifact_directory(self) -> None:
with tempfile.TemporaryDirectory() as temp:
root = Path(temp)