검증 문서 갱신
This commit is contained in:
parent
ac7db95542
commit
6ec4a42d90
3 changed files with 34 additions and 8 deletions
|
|
@ -633,7 +633,7 @@
|
||||||
</article>
|
</article>
|
||||||
<article class="scard" data-status="done" data-cat="보안·PII" data-owner="0">
|
<article class="scard" data-status="done" data-cat="보안·PII" data-owner="0">
|
||||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">H4 부분 DONE</span><span class="scard-mid"><span class="scard-title">PII 마스킹 + LLM 호출 감사 + 프로필 온보딩·동의 게이트</span><span class="scard-sum">metadata-only llm_call_log, 단순 가입정보 온보딩, 약관 버전, 전역 URL 하드게이트. 전체 E2E 113.</span></span><span class="caret" aria-hidden="true"></span></button>
|
<button class="scard-head" aria-expanded="false"><span class="chip c-done">H4 부분 DONE</span><span class="scard-mid"><span class="scard-title">PII 마스킹 + LLM 호출 감사 + 프로필 온보딩·동의 게이트</span><span class="scard-sum">metadata-only llm_call_log, 단순 가입정보 온보딩, 약관 버전, 전역 URL 하드게이트. 전체 E2E 113.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||||
<div class="scard-body"><div class="kv"><b>현재</b><p>정규식 폴백에 한국어 고위험 패턴 추가: 구체 날짜(2001.4.18→[DATE])·금액(1200원→[MONEY])·행정구역 주소(서울시 강남구 역삼동→[ADDR]). 상담 생성(generate/stream)·fast/deep 평가의 외부 LLM 호출 직후 <code>audit.llm_call_log</code>에 provider/model/token/cost/inference_geo/latency만 적재한다. prompt/completion 본문은 저장하지 않는다. 로컬 dev-login 실제 <code>/turn</code> smoke에서 <code>audit.llm_call_log</code>가 8→11로 3행 증가했다. 이번 패스에서 <code>app_user</code>에 이름·소속·학과·학년/직위·연락처·주소/수령지·닉네임·자기소개·아바타 URL·약관/개인정보 동의 시각·버전을 추가했다. 로그인 후 <code>onboarding_completed_at</code>이 없으면 전역 <code>OnboardingGate</code>가 <code>/onboarding</code> 외 URL(<code>/learn</code>, <code>/settings</code>, <code>/admin</code>, <code>/dev/avatar-preview</code>, <code>/login</code> 포함)을 모두 온보딩으로 돌리고, 온보딩 중에는 공용 셸 메뉴 없이 가입 직후 사용자 정보 입력 폼만 보여준다. learner 회기 시작도 온보딩 완료 전에는 막는다. <code>/users/me/avatar</code>는 PNG/JPG/WebP MIME·시그니처·3MB 제한 후 파일을 <code>USER_UPLOAD_DIR/profile-avatars</code>에 저장하고 URL만 보관한다. 약관·개인정보 처리방침 초안은 <code>/users/legal-docs</code>에서 제공한다. 관리자/교수자 권한은 온보딩에서 받지 않고 allowlist/관리자 사용자 관리로만 부여한다. 검증: <code>pytest app/ -q</code> 145 passed, <code>pytest engine_gateway/ -q</code> 9 passed, <code>npm run check:api-types</code>, <code>npm run typecheck</code>, <code>npm run build</code>, auth E2E 7 passed, 최종 <code>PLAYWRIGHT_PORT=5174 npm run e2e</code> 113 passed.</p></div><div class="kv k-warn"><b>잔여</b><p>이름/기관명은 NER 필요(Presidio ko 모델·spaCy ko, 무거움 — TODO 명시). 미성년/guardian 및 법무 검토가 필요한 최종 서명 동의서·개인정보 처리방침·약관 evidence는 외부 게이트다. 공개 Google OAuth 실제 <code>/turn</code> proof는 별도 운영 게이트.</p></div></div>
|
<div class="scard-body"><div class="kv"><b>현재</b><p>정규식 폴백에 한국어 고위험 패턴 추가: 구체 날짜(2001.4.18→[DATE])·금액(1200원→[MONEY])·행정구역 주소(서울시 강남구 역삼동→[ADDR]). 상담 생성(generate/stream)·fast/deep 평가의 외부 LLM 호출 직후 <code>audit.llm_call_log</code>에 provider/model/token/cost/inference_geo/latency만 적재한다. prompt/completion 본문은 저장하지 않는다. 로컬 dev-login 실제 <code>/turn</code> smoke에서 <code>audit.llm_call_log</code>가 8→11로 3행 증가했다. 이번 패스에서 <code>app_user</code>에 이름·소속·학과·학년/직위·연락처·주소/수령지·닉네임·자기소개·아바타 URL·약관/개인정보 동의 시각·버전을 추가했다. 로그인 후 <code>onboarding_completed_at</code>이 없으면 전역 <code>OnboardingGate</code>가 <code>/onboarding</code> 외 URL(<code>/learn</code>, <code>/settings</code>, <code>/admin</code>, <code>/dev/avatar-preview</code>, <code>/login</code> 포함)을 모두 온보딩으로 돌리고, 온보딩 중에는 공용 셸 메뉴 없이 가입 직후 사용자 정보 입력 폼만 보여준다. learner 회기 시작도 온보딩 완료 전에는 막는다. <code>/users/me/avatar</code>는 PNG/JPG/WebP MIME·시그니처·3MB 제한 후 파일을 <code>USER_UPLOAD_DIR/profile-avatars</code>에 저장하고 URL만 보관한다. 약관·개인정보 처리방침 초안은 <code>/users/legal-docs</code>에서 제공한다. 관리자/교수자 권한은 온보딩에서 받지 않고 allowlist/관리자 사용자 관리로만 부여한다. 검증: <code>pytest app/ -q</code> 178 passed, <code>pytest engine_gateway/ -q</code> 11 passed, <code>npm run check:api-types</code>, <code>npm run typecheck</code>, <code>npm run build</code>, auth E2E 7 passed, 최종 <code>PLAYWRIGHT_PORT=5174 npm run e2e</code> 113 passed.</p></div><div class="kv k-warn"><b>잔여</b><p>이름/기관명은 NER 필요(Presidio ko 모델·spaCy ko, 무거움 — TODO 명시). 미성년/guardian 및 법무 검토가 필요한 최종 서명 동의서·개인정보 처리방침·약관 evidence는 외부 게이트다. 공개 Google OAuth 실제 <code>/turn</code> proof는 별도 운영 게이트.</p></div></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="scard" data-status="planned" data-cat="리팩터·정리" data-owner="0">
|
<article class="scard" data-status="planned" data-cat="리팩터·정리" data-owner="0">
|
||||||
<button class="scard-head" aria-expanded="false"><span class="chip c-plan">MED M1~M3</span><span class="scard-mid"><span class="scard-title">비언어 임상이벤트(M1)·다회기 케이스 아크(M2)·SSO claim/식별자/감사(M3)</span><span class="scard-sum">M1/M2 1차 완료. M3는 cohort map + provider external_id 1차 완료, 기관 SAML/거버넌스는 남음.</span></span><span class="caret" aria-hidden="true"></span></button>
|
<button class="scard-head" aria-expanded="false"><span class="chip c-plan">MED M1~M3</span><span class="scard-mid"><span class="scard-title">비언어 임상이벤트(M1)·다회기 케이스 아크(M2)·SSO claim/식별자/감사(M3)</span><span class="scard-sum">M1/M2 1차 완료. M3는 cohort map + provider external_id 1차 완료, 기관 SAML/거버넌스는 남음.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||||
|
|
@ -677,7 +677,7 @@
|
||||||
</article>
|
</article>
|
||||||
<article class="scard" data-status="done" data-cat="인프라·스택·RAG" data-owner="0">
|
<article class="scard" data-status="done" data-cat="인프라·스택·RAG" data-owner="0">
|
||||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">SEO/GEO 공유</span><span class="scard-mid"><span class="scard-title">검색 메타 + 회기 리뷰 URL 공유 카드</span><span class="scard-sum">robots/sitemap/llms + OG/JSON-LD. 종료 회기는 토큰 공유 URL로 교수자에게 요약·썸네일 전달.</span></span><span class="caret" aria-hidden="true"></span></button>
|
<button class="scard-head" aria-expanded="false"><span class="chip c-done">SEO/GEO 공유</span><span class="scard-mid"><span class="scard-title">검색 메타 + 회기 리뷰 URL 공유 카드</span><span class="scard-sum">robots/sitemap/llms + OG/JSON-LD. 종료 회기는 토큰 공유 URL로 교수자에게 요약·썸네일 전달.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||||
<div class="scard-body"><div class="kv"><b>현재</b><p><code>apps/web/index.html</code>에 canonical, description, Open Graph/Twitter Card, JSON-LD(WebApplication)를 추가했고, <code>public/robots.txt</code>, <code>sitemap.xml</code>, <code>llms.txt</code>로 공개 사이트와 AI 검색용 설명을 정리했다. 인증 내부 경로는 robots에서 차단한다. 학습자 회기 리뷰는 종료 후 <code>POST /sessions/{id}/share</code>로 공개 토큰을 만들고, <code>GET /share/session/{token}</code>이 서버 렌더 HTML/OG/JSON-LD를 반환한다. <code>app.session_share_link</code>에는 토큰 해시와 sanitized preview payload만 저장하며 원문 축어록·학습자 식별자는 저장하지 않는다.</p></div><div class="kv"><b>검증</b><p><code>pytest app/test_session_share.py app/test_session_turn_persistence.py -q</code> 21 passed, <code>python -m pytest app/ -q</code> 145 passed, <code>python -m pytest engine_gateway/ -q</code> 9 passed, <code>npm run check:api-types</code>, <code>npm run typecheck</code>, <code>npm run build</code> passed.</p></div><div class="kv k-warn"><b>판정</b><p>공개 제품 페이지는 색인 가능, 회기 공유 페이지는 교수자 전달용 unfurl 카드라 <code>noindex</code>와 API robots <code>Disallow: /share/</code>를 유지한다. 실제 공개 도메인 unfurl 검증은 배포 후 1회 필요하다.</p></div></div>
|
<div class="scard-body"><div class="kv"><b>현재</b><p><code>apps/web/index.html</code>에 canonical, description, Open Graph/Twitter Card, JSON-LD(WebApplication)를 추가했고, <code>public/robots.txt</code>, <code>sitemap.xml</code>, <code>llms.txt</code>로 공개 사이트와 AI 검색용 설명을 정리했다. 인증 내부 경로는 robots에서 차단한다. 학습자 회기 리뷰는 종료 후 <code>POST /sessions/{id}/share</code>로 공개 토큰을 만들고, <code>GET /share/session/{token}</code>이 서버 렌더 HTML/OG/JSON-LD를 반환한다. <code>app.session_share_link</code>에는 토큰 해시와 sanitized preview payload만 저장하며 원문 축어록·학습자 식별자는 저장하지 않는다.</p></div><div class="kv"><b>검증</b><p><code>pytest app/test_session_share.py app/test_session_turn_persistence.py -q</code> 21 passed, <code>python -m pytest app/ -q</code> 178 passed, <code>python -m pytest engine_gateway/ -q</code> 11 passed, <code>npm run check:api-types</code>, <code>npm run typecheck</code>, <code>npm run build</code> passed.</p></div><div class="kv k-warn"><b>판정</b><p>공개 제품 페이지는 색인 가능, 회기 공유 페이지는 교수자 전달용 unfurl 카드라 <code>noindex</code>와 API robots <code>Disallow: /share/</code>를 유지한다. 실제 공개 도메인 unfurl 검증은 배포 후 1회 필요하다.</p></div></div>
|
||||||
</article>
|
</article>
|
||||||
<article class="scard" data-status="done" data-cat="리팩터·정리" data-owner="0">
|
<article class="scard" data-status="done" data-cat="리팩터·정리" data-owner="0">
|
||||||
<button class="scard-head" aria-expanded="false"><span class="chip c-done">REFACTOR 3 일부</span><span class="scard-mid"><span class="scard-title">죽은 확장점/계약 정리 — LogHook·tier 제거 적용</span><span class="scard-sum">caller 0 타입·분기·export 제거. 잔여는 패치 문서에 스테이징. 전체 API 119.</span></span><span class="caret" aria-hidden="true"></span></button>
|
<button class="scard-head" aria-expanded="false"><span class="chip c-done">REFACTOR 3 일부</span><span class="scard-mid"><span class="scard-title">죽은 확장점/계약 정리 — LogHook·tier 제거 적용</span><span class="scard-sum">caller 0 타입·분기·export 제거. 잔여는 패치 문서에 스테이징. 전체 API 119.</span></span><span class="caret" aria-hidden="true"></span></button>
|
||||||
|
|
@ -828,7 +828,7 @@
|
||||||
<tr><td>API typegen SSOT</td><td><code>npm run check:api-types</code></td><td>Passed; FastAPI OpenAPI → <code>src/lib/api.gen.ts</code> stale check</td></tr>
|
<tr><td>API typegen SSOT</td><td><code>npm run check:api-types</code></td><td>Passed; FastAPI OpenAPI → <code>src/lib/api.gen.ts</code> stale check</td></tr>
|
||||||
<tr><td>Contract SSOT aggregate DTO</td><td><code>npm run typecheck</code> / <code>npm run build</code> / <code>session persistence</code> / <code>learner + session-review + teacher + layout gate</code></td><td>Passed; learner sessions, session review/worksheet, teacher dashboard, session start/detail DTOs now use generated <code>ApiSchema</code> aliases with UI fallback. Stage responses are OpenAPI enum unions. Focused E2E 32 passed. <code>.github/workflows/api-contract.yml</code> now runs <code>npm run check:api-types</code> on API/Web contract changes.</td></tr>
|
<tr><td>Contract SSOT aggregate DTO</td><td><code>npm run typecheck</code> / <code>npm run build</code> / <code>session persistence</code> / <code>learner + session-review + teacher + layout gate</code></td><td>Passed; learner sessions, session review/worksheet, teacher dashboard, session start/detail DTOs now use generated <code>ApiSchema</code> aliases with UI fallback. Stage responses are OpenAPI enum unions. Focused E2E 32 passed. <code>.github/workflows/api-contract.yml</code> now runs <code>npm run check:api-types</code> on API/Web contract changes.</td></tr>
|
||||||
<tr><td>SEO/GEO share cards</td><td><code>pytest app/test_session_share.py app/test_session_turn_persistence.py -q</code> / <code>npm run generate:api-types</code> / <code>npm run typecheck</code></td><td>21 passed; session share creates hashed-token public unfurl payload without raw transcript, revoked token returns 404, OpenAPI generated share DTOs, review screen share button typechecks. Static <code>robots.txt</code>/<code>sitemap.xml</code>/<code>llms.txt</code> added.</td></tr>
|
<tr><td>SEO/GEO share cards</td><td><code>pytest app/test_session_share.py app/test_session_turn_persistence.py -q</code> / <code>npm run generate:api-types</code> / <code>npm run typecheck</code></td><td>21 passed; session share creates hashed-token public unfurl payload without raw transcript, revoked token returns 404, OpenAPI generated share DTOs, review screen share button typechecks. Static <code>robots.txt</code>/<code>sitemap.xml</code>/<code>llms.txt</code> added.</td></tr>
|
||||||
<tr><td>Backend pytest</td><td><code>python -m pytest app/ -q</code></td><td>145 passed</td></tr>
|
<tr><td>Backend pytest</td><td><code>python -m pytest app/ -q</code></td><td>178 passed</td></tr>
|
||||||
<tr><td>X2 evaluator model routing</td><td><code>python -m pytest app/test_evaluator_model_routing.py app/test_runtime_policy.py -q</code></td><td>23 passed; fast/deep evaluator model override and blank-default routing covered</td></tr>
|
<tr><td>X2 evaluator model routing</td><td><code>python -m pytest app/test_evaluator_model_routing.py app/test_runtime_policy.py -q</code></td><td>23 passed; fast/deep evaluator model override and blank-default routing covered</td></tr>
|
||||||
<tr><td>C1 case worksheet</td><td><code>python -m pytest app/test_session_turn_persistence.py app/test_rbac_idor.py -q</code> / <code>npm run check:api-types</code> / <code>npm run typecheck</code> / <code>npm run build</code> / local API smoke</td><td>25 passed; session review exposes transcript-grounded <code>caseWorksheet</code>, learner-edited <code>saved_by_learner</code> worksheet persists through <code>app.case_worksheet</code>, and evaluator-only turns stay hidden. Local smoke returned <code>saved_by_learner:local smoke saved worksheet</code>. Stage labels are normalized at response boundaries.</td></tr>
|
<tr><td>C1 case worksheet</td><td><code>python -m pytest app/test_session_turn_persistence.py app/test_rbac_idor.py -q</code> / <code>npm run check:api-types</code> / <code>npm run typecheck</code> / <code>npm run build</code> / local API smoke</td><td>25 passed; session review exposes transcript-grounded <code>caseWorksheet</code>, learner-edited <code>saved_by_learner</code> worksheet persists through <code>app.case_worksheet</code>, and evaluator-only turns stay hidden. Local smoke returned <code>saved_by_learner:local smoke saved worksheet</code>. Stage labels are normalized at response boundaries.</td></tr>
|
||||||
<tr><td>M3 auth claim mapping</td><td><code>python -m pytest app/test_auth_providers.py -q</code></td><td>22 passed; Google/SAML cohort maps, provider external_id, provider error reason covered</td></tr>
|
<tr><td>M3 auth claim mapping</td><td><code>python -m pytest app/test_auth_providers.py -q</code></td><td>22 passed; Google/SAML cohort maps, provider external_id, provider error reason covered</td></tr>
|
||||||
|
|
|
||||||
|
|
@ -381,7 +381,7 @@ RAG 임베딩/리랭커 의존성은 기본 이미지에 설치하지 않는다.
|
||||||
```powershell
|
```powershell
|
||||||
# 백엔드 (apps/api)
|
# 백엔드 (apps/api)
|
||||||
cd apps\api
|
cd apps\api
|
||||||
python -m pytest app/ -q # 현재 145 pass
|
python -m pytest app/ -q # 현재 178 pass
|
||||||
python -m pytest engine_gateway\ -q # 현재 11 pass
|
python -m pytest engine_gateway\ -q # 현재 11 pass
|
||||||
|
|
||||||
# 웹 (apps/web)
|
# 웹 (apps/web)
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ Vignette 저장소의 모든 검증 수단(백엔드 단위 테스트, 웹 타
|
||||||
|
|
||||||
| 검증 | 작업 디렉터리 | 명령 | DB | API(8000) | 웹(5173) | 엔진GW(9099) | 브라우저 | 현재 통과 |
|
| 검증 | 작업 디렉터리 | 명령 | DB | API(8000) | 웹(5173) | 엔진GW(9099) | 브라우저 | 현재 통과 |
|
||||||
|---|---|---|---|---|---|---|---|---|
|
|---|---|---|---|---|---|---|---|---|
|
||||||
| 백엔드 단위 테스트 | `apps/api` | `python -m pytest app/ -q` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 145 pass |
|
| 백엔드 단위 테스트 | `apps/api` | `python -m pytest app/ -q` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 178 pass |
|
||||||
| 엔진 게이트웨이 테스트 | `apps/api` | `python -m pytest engine_gateway/ -q` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 11 pass |
|
| 엔진 게이트웨이 테스트 | `apps/api` | `python -m pytest engine_gateway/ -q` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | 11 pass |
|
||||||
| API 타입 생성 체크 | `apps/web` | `npm run check:api-types` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
|
| API 타입 생성 체크 | `apps/web` | `npm run check:api-types` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
|
||||||
| 웹 타입체크 | `apps/web` | `npm run typecheck` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
|
| 웹 타입체크 | `apps/web` | `npm run typecheck` | 불필요 | 불필요 | 불필요 | 불필요 | 불필요 | pass |
|
||||||
|
|
@ -42,15 +42,15 @@ Vignette 저장소의 모든 검증 수단(백엔드 단위 테스트, 웹 타
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
# apps/api
|
# apps/api
|
||||||
python -m pytest app/ -q # 앱 단위 테스트 (현재 145 pass)
|
python -m pytest app/ -q # 앱 단위 테스트 (현재 178 pass)
|
||||||
python -m pytest engine_gateway/ -q # 게이트웨이 단위 테스트 (현재 11 pass)
|
python -m pytest engine_gateway/ -q # 게이트웨이 단위 테스트 (현재 11 pass)
|
||||||
```
|
```
|
||||||
|
|
||||||
수집만 빠르게 확인하려면:
|
수집만 빠르게 확인하려면:
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
python -m pytest app/ --collect-only -q # → "145 tests collected"
|
python -m pytest app/ --collect-only -q # → "178 tests collected"
|
||||||
python -m pytest engine_gateway/ --collect-only -q # → "9 tests collected"
|
python -m pytest engine_gateway/ --collect-only -q # → "11 tests collected"
|
||||||
```
|
```
|
||||||
|
|
||||||
> 참고: 실행 중 `PendingDeprecationWarning: Please use 'import python_multipart'`
|
> 참고: 실행 중 `PendingDeprecationWarning: Please use 'import python_multipart'`
|
||||||
|
|
@ -81,6 +81,32 @@ python -m pytest engine_gateway/ --collect-only -q # → "9 tests collected"
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
### 1.5 로컬 DB smoke — 저항엔진 openness 곡선
|
||||||
|
|
||||||
|
저항엔진은 단위 테스트와 별도로 실제 API 경로와 Postgres 저장 상태를 함께 확인할 수 있다.
|
||||||
|
이 smoke는 dev-login으로 학습자 세션을 만들고, P1 상담 2개에 공감 발화/조언점프 발화를 각각
|
||||||
|
5턴씩 넣은 뒤 `app.session_state`, `app.turns`, `app.turn_client_state`를 직접 조회한다.
|
||||||
|
|
||||||
|
사전 조건:
|
||||||
|
|
||||||
|
- API가 DB 연결 상태로 떠 있어야 한다.
|
||||||
|
- `apps/api/.env` 또는 환경변수에 `DATABASE_URL`이 있어야 한다.
|
||||||
|
- API는 dev-login과 onboarding 저장이 가능한 dev 런타임이어야 한다.
|
||||||
|
|
||||||
|
```sh
|
||||||
|
# repo root
|
||||||
|
python scripts/smoke-resistance-openness-db.py \
|
||||||
|
--api-base-url http://127.0.0.1:8000 \
|
||||||
|
--timeout 240 \
|
||||||
|
--out docs/ops/resistance-openness-db-smoke-YYYY-MM-DD.json
|
||||||
|
```
|
||||||
|
|
||||||
|
통과 기준:
|
||||||
|
|
||||||
|
- 공감 세션은 5턴 안에 `stage=탐색`으로 열리고 `effective_openness`가 0보다 커진다.
|
||||||
|
- 조언점프 세션은 `stage=라포`, `effective_openness=0.0`을 유지한다.
|
||||||
|
- DB의 `turn_seq`와 `app.turns` 저장 행 수가 API 턴 수와 일치한다.
|
||||||
|
|
||||||
## 2. 웹 타입체크 / 빌드
|
## 2. 웹 타입체크 / 빌드
|
||||||
|
|
||||||
### 2.1 대상과 구성
|
### 2.1 대상과 구성
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue