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
+15 -15
View File
@@ -109,36 +109,36 @@ def _korean_body(brief: Brief, intent: str) -> list[str]:
technical_blog: dict[str, list[str]] = {
"problem_scene": [
f"작은 구현 선택처럼 보였던 문제가 실제 흐름을 따라가 여러 경계에 걸쳐 있다. {topics} 가운데 하나만 고치면 다른 지점에서 부하, 중복, 조립 비용, 복구 비용이 커질 수 있었다. 이 글 다음 질문을 다다. **{brief.reader_goal}**",
f"핵심 판단은 명확하다. **{brief.core_message}** 여기서는 {scope}에 집중하며, {non_scope}까지 보편적인 결론으로 확대하지 않다.",
f"처음에는 작은 구현 선택 하나만 고치면 된다고 생각했습니다. 그런데 저는 실제 흐름을 따라가면서 문제가 여러 경계에 걸쳐 있다는 점을 확인했습니다. {topics} 가운데 하나만 바꾸어도 다른 지점에서 부하, 중복, 조립 비용, 복구 비용이 커질 수 있었습니다. 이 글에서는 다음 질문을 다룹니다. **{brief.reader_goal}**",
f"제가 이 과정에서 내린 핵심 판단은 **{brief.core_message}**”입니다. 여기서는 {scope}에 집중하며, {non_scope}까지 보편적인 결론으로 확대하지 않습니다.",
],
"constraints": [
f"{topics} 입력과 상태, 실패와 복구를 통해 서로 연결다. 한 부분의 편의를 높이면 다른 경계로 부하나 중복, 복구 비용이 이동할 수 있어서 각 요소를 독립적으로 바꾸기 어려웠다.",
"근거의 역할도 서로 달랐다. 현재 구현, 결정 기록, 공식 동작, 다른 회사의 사례는 같은 단어를 사용하더라도 같은 사실을 증명하지 않다. 프로젝트의 선택 이유는 그 이유를 직접 기록한 자료가 있을 때만 설명할 수 있다.",
f"저는 {topics} 입력과 상태, 실패와 복구를 통해 서로 연결되는 모습을 확인했습니다. 한 부분의 편의를 높이면 다른 경계로 부하나 중복, 복구 비용이 이동할 수 있어서 각 요소를 독립적으로 바꾸기 어려웠습니다.",
"근거의 역할도 서로 달랐습니다. 현재 구현, 결정 기록, 공식 동작, 다른 회사의 사례는 같은 단어를 사용하더라도 같은 사실을 증명하지 않습니다. 프로젝트의 선택 이유는 그 이유를 직접 기록한 자료가 있을 때만 설명할 수 있습니다.",
],
"options": [
"검토 선택지는 최소 두 가지다. 첫째, 현재 방식을 유지하고 문제가 드러난 지점만 보완다. 변경 범위는 작지만 상호작용을 놓치기 쉽다. 둘째, 관련 요소를 하나의 정책 경계로 묶는다. 초기 설계와 검증 비용은 늘지만 판단 기준과 실패 범위를 함께 관리할 수 있다.",
"비교 기준은 구현량이 아니라 실패 시 부하가 어디로 이동하는지, 중복 부작용을 막을 수 있는지, 검증 결과를 관측할 수 있는지, 잘못됐을 때 되돌릴 수 있는지다. 실패한 시도나 제외한 대안도 같은 기준으로 설명해야 독자가 선택을 재현할 수 있다.",
"제가 검토 선택지는 최소 두 가지였습니다. 첫째, 현재 방식을 유지하고 문제가 드러난 지점만 보완하는 방법입니다. 변경 범위는 작지만 상호작용을 놓치기 쉽습니다. 둘째, 관련 요소를 하나의 정책 경계로 묶는 방법입니다. 초기 설계와 검증 비용은 늘지만 판단 기준과 실패 범위를 함께 관리할 수 있습니다.",
"비교 기준은 구현량이 아니라 실패 시 부하가 어디로 이동하는지, 중복 부작용을 막을 수 있는지, 검증 결과를 관측할 수 있는지, 잘못됐을 때 되돌릴 수 있는지입니다. 실패한 시도나 제외한 대안도 같은 기준으로 설명해야 독자가 선택을 재현할 수 있습니다.",
],
"decision_rationale": [
f"이 글이 선택한 방향은 **{brief.core_message}** 여러 설정을 함께 다루기로 한 이유는 각각의 값이 서로의 안전 조건을 바꾸기 때문다. 한 항목만 최적화하면 전체 요청 경로나 모듈 경계에서 예상하지 못한 비용이 발생다.",
"대안은 설정을 완전히 분리하거나 편의를 위해 관련 경계를 넓게 허용하는 방식다. 전자는 상호작용을 운영자에게 떠넘기고, 후자는 정책이 코어 안으로 번질 위험을 키다. 따라서 초기 설계와 테스트 비용을 수용하되, 허용 범위와 금지 범위를 자동 검사하는 가드레일을 함께 다.",
f"그래서 저는 “**{brief.core_message}**”라는 방향을 선택했습니다. 여러 설정을 함께 다루기로 한 이유는 각각의 값이 서로의 안전 조건을 바꾸기 때문입니다. 한 항목만 최적화하면 전체 요청 경로나 모듈 경계에서 예상하지 못한 비용이 발생합니다.",
"대안은 설정을 완전히 분리하거나 편의를 위해 관련 경계를 넓게 허용하는 방식입니다. 전자는 상호작용을 운영자에게 떠넘기고, 후자는 정책이 코어 안으로 번질 위험을 키웁니다. 따라서 초기 설계와 테스트 비용을 수용하되, 허용 범위와 금지 범위를 자동 검사하는 가드레일을 함께 둡니다.",
],
"mechanism": [
"결정은 입력에서 관측까지 끊기지 않는 흐름으로 반영다. 요청이나 변경이 들어오면 사전 조건을 확인하고, 같은 기준에서 실행 경로와 상태 변경 범위를 정다. 실행 뒤에는 결과와 실패 신호를 기록해 성공, 중단, 복구 중 하나를 결정다.",
"결정은 입력에서 관측까지 끊기지 않는 흐름으로 반영합니다. 요청이나 변경이 들어오면 사전 조건을 확인하고, 같은 기준에서 실행 경로와 상태 변경 범위를 정합니다. 실행 뒤에는 결과와 실패 신호를 기록해 성공, 중단, 복구 중 하나를 결정합니다.",
"```text\n입력과 현재 상태\n → 안전 조건 확인\n → 한정된 실행 경로 선택\n → 상태 변경 또는 호출\n → 로그·지표·테스트 결과 관측\n → 확정 / 중단 / 복구\n```",
"이 흐름의 불변조건은 실패한 작업이 성공으로 기록되지 않고, 같은 입력을 다시 처리했을 때 허용하지 않은 부작용이 늘어나지 않는 것이다. 실제 글에서는 일반 명칭 대신 프로젝트의 모듈, 인터페이스, 테스트 이름을 사용다.",
"이 흐름의 불변조건은 실패한 작업이 성공으로 기록되지 않고, 같은 입력을 다시 처리했을 때 허용하지 않은 부작용이 늘어나지 않는다는 점입니다. 실제 글에서는 일반 명칭 대신 프로젝트의 모듈, 인터페이스, 테스트 이름을 사용합니다.",
],
"evidence_verification": [
"검증은 주장마다 관측 가능한 증거를 붙이는 방식으로 설계한다. 구조적 경계는 빌드 규칙이나 정적 분석으로, 런타임 동작은 단위·통합 테스트와 로그·지표로, 실패 복구는 의도된 오류 주입과 롤백 확인으로 검증다.",
f"성공 기준은 독자가 다음 목표를 반복 가능한 결과로 확인할 수 있는지다. **{brief.reader_goal}** 반대로 운영 배포, 장기 부하, 특정 장애 조합을 검증하지 않았다면 그 범위는 명시적으로 남겨야 다. 로컬 테스트 통과를 운영 검증으로 확대해 쓰지 않다.",
"저는 주장마다 관측 가능한 증거를 붙이는 방식으로 검증을 설계했습니다. 구조적 경계는 빌드 규칙이나 정적 분석으로, 런타임 동작은 단위·통합 테스트와 로그·지표로, 실패 복구는 의도된 오류 주입과 롤백 확인으로 검증합니다.",
f"성공 기준은 독자가 다음 목표를 반복 가능한 결과로 확인할 수 있는지입니다. **{brief.reader_goal}** 반대로 운영 배포, 장기 부하, 특정 장애 조합을 검증하지 않았다면 그 범위는 명시적으로 남겨야 합니다. 로컬 테스트 통과를 운영 검증으로 확대해 쓰지 않습니다.",
],
"tradeoffs": [
"얻는 것은 판단 기준의 일관성, 실패 범위의 가시성, 자동 검증 가능성다. 잃는 것은 초기 설계 시간과 정책을 유지하는 비용다. 작은 실험이나 폐기 예정 코드에서는 이 구조가 과할 수 있지만, 반복 사용되거나 장애 시 비용이 큰 경로에서는 그 비용이 가드레일로 작동다.",
"이 선택은 보편 법칙이 아니다. 성공 기준을 관측할 수 없거나 관련 요소의 소유권이 분리돼 있다면 더 작은 경계가 나을 수 있다. 남은 위험은 자동 검사가 잡지 못하는 런타임 우회와 문서·구현 간 시차이며, 코드 리뷰와 주기적인 근거 재검증으로 보완다.",
"제가 얻은 것은 판단 기준의 일관성, 실패 범위의 가시성, 자동 검증 가능성입니다. 대신 초기 설계 시간과 정책을 유지하는 비용을 수용했습니다. 작은 실험이나 폐기 예정 코드에서는 이 구조가 과할 수 있지만, 반복 사용되거나 장애 시 비용이 큰 경로에서는 그 비용이 가드레일로 작동합니다.",
"이 선택은 보편 법칙이 아니다. 성공 기준을 관측할 수 없거나 관련 요소의 소유권이 분리돼 있다면 더 작은 경계가 나을 수 있습니다. 남은 위험은 자동 검사가 잡지 못하는 런타임 우회와 문서·구현 간 시차이며, 코드 리뷰와 주기적인 근거 재검증으로 보완합니다.",
],
"conclusion": [
f"결국 지키려던 것은 특정 도구가 아니라 판단 가능한 경계다. **{brief.core_message}** 자신의 환경에서는 ‘왜 이 선택이 필요한가’, ‘대안보다 어떤 비용을 덜어 주는가’, ‘그 대가를 어떤 테스트가 제한하는가’를 연속해서 답할 수 있어야 다.",
f"결국 제가 지키려던 것은 특정 도구가 아니라 판단 가능한 경계였습니다. 핵심은 “**{brief.core_message}**”라는 점입니다. 자신의 환경에서는 ‘왜 이 선택이 필요한가’, ‘대안보다 어떤 비용을 덜어 주는가’, ‘그 대가를 어떤 테스트가 제한하는가’를 연속해서 답할 수 있어야 합니다.",
],
}
if intent in technical_blog:
+19
View File
@@ -69,6 +69,25 @@ def render_run_report(
f"| {issue.severity.value} | `{issue.code}` | {location} | {message} |"
)
metrics = final.lint_report.metrics
style_contract = str(metrics.get("style_contract", "none"))
if style_contract != "none":
coverage = float(metrics.get("experience_section_coverage", 0.0))
lines.extend([
"",
"## Reader-prose contract",
"",
f"- Contract: `{style_contract}`",
f"- Plain-form endings found: {int(metrics.get('plain_form_ending_count', 0))}",
f"- First-person markers: {int(metrics.get('first_person_marker_count', 0))}",
f"- Opening establishes first-person experience: {'yes' if metrics.get('opening_has_first_person') else 'no'}",
(
"- Substantive sections with first-person experience: "
f"{int(metrics.get('marked_experience_section_count', 0))}/"
f"{int(metrics.get('experience_section_count', 0))} ({coverage:.0%})"
),
])
lines.extend(["", "## Final independent reviews", ""])
for review in final.reviews:
lines.extend([