chore: 문서를 작성할 때 한국어의 표현 작성 스킬 추가 및 1인칭 관점의 글 작성 검증 테스트 추가

This commit is contained in:
DongHyeonka
2026-07-29 16:48:03 +09:00
parent c39406bbdd
commit 41501b5d06
520 changed files with 95494 additions and 2231 deletions
+18
View File
@@ -0,0 +1,18 @@
{
"score": 100.0,
"word_count": 993,
"issues": [],
"metrics": {
"heading_count": 9,
"h2_count": 8,
"source_count": 10,
"cited_source_count": 0,
"citation_style": "hidden",
"decision_section_count": 3,
"numbered_steps": false,
"formulaic_ordinal_opening_count": 0,
"has_verification": true,
"has_tradeoffs": true,
"severity_counts": {}
}
}
+216
View File
@@ -0,0 +1,216 @@
{
"title": "`application-core`는 왜 Spring DI만 허용했을까",
"document_type": "technical_blog",
"sections": [
{
"id": "01-problem-scene",
"intent": "problem_scene",
"title": "코드보다 먼저 드러난 문제",
"reader_question": "독자가 공감할 수 있는 구체적인 상황에서 어떤 문제가 드러났는가?",
"purpose": "추상적인 글쓰기 계약이 아니라 실제 장면, 증상, 비용으로 시작한다.",
"must_include": [
"구체적인 상황",
"문제가 만든 비용",
"이 글에서 풀 질문",
"`application-core`에서 Spring DI는 허용하면서 transaction, web, persistence 의존은 금지한 이유와 트레이드오프를 설명할 수 있다",
"framework-free라는 구호보다 의존 목적을 좁히고 자동 검증하는 편이 이 프로젝트의 문제에 맞았다. bean 등록을 위한 Spring DI는 허용하되 transaction, transport, persistence 정책은 application 경계 밖에 남겼다.",
"ca-tmpl의 `application-core` 의존성 결정",
"Spring DI 허용 이유",
"Gradle과 ArchUnit을 통한 경계 검증",
"모든 Clean Architecture 프로젝트의 보편 규칙",
"SLF4J 사용 이유",
"운영 환경 성능 검증"
],
"evidence_ids": [
"Lbe6cb7d8e8",
"Lf440ea562d",
"Ld4394f2f14",
"L8db0ff5b86"
],
"decision_requirements": [],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
},
{
"id": "02-constraints",
"intent": "constraints",
"title": "문제를 어렵게 만든 제약",
"reader_question": "단순한 해법을 막은 프로젝트 제약은 무엇이었는가?",
"purpose": "현재 구조, 독자에게 필요한 배경, 확인된 사실과 미확인 영역을 분리한다.",
"must_include": [
"현재 구조",
"제약",
"확인된 사실과 사실 경계"
],
"evidence_ids": [
"Lbe6cb7d8e8",
"Lf440ea562d",
"Ld4394f2f14",
"L8db0ff5b86"
],
"decision_requirements": [],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
},
{
"id": "03-options",
"intent": "options",
"title": "검토한 선택지와 막힌 지점",
"reader_question": "어떤 대안들을 검토했고 각각 어디에서 비용이 생겼는가?",
"purpose": "최소 두 선택지를 같은 기준으로 비교하고, 실패한 시도나 제외 이유를 숨기지 않는다.",
"must_include": [
"대안",
"비교 기준",
"제외 이유 또는 실패한 시도",
"수동 bean 등록의 조립 코드 비용",
"Spring DI 허용 범위",
"`spring-tx`, Spring Web, JPA 금지",
"`TransactionPort`",
"Gradle dependency matrix",
"ArchUnit rule과 정적 분석 한계"
],
"evidence_ids": [
"Lbe6cb7d8e8",
"Lf440ea562d",
"Ld4394f2f14",
"L8db0ff5b86"
],
"decision_requirements": [
"상황·제약",
"선택",
"선택 이유",
"검토한 대안",
"수용한 비용",
"보완 가드레일"
],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
},
{
"id": "04-decision-rationale",
"intent": "decision_rationale",
"title": "선택의 이유와 지킨 경계",
"reader_question": "왜 이 선택을 했으며 무엇을 일부러 포기하거나 금지했는가?",
"purpose": "선택을 제약, 이유, 대안, 수용 비용, 보완 가드레일까지 한 묶음으로 설명한다.",
"must_include": [
"선택",
"왜 선택했는가",
"대안",
"수용한 비용",
"가드레일"
],
"evidence_ids": [
"Lbe6cb7d8e8",
"Lf440ea562d",
"Ld4394f2f14",
"L8db0ff5b86"
],
"decision_requirements": [
"상황·제약",
"선택",
"선택 이유",
"검토한 대안",
"수용한 비용",
"보완 가드레일"
],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
},
{
"id": "05-mechanism",
"intent": "mechanism",
"title": "선택이 코드와 흐름에 반영되는 방식",
"reader_question": "결정이 모듈, 인터페이스, 제어 흐름에 어떻게 반영되는가?",
"purpose": "실제 이름과 경계를 사용해 인과 흐름을 설명하고, 하나의 구체적인 예시를 끝까지 따라간다.",
"must_include": [
"실제 구성요소",
"제어 또는 데이터 흐름",
"구체적인 예시",
"불변조건",
"수동 bean 등록의 조립 코드 비용",
"Spring DI 허용 범위",
"`spring-tx`, Spring Web, JPA 금지",
"`TransactionPort`",
"Gradle dependency matrix",
"ArchUnit rule과 정적 분석 한계"
],
"evidence_ids": [
"Lbe6cb7d8e8",
"Lf440ea562d",
"Ld4394f2f14",
"L8db0ff5b86"
],
"decision_requirements": [],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
},
{
"id": "06-evidence-verification",
"intent": "evidence_verification",
"title": "결정이 지켜지는지 확인하는 방법",
"reader_question": "설명한 경계와 결과가 실제로 유지되는지 어떻게 확인하는가?",
"purpose": "테스트, 빌드 규칙, 관측값을 주장과 연결하고 검증 범위를 과장하지 않는다.",
"must_include": [
"검증 절차",
"성공 기준",
"검증하지 못한 범위",
"수동 bean 등록의 조립 코드 비용",
"Spring DI 허용 범위",
"`spring-tx`, Spring Web, JPA 금지",
"`TransactionPort`",
"Gradle dependency matrix",
"ArchUnit rule과 정적 분석 한계"
],
"evidence_ids": [
"Lbe6cb7d8e8",
"Lf440ea562d",
"L8db0ff5b86",
"Ld4394f2f14"
],
"decision_requirements": [],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
},
{
"id": "07-tradeoffs",
"intent": "tradeoffs",
"title": "얻은 것, 잃은 것, 적용하지 않을 때",
"reader_question": "이 선택의 비용과 한계는 무엇이며 언제 다른 선택이 나은가?",
"purpose": "프로젝트 지역 결정을 보편 법칙처럼 쓰지 않고, 적용 조건과 남은 위험을 제시한다.",
"must_include": [
"얻은 것",
"잃은 것",
"적용 조건",
"남은 위험"
],
"evidence_ids": [
"Lbe6cb7d8e8",
"Lf440ea562d",
"Ld4394f2f14",
"L54271e62b5"
],
"decision_requirements": [
"상황·제약",
"선택",
"선택 이유",
"검토한 대안",
"수용한 비용",
"보완 가드레일"
],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
},
{
"id": "08-conclusion",
"intent": "conclusion",
"title": "결국 지키려던 것은 무엇이었나",
"reader_question": "세부 기술을 걷어냈을 때 남는 판단은 무엇인가?",
"purpose": "앞 내용을 반복하지 않고, 문제와 선택을 연결하는 한 문장 판단으로 닫는다.",
"must_include": [
"압축된 판단",
"독자가 자신의 환경에서 확인할 질문"
],
"evidence_ids": [],
"decision_requirements": [],
"transition_to_next": "이 답을 바탕으로 다음 독자 질문으로 자연스럽게 연결한다."
}
],
"planning_notes": [
"Each section answers one reader question.",
"The order moves from reader goal to context, model, mechanism, evidence, limits, and action as applicable.",
"Required section intents are a contract; a model may refine wording but must not remove or reorder them."
]
}
+208
View File
@@ -0,0 +1,208 @@
{
"sources": [
{
"id": "Lbe6cb7d8e8",
"title": "branch / feature-application-port-usecase-contract — 결정 사항",
"url": "repo:///raw/branch-notes/feature-application-port-usecase-contract.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 결정 사항\n\n- D3: transaction boundary는 application use case 책임이지만 Spring `@Transactional` 직접 import는 금지하고 `TransactionPort` abstraction을 기본값으로 둔다.\n- D11: `TransactionPort`는 `Supplier<T>`와 `Runnable` 시그니처를 유지한다.\n- D13: `application-core`는 `org.springframework.stereotype.Service`와 `Component` 사용을 DI 등록 목적으로 허용한다. `spring-context`와 `spring-beans` 의존은 유지한다.\n- D13 이유: Spring DI까지 제거하면 use case bean마다 `@Configuration`에서 수동 등록해야 하므로 조립 코드가 급격히 늘어난다.\n- D13 경계: `spring-tx`, Spring Web, JPA annotation은 계속 금지한다. 편의 때문에 application layer의 책임을 transaction, transport, persistence까지 넓히지 않는다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "branch-note",
"status": "verified",
"path": "raw/branch-notes/feature-application-port-usecase-contract.md",
"heading": "결정 사항",
"line_start": 14,
"line_end": 21,
"claim_ids": [],
"decision_ids": [
"D11",
"D13",
"D3"
],
"priority": 26.36788
},
{
"id": "Lf440ea562d",
"title": "branch / feature-application-port-usecase-contract — 선택의 비용",
"url": "repo:///raw/branch-notes/feature-application-port-usecase-contract.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 선택의 비용\n\n`application-core`가 Spring core DI 의존을 갖는다는 비용은 수용한다. 대신 허용 목적을 bean 등록으로 좁히고, transaction, transport, persistence 의존은 빌드 규칙과 ArchUnit으로 차단한다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "branch-note",
"status": "verified",
"path": "raw/branch-notes/feature-application-port-usecase-contract.md",
"heading": "선택의 비용",
"line_start": 26,
"line_end": 28,
"claim_ids": [],
"decision_ids": [],
"priority": 24.851643
},
{
"id": "L1259369d94",
"title": "branch / feature-log-management-contract — 근거 경계",
"url": "repo:///raw/branch-notes/feature-log-management-contract.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 근거 경계\n\n`domain layer logger 금지`는 외부 공식 문서가 직접 증명한 보편 원칙이 아니라 ca-tmpl 내부 정책이다. 외부 공개 글에서는 프로젝트 지역 결정으로만 표현한다.\n\n이 문서는 `application-core`가 SLF4J를 사용하는 이유를 설명하지 않는다. 단어가 등장하거나 로거가 존재한다는 사실만으로 선택 이유를 만들어내지 않는다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "branch-note",
"status": "raw",
"path": "raw/branch-notes/feature-log-management-contract.md",
"heading": "근거 경계",
"line_start": 15,
"line_end": 19,
"claim_ids": [],
"decision_ids": [],
"priority": 16.777009
},
{
"id": "L6d3ebbb7a0",
"title": "branch / feature-application-port-usecase-contract — 구현 및 검증",
"url": "repo:///raw/branch-notes/feature-application-port-usecase-contract.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 구현 및 검증\n\n`application-core`의 `spring-tx` 의존성을 제거했다. `@Transactional`이 compile classpath에 없도록 했다. `application_does_not_use_spring_transactional_annotation`과 `application_does_not_depend_on_application_context` ArchUnit rule을 두고 negative fixture로 위반 검출을 확인했다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "branch-note",
"status": "verified",
"path": "raw/branch-notes/feature-application-port-usecase-contract.md",
"heading": "구현 및 검증",
"line_start": 22,
"line_end": 25,
"claim_ids": [],
"decision_ids": [],
"priority": 16.747122
},
{
"id": "L8db0ff5b86",
"title": "ca-tmpl - Clean Architecture 패키지 레이아웃 결정 — 검증 범위",
"url": "repo:///wiki/projects/ca-tmpl/clean-architecture-package-layout.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 검증 범위\n\nmodule dependency matrix와 ArchUnit rule은 로컬에서 검증했다. 운영 배포와 운영 metric으로 검증한 결과는 없다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "canonical-project",
"status": "verified",
"path": "wiki/projects/ca-tmpl/clean-architecture-package-layout.md",
"heading": "검증 범위",
"line_start": 28,
"line_end": 30,
"claim_ids": [],
"decision_ids": [],
"priority": 12.97464
},
{
"id": "L54271e62b5",
"title": "ca-tmpl - Clean Architecture 패키지 레이아웃 결정 — 경계 검증",
"url": "repo:///wiki/projects/ca-tmpl/clean-architecture-package-layout.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 경계 검증\n\nGradle의 `verifyCleanArchitectureDependencies`는 project dependency graph를 검사한다. ArchUnit의 `CleanArchitectureTest`는 source import graph를 검사한다. 두 검사는 서로 다른 그래프를 담당한다.\n\n정적 분석은 모든 우회를 잡지 못한다. `getBean(String)`, `Class.forName(String)`, `BeanFactory#getBeansOfType` 같은 reflection-style bypass는 code review checklist로 보완한다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "canonical-project",
"status": "verified",
"path": "wiki/projects/ca-tmpl/clean-architecture-package-layout.md",
"heading": "경계 검증",
"line_start": 22,
"line_end": 27,
"claim_ids": [],
"decision_ids": [],
"priority": 11.338676
},
{
"id": "Ld4394f2f14",
"title": "ca-tmpl - Clean Architecture 패키지 레이아웃 결정 — 실제 구현 내용",
"url": "repo:///wiki/projects/ca-tmpl/clean-architecture-package-layout.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 실제 구현 내용\n\n`domain-core`는 Spring, JPA, Servlet, Hibernate, Lombok, application, adapter, bootstrap 의존을 금지해 framework-neutral POJO 경계를 유지한다.\n\n`application-core`는 adapter와 bootstrap, Spring Web, persistence, Hibernate에 의존하지 못한다. `@Transactional`과 `ApplicationContext` 직접 의존도 금지한다.\n\n`shared-contract`는 response, request, error, operation, headers, logging, tracing, metrics, registry, annotation 같은 운영 계약 package만 허용한다. business common dumping ground로 사용하지 않는다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "canonical-project",
"status": "verified",
"path": "wiki/projects/ca-tmpl/clean-architecture-package-layout.md",
"heading": "실제 구현 내용",
"line_start": 14,
"line_end": 21,
"claim_ids": [],
"decision_ids": [],
"priority": 11.111827
},
{
"id": "Lcb081a533b",
"title": "Spring component stereotype and scanning notes — Evidence boundary",
"url": "repo:///raw/official-docs/spring-component-scanning.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## Evidence boundary\n\nThis vendor behavior explains what the annotations do. It does not prove why a particular project chose to use them, nor does it prove which other Spring dependencies the project allows. Project rationale must come from the project's own decision record."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "official-doc",
"status": "reviewed",
"path": "raw/official-docs/spring-component-scanning.md",
"heading": "Evidence boundary",
"line_start": 13,
"line_end": 15,
"claim_ids": [],
"decision_ids": [],
"priority": 7.048668
},
{
"id": "L058b642200",
"title": "Spring component stereotype and scanning notes — Supported behavior",
"url": "repo:///raw/official-docs/spring-component-scanning.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## Supported behavior\n\nSpring stereotype annotations such as `@Component` and `@Service` mark classes as candidates for component scanning and container registration."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "official-doc",
"status": "reviewed",
"path": "raw/official-docs/spring-component-scanning.md",
"heading": "Supported behavior",
"line_start": 9,
"line_end": 12,
"claim_ids": [],
"decision_ids": [],
"priority": 4.977786
},
{
"id": "L0ed1686206",
"title": "branch / feature-log-management-contract — 결정 사항",
"url": "repo:///raw/branch-notes/feature-log-management-contract.md",
"publisher": "local documentation corpus",
"accessed": "",
"facts": [
"## 결정 사항\n\n- 운영 로그는 structured JSON을 기본 포맷으로 둔다.\n- domain layer logger는 금지하고 domain invariant violation을 application layer에서 client-safe diagnostic log로 변환한다."
],
"notes": "Internal retrieval excerpt. Preserve provenance in the sidecar evidence map; do not copy repository paths, source IDs, access dates, or process language into reader-facing prose.",
"source_type": "branch-note",
"status": "raw",
"path": "raw/branch-notes/feature-log-management-contract.md",
"heading": "결정 사항",
"line_start": 10,
"line_end": 14,
"claim_ids": [],
"decision_ids": [],
"priority": 2.365
}
]
}
+1
View File
@@ -0,0 +1 @@
coverage package unavailable; coverage report skipped
+3
View File
@@ -0,0 +1,3 @@
[OK] codex: codex exec — /home/donghyeon/.nvm/versions/node/v24.14.0/bin/codex
[OK] claude: claude -p — /home/donghyeon/.local/bin/claude
[MISSING] antigravity: google-antigravity SDK — Credentials and local agent access are verified only by a live invocation.
+1
View File
@@ -0,0 +1 @@
claridoc 0.2.0
+3
View File
@@ -0,0 +1,3 @@
Processing ./dist/claridoc_harness-0.2.0-py3-none-any.whl
Installing collected packages: claridoc-harness
Successfully installed claridoc-harness-0.2.0
+9
View File
@@ -0,0 +1,9 @@
Processing /home/donghyeon/workspace/ai-tool/document-haness
Preparing metadata (pyproject.toml): started
Preparing metadata (pyproject.toml): finished with status 'done'
Building wheels for collected packages: claridoc-harness
Building wheel for claridoc-harness (pyproject.toml): started
Building wheel for claridoc-harness (pyproject.toml): finished with status 'done'
Created wheel for claridoc-harness: filename=claridoc_harness-0.2.0-py3-none-any.whl size=193673 sha256=9a5dddeee5df1fb98ae949c56213b1f6ff0143b3b5eb31f541eeb1f907ebbf83
Stored in directory: /tmp/pip-ephem-wheel-cache-04qw0e2v/wheels/35/8c/5a/14e7d960f0df960a7b2711bf02a47dcdc5925d278104863c49
Successfully built claridoc-harness