Dashboard 인증 상태를 server-signed Secure/HttpOnly cookie로 복원하도록 변경했습니다.
기본 로그인은 24시간, 로그인 상태 유지 선택 시 30일의 절대 만료를 사용하며 일반 요청·페이지 이동·재접속으로 만료를 연장하지 않습니다.
token의 sub를 비민감 고정 role dashboard-admin으로 사용하고 관리자 ID·password·signing secret은 server-only key binding에만 사용합니다.
malformed·tampered·expired·rotation-invalid token과 설정 오류는 DB path/gateway/page dispatch 전에 fail-closed하고, same-origin cleanup route가 stale cookie를 matching scope로 삭제합니다.
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py — app 2832의 gate 실패 시 즉시 반환과 authentication 295313의 cookie 재검증을 확인했다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 정적 제어 흐름과 기존 gate 차단 test가 승인된 호출 순서를 유지한다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example — authentication 73107에서 누락과 빈 값 및 읽기 오류를 거부하고 295303에서 보호 기능 진입을 중단한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_persistent_authentication.py
판정 근거: 세 설정의 명시적 source와 오류 차단이 구현되어 있다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 110146과 auth server 96102 및 변경하지 않은 기존 credential test를 읽었다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기존 비교와 실패 지연 함수 및 비민감 correlation event 기록을 유지한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 26 및 172189와 auth server 104118에서 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 단위 test와 응답 header test가 기본 발급 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 27 및 172189와 auth server 104118에서 2592000초 발급 규칙을 확인했다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 선택값에 따른 30일 token과 cookie TTL 구현 및 발급 test가 있다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py — authentication 270~276의 form checkbox에 checked 속성이 없고 30일 선택값은 제출 시 on으로 판정한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기본 form과 제출 처리에서 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — token 발급은 성공 login route에서만 호출되고 gate는 검증만 수행하며 authentication 230~235가 server 시각으로 만료를 거부한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 정적 구현에서 복원 및 활동에 의한 token 재발급 경로가 없다. 실행 검증의 부족은 별도 항목으로 판정한다.
D6-08 — indeterminate
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/app.py, dashboard/common.py — common 358~364의 query link는 유지된다. AppTest는 cookie와 query를 주입하고 protocol script는 단일 새 WebSocket 연결만 검사한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py, scripts/persistent_auth_http_evidence.py, docs/evidence/issue-6-persistent-auth-https.json
판정 근거: 실제 browser의 링크 이동과 재접속에서 인증 복원이 동작하는지 확인한 head bound 실행 evidence가 없다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py, pyproject.toml — server 510에서 Streamlit App의 동일 process route를 조립하고 auth server 109118 및 128~135에서 response cookie를 설정한다.
테스트 근거: tests/test_dashboard_auth_server.py, scripts/persistent_auth_http_evidence.py
판정 근거: 승인된 내부 adapter 구현 경로와 HTTP 응답 test가 있다. 실제 browser 완료 여부는 별도 항목으로 판정한다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — auth server 110118과 129135에서 Secure와 HttpOnly 및 SameSite lax와 Path slash를 적용하고 Domain을 지정하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: host 전용 cookie 이름과 matching 삭제 속성이 구현되어 있다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py — authentication 193242가 서명과 strict claim 및 TTL과 만료를 검증하고 295313의 gate에는 이전 인증 boolean 우회가 없다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 유효 token 검증에 의해서만 gate가 true를 반환한다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py — authentication 149~162에서 현재 secret과 password로 signing key를 파생하며 매 gate에서 현재 설정을 다시 읽는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 단위 rotation test와 password 변경 AppTest가 구현 경로에 대응한다. signing rotation 통합 coverage 부족은 별도 항목이다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/auth_server.py, dashboard/authentication.py — auth server 123~137은 matching cookie deletion response를 반환하고 금융 DB를 호출하지 않는다. 인증 권한을 가진 session boolean도 저장하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py, scripts/persistent_auth_http_evidence.py
판정 근거: 삭제 응답과 stateless gate 구현을 확인했다. browser에서 실제 삭제되는지는 별도 실행 항목으로 판정한다.
D6-14 — unsatisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md — authentication 178~186은 sub claim에 현재 관리자 ID 원문을 넣어 서명한다. 고정된 itsdangerous 2.2.0의 URL safe payload는 가역적 base64와 선택적 압축이며 암호화가 아니다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 높음: HttpOnly는 payload 암호화가 아니다. browser cookie에서 관리자 ID 원문을 복원할 수 있어 승인 계약의 비노출 조건을 위반한다. PR 요구 문서도 이 원문 payload를 새 계약으로 기록한다.
D6-15 — unsatisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 299~309는 로그인 화면과 false만 반환한다. 전체 변경 Python 파일의 delete cookie 호출은 auth server 129의 명시적 logout 한 곳뿐이다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 중간: 접근 차단은 구현되었으나 승인된 runtime 계약이 요구한 실패 경로의 cookie 폐기는 없다.
D6-16 — unsatisfied
계약: canonical REQ 001의 revision과 기존 계약을 유지하며 정본 요구사항 구조와 요구사항별 acceptance criteria 및 추적성을 갱신한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md — 요구 문서 103~159에서 필수 제약과 불변조건 및 하위 호환성 섹션을 제거했고 AC 001부터 AC 017의 checklist로 요구사항별 criterion 구조를 대체했다.
테스트 근거: 없음
판정 근거: 높음: front matter와 revision 및 실제 PR 번호는 맞지만 정본 TEMPLATE의 필수 섹션과 요구사항별 AC 식별자 및 영향 범위와 상태 불변조건이 누락되었다. 같은 저장소 Issue 표기도 정본 형식으로 정리해야 한다.
D6-17 — unsatisfied
계약: interface와 runtime 문서에 비민감 token 필드 및 실패 시 cookie 폐기와 보호 순서를 일관되게 반영한다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 내부 adapter 책임과 금융 DB 비의존 경계 및 source와 dependency 배치가 승인 범위에 대응한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md — env example 49와 Compose 3247 및 Kubernetes 124~140에 별도 signing 설정과 proxy trust 경계가 있고 README가 HTTPS와 rotation 및 rate limit을 설명한다.
테스트 근거: 없음
판정 근거: 실제 값을 포함하지 않는 별도 환경변수와 Kubernetes Secret 주입 및 운영 설명을 확인했다. Compose의 native secret 객체 사용을 새로운 요구사항으로 추가하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, dashboard/common.py, pyproject.toml, uv.lock — authoritative diff의 변경 경로 20개를 두 immutable tree와 대조했다. 금융 schema 및 MCP source와 page dispatch 목록 및 sidebar link는 변경하지 않았다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: DB backed session이나 외부 인증 middleware 및 다중 사용자 기능을 추가하지 않았다.
D6-21 — unsatisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — persistent test 7792의 만료 경계는 기본 TTL만 검사하고 6575의 30일 case는 발급 값만 확인한다. AppTest 138~155는 password rotation만 통합 검사한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py, scripts/persistent_auth_http_evidence.py
판정 근거: 중간: 30일 만료 경계와 signing secret rotation 후 복원 session 차단 및 logout 후 재접속 차단을 검증하는 통합 case가 없다. protocol script도 cookie 삭제 응답 이후 접근 차단을 검사하지 않는다.
D6-22 — unsatisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
테스트 근거: scripts/persistent_auth_http_evidence.py, docs/evidence/issue-6-persistent-auth-https.json, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 높음: protocol client와 AppTest는 실제 browser가 아니다. PR requirement의 AC 015는 이 필수 browser 검증을 protocol 검증으로 완화했다.
D6-23 — indeterminate
계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, docs/requirements/req-001_dashboard-administrator-authentication.md — evidence 5의 source revision은 PR base다. 그 revision에는 evidence script와 신규 adapter가 존재하지 않는다. evidence에는 exit code와 실행 환경 및 실행된 source tree binding이 없고 현재 head status는 결과 0개인 pending이다.
테스트 근거: scripts/persistent_auth_http_evidence.py
판정 근거: 중간: PR 본문의 test 성공 선언만으로 최종 head 실행을 입증할 수 없다. 본 검토는 새 checkout이나 구현 worktree를 사용해 실행을 대체하지 않았다.
범위 판정
누락: 관리자 ID 원문을 포함하지 않는 지속 인증 payload와 관련 문서 및 회귀 검증 ; 실패한 지속 인증 cookie의 폐기 경로와 문서 ; 정본 요구사항 문서의 필수 섹션과 요구사항별 acceptance criteria 및 기존 불변조건 ; 30일 만료 경계와 signing rotation 및 logout 이후 복원 차단 통합 검증 ; 실제 browser cookie 발급 및 복원과 삭제 검증 ; 최종 head에 결합된 신뢰 가능한 실행 provenance
범위 초과: 없음
명세 모순: 없음
결론
승인 계약에 대한 명확한 구현 및 문서 위반과 필수 검증 누락이 있으므로 변경을 요청한다. 확인된 정적 구현과 미확인 실행 결과를 구분하며 승인 계약을 구현에 맞춰 완화하지 않는다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `91e106d75c32905e0b9d6c89d04f537c72a7c4f8`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `request_changes`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/app.py` — app 28~32의 gate 실패 시 즉시 반환과 authentication 295~313의 cookie 재검증을 확인했다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`
- 판정 근거: 정적 제어 흐름과 기존 gate 차단 test가 승인된 호출 순서를 유지한다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: `dashboard/authentication.py`, `.env.example` — authentication 73~107에서 누락과 빈 값 및 읽기 오류를 거부하고 295~303에서 보호 기능 진입을 중단한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_persistent_authentication.py`
- 판정 근거: 세 설정의 명시적 source와 오류 차단이 구현되어 있다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 110~146과 auth server 96~102 및 변경하지 않은 기존 credential test를 읽었다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기존 비교와 실패 지연 함수 및 비민감 correlation event 기록을 유지한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 26 및 172~189와 auth server 104~118에서 86400초 token과 cookie를 발급한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 단위 test와 응답 header test가 기본 발급 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 27 및 172~189와 auth server 104~118에서 2592000초 발급 규칙을 확인했다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 선택값에 따른 30일 token과 cookie TTL 구현 및 발급 test가 있다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: `dashboard/authentication.py` — authentication 270~276의 form checkbox에 checked 속성이 없고 30일 선택값은 제출 시 on으로 판정한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`
- 판정 근거: 기본 form과 제출 처리에서 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — token 발급은 성공 login route에서만 호출되고 gate는 검증만 수행하며 authentication 230~235가 server 시각으로 만료를 거부한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`
- 판정 근거: 정적 구현에서 복원 및 활동에 의한 token 재발급 경로가 없다. 실행 검증의 부족은 별도 항목으로 판정한다.
- `D6-08` — `indeterminate`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/app.py`, `dashboard/common.py` — common 358~364의 query link는 유지된다. AppTest는 cookie와 query를 주입하고 protocol script는 단일 새 WebSocket 연결만 검사한다.
- 테스트 근거: `tests/test_dashboard_app.py`, `tests/test_dashboard_authentication_app.py`, `scripts/persistent_auth_http_evidence.py`, `docs/evidence/issue-6-persistent-auth-https.json`
- 판정 근거: 실제 browser의 링크 이동과 재접속에서 인증 복원이 동작하는지 확인한 head bound 실행 evidence가 없다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/server.py`, `pyproject.toml` — server 5~10에서 Streamlit App의 동일 process route를 조립하고 auth server 109~118 및 128~135에서 response cookie를 설정한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 승인된 내부 adapter 구현 경로와 HTTP 응답 test가 있다. 실제 browser 완료 여부는 별도 항목으로 판정한다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — auth server 110~118과 129~135에서 Secure와 HttpOnly 및 SameSite lax와 Path slash를 적용하고 Domain을 지정하지 않는다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: host 전용 cookie 이름과 matching 삭제 속성이 구현되어 있다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: `dashboard/authentication.py` — authentication 193~242가 서명과 strict claim 및 TTL과 만료를 검증하고 295~313의 gate에는 이전 인증 boolean 우회가 없다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 유효 token 검증에 의해서만 gate가 true를 반환한다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: `dashboard/authentication.py` — authentication 149~162에서 현재 secret과 password로 signing key를 파생하며 매 gate에서 현재 설정을 다시 읽는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 단위 rotation test와 password 변경 AppTest가 구현 경로에 대응한다. signing rotation 통합 coverage 부족은 별도 항목이다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/authentication.py` — auth server 123~137은 matching cookie deletion response를 반환하고 금융 DB를 호출하지 않는다. 인증 권한을 가진 session boolean도 저장하지 않는다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 삭제 응답과 stateless gate 구현을 확인했다. browser에서 실제 삭제되는지는 별도 실행 항목으로 판정한다.
- `D6-14` — `unsatisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/requirements/req-001_dashboard-administrator-authentication.md`, `docs/interface-contract.md` — authentication 178~186은 sub claim에 현재 관리자 ID 원문을 넣어 서명한다. 고정된 itsdangerous 2.2.0의 URL safe payload는 가역적 base64와 선택적 압축이며 암호화가 아니다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 높음: HttpOnly는 payload 암호화가 아니다. browser cookie에서 관리자 ID 원문을 복원할 수 있어 승인 계약의 비노출 조건을 위반한다. PR 요구 문서도 이 원문 payload를 새 계약으로 기록한다.
- `D6-15` — `unsatisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 299~309는 로그인 화면과 false만 반환한다. 전체 변경 Python 파일의 delete cookie 호출은 auth server 129의 명시적 logout 한 곳뿐이다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 중간: 접근 차단은 구현되었으나 승인된 runtime 계약이 요구한 실패 경로의 cookie 폐기는 없다.
- `D6-16` — `unsatisfied`
- 계약: canonical REQ 001의 revision과 기존 계약을 유지하며 정본 요구사항 구조와 요구사항별 acceptance criteria 및 추적성을 갱신한다.
- 구현 근거: `docs/requirements/req-001_dashboard-administrator-authentication.md` — 요구 문서 103~159에서 필수 제약과 불변조건 및 하위 호환성 섹션을 제거했고 AC 001부터 AC 017의 checklist로 요구사항별 criterion 구조를 대체했다.
- 테스트 근거: 없음
- 판정 근거: 높음: front matter와 revision 및 실제 PR 번호는 맞지만 정본 TEMPLATE의 필수 섹션과 요구사항별 AC 식별자 및 영향 범위와 상태 불변조건이 누락되었다. 같은 저장소 Issue 표기도 정본 형식으로 정리해야 한다.
- `D6-17` — `unsatisfied`
- 계약: interface와 runtime 문서에 비민감 token 필드 및 실패 시 cookie 폐기와 보호 순서를 일관되게 반영한다.
- 구현 근거: `docs/interface-contract.md`, `docs/runtime-flow.md`, `docs/requirements/req-001_dashboard-administrator-authentication.md` — interface 27은 관리자 ID를 민감정보로 분류하지만 requirement 85는 이를 token sub에 넣도록 규정한다. runtime 58~66의 실패 복구에는 cookie 삭제가 없다.
- 테스트 근거: 없음
- 판정 근거: 승인된 credential 비노출과 실패 시 cookie 폐기 계약이 문서와 구현에 일관되게 전파되지 않았다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: `docs/project-structure.md`, `dashboard/server.py`, `dashboard/auth_server.py`, `dashboard/authentication.py`, `pyproject.toml`, `uv.lock` — project structure 9~18과 pyproject 19~28 및 locked dependency를 확인했다. 기존 Dockerfile은 dashboard extra와 Dashboard source를 설치한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 내부 adapter 책임과 금융 DB 비의존 경계 및 source와 dependency 배치가 승인 범위에 대응한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: `.env.example`, `docker-compose.yml`, `deploy/k8s/base/deployment.yaml`, `deploy/k8s/README.md`, `README.md` — env example 4~9와 Compose 32~47 및 Kubernetes 124~140에 별도 signing 설정과 proxy trust 경계가 있고 README가 HTTPS와 rotation 및 rate limit을 설명한다.
- 테스트 근거: 없음
- 판정 근거: 실제 값을 포함하지 않는 별도 환경변수와 Kubernetes Secret 주입 및 운영 설명을 확인했다. Compose의 native secret 객체 사용을 새로운 요구사항으로 추가하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: `dashboard/app.py`, `dashboard/common.py`, `pyproject.toml`, `uv.lock` — authoritative diff의 변경 경로 20개를 두 immutable tree와 대조했다. 금융 schema 및 MCP source와 page dispatch 목록 및 sidebar link는 변경하지 않았다.
- 테스트 근거: `tests/test_dashboard_app.py`
- 판정 근거: DB backed session이나 외부 인증 middleware 및 다중 사용자 기능을 추가하지 않았다.
- `D6-21` — `unsatisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — persistent test 77~92의 만료 경계는 기본 TTL만 검사하고 65~75의 30일 case는 발급 값만 확인한다. AppTest 138~155는 password rotation만 통합 검사한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`, `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 중간: 30일 만료 경계와 signing secret rotation 후 복원 session 차단 및 logout 후 재접속 차단을 검증하는 통합 case가 없다. protocol script도 cookie 삭제 응답 이후 접근 차단을 검사하지 않는다.
- `D6-22` — `unsatisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/authentication.py` — 저장된 evidence 6~7은 HTTP와 Streamlit WebSocket protocol client라고 명시한다. PR 본문도 Chromium UI 자동화를 실행하지 못했다고 명시한다.
- 테스트 근거: `scripts/persistent_auth_http_evidence.py`, `docs/evidence/issue-6-persistent-auth-https.json`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 높음: protocol client와 AppTest는 실제 browser가 아니다. PR requirement의 AC 015는 이 필수 browser 검증을 protocol 검증으로 완화했다.
- `D6-23` — `indeterminate`
- 계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json`, `docs/requirements/req-001_dashboard-administrator-authentication.md` — evidence 5의 source revision은 PR base다. 그 revision에는 evidence script와 신규 adapter가 존재하지 않는다. evidence에는 exit code와 실행 환경 및 실행된 source tree binding이 없고 현재 head status는 결과 0개인 pending이다.
- 테스트 근거: `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 중간: PR 본문의 test 성공 선언만으로 최종 head 실행을 입증할 수 없다. 본 검토는 새 checkout이나 구현 worktree를 사용해 실행을 대체하지 않았다.
### 범위 판정
- 누락: 관리자 ID 원문을 포함하지 않는 지속 인증 payload와 관련 문서 및 회귀 검증 ; 실패한 지속 인증 cookie의 폐기 경로와 문서 ; 정본 요구사항 문서의 필수 섹션과 요구사항별 acceptance criteria 및 기존 불변조건 ; 30일 만료 경계와 signing rotation 및 logout 이후 복원 차단 통합 검증 ; 실제 browser cookie 발급 및 복원과 삭제 검증 ; 최종 head에 결합된 신뢰 가능한 실행 provenance
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
승인 계약에 대한 명확한 구현 및 문서 위반과 필수 검증 누락이 있으므로 변경을 요청한다. 확인된 정적 구현과 미확인 실행 결과를 구분하며 승인 계약을 구현에 맞춰 완화하지 않는다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=91e106d75c32905e0b9d6c89d04f537c72a7c4f8 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=a52a00b09d347238591fa640573376735157348f0263c5f6d2c0dab45228e2e4 correlation=4bb9ee69891150833975991a9b4820646fbe768608d6ffce8a0d4e65841a4378 -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py — app 2832의 gate 실패 시 즉시 반환과 authentication 295313의 cookie 재검증을 확인했다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 정적 제어 흐름과 기존 gate 차단 test가 승인된 호출 순서를 유지한다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example — authentication 73107에서 누락과 빈 값 및 읽기 오류를 거부하고 295303에서 보호 기능 진입을 중단한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_persistent_authentication.py
판정 근거: 세 설정의 명시적 source와 오류 차단이 구현되어 있다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 110146과 auth server 96102 및 변경하지 않은 기존 credential test를 읽었다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기존 비교와 실패 지연 함수 및 비민감 correlation event 기록을 유지한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 26 및 172189와 auth server 104118에서 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 단위 test와 응답 header test가 기본 발급 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 27 및 172189와 auth server 104118에서 2592000초 발급 규칙을 확인했다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 선택값에 따른 30일 token과 cookie TTL 구현 및 발급 test가 있다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py — authentication 270~276의 form checkbox에 checked 속성이 없고 30일 선택값은 제출 시 on으로 판정한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기본 form과 제출 처리에서 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — token 발급은 성공 login route에서만 호출되고 gate는 검증만 수행하며 authentication 230~235가 server 시각으로 만료를 거부한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 정적 구현에서 복원 및 활동에 의한 token 재발급 경로가 없다. 실행 검증의 부족은 별도 항목으로 판정한다.
D6-08 — indeterminate
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/app.py, dashboard/common.py — common 358~364의 query link는 유지된다. AppTest는 cookie와 query를 주입하고 protocol script는 단일 새 WebSocket 연결만 검사한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py, scripts/persistent_auth_http_evidence.py, docs/evidence/issue-6-persistent-auth-https.json
판정 근거: 실제 browser의 링크 이동과 재접속에서 인증 복원이 동작하는지 확인한 head bound 실행 evidence가 없다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py, pyproject.toml — server 510에서 Streamlit App의 동일 process route를 조립하고 auth server 109118 및 128~135에서 response cookie를 설정한다.
테스트 근거: tests/test_dashboard_auth_server.py, scripts/persistent_auth_http_evidence.py
판정 근거: 승인된 내부 adapter 구현 경로와 HTTP 응답 test가 있다. 실제 browser 완료 여부는 별도 항목으로 판정한다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — auth server 110118과 129135에서 Secure와 HttpOnly 및 SameSite lax와 Path slash를 적용하고 Domain을 지정하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: host 전용 cookie 이름과 matching 삭제 속성이 구현되어 있다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py — authentication 193242가 서명과 strict claim 및 TTL과 만료를 검증하고 295313의 gate에는 이전 인증 boolean 우회가 없다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 유효 token 검증에 의해서만 gate가 true를 반환한다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py — authentication 149~162에서 현재 secret과 password로 signing key를 파생하며 매 gate에서 현재 설정을 다시 읽는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 단위 rotation test와 password 변경 AppTest가 구현 경로에 대응한다. signing rotation 통합 coverage 부족은 별도 항목이다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/auth_server.py, dashboard/authentication.py — auth server 123~137은 matching cookie deletion response를 반환하고 금융 DB를 호출하지 않는다. 인증 권한을 가진 session boolean도 저장하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py, scripts/persistent_auth_http_evidence.py
판정 근거: 삭제 응답과 stateless gate 구현을 확인했다. browser에서 실제 삭제되는지는 별도 실행 항목으로 판정한다.
D6-14 — unsatisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md — authentication 178~186은 sub claim에 현재 관리자 ID 원문을 넣어 서명한다. 고정된 itsdangerous 2.2.0의 URL safe payload는 가역적 base64와 선택적 압축이며 암호화가 아니다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 높음: HttpOnly는 payload 암호화가 아니다. browser cookie에서 관리자 ID 원문을 복원할 수 있어 승인 계약의 비노출 조건을 위반한다. PR 요구 문서도 이 원문 payload를 새 계약으로 기록한다.
D6-15 — unsatisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 299~309는 로그인 화면과 false만 반환한다. 전체 변경 Python 파일의 delete cookie 호출은 auth server 129의 명시적 logout 한 곳뿐이다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 중간: 접근 차단은 구현되었으나 승인된 runtime 계약이 요구한 실패 경로의 cookie 폐기는 없다.
D6-16 — unsatisfied
계약: canonical REQ 001의 revision과 기존 계약을 유지하며 정본 요구사항 구조와 요구사항별 acceptance criteria 및 추적성을 갱신한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md — 요구 문서 103~159에서 필수 제약과 불변조건 및 하위 호환성 섹션을 제거했고 AC 001부터 AC 017의 checklist로 요구사항별 criterion 구조를 대체했다.
테스트 근거: 없음
판정 근거: 높음: front matter와 revision 및 실제 PR 번호는 맞지만 정본 TEMPLATE의 필수 섹션과 요구사항별 AC 식별자 및 영향 범위와 상태 불변조건이 누락되었다. 같은 저장소 Issue 표기도 정본 형식으로 정리해야 한다.
D6-17 — unsatisfied
계약: interface와 runtime 문서에 비민감 token 필드 및 실패 시 cookie 폐기와 보호 순서를 일관되게 반영한다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 내부 adapter 책임과 금융 DB 비의존 경계 및 source와 dependency 배치가 승인 범위에 대응한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md — env example 49와 Compose 3247 및 Kubernetes 124~140에 별도 signing 설정과 proxy trust 경계가 있고 README가 HTTPS와 rotation 및 rate limit을 설명한다.
테스트 근거: 없음
판정 근거: 실제 값을 포함하지 않는 별도 환경변수와 Kubernetes Secret 주입 및 운영 설명을 확인했다. Compose의 native secret 객체 사용을 새로운 요구사항으로 추가하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, dashboard/common.py, pyproject.toml, uv.lock — authoritative diff의 변경 경로 20개를 두 immutable tree와 대조했다. 금융 schema 및 MCP source와 page dispatch 목록 및 sidebar link는 변경하지 않았다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: DB backed session이나 외부 인증 middleware 및 다중 사용자 기능을 추가하지 않았다.
D6-21 — unsatisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — persistent test 7792의 만료 경계는 기본 TTL만 검사하고 6575의 30일 case는 발급 값만 확인한다. AppTest 138~155는 password rotation만 통합 검사한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py, scripts/persistent_auth_http_evidence.py
판정 근거: 중간: 30일 만료 경계와 signing secret rotation 후 복원 session 차단 및 logout 후 재접속 차단을 검증하는 통합 case가 없다. protocol script도 cookie 삭제 응답 이후 접근 차단을 검사하지 않는다.
D6-22 — unsatisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
테스트 근거: scripts/persistent_auth_http_evidence.py, docs/evidence/issue-6-persistent-auth-https.json, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 높음: protocol client와 AppTest는 실제 browser가 아니다. PR requirement의 AC 015는 이 필수 browser 검증을 protocol 검증으로 완화했다.
D6-23 — indeterminate
계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, docs/requirements/req-001_dashboard-administrator-authentication.md — evidence 5의 source revision은 PR base다. 그 revision에는 evidence script와 신규 adapter가 존재하지 않는다. evidence에는 exit code와 실행 환경 및 실행된 source tree binding이 없고 현재 head status는 결과 0개인 pending이다.
테스트 근거: scripts/persistent_auth_http_evidence.py
판정 근거: 중간: PR 본문의 test 성공 선언만으로 최종 head 실행을 입증할 수 없다. 본 검토는 새 checkout이나 구현 worktree를 사용해 실행을 대체하지 않았다.
범위 판정
누락: 관리자 ID 원문을 포함하지 않는 지속 인증 payload와 관련 문서 및 회귀 검증 ; 실패한 지속 인증 cookie의 폐기 경로와 문서 ; 정본 요구사항 문서의 필수 섹션과 요구사항별 acceptance criteria 및 기존 불변조건 ; 30일 만료 경계와 signing rotation 및 logout 이후 복원 차단 통합 검증 ; 실제 browser cookie 발급 및 복원과 삭제 검증 ; 최종 head에 결합된 신뢰 가능한 실행 provenance
범위 초과: 없음
명세 모순: 없음
결론
승인 계약에 대한 명확한 구현 및 문서 위반과 필수 검증 누락이 있으므로 변경을 요청한다. 확인된 정적 구현과 미확인 실행 결과를 구분하며 승인 계약을 구현에 맞춰 완화하지 않는다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `91e106d75c32905e0b9d6c89d04f537c72a7c4f8`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `request_changes`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/app.py` — app 28~32의 gate 실패 시 즉시 반환과 authentication 295~313의 cookie 재검증을 확인했다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`
- 판정 근거: 정적 제어 흐름과 기존 gate 차단 test가 승인된 호출 순서를 유지한다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: `dashboard/authentication.py`, `.env.example` — authentication 73~107에서 누락과 빈 값 및 읽기 오류를 거부하고 295~303에서 보호 기능 진입을 중단한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_persistent_authentication.py`
- 판정 근거: 세 설정의 명시적 source와 오류 차단이 구현되어 있다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 110~146과 auth server 96~102 및 변경하지 않은 기존 credential test를 읽었다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기존 비교와 실패 지연 함수 및 비민감 correlation event 기록을 유지한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 26 및 172~189와 auth server 104~118에서 86400초 token과 cookie를 발급한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 단위 test와 응답 header test가 기본 발급 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 27 및 172~189와 auth server 104~118에서 2592000초 발급 규칙을 확인했다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 선택값에 따른 30일 token과 cookie TTL 구현 및 발급 test가 있다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: `dashboard/authentication.py` — authentication 270~276의 form checkbox에 checked 속성이 없고 30일 선택값은 제출 시 on으로 판정한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`
- 판정 근거: 기본 form과 제출 처리에서 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — token 발급은 성공 login route에서만 호출되고 gate는 검증만 수행하며 authentication 230~235가 server 시각으로 만료를 거부한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`
- 판정 근거: 정적 구현에서 복원 및 활동에 의한 token 재발급 경로가 없다. 실행 검증의 부족은 별도 항목으로 판정한다.
- `D6-08` — `indeterminate`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/app.py`, `dashboard/common.py` — common 358~364의 query link는 유지된다. AppTest는 cookie와 query를 주입하고 protocol script는 단일 새 WebSocket 연결만 검사한다.
- 테스트 근거: `tests/test_dashboard_app.py`, `tests/test_dashboard_authentication_app.py`, `scripts/persistent_auth_http_evidence.py`, `docs/evidence/issue-6-persistent-auth-https.json`
- 판정 근거: 실제 browser의 링크 이동과 재접속에서 인증 복원이 동작하는지 확인한 head bound 실행 evidence가 없다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/server.py`, `pyproject.toml` — server 5~10에서 Streamlit App의 동일 process route를 조립하고 auth server 109~118 및 128~135에서 response cookie를 설정한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 승인된 내부 adapter 구현 경로와 HTTP 응답 test가 있다. 실제 browser 완료 여부는 별도 항목으로 판정한다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — auth server 110~118과 129~135에서 Secure와 HttpOnly 및 SameSite lax와 Path slash를 적용하고 Domain을 지정하지 않는다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: host 전용 cookie 이름과 matching 삭제 속성이 구현되어 있다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: `dashboard/authentication.py` — authentication 193~242가 서명과 strict claim 및 TTL과 만료를 검증하고 295~313의 gate에는 이전 인증 boolean 우회가 없다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 유효 token 검증에 의해서만 gate가 true를 반환한다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: `dashboard/authentication.py` — authentication 149~162에서 현재 secret과 password로 signing key를 파생하며 매 gate에서 현재 설정을 다시 읽는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 단위 rotation test와 password 변경 AppTest가 구현 경로에 대응한다. signing rotation 통합 coverage 부족은 별도 항목이다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/authentication.py` — auth server 123~137은 matching cookie deletion response를 반환하고 금융 DB를 호출하지 않는다. 인증 권한을 가진 session boolean도 저장하지 않는다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 삭제 응답과 stateless gate 구현을 확인했다. browser에서 실제 삭제되는지는 별도 실행 항목으로 판정한다.
- `D6-14` — `unsatisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/requirements/req-001_dashboard-administrator-authentication.md`, `docs/interface-contract.md` — authentication 178~186은 sub claim에 현재 관리자 ID 원문을 넣어 서명한다. 고정된 itsdangerous 2.2.0의 URL safe payload는 가역적 base64와 선택적 압축이며 암호화가 아니다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 높음: HttpOnly는 payload 암호화가 아니다. browser cookie에서 관리자 ID 원문을 복원할 수 있어 승인 계약의 비노출 조건을 위반한다. PR 요구 문서도 이 원문 payload를 새 계약으로 기록한다.
- `D6-15` — `unsatisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 299~309는 로그인 화면과 false만 반환한다. 전체 변경 Python 파일의 delete cookie 호출은 auth server 129의 명시적 logout 한 곳뿐이다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 중간: 접근 차단은 구현되었으나 승인된 runtime 계약이 요구한 실패 경로의 cookie 폐기는 없다.
- `D6-16` — `unsatisfied`
- 계약: canonical REQ 001의 revision과 기존 계약을 유지하며 정본 요구사항 구조와 요구사항별 acceptance criteria 및 추적성을 갱신한다.
- 구현 근거: `docs/requirements/req-001_dashboard-administrator-authentication.md` — 요구 문서 103~159에서 필수 제약과 불변조건 및 하위 호환성 섹션을 제거했고 AC 001부터 AC 017의 checklist로 요구사항별 criterion 구조를 대체했다.
- 테스트 근거: 없음
- 판정 근거: 높음: front matter와 revision 및 실제 PR 번호는 맞지만 정본 TEMPLATE의 필수 섹션과 요구사항별 AC 식별자 및 영향 범위와 상태 불변조건이 누락되었다. 같은 저장소 Issue 표기도 정본 형식으로 정리해야 한다.
- `D6-17` — `unsatisfied`
- 계약: interface와 runtime 문서에 비민감 token 필드 및 실패 시 cookie 폐기와 보호 순서를 일관되게 반영한다.
- 구현 근거: `docs/interface-contract.md`, `docs/runtime-flow.md`, `docs/requirements/req-001_dashboard-administrator-authentication.md` — interface 27은 관리자 ID를 민감정보로 분류하지만 requirement 85는 이를 token sub에 넣도록 규정한다. runtime 58~66의 실패 복구에는 cookie 삭제가 없다.
- 테스트 근거: 없음
- 판정 근거: 승인된 credential 비노출과 실패 시 cookie 폐기 계약이 문서와 구현에 일관되게 전파되지 않았다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: `docs/project-structure.md`, `dashboard/server.py`, `dashboard/auth_server.py`, `dashboard/authentication.py`, `pyproject.toml`, `uv.lock` — project structure 9~18과 pyproject 19~28 및 locked dependency를 확인했다. 기존 Dockerfile은 dashboard extra와 Dashboard source를 설치한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 내부 adapter 책임과 금융 DB 비의존 경계 및 source와 dependency 배치가 승인 범위에 대응한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: `.env.example`, `docker-compose.yml`, `deploy/k8s/base/deployment.yaml`, `deploy/k8s/README.md`, `README.md` — env example 4~9와 Compose 32~47 및 Kubernetes 124~140에 별도 signing 설정과 proxy trust 경계가 있고 README가 HTTPS와 rotation 및 rate limit을 설명한다.
- 테스트 근거: 없음
- 판정 근거: 실제 값을 포함하지 않는 별도 환경변수와 Kubernetes Secret 주입 및 운영 설명을 확인했다. Compose의 native secret 객체 사용을 새로운 요구사항으로 추가하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: `dashboard/app.py`, `dashboard/common.py`, `pyproject.toml`, `uv.lock` — authoritative diff의 변경 경로 20개를 두 immutable tree와 대조했다. 금융 schema 및 MCP source와 page dispatch 목록 및 sidebar link는 변경하지 않았다.
- 테스트 근거: `tests/test_dashboard_app.py`
- 판정 근거: DB backed session이나 외부 인증 middleware 및 다중 사용자 기능을 추가하지 않았다.
- `D6-21` — `unsatisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — persistent test 77~92의 만료 경계는 기본 TTL만 검사하고 65~75의 30일 case는 발급 값만 확인한다. AppTest 138~155는 password rotation만 통합 검사한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`, `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 중간: 30일 만료 경계와 signing secret rotation 후 복원 session 차단 및 logout 후 재접속 차단을 검증하는 통합 case가 없다. protocol script도 cookie 삭제 응답 이후 접근 차단을 검사하지 않는다.
- `D6-22` — `unsatisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/authentication.py` — 저장된 evidence 6~7은 HTTP와 Streamlit WebSocket protocol client라고 명시한다. PR 본문도 Chromium UI 자동화를 실행하지 못했다고 명시한다.
- 테스트 근거: `scripts/persistent_auth_http_evidence.py`, `docs/evidence/issue-6-persistent-auth-https.json`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 높음: protocol client와 AppTest는 실제 browser가 아니다. PR requirement의 AC 015는 이 필수 browser 검증을 protocol 검증으로 완화했다.
- `D6-23` — `indeterminate`
- 계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json`, `docs/requirements/req-001_dashboard-administrator-authentication.md` — evidence 5의 source revision은 PR base다. 그 revision에는 evidence script와 신규 adapter가 존재하지 않는다. evidence에는 exit code와 실행 환경 및 실행된 source tree binding이 없고 현재 head status는 결과 0개인 pending이다.
- 테스트 근거: `scripts/persistent_auth_http_evidence.py`
- 판정 근거: 중간: PR 본문의 test 성공 선언만으로 최종 head 실행을 입증할 수 없다. 본 검토는 새 checkout이나 구현 worktree를 사용해 실행을 대체하지 않았다.
### 범위 판정
- 누락: 관리자 ID 원문을 포함하지 않는 지속 인증 payload와 관련 문서 및 회귀 검증 ; 실패한 지속 인증 cookie의 폐기 경로와 문서 ; 정본 요구사항 문서의 필수 섹션과 요구사항별 acceptance criteria 및 기존 불변조건 ; 30일 만료 경계와 signing rotation 및 logout 이후 복원 차단 통합 검증 ; 실제 browser cookie 발급 및 복원과 삭제 검증 ; 최종 head에 결합된 신뢰 가능한 실행 provenance
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
승인 계약에 대한 명확한 구현 및 문서 위반과 필수 검증 누락이 있으므로 변경을 요청한다. 확인된 정적 구현과 미확인 실행 결과를 구분하며 승인 계약을 구현에 맞춰 완화하지 않는다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=91e106d75c32905e0b9d6c89d04f537c72a7c4f8 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=a52a00b09d347238591fa640573376735157348f0263c5f6d2c0dab45228e2e4 correlation=4bb9ee69891150833975991a9b4820646fbe768608d6ffce8a0d4e65841a4378 -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py — app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기존 공통 gate와 no call test가 유지된다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example — authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 111147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json — 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py — server 510은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — authentication 25의 Host prefix와 auth server 134142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py — authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py — authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — auth server 147165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md — authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md — authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
D6-16 — satisfied
계약: canonical REQ 001의 revision과 기존 계약을 유지하며 정본 요구사항 구조와 요구사항별 acceptance criteria 및 추적성을 갱신한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md — REQ 001 revision 2.0과 정본 H2 11개 및 요구사항 11개와 요구사항별 AC 11개를 파싱했고 기존 인증 경계와 보호 범위 및 추적성을 대조했다.
테스트 근거: 없음
판정 근거: 정본 구조와 영향 범위 및 상태 불변조건과 같은 저장소 Issue 및 실제 PR 번호를 복원했다.
D6-17 — unsatisfied
계약: interface와 runtime 문서에 비민감 token 필드 및 실패 시 cookie 폐기와 보호 순서를 일관되게 반영한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md — interface 4376과 137172 및 213218에는 기본 24시간과 로그인 성공 시각 기준 절대 만료 및 미연장과 전체 token claim 계약이 정의되지 않았다. runtime 2753은 발급 및 검증 흐름을 적지만 두 TTL과 로그인 성공 기준 및 활동 복원 미연장을 정의하지 않는다.
테스트 근거: 없음
판정 근거: 중간: credential 원문 제거와 cleanup 문서 반영은 해소됐으나 승인 Discovery의 인터페이스 계약 및 런타임 흐름 산출물에 명시된 지속 인증 계약 일부가 여전히 누락됐다. interface에 token 비민감 필드와 두 TTL 및 절대 만료와 미연장 및 rotation 무효화 계약을 정의하고 runtime에 대응 발급 및 복원 순서를 명시해야 한다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock — project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md — 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
테스트 근거: 없음
판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md — 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — persistent test 168179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
D6-22 — satisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py — browser 기록 1023 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
D6-23 — satisfied
계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json — 기록 source 7bb959859a84f1d1293afd1f0270cca18f4cd576은 current head의 ancestor이고 이후 변경은 evidence JSON 하나뿐이다. 실행 대상 hash 다섯 개 및 commit provenance와 명령 및 exit code 및 환경 및 결과를 검증했다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 111 passed 및 4 skipped와 ruff 및 compileall exit 0은 source bound 구현자 실행 기록이다. CI context는 0개이고 spec agent의 독립 runtime 실행은 없다.
범위 판정
누락: 인터페이스 계약의 비민감 token 필드와 로그인 성공 시각 기준 24시간 및 30일 절대 만료와 미연장 및 rotation 무효화 정의와 런타임의 대응 발급 및 복원 계약
범위 초과: 없음
명세 모순: 없음
결론
재작업으로 관리자 ID 원문 payload와 실패 cookie 폐기 및 REQ 001 구조와 추가 검증 누락은 해소됐다. 다만 승인된 인터페이스 및 런타임 문서 산출물의 만료와 미연장 등의 정의가 누락되어 변경을 요청한다. 정적 source와 source bound 구현자 실행 기록을 검토했으며 spec agent가 runtime test를 새로 실행하거나 CI 성공을 확인한 것은 아니다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `9bc64699cb22380b3557ab5c41952017a21ccc69`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `request_changes`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/app.py` — app 28~32에서 gate 실패 시 반환하고 authentication 297~316에서 검증 성공 때만 보호 기능을 허용한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`
- 판정 근거: 기존 공통 gate와 no call test가 유지된다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: `dashboard/authentication.py`, `.env.example` — authentication 74~108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302~306에서 접근을 차단한다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 111~147의 비교와 지연 및 event log를 auth server 120~126에서 호출한다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 26과 173~190 및 auth server 128~142에서 기본 86400초 token과 cookie를 발급한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 27과 173~190 및 auth server 128~142에서 선택 시 2592000초를 적용한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`
- 판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/common.py`, `docs/evidence/issue-6-persistent-auth-https.json` — 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
- 테스트 근거: `tests/test_dashboard_app.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/server.py` — server 5~10은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134~142 및 158~165는 실제 응답 cookie를 제어한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — authentication 25의 Host prefix와 auth server 134~142 및 158~165의 보안 속성이 browser 기록 44~55 및 72~79에 대응한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: `dashboard/authentication.py` — authentication 194~239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297~316은 session boolean을 사용하지 않는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: `dashboard/authentication.py` — authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — auth server 147~165의 matching deletion과 browser 기록 72~79의 cookie 제거 및 미인증 전이를 확인했다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `docs/requirements/req-001_dashboard-administrator-authentication.md`, `docs/interface-contract.md` — authentication 34 및 179~185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152~165가 관리자 ID 원문의 부재를 확인한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/runtime-flow.md` — authentication 302~312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168~176은 matching deletion과 redirect를 반환한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
- `D6-16` — `satisfied`
- 계약: canonical REQ 001의 revision과 기존 계약을 유지하며 정본 요구사항 구조와 요구사항별 acceptance criteria 및 추적성을 갱신한다.
- 구현 근거: `docs/requirements/req-001_dashboard-administrator-authentication.md` — REQ 001 revision 2.0과 정본 H2 11개 및 요구사항 11개와 요구사항별 AC 11개를 파싱했고 기존 인증 경계와 보호 범위 및 추적성을 대조했다.
- 테스트 근거: 없음
- 판정 근거: 정본 구조와 영향 범위 및 상태 불변조건과 같은 저장소 Issue 및 실제 PR 번호를 복원했다.
- `D6-17` — `unsatisfied`
- 계약: interface와 runtime 문서에 비민감 token 필드 및 실패 시 cookie 폐기와 보호 순서를 일관되게 반영한다.
- 구현 근거: `docs/interface-contract.md`, `docs/runtime-flow.md`, `docs/requirements/req-001_dashboard-administrator-authentication.md` — interface 43~76과 137~172 및 213~218에는 기본 24시간과 로그인 성공 시각 기준 절대 만료 및 미연장과 전체 token claim 계약이 정의되지 않았다. runtime 27~53은 발급 및 검증 흐름을 적지만 두 TTL과 로그인 성공 기준 및 활동 복원 미연장을 정의하지 않는다.
- 테스트 근거: 없음
- 판정 근거: 중간: credential 원문 제거와 cleanup 문서 반영은 해소됐으나 승인 Discovery의 인터페이스 계약 및 런타임 흐름 산출물에 명시된 지속 인증 계약 일부가 여전히 누락됐다. interface에 token 비민감 필드와 두 TTL 및 절대 만료와 미연장 및 rotation 무효화 계약을 정의하고 runtime에 대응 발급 및 복원 순서를 명시해야 한다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: `docs/project-structure.md`, `dashboard/server.py`, `dashboard/auth_server.py`, `pyproject.toml`, `uv.lock` — project 23~46 및 66~83에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: `.env.example`, `docker-compose.yml`, `deploy/k8s/base/deployment.yaml`, `deploy/k8s/README.md`, `README.md` — 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
- 테스트 근거: 없음
- 판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: `dashboard/app.py`, `docs/project-structure.md`, `README.md` — 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
- 테스트 근거: `tests/test_dashboard_app.py`
- 판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — persistent test 168~179의 30일 경계와 AppTest 165~183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`
- 판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
- `D6-22` — `satisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json`, `dashboard/authentication.py`, `dashboard/auth_server.py`, `scripts/persistent_auth_http_evidence.py` — browser 기록 10~23 및 42~80은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
- `D6-23` — `satisfied`
- 계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json` — 기록 source 7bb959859a84f1d1293afd1f0270cca18f4cd576은 current head의 ancestor이고 이후 변경은 evidence JSON 하나뿐이다. 실행 대상 hash 다섯 개 및 commit provenance와 명령 및 exit code 및 환경 및 결과를 검증했다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`
- 판정 근거: 111 passed 및 4 skipped와 ruff 및 compileall exit 0은 source bound 구현자 실행 기록이다. CI context는 0개이고 spec agent의 독립 runtime 실행은 없다.
### 범위 판정
- 누락: 인터페이스 계약의 비민감 token 필드와 로그인 성공 시각 기준 24시간 및 30일 절대 만료와 미연장 및 rotation 무효화 정의와 런타임의 대응 발급 및 복원 계약
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
재작업으로 관리자 ID 원문 payload와 실패 cookie 폐기 및 REQ 001 구조와 추가 검증 누락은 해소됐다. 다만 승인된 인터페이스 및 런타임 문서 산출물의 만료와 미연장 등의 정의가 누락되어 변경을 요청한다. 정적 source와 source bound 구현자 실행 기록을 검토했으며 spec agent가 runtime test를 새로 실행하거나 CI 성공을 확인한 것은 아니다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=9bc64699cb22380b3557ab5c41952017a21ccc69 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=0c7e016b2a46d427d181d2ea783e957839c5f00f622e964e3a65da6d880767ad correlation=423b8de82bfdd4e3c87ae2f3185deba41b149ec72e3e9b8a1c6b43e9cabe73b2 -->
검증: canonical contract assertion, 전체 pytest, ruff, compileall 및 독립 문서 검토
근거: exact token claim, 로그인 성공 시각 기준 24시간/30일 absolute TTL, 활동·rerun·복원 미연장, admin ID/password/signing-secret rotation 무효화와 cleanup 전이를 두 문서에 일관되게 명시했습니다.
최종 검증
Local checks: uv run --extra dashboard --extra dev pytest -q exit 0 (111 passed, 4 skipped); ruff exit 0; compileall exit 0; git diff --check exit 0
Runtime/browser: 이번 변경은 문서 두 파일로 제한됐으며 기존 ARM64 Chromium evidence와 관련 source·test는 변경하지 않았습니다.
Commit status: pending (status context 0개)
Actions runs: 없음
다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존 REQUEST_CHANGES를 해제하거나 승인을 의미하지 않는다.
## 구현 재작업 결과
- Source Issue: #6
- Pull Request: #7
- Source Review: #45
- Reviewed head: `9bc64699cb22380b3557ab5c41952017a21ccc69`
- Rework head: `7056e012a7c51eab4405d2e380f44f20e885170e`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Result: `rework_delivered`
### 지적 처리
- `D6-17` — `resolved`
- 분류: `valid_fix`
- 변경: `docs/interface-contract.md`, `docs/runtime-flow.md`
- 검증: canonical contract assertion, 전체 pytest, ruff, compileall 및 독립 문서 검토
- 근거: exact token claim, 로그인 성공 시각 기준 24시간/30일 absolute TTL, 활동·rerun·복원 미연장, admin ID/password/signing-secret rotation 무효화와 cleanup 전이를 두 문서에 일관되게 명시했습니다.
### 최종 검증
- Local checks: `uv run --extra dashboard --extra dev pytest -q` exit 0 (111 passed, 4 skipped); ruff exit 0; compileall exit 0; `git diff --check` exit 0
- Runtime/browser: 이번 변경은 문서 두 파일로 제한됐으며 기존 ARM64 Chromium evidence와 관련 source·test는 변경하지 않았습니다.
- Commit status: `pending` (status context 0개)
- Actions runs: 없음
### 다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존 `REQUEST_CHANGES`를 해제하거나 승인을 의미하지 않는다.
<!-- gitea-agent:implementation-rework/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 review=45 reviewed-head=9bc64699cb22380b3557ab5c41952017a21ccc69 head=7056e012a7c51eab4405d2e380f44f20e885170e contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=e85701dfd5e73a74eec13bb521864b184ac71979aac78da71d17e551ee54a1ff correlation=c1b0ebd755a87801078dbdc5c7b0728b06169abb60c4dc9ddffd99a987db8436 -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py — app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기존 공통 gate와 no call test가 유지된다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example — authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 111147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json — 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py — server 510은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — authentication 25의 Host prefix와 auth server 134142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py — authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py — authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — auth server 147165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md — authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md — authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
D6-16 — unsatisfied
계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md — 현재 정본 구조와 식별자 및 revision 및 추적성은 충족한다. 그러나 기존 REQ 001.03-AC01의 두 constant time 비교 계측 검증에 대응하는 현재 AC는 없고 현재 03-AC01은 비노출만 검증한다. 기존 06-AC01 인증 후 핵심 페이지 회귀 및 06-AC02 MCP 범위 분리 검증도 현재 10-AC01의 배포 문서 검증으로 대체되어 해당 인수 기준이 빠졌다.
테스트 근거: 없음
판정 근거: 중간: 기존 요구사항의 관련 인수 기준을 보존 및 갱신하라는 승인 산출물 계약을 충족하지 않는다. 이미 존재하는 credential 비교와 인증 후 페이지 호환성 및 MCP 범위 분리 검증의 의미와 test 근거를 정본 AC에 복원해야 한다. AC 개수 또는 예전 번호를 그대로 유지하라는 요구는 아니며 코드나 새 기능 추가를 요구하지 않는다.
D6-17 — satisfied
계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md — interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
테스트 근거: 없음
판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock — project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md — 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
테스트 근거: 없음
판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md — 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — persistent test 168179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
D6-22 — satisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py — browser 기록 1023 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
D6-23 — satisfied
계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json — 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 current head까지 변경은 evidence와 두 계약 문서뿐이다. 전체 diff와 현재 blob 및 실행 코드 설정 test fixture dependency lockfile의 불변성과 문서 입력 참조 부재를 확인했다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 구현자의 source bound 실행 기록을 실행 입력 동일성으로 연결한다. 111 passed 및 4 skipped와 ruff 및 compileall exit 0은 구현자 기록이며 reviewer 독립 runtime 실행이나 current head CI 성공으로 표현하지 않는다.
범위 판정
누락: REQ 001의 기존 credential 비교 및 인증 후 페이지 호환성과 MCP 범위 분리 인수 검증 계약
범위 초과: 없음
명세 모순: 없음
결론
직전 TTL과 token 및 rotation 문서 변경 요청은 해소됐다. 다만 기존 REQ 001의 credential 비교와 인증 후 페이지 호환성 및 MCP 범위 분리에 대한 인수 검증 계약 누락을 확인하여 변경을 요청한다. 이는 앞선 검토에서 충분히 발견하지 못한 기존 산출물 누락이며 새 요구사항이 아니다. 구현자 실행 기록과 reviewer 독립 실행을 구분하며 native 게시 여부와 절차 전체 상태는 별도 execution report에서 판정한다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `7056e012a7c51eab4405d2e380f44f20e885170e`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `request_changes`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/app.py` — app 28~32에서 gate 실패 시 반환하고 authentication 297~316에서 검증 성공 때만 보호 기능을 허용한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`
- 판정 근거: 기존 공통 gate와 no call test가 유지된다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: `dashboard/authentication.py`, `.env.example` — authentication 74~108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302~306에서 접근을 차단한다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 111~147의 비교와 지연 및 event log를 auth server 120~126에서 호출한다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 26과 173~190 및 auth server 128~142에서 기본 86400초 token과 cookie를 발급한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 27과 173~190 및 auth server 128~142에서 선택 시 2592000초를 적용한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`
- 판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/common.py`, `docs/evidence/issue-6-persistent-auth-https.json` — 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
- 테스트 근거: `tests/test_dashboard_app.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/server.py` — server 5~10은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134~142 및 158~165는 실제 응답 cookie를 제어한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — authentication 25의 Host prefix와 auth server 134~142 및 158~165의 보안 속성이 browser 기록 44~55 및 72~79에 대응한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: `dashboard/authentication.py` — authentication 194~239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297~316은 session boolean을 사용하지 않는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: `dashboard/authentication.py` — authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — auth server 147~165의 matching deletion과 browser 기록 72~79의 cookie 제거 및 미인증 전이를 확인했다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `docs/requirements/req-001_dashboard-administrator-authentication.md`, `docs/interface-contract.md` — authentication 34 및 179~185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152~165가 관리자 ID 원문의 부재를 확인한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/runtime-flow.md` — authentication 302~312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168~176은 matching deletion과 redirect를 반환한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
- `D6-16` — `unsatisfied`
- 계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
- 구현 근거: `docs/requirements/req-001_dashboard-administrator-authentication.md` — 현재 정본 구조와 식별자 및 revision 및 추적성은 충족한다. 그러나 기존 REQ 001.03-AC01의 두 constant time 비교 계측 검증에 대응하는 현재 AC는 없고 현재 03-AC01은 비노출만 검증한다. 기존 06-AC01 인증 후 핵심 페이지 회귀 및 06-AC02 MCP 범위 분리 검증도 현재 10-AC01의 배포 문서 검증으로 대체되어 해당 인수 기준이 빠졌다.
- 테스트 근거: 없음
- 판정 근거: 중간: 기존 요구사항의 관련 인수 기준을 보존 및 갱신하라는 승인 산출물 계약을 충족하지 않는다. 이미 존재하는 credential 비교와 인증 후 페이지 호환성 및 MCP 범위 분리 검증의 의미와 test 근거를 정본 AC에 복원해야 한다. AC 개수 또는 예전 번호를 그대로 유지하라는 요구는 아니며 코드나 새 기능 추가를 요구하지 않는다.
- `D6-17` — `satisfied`
- 계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
- 구현 근거: `docs/interface-contract.md`, `docs/runtime-flow.md`, `docs/requirements/req-001_dashboard-administrator-authentication.md` — interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
- 테스트 근거: 없음
- 판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: `docs/project-structure.md`, `dashboard/server.py`, `dashboard/auth_server.py`, `pyproject.toml`, `uv.lock` — project 23~46 및 66~83에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: `.env.example`, `docker-compose.yml`, `deploy/k8s/base/deployment.yaml`, `deploy/k8s/README.md`, `README.md` — 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
- 테스트 근거: 없음
- 판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: `dashboard/app.py`, `docs/project-structure.md`, `README.md` — 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
- 테스트 근거: `tests/test_dashboard_app.py`
- 판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — persistent test 168~179의 30일 경계와 AppTest 165~183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`
- 판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
- `D6-22` — `satisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json`, `dashboard/authentication.py`, `dashboard/auth_server.py`, `scripts/persistent_auth_http_evidence.py` — browser 기록 10~23 및 42~80은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
- `D6-23` — `satisfied`
- 계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json` — 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 current head까지 변경은 evidence와 두 계약 문서뿐이다. 전체 diff와 현재 blob 및 실행 코드 설정 test fixture dependency lockfile의 불변성과 문서 입력 참조 부재를 확인했다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`
- 판정 근거: 구현자의 source bound 실행 기록을 실행 입력 동일성으로 연결한다. 111 passed 및 4 skipped와 ruff 및 compileall exit 0은 구현자 기록이며 reviewer 독립 runtime 실행이나 current head CI 성공으로 표현하지 않는다.
### 범위 판정
- 누락: REQ 001의 기존 credential 비교 및 인증 후 페이지 호환성과 MCP 범위 분리 인수 검증 계약
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
직전 TTL과 token 및 rotation 문서 변경 요청은 해소됐다. 다만 기존 REQ 001의 credential 비교와 인증 후 페이지 호환성 및 MCP 범위 분리에 대한 인수 검증 계약 누락을 확인하여 변경을 요청한다. 이는 앞선 검토에서 충분히 발견하지 못한 기존 산출물 누락이며 새 요구사항이 아니다. 구현자 실행 기록과 reviewer 독립 실행을 구분하며 native 게시 여부와 절차 전체 상태는 별도 execution report에서 판정한다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=7056e012a7c51eab4405d2e380f44f20e885170e contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=a41b7cb88901ab3404aac7748adad0217fadabe4239fbbe50b109b947b6e975f correlation=1a637f6f31759525fad8535f0ed5f1ce799c76a32559ed7a315ae76d7940994e -->
검증: targeted pytest 6 passed, 전체 pytest 111 passed/4 skipped, ruff, compileall, canonical AC assertion 및 독립 재검토
근거: 두 credential의 constant-time/non-short-circuit 비교, 인증 후 핵심 Dashboard page/query 호환성, Dashboard와 MCP 인증 범위 분리의 acceptance contract와 기존 test 근거를 복원했습니다.
최종 검증
Local checks: uv run --extra dashboard --extra dev pytest -q exit 0 (111 passed, 4 skipped); ruff exit 0; compileall exit 0; git diff --check exit 0
Runtime/browser: 요구사항 문서 한 파일만 변경됐으며 reviewed head부터 rework head까지 source·test·browser evidence input이 byte-identical하여 기존 ARM64 Chromium evidence를 재사용했습니다.
Independent review: PASS, blocker 없음
Commit status: pending (status context 0개)
Actions runs: 없음
다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존 REQUEST_CHANGES를 해제하거나 승인을 의미하지 않는다.
## 구현 재작업 결과
- Source Issue: #6
- Pull Request: #7
- Source Review: #46
- Reviewed head: `7056e012a7c51eab4405d2e380f44f20e885170e`
- Rework head: `8544ea30f4c4cea5489b7a136d6c53a8a538341a`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Result: `rework_delivered`
### 지적 처리
- `D6-16` — `resolved`
- 분류: `valid_fix`
- 변경: `docs/requirements/req-001_dashboard-administrator-authentication.md`
- 검증: targeted pytest 6 passed, 전체 pytest 111 passed/4 skipped, ruff, compileall, canonical AC assertion 및 독립 재검토
- 근거: 두 credential의 constant-time/non-short-circuit 비교, 인증 후 핵심 Dashboard page/query 호환성, Dashboard와 MCP 인증 범위 분리의 acceptance contract와 기존 test 근거를 복원했습니다.
### 최종 검증
- Local checks: `uv run --extra dashboard --extra dev pytest -q` exit 0 (111 passed, 4 skipped); ruff exit 0; compileall exit 0; `git diff --check` exit 0
- Runtime/browser: 요구사항 문서 한 파일만 변경됐으며 reviewed head부터 rework head까지 source·test·browser evidence input이 byte-identical하여 기존 ARM64 Chromium evidence를 재사용했습니다.
- Independent review: PASS, blocker 없음
- Commit status: `pending` (status context 0개)
- Actions runs: 없음
### 다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존 `REQUEST_CHANGES`를 해제하거나 승인을 의미하지 않는다.
<!-- gitea-agent:implementation-rework/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 review=46 reviewed-head=7056e012a7c51eab4405d2e380f44f20e885170e head=8544ea30f4c4cea5489b7a136d6c53a8a538341a contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=8c4e4aebcd0df26b38236a09fdcfe6f6a72d1b128503be4602b15cff44c7fb66 correlation=6045986d83d4022f8f79f35d49c9f6826601a151dc1c28b6a1c6cc533b4c5789 -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py — app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기존 공통 gate와 no call test가 유지된다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example — authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 111147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py — 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json — 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py — server 510은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — authentication 25의 Host prefix와 auth server 134142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py — authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py — authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — auth server 147165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md — authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md — authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
D6-16 — unsatisfied
계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md — 현재 req 문서 269287 및 429467에서 직전 두 credential 비교 계측과 핵심 page 회귀 및 MCP 범위 분리 AC가 복원됐다. Baseline AC 15개를 의미별로 대조한 결과 기존 REQ 001.04-AC02의 성공 시 실패 지연 함수 미사용 및 반복 실패 후 failure counter와 lock state 미생성 검증에 대응하는 AC가 없다. 현재 289~307의 04-AC01은 두 credential 실패 지연과 일반 오류 및 cookie DB gateway 불변성만 검증한다. 성공 무지연은 요구사항 96에 남아 있으나 인수 검증 대응은 누락됐다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 중간: 직전 세 변경 요청은 해소됐다. 그러나 기존 성공 무지연과 애플리케이션 잠금 미도입의 인수 검증 의미를 정본 AC에 보존 및 갱신하라는 승인 계약은 아직 미충족이다. 현재 구현은 이 동작을 유지하며 코드 변경이나 새 기능 또는 예전 AC ID와 개수의 동일성을 요구하지 않는다. 앞선 리뷰가 baseline 전체 의미 대응을 끝내지 못해 놓친 기존 문서 누락을 이번에 확인했다.
D6-17 — satisfied
계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md — interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
테스트 근거: 없음
판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock — project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md — 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
테스트 근거: 없음
판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md — 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json — persistent test 168179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
D6-22 — satisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py — browser 기록 1023 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
D6-23 — satisfied
계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json — 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 current head까지 변경은 evidence 및 두 계약 문서 및 REQ 001뿐이다. 전체 diff와 blob hash 및 실행 코드 설정 test fixture dependency lockfile의 동일성과 실행 입력의 문서 참조 부재를 검증했다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 구현자의 source bound 실행 기록을 실행 입력 동일성으로 연결한다. 111 passed 및 4 skipped와 ruff 및 compileall exit 0은 구현자 기록이며 reviewer 독립 runtime 실행이나 current head CI 성공으로 표현하지 않는다.
범위 판정
누락: REQ 001의 기존 성공 무지연 및 실패 횟수와 잠금 상태 미생성 인수 검증 계약
범위 초과: 없음
명세 모순: 없음
결론
직전 두 credential 비교 계측과 핵심 page 회귀 및 MCP 범위 분리 AC는 복원됐다. Baseline AC 전체를 의미별로 재검토하여 기존 성공 무지연 및 애플리케이션 잠금 미도입의 인수 검증 대응 누락을 확인했으므로 변경을 요청한다. 구현 동작의 회귀나 새 요구사항이 아니라 앞선 검토가 놓친 기존 문서 계약 누락이다. 구현자 실행 기록과 reviewer 독립 실행 및 native 게시와 전체 절차 상태를 구분한다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `8544ea30f4c4cea5489b7a136d6c53a8a538341a`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `request_changes`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/app.py` — app 28~32에서 gate 실패 시 반환하고 authentication 297~316에서 검증 성공 때만 보호 기능을 허용한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`
- 판정 근거: 기존 공통 gate와 no call test가 유지된다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: `dashboard/authentication.py`, `.env.example` — authentication 74~108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302~306에서 접근을 차단한다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 111~147의 비교와 지연 및 event log를 auth server 120~126에서 호출한다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 26과 173~190 및 auth server 128~142에서 기본 86400초 token과 cookie를 발급한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 27과 173~190 및 auth server 128~142에서 선택 시 2592000초를 적용한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
- 테스트 근거: `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py` — 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`
- 판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/common.py`, `docs/evidence/issue-6-persistent-auth-https.json` — 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
- 테스트 근거: `tests/test_dashboard_app.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: `dashboard/auth_server.py`, `dashboard/server.py` — server 5~10은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134~142 및 158~165는 실제 응답 cookie를 제어한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — authentication 25의 Host prefix와 auth server 134~142 및 158~165의 보안 속성이 browser 기록 44~55 및 72~79에 대응한다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: `dashboard/authentication.py` — authentication 194~239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297~316은 session boolean을 사용하지 않는다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: `dashboard/authentication.py` — authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — auth server 147~165의 matching deletion과 browser 기록 72~79의 cookie 제거 및 미인증 전이를 확인했다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: `dashboard/authentication.py`, `docs/requirements/req-001_dashboard-administrator-authentication.md`, `docs/interface-contract.md` — authentication 34 및 179~185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152~165가 관리자 ID 원문의 부재를 확인한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/runtime-flow.md` — authentication 302~312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168~176은 matching deletion과 redirect를 반환한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
- `D6-16` — `unsatisfied`
- 계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
- 구현 근거: `docs/requirements/req-001_dashboard-administrator-authentication.md` — 현재 req 문서 269~287 및 429~467에서 직전 두 credential 비교 계측과 핵심 page 회귀 및 MCP 범위 분리 AC가 복원됐다. Baseline AC 15개를 의미별로 대조한 결과 기존 REQ 001.04-AC02의 성공 시 실패 지연 함수 미사용 및 반복 실패 후 failure counter와 lock state 미생성 검증에 대응하는 AC가 없다. 현재 289~307의 04-AC01은 두 credential 실패 지연과 일반 오류 및 cookie DB gateway 불변성만 검증한다. 성공 무지연은 요구사항 96에 남아 있으나 인수 검증 대응은 누락됐다.
- 테스트 근거: `tests/test_dashboard_authentication.py`, `tests/test_dashboard_auth_server.py`
- 판정 근거: 중간: 직전 세 변경 요청은 해소됐다. 그러나 기존 성공 무지연과 애플리케이션 잠금 미도입의 인수 검증 의미를 정본 AC에 보존 및 갱신하라는 승인 계약은 아직 미충족이다. 현재 구현은 이 동작을 유지하며 코드 변경이나 새 기능 또는 예전 AC ID와 개수의 동일성을 요구하지 않는다. 앞선 리뷰가 baseline 전체 의미 대응을 끝내지 못해 놓친 기존 문서 누락을 이번에 확인했다.
- `D6-17` — `satisfied`
- 계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
- 구현 근거: `docs/interface-contract.md`, `docs/runtime-flow.md`, `docs/requirements/req-001_dashboard-administrator-authentication.md` — interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
- 테스트 근거: 없음
- 판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: `docs/project-structure.md`, `dashboard/server.py`, `dashboard/auth_server.py`, `pyproject.toml`, `uv.lock` — project 23~46 및 66~83에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`
- 판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: `.env.example`, `docker-compose.yml`, `deploy/k8s/base/deployment.yaml`, `deploy/k8s/README.md`, `README.md` — 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
- 테스트 근거: 없음
- 판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: `dashboard/app.py`, `docs/project-structure.md`, `README.md` — 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
- 테스트 근거: `tests/test_dashboard_app.py`
- 판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: `dashboard/authentication.py`, `dashboard/auth_server.py`, `docs/evidence/issue-6-persistent-auth-https.json` — persistent test 168~179의 30일 경계와 AppTest 165~183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`
- 판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
- `D6-22` — `satisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json`, `dashboard/authentication.py`, `dashboard/auth_server.py`, `scripts/persistent_auth_http_evidence.py` — browser 기록 10~23 및 42~80은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
- 테스트 근거: `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_authentication_app.py`
- 판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
- `D6-23` — `satisfied`
- 계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
- 구현 근거: `docs/evidence/issue-6-persistent-auth-https.json` — 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 current head까지 변경은 evidence 및 두 계약 문서 및 REQ 001뿐이다. 전체 diff와 blob hash 및 실행 코드 설정 test fixture dependency lockfile의 동일성과 실행 입력의 문서 참조 부재를 검증했다.
- 테스트 근거: `tests/test_dashboard_persistent_authentication.py`, `tests/test_dashboard_authentication_app.py`, `tests/test_dashboard_auth_server.py`, `tests/test_dashboard_app.py`
- 판정 근거: 구현자의 source bound 실행 기록을 실행 입력 동일성으로 연결한다. 111 passed 및 4 skipped와 ruff 및 compileall exit 0은 구현자 기록이며 reviewer 독립 runtime 실행이나 current head CI 성공으로 표현하지 않는다.
### 범위 판정
- 누락: REQ 001의 기존 성공 무지연 및 실패 횟수와 잠금 상태 미생성 인수 검증 계약
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
직전 두 credential 비교 계측과 핵심 page 회귀 및 MCP 범위 분리 AC는 복원됐다. Baseline AC 전체를 의미별로 재검토하여 기존 성공 무지연 및 애플리케이션 잠금 미도입의 인수 검증 대응 누락을 확인했으므로 변경을 요청한다. 구현 동작의 회귀나 새 요구사항이 아니라 앞선 검토가 놓친 기존 문서 계약 누락이다. 구현자 실행 기록과 reviewer 독립 실행 및 native 게시와 전체 절차 상태를 구분한다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=8544ea30f4c4cea5489b7a136d6c53a8a538341a contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=ade1e446dd21f9065933052d116abf617baf1cc4ef2e6ffe6864b0e60046553c correlation=ce40550f9f5e01fd077e0f9269f5400e43385ccf2a0ceea98b78aa833c44336d -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py; app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기존 공통 gate와 no call test가 유지된다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example; authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py; server 510은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication 25의 Host prefix와 auth server 134142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py; authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 147165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
D6-16 — satisfied
계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001 문서 269327에서 두 credential 비교 계측과 성공 지연 및 잠금 없음 AC를 확인했다. 449487에서 핵심 page 회귀와 MCP 범위 분리 AC가 유지된다. Baseline AC 15개 전체를 문서 전문 및 참조 test에 의미 대응했고 승인된 session 대체와 병합을 확인했다. 새 04 AC02는 auth server 120~132의 실패 분기 전용 지연 호출과 authentication source의 failure counter 및 lock state 부재와 기존 지연 helper 및 성공 login test를 연결한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
D6-17 — satisfied
계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
테스트 근거: 없음
판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
테스트 근거: 없음
판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; persistent test 168179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
D6-22 — satisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; browser 기록 1023 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
D6-23 — satisfied
계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json; 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 reviewed head 33118664316707e19146d803fb21d400073c5206까지 전체 diff는 evidence와 세 계약 문서뿐이다. 비문서 실행 입력 112개 전체의 blob 동일성과 ancestor 관계 및 evidence에 기록된 source hash를 검증했다. 현재 재작업은 요구사항 문서 한 파일만 변경했다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: trusted source bound 구현자 실행 기록의 111 passed 및 4 skipped와 ruff 및 compileall exit 0을 재사용한다. reviewer가 runtime 또는 browser test를 독립 실행한 것은 아니며 status는 pending이고 context 0개이므로 CI 성공으로 취급하지 않는다.
범위 판정
누락: 없음
범위 초과: 없음
명세 모순: 없음
결론
승인된 Issue 6 계약의 전체 항목과 baseline AC 전체의 의미를 현재 문서 및 구현과 검증 근거에 대조했다. 직전 D6-16의 성공 무지연 및 잠금 상태 미생성 인수 검증 계약은 복원됐으며 필수 누락과 범위 초과 및 명세 모순은 없다. 구현자의 source bound 실행 기록을 실행 입력 동일성으로 연결했으며 reviewer 독립 runtime 실행이나 CI 성공으로 표현하지 않는다. 명세 적합성을 승인한다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `33118664316707e19146d803fb21d400073c5206`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `approve`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: dashboard/authentication.py, dashboard/app.py; app 28~32에서 gate 실패 시 반환하고 authentication 297~316에서 검증 성공 때만 보호 기능을 허용한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 공통 gate와 no call test가 유지된다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: dashboard/authentication.py, .env.example; authentication 74~108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302~306에서 접근을 차단한다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111~147의 비교와 지연 및 event log를 auth server 120~126에서 호출한다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 기존 credential 비교와 실패 지연 및 비민감 log 계약을 유지한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173~190 및 auth server 128~142에서 기본 86400초 token과 cookie를 발급한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173~190 및 auth server 128~142에서 선택 시 2592000초를 적용한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py
- 판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
- 테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 단위 및 통합 source와 연결된 실제 browser 복원 기록을 함께 확인했다. 브라우저 종료는 persistence 속성과 동일 복원 경계로 설명된다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: dashboard/auth_server.py, dashboard/server.py; server 5~10은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134~142 및 158~165는 실제 응답 cookie를 제어한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 실제 HTTP response의 발급과 삭제 책임이 Dashboard 내부에 있다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication 25의 Host prefix와 auth server 134~142 및 158~165의 보안 속성이 browser 기록 44~55 및 72~79에 대응한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: HTTPS host only scope와 Secure 및 HttpOnly 및 SameSite 및 matching deletion을 확인했다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: dashboard/authentication.py; authentication 194~239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297~316은 session boolean을 사용하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 147~165의 matching deletion과 browser 기록 72~79의 cookie 제거 및 미인증 전이를 확인했다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: DB 비의존 logout과 cookie 제거 후 gate 재검증이 구현돼 있다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179~185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152~165가 관리자 ID 원문의 부재를 확인한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302~312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168~176은 matching deletion과 redirect를 반환한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 이전 실패 cookie 폐기 누락은 해소됐다. 단위 실패 검증과 AppTest 및 browser malformed cleanup 기록을 구분해 확인했다.
- `D6-16` — `satisfied`
- 계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
- 구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001 문서 269~327에서 두 credential 비교 계측과 성공 지연 및 잠금 없음 AC를 확인했다. 449~487에서 핵심 page 회귀와 MCP 범위 분리 AC가 유지된다. Baseline AC 15개 전체를 문서 전문 및 참조 test에 의미 대응했고 승인된 session 대체와 병합을 확인했다. 새 04 AC02는 auth server 120~132의 실패 분기 전용 지연 호출과 authentication source의 failure counter 및 lock state 부재와 기존 지연 helper 및 성공 login test를 연결한다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
- `D6-17` — `satisfied`
- 계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
- 구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
- 테스트 근거: 없음
- 판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 23~46 및 66~83에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
- 테스트 근거: 없음
- 판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
- 테스트 근거: tests/test_dashboard_app.py
- 판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; persistent test 168~179의 30일 경계와 AppTest 165~183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
- 판정 근거: 이전 필수 만료 경계 및 signing rotation 및 logout 후 차단 evidence의 누락은 해소됐다. 모든 실패 종류마다 별도 browser case를 새 요구로 만들지 않는다.
- `D6-22` — `satisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; browser 기록 10~23 및 42~80은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 이전 protocol only evidence와 구분되는 실제 browser 실행 기록이며 raw cookie 값은 삭제돼 있다. 본 spec reviewer가 browser를 새로 실행한 것은 아니다.
- `D6-23` — `satisfied`
- 계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
- 구현 근거: docs/evidence/issue-6-persistent-auth-https.json; 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 reviewed head 33118664316707e19146d803fb21d400073c5206까지 전체 diff는 evidence와 세 계약 문서뿐이다. 비문서 실행 입력 112개 전체의 blob 동일성과 ancestor 관계 및 evidence에 기록된 source hash를 검증했다. 현재 재작업은 요구사항 문서 한 파일만 변경했다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
- 판정 근거: trusted source bound 구현자 실행 기록의 111 passed 및 4 skipped와 ruff 및 compileall exit 0을 재사용한다. reviewer가 runtime 또는 browser test를 독립 실행한 것은 아니며 status는 pending이고 context 0개이므로 CI 성공으로 취급하지 않는다.
### 범위 판정
- 누락: 없음
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
승인된 Issue 6 계약의 전체 항목과 baseline AC 전체의 의미를 현재 문서 및 구현과 검증 근거에 대조했다. 직전 D6-16의 성공 무지연 및 잠금 상태 미생성 인수 검증 계약은 복원됐으며 필수 누락과 범위 초과 및 명세 모순은 없다. 구현자의 source bound 실행 기록을 실행 입력 동일성으로 연결했으며 reviewer 독립 runtime 실행이나 CI 성공으로 표현하지 않는다. 명세 적합성을 승인한다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=33118664316707e19146d803fb21d400073c5206 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=4b7f77f4e36f7e674bda52440307bb6a583541878a0623cb6c5ba2677ba9bab9 correlation=0d43cf5bee51fa1d282534f574dd629f046755dbe471ccf43cd590958bfd4e86 -->
Reviewer: gitea-review-agent (stable account ID 7), 독립 코드 리뷰
Verdict: request_changes
검토 범위
변경 파일 20개 전체 diff, 인증 gate와 기존 호출자, 관련 unit/AppTest/HTTP tests, dependency/lockfile, Compose/Kubernetes 및 운영 문서, 실제 Uvicorn/Streamlit 경로를 확인했다. 요구사항을 추가하거나 변경하지 않았다. 두 결함은 승인된 인증 방식 자체가 아니라 새 ASGI adapter의 실행·자원 경계 문제다.
확정 결함
F-001 — High: 실패 로그인 지연이 공유 event loop를 차단한다
위치: dashboard/auth_server.py:120-126, 특히 124; 호출되는 dashboard/authentication.py:121-130.
조건: 같은 origin의 HTTPS /api/auth/login에 잘못된 ID/password를 제출한다. 관리자 credential이나 cookie가 없어도 가능하며 비브라우저 client가 Origin을 설정할 수 있다.
실패 모드: async def login()이 동기 enforce_failed_login_delay()를 직접 실행하여 time.sleep()으로 Uvicorn event loop를 멈춘다. 이전 Streamlit 실행 경로의 helper를 ASGI coroutine으로 옮기면서 요청 간 독립 처리가 깨졌다.
영향: 실패 요청마다 최소 1초 동안 같은 프로세스의 정상 로그인, logout, health 및 WebSocket I/O가 지연된다. 적은 수의 미인증 요청으로 서비스 가용성을 저하시킬 수 있고 health probe timeout과 맞물릴 수 있다. 이것은 승인된 1초 지연을 없애라는 요구가 아니며 rate limiting 정책 변경도 아니다.
직접 재현: 실제 dashboard.server:app을 loopback HTTPS/Uvicorn으로 실행했다. baseline health는 0.016754406038671732초였다. 실패 login 6건을 동시에 제출하자 모두 303을 반환하는 데 6.093019616790116초가 걸렸고, 별도 health 요청은 5.932137297000736초 지연됐다. 첫 재현에서도 health 지연 6.160729866940528초가 확인됐다. 지연을 mock하지 않았다.
근거: https-runtime.json, https-runtime-first-attempt.json, reproductions.json, 해당 source 위치. 기존 테스트 111개가 통과해도 이 동시성 회귀를 검출하지 못한다.
계약 관계: 기존 실패당 1초 지연과 무잠금 정책을 유지하면서 docs/runtime-flow.md:89-95의 독립 요청 처리에 맞아야 한다.
제한된 수정 방향: 실패 응답의 최소 지연은 유지하되 event loop를 양보하는 async wait 또는 적절한 thread offload를 사용한다. 실제 delay를 켠 채 동시 실패 요청과 health/정상 요청의 응답성을 검증하는 회귀 테스트를 추가한다.
F-002 — Medium: 4096바이트 제한이 전체 body 수신 후 적용되어 자원 상한이 아니다
위치: dashboard/auth_server.py:94-97.
조건: 허용된 HTTPS Origin과 application/x-www-form-urlencoded로 oversized 또는 chunked login body를 보낸다. credential 검증 전에 실행된다.
실패 모드: await request.body()가 body 전체를 누적한 다음에야 len(body) > 4096을 검사한다. 그러므로 명시된 상한은 응답 검증값일 뿐 수신·메모리 사용을 제한하지 않는다.
영향: 인증 전 요청으로 제한보다 큰 데이터를 계속 수신·저장할 수 있고 큰 body 또는 병렬 요청이 공유 Dashboard의 메모리·대역폭을 소모한다. 상위 proxy의 별도 제한이 없는 직접 ASGI 경로에서 발생하며, resource exhaustion으로 이어질 수 있다. OOM 자체를 유발한 것은 아니다.
직접 재현: 실제 dashboard.server:app의 ASGI 경로에 8192바이트 chunk 16개를 전달했다. 제한은 4096바이트인데 16개 chunk, 총 131072바이트를 모두 소비한 후 400을 반환했다. credential 검증 전의 거부였다.
근거: reproductions.json의 body_limit 및 해당 source 위치.
계약 관계: 새로운 bounded login request와 malformed/oversized body 거부 경계의 구현·자원 안전성 문제이며 새 기능 요구가 아니다.
제한된 수정 방향: stream을 읽으며 누적 byte 상한을 검사하고 초과 시 즉시 반환한다. Content-Length 사전 검사는 보조로만 사용하고 chunked/missing/incorrect length에도 상한을 적용한다. 4096/4097바이트와 multi-chunk 입력에서 초과 이후 chunk를 소비하지 않는 테스트를 추가한다.
비차단 제안·잔여 위험
S-001 — 실행 방식별 proxy 변수 구분:README.md:52와 .env.example:8-9의 JARCA_DASHBOARD_FORWARDED_ALLOW_IPS는 Compose가 docker-compose.yml:35에서 실제 FORWARDED_ALLOW_IPS로 매핑할 때만 적용된다. README의 직접 Uvicorn 명령에서는 자동 매핑되지 않는다. non-Compose 실행에는 실제 변수 또는 CLI flag를 안내하면 좋다. 실험에서 Compose 없이 prefixed 변수를 설정하면 effective allowlist가 loopback에 남아 비loopback proxy 경로가 403이고, 실제 Uvicorn allowlist를 설정하면 303이었다. Kubernetes 문서 deploy/k8s/README.md:43는 이미 올바른 변수를 사용한다. 기본 loopback trust와 HTTPS-only 요구 자체를 결함으로 보지 않는다.
S-002 — 기존 smoke의 진입점 불일치: 변경된 배포는 uvicorn dashboard.server:app인데 기존 scripts/ci_dashboard_http_smoke.py:79-93와 .gitea/release.yaml:72-92는 streamlit run dashboard/app.py를 검증한다. 기존 파일 자체의 결함으로 단정하지 않지만 새 adapter와 auth routes의 packaging/runtime 회귀를 놓칠 수 있다. 실제 배포 진입점을 사용하는 focused smoke로 정렬할 것을 제안한다.
브라우저 종료/새 profile 생성, 실제 Kubernetes/Compose 배포, 운영 Ingress·rate limiting, Docker 이미지 빌드는 reviewer가 실행하지 않았다. MCP 인증은 승인 범위 밖이며 변경하지 않았다.
구현자 Chromium 기록은 재사용 근거이지 reviewer의 새 browser 실행이 아니다. 기록된 source revision에서 현재 head까지 후속 diff가 문서/evidence뿐이고 기록된 5개 hash 및 non-doc runtime input 111개의 blob 동일성을 직접 확인했다. 다른 enumeration 방식의 총수와 혼동하지 않는다.
PYTHONDONTWRITEBYTECODE=1 .venv/bin/python /opt/data/.review-runs/jarca-pr7-resume-20261002T110307Z/https_runtime.py: 최종 exit 0. 실제 HTTPS login 303, reconnect HTTP 200, 새 Streamlit WebSocket에서 인증된 화면 복원(53 messages), logout deletion 303. HTTP/framework/protocol 실행이며 browser 실행으로 주장하지 않는다.
첫 HTTPS attempt는 WebSocket 15초 timeout으로 exit 1이었다. 전체 pytest와 겹친 실행이었고 이후 단독 재실행은 통과했다. contention이 확정 원인이라고 단정하지 않고 https-protocol-first-attempt.log와 첫 run 결과를 보존한다. 이 timeout 자체는 확정 제품 결함으로 채택하지 않았다.
helper의 첫 사전 준비도 fixture filename guard에 거부돼 수정 후 재실행했다. 수정한 것은 checkout 밖 reviewer helper뿐이다.
Static added-line security scan 후보 4건은 secret store placeholder 또는 synthetic test-only password이며 실제 secret 노출로 채택하지 않았다. shell=True/eval/pickle/formatted-SQL 후보는 없었다. 이는 전체 보안의 무결함 증명은 아니다.
Snapshot 시 CI status와 Actions run은 없었다. reviewer execution과 CI 성공을 혼동하지 않는다.
재작업과 재리뷰
같은 PR에서 F-001/F-002를 수정하고 source·test 변경을 새 head로 제출한다. 구현 재작업 → 새 head의 spec-agent pass → 독립 code 재리뷰 순서다. 이전 spec 승인 #48을 새 head에 승계하지 않는다. 본 결과는 merge·release·deployment 승인이 아니다.
# 독립 코드 리뷰 — jsfamily/jarca-ledger-mcp Issue #6 / PR #7
- Reviewed head: `33118664316707e19146d803fb21d400073c5206`
- Base: `7bea8b368e02e98afd4b2dc463fb270b0e6eb297`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Current-head spec pass: native review #48, `APPROVED`
- Reviewer: `gitea-review-agent` (stable account ID 7), 독립 코드 리뷰
- Verdict: `request_changes`
## 검토 범위
변경 파일 20개 전체 diff, 인증 gate와 기존 호출자, 관련 unit/AppTest/HTTP tests, dependency/lockfile, Compose/Kubernetes 및 운영 문서, 실제 Uvicorn/Streamlit 경로를 확인했다. 요구사항을 추가하거나 변경하지 않았다. 두 결함은 승인된 인증 방식 자체가 아니라 새 ASGI adapter의 실행·자원 경계 문제다.
## 확정 결함
### F-001 — High: 실패 로그인 지연이 공유 event loop를 차단한다
- 위치: `dashboard/auth_server.py:120-126`, 특히 `124`; 호출되는 `dashboard/authentication.py:121-130`.
- 조건: 같은 origin의 HTTPS `/api/auth/login`에 잘못된 ID/password를 제출한다. 관리자 credential이나 cookie가 없어도 가능하며 비브라우저 client가 Origin을 설정할 수 있다.
- 실패 모드: `async def login()`이 동기 `enforce_failed_login_delay()`를 직접 실행하여 `time.sleep()`으로 Uvicorn event loop를 멈춘다. 이전 Streamlit 실행 경로의 helper를 ASGI coroutine으로 옮기면서 요청 간 독립 처리가 깨졌다.
- 영향: 실패 요청마다 최소 1초 동안 같은 프로세스의 정상 로그인, logout, health 및 WebSocket I/O가 지연된다. 적은 수의 미인증 요청으로 서비스 가용성을 저하시킬 수 있고 health probe timeout과 맞물릴 수 있다. 이것은 승인된 1초 지연을 없애라는 요구가 아니며 rate limiting 정책 변경도 아니다.
- 직접 재현: 실제 `dashboard.server:app`을 loopback HTTPS/Uvicorn으로 실행했다. baseline health는 `0.016754406038671732`초였다. 실패 login 6건을 동시에 제출하자 모두 303을 반환하는 데 `6.093019616790116`초가 걸렸고, 별도 health 요청은 `5.932137297000736`초 지연됐다. 첫 재현에서도 health 지연 `6.160729866940528`초가 확인됐다. 지연을 mock하지 않았다.
- 근거: `https-runtime.json`, `https-runtime-first-attempt.json`, `reproductions.json`, 해당 source 위치. 기존 테스트 111개가 통과해도 이 동시성 회귀를 검출하지 못한다.
- 계약 관계: 기존 실패당 1초 지연과 무잠금 정책을 유지하면서 `docs/runtime-flow.md:89-95`의 독립 요청 처리에 맞아야 한다.
- 제한된 수정 방향: 실패 응답의 최소 지연은 유지하되 event loop를 양보하는 async wait 또는 적절한 thread offload를 사용한다. 실제 delay를 켠 채 동시 실패 요청과 health/정상 요청의 응답성을 검증하는 회귀 테스트를 추가한다.
### F-002 — Medium: 4096바이트 제한이 전체 body 수신 후 적용되어 자원 상한이 아니다
- 위치: `dashboard/auth_server.py:94-97`.
- 조건: 허용된 HTTPS Origin과 `application/x-www-form-urlencoded`로 oversized 또는 chunked login body를 보낸다. credential 검증 전에 실행된다.
- 실패 모드: `await request.body()`가 body 전체를 누적한 다음에야 `len(body) > 4096`을 검사한다. 그러므로 명시된 상한은 응답 검증값일 뿐 수신·메모리 사용을 제한하지 않는다.
- 영향: 인증 전 요청으로 제한보다 큰 데이터를 계속 수신·저장할 수 있고 큰 body 또는 병렬 요청이 공유 Dashboard의 메모리·대역폭을 소모한다. 상위 proxy의 별도 제한이 없는 직접 ASGI 경로에서 발생하며, resource exhaustion으로 이어질 수 있다. OOM 자체를 유발한 것은 아니다.
- 직접 재현: 실제 `dashboard.server:app`의 ASGI 경로에 8192바이트 chunk 16개를 전달했다. 제한은 4096바이트인데 16개 chunk, 총 `131072`바이트를 모두 소비한 후 400을 반환했다. credential 검증 전의 거부였다.
- 근거: `reproductions.json`의 `body_limit` 및 해당 source 위치.
- 계약 관계: 새로운 bounded login request와 malformed/oversized body 거부 경계의 구현·자원 안전성 문제이며 새 기능 요구가 아니다.
- 제한된 수정 방향: stream을 읽으며 누적 byte 상한을 검사하고 초과 시 즉시 반환한다. Content-Length 사전 검사는 보조로만 사용하고 chunked/missing/incorrect length에도 상한을 적용한다. 4096/4097바이트와 multi-chunk 입력에서 초과 이후 chunk를 소비하지 않는 테스트를 추가한다.
## 비차단 제안·잔여 위험
- **S-001 — 실행 방식별 proxy 변수 구분:** `README.md:52`와 `.env.example:8-9`의 `JARCA_DASHBOARD_FORWARDED_ALLOW_IPS`는 Compose가 `docker-compose.yml:35`에서 실제 `FORWARDED_ALLOW_IPS`로 매핑할 때만 적용된다. README의 직접 Uvicorn 명령에서는 자동 매핑되지 않는다. non-Compose 실행에는 실제 변수 또는 CLI flag를 안내하면 좋다. 실험에서 Compose 없이 prefixed 변수를 설정하면 effective allowlist가 loopback에 남아 비loopback proxy 경로가 403이고, 실제 Uvicorn allowlist를 설정하면 303이었다. Kubernetes 문서 `deploy/k8s/README.md:43`는 이미 올바른 변수를 사용한다. 기본 loopback trust와 HTTPS-only 요구 자체를 결함으로 보지 않는다.
- **S-002 — 기존 smoke의 진입점 불일치:** 변경된 배포는 `uvicorn dashboard.server:app`인데 기존 `scripts/ci_dashboard_http_smoke.py:79-93`와 `.gitea/release.yaml:72-92`는 `streamlit run dashboard/app.py`를 검증한다. 기존 파일 자체의 결함으로 단정하지 않지만 새 adapter와 auth routes의 packaging/runtime 회귀를 놓칠 수 있다. 실제 배포 진입점을 사용하는 focused smoke로 정렬할 것을 제안한다.
- 브라우저 종료/새 profile 생성, 실제 Kubernetes/Compose 배포, 운영 Ingress·rate limiting, Docker 이미지 빌드는 reviewer가 실행하지 않았다. MCP 인증은 승인 범위 밖이며 변경하지 않았다.
- 구현자 Chromium 기록은 재사용 근거이지 reviewer의 새 browser 실행이 아니다. 기록된 source revision에서 현재 head까지 후속 diff가 문서/evidence뿐이고 기록된 5개 hash 및 non-doc runtime input 111개의 blob 동일성을 직접 확인했다. 다른 enumeration 방식의 총수와 혼동하지 않는다.
## 직접 실행 결과와 provenance
- Exact-head borrowed implementation worktree; Python `3.13.5`, Streamlit `1.58.0`, Uvicorn `0.49.0`, Starlette `1.3.1`, HTTPX `0.28.1`, itsdangerous `2.2.0`, websockets `16.0`.
- `PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -q -p no:cacheprovider --basetemp=/opt/data/.review-runs/jarca-pr7-resume-20261002T110307Z/pytest-tmp`: exit 0, **111 passed, 4 skipped**. 출력은 project addopts와 겹친 quiet mode여서 progress 문자를 프로그램으로 집계했다. Starlette TestClient의 httpx deprecation warning 1건.
- `PYTHONDONTWRITEBYTECODE=1 .venv/bin/ruff check --no-cache dashboard tests scripts`: exit 0, `All checks passed!`.
- `git diff --check 7bea8b368e02e98afd4b2dc463fb270b0e6eb297 33118664316707e19146d803fb21d400073c5206`: exit 0.
- `PYTHONDONTWRITEBYTECODE=1 uv lock --check --offline`: exit 0, 72 packages resolved; install이나 source 수정은 하지 않았다.
- `PYTHONDONTWRITEBYTECODE=1 .venv/bin/python /opt/data/.review-runs/jarca-pr7-resume-20261002T110307Z/reproduce.py`: exit 0, concurrency/body/proxy 재현.
- `PYTHONDONTWRITEBYTECODE=1 .venv/bin/python /opt/data/.review-runs/jarca-pr7-resume-20261002T110307Z/https_runtime.py`: 최종 exit 0. 실제 HTTPS login 303, reconnect HTTP 200, **새 Streamlit WebSocket에서 인증된 화면 복원(53 messages)**, logout deletion 303. HTTP/framework/protocol 실행이며 browser 실행으로 주장하지 않는다.
- 첫 HTTPS attempt는 WebSocket 15초 timeout으로 exit 1이었다. 전체 pytest와 겹친 실행이었고 이후 단독 재실행은 통과했다. contention이 확정 원인이라고 단정하지 않고 `https-protocol-first-attempt.log`와 첫 run 결과를 보존한다. 이 timeout 자체는 확정 제품 결함으로 채택하지 않았다.
- helper의 첫 사전 준비도 fixture filename guard에 거부돼 수정 후 재실행했다. 수정한 것은 checkout 밖 reviewer helper뿐이다.
- Static added-line security scan 후보 4건은 secret store placeholder 또는 synthetic test-only password이며 실제 secret 노출로 채택하지 않았다. shell=True/eval/pickle/formatted-SQL 후보는 없었다. 이는 전체 보안의 무결함 증명은 아니다.
- Snapshot 시 CI status와 Actions run은 없었다. reviewer execution과 CI 성공을 혼동하지 않는다.
## 재작업과 재리뷰
같은 PR에서 F-001/F-002를 수정하고 source·test 변경을 새 head로 제출한다. **구현 재작업 → 새 head의 spec-agent pass → 독립 code 재리뷰** 순서다. 이전 spec 승인 #48을 새 head에 승계하지 않는다. 본 결과는 merge·release·deployment 승인이 아니다.
<!-- gitea-agent:code-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=33118664316707e19146d803fb21d400073c5206 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py; app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기존 공통 gate와 no call test가 유지된다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example; authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 실패당 최소 지연과 성공 무지연 및 무잠금 계약의 정적 구현은 유지된다. 구현자가 주장한 현재 head 실행 성공과 reviewer의 정적 판단은 구분한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 변경되지 않은 gate 및 page query와 복원 test의 정적 대응을 확인했다. 과거 browser 복원 관찰을 새 login source의 실행 성공으로 표현하지 않는다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py; server의 같은 process route wiring은 유지된다. auth server 139146과 163169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 발급 및 삭제 책임은 Dashboard 내부에 있고 외부 인증 middleware로 이전하지 않았다. 새 source의 실제 browser 실행 확인은 D6-22에서 별도 판정한다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication의 Host prefix와 auth server 139146 및 163169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: cookie 보안 속성은 정적 source와 test 대응으로 확인했다. 과거 browser 기록을 현재 source 실행으로 승계하지 않는다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py; authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 152169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166180의 logout response test가 대응한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 기존 logout 및 보호 gate는 변경되지 않았다. 과거 browser 관찰과 현재 head 실행은 구분한다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: invalid cookie 거부와 matching deletion은 source와 test로 확인했다. 과거 browser malformed cleanup은 과거 관찰만으로 남기며 현재 head browser 실행 증명은 D6-22에서 별도로 판정한다.
D6-16 — satisfied
계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001의 AC 15개와 baseline 전체의 인수 검증 의미를 문서 전문과 source 및 test에 재대응했다. 직전 복원된 성공 무지연 및 counter와 lock state 부재 AC는 유지된다. 현재 rework는 auth server와 route test 두 파일뿐이며 요구사항과 추적성 문서는 직전 승인 head와 동일하다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
D6-17 — satisfied
계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
테스트 근거: 없음
판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
테스트 근거: 없음
판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; 두 TTL 경계와 미연장 및 새 session 복원과 logout 및 rotation 및 설정 오류 test가 유지된다. 새 route test는 delay offload 및 4096 byte 경계와 초과 후 chunk 미소비를 검증한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 필수 test의 존재와 정적 계약 대응은 확인했다. 새 head 실행 결과는 D6-23에서 미확인으로 판정하고 CI나 모든 실패 종류의 별도 browser case를 요구하지 않는다.
D6-22 — indeterminate
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 저장된 HTTPS browser evidence의 source는 7bb959859a84f1d1293afd1f0270cca18f4cd576이다. 현재 auth server login 95107과 123129의 stream 수신 및 delay 실행 코드가 바뀌어 기록된 auth server hash와 current head blob이 다르다. 구현자 PR 본문과 재작업 comment 1128도 기존 browser evidence를 새 head 실행으로 주장하지 않는다고 명시한다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: 승인된 실제 browser 발급 및 복원 및 삭제 확인을 현재 실행 source에 연결할 근거가 없다. 이전 관찰 자체를 부정하거나 신규 기능 및 모든 실패 조합을 요구하지 않는다. 변경된 login 경로를 포함한 current source bound browser evidence를 제공해야 한다.
D6-23 — indeterminate
계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json; 저장된 실행 기록은 7bb959859a84f1d1293afd1f0270cca18f4cd576의 111 passed 및 4 skipped이다. source to head diff에는 auth server 및 해당 test 변경이 포함돼 비실행 후속 변경 예외를 적용할 수 없다. PR 본문의 116 passed 및 4 skipped와 focused 29 passed 및 probe 성공은 확인 가능한 command output과 실행 환경 및 source binding artifact 없이 요약만 제공됐다. Current head status context와 Actions run은 0개다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 실행 input이 동일했던 직전 승인과 달리 이번에는 실제 source와 test가 바뀌었다. 새 기능 결함이나 직전 AC 보존 판정 번복이 아니라 현재 실행 evidence의 검증 불가다. 변경 source에 결합된 command 및 exit code 및 output 및 환경과 source provenance를 제공하면 된다. CI 성공 자체를 필수로 요구하지 않는다.
범위 판정
누락: 현재 변경된 login source에 결합된 필수 browser 검증 근거 / 현재 변경 source와 test에 결합된 검증 가능한 command 실행 evidence
범위 초과: 없음
명세 모순: 없음
결론
정적 검토에서 F-001의 지연 offload와 F-002의 streaming 상한 수정은 승인 계약에 부합하며 baseline AC의 의미와 문서 계약은 유지된다. 그러나 새 실행 source 및 test에 대해 필수 browser 관찰과 command 실행 결과를 신뢰 가능한 provenance로 연결할 수 없어 변경을 요청한다. 이전 문서 전용 후속 변경과 달리 이번에는 실행 input이 변경됐다. 제품 기능 추가나 과거 인수 기준 재번복이 아니라 현재 head 검증 근거의 보완 요청이며 독립 code 재리뷰와 merge 승인은 별도다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `e7242a89f7f1faacbe984713a5ad211bd7bcac2b`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `request_changes`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: dashboard/authentication.py, dashboard/app.py; app 28~32에서 gate 실패 시 반환하고 authentication 297~316에서 검증 성공 때만 보호 기능을 허용한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 공통 gate와 no call test가 유지된다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: dashboard/authentication.py, .env.example; authentication 74~108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302~306에서 접근을 차단한다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111~147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123~131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 실패당 최소 지연과 성공 무지연 및 무잠금 계약의 정적 구현은 유지된다. 구현자가 주장한 현재 head 실행 성공과 reviewer의 정적 판단은 구분한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173~190 및 auth server 128~142에서 기본 86400초 token과 cookie를 발급한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173~190 및 auth server 128~142에서 선택 시 2592000초를 적용한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py
- 판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
- 테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 변경되지 않은 gate 및 page query와 복원 test의 정적 대응을 확인했다. 과거 browser 복원 관찰을 새 login source의 실행 성공으로 표현하지 않는다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: dashboard/auth_server.py, dashboard/server.py; server의 같은 process route wiring은 유지된다. auth server 139~146과 163~169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 발급 및 삭제 책임은 Dashboard 내부에 있고 외부 인증 middleware로 이전하지 않았다. 새 source의 실제 browser 실행 확인은 D6-22에서 별도 판정한다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication의 Host prefix와 auth server 139~146 및 163~169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: cookie 보안 속성은 정적 source와 test 대응으로 확인했다. 과거 browser 기록을 현재 source 실행으로 승계하지 않는다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: dashboard/authentication.py; authentication 194~239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297~316은 session boolean을 사용하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 152~169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166~180의 logout response test가 대응한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 기존 logout 및 보호 gate는 변경되지 않았다. 과거 browser 관찰과 현재 head 실행은 구분한다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179~185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152~165가 관리자 ID 원문의 부재를 확인한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302~312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168~176은 matching deletion과 redirect를 반환한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: invalid cookie 거부와 matching deletion은 source와 test로 확인했다. 과거 browser malformed cleanup은 과거 관찰만으로 남기며 현재 head browser 실행 증명은 D6-22에서 별도로 판정한다.
- `D6-16` — `satisfied`
- 계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
- 구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001의 AC 15개와 baseline 전체의 인수 검증 의미를 문서 전문과 source 및 test에 재대응했다. 직전 복원된 성공 무지연 및 counter와 lock state 부재 AC는 유지된다. 현재 rework는 auth server와 route test 두 파일뿐이며 요구사항과 추적성 문서는 직전 승인 head와 동일하다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
- `D6-17` — `satisfied`
- 계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
- 구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
- 테스트 근거: 없음
- 판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 23~46 및 66~83에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
- 테스트 근거: 없음
- 판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
- 테스트 근거: tests/test_dashboard_app.py
- 판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; 두 TTL 경계와 미연장 및 새 session 복원과 logout 및 rotation 및 설정 오류 test가 유지된다. 새 route test는 delay offload 및 4096 byte 경계와 초과 후 chunk 미소비를 검증한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
- 판정 근거: 필수 test의 존재와 정적 계약 대응은 확인했다. 새 head 실행 결과는 D6-23에서 미확인으로 판정하고 CI나 모든 실패 종류의 별도 browser case를 요구하지 않는다.
- `D6-22` — `indeterminate`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 저장된 HTTPS browser evidence의 source는 7bb959859a84f1d1293afd1f0270cca18f4cd576이다. 현재 auth server login 95~107과 123~129의 stream 수신 및 delay 실행 코드가 바뀌어 기록된 auth server hash와 current head blob이 다르다. 구현자 PR 본문과 재작업 comment 1128도 기존 browser evidence를 새 head 실행으로 주장하지 않는다고 명시한다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 승인된 실제 browser 발급 및 복원 및 삭제 확인을 현재 실행 source에 연결할 근거가 없다. 이전 관찰 자체를 부정하거나 신규 기능 및 모든 실패 조합을 요구하지 않는다. 변경된 login 경로를 포함한 current source bound browser evidence를 제공해야 한다.
- `D6-23` — `indeterminate`
- 계약: 필수 실행 evidence는 검토한 head와 실행 명령 및 exit code와 필요한 output 및 실행 환경에 결합되어야 한다.
- 구현 근거: docs/evidence/issue-6-persistent-auth-https.json; 저장된 실행 기록은 7bb959859a84f1d1293afd1f0270cca18f4cd576의 111 passed 및 4 skipped이다. source to head diff에는 auth server 및 해당 test 변경이 포함돼 비실행 후속 변경 예외를 적용할 수 없다. PR 본문의 116 passed 및 4 skipped와 focused 29 passed 및 probe 성공은 확인 가능한 command output과 실행 환경 및 source binding artifact 없이 요약만 제공됐다. Current head status context와 Actions run은 0개다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
- 판정 근거: 실행 input이 동일했던 직전 승인과 달리 이번에는 실제 source와 test가 바뀌었다. 새 기능 결함이나 직전 AC 보존 판정 번복이 아니라 현재 실행 evidence의 검증 불가다. 변경 source에 결합된 command 및 exit code 및 output 및 환경과 source provenance를 제공하면 된다. CI 성공 자체를 필수로 요구하지 않는다.
### 범위 판정
- 누락: 현재 변경된 login source에 결합된 필수 browser 검증 근거 / 현재 변경 source와 test에 결합된 검증 가능한 command 실행 evidence
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
정적 검토에서 F-001의 지연 offload와 F-002의 streaming 상한 수정은 승인 계약에 부합하며 baseline AC의 의미와 문서 계약은 유지된다. 그러나 새 실행 source 및 test에 대해 필수 browser 관찰과 command 실행 결과를 신뢰 가능한 provenance로 연결할 수 없어 변경을 요청한다. 이전 문서 전용 후속 변경과 달리 이번에는 실행 input이 변경됐다. 제품 기능 추가나 과거 인수 기준 재번복이 아니라 현재 head 검증 근거의 보완 요청이며 독립 code 재리뷰와 merge 승인은 별도다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=e7242a89f7f1faacbe984713a5ad211bd7bcac2b contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=4069f84c5b6c34caafbed680f50ede7507cd89a289203f435797da884f77c2dc correlation=e121a191bdafe006e73a733bcffd6a246709d09bfb2b678a296417da7abb9b9a -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py; app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기존 공통 gate와 no call test가 유지된다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example; authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 실패당 최소 지연과 성공 무지연 및 무잠금 계약의 정적 구현은 유지된다. 구현자가 주장한 현재 head 실행 성공과 reviewer의 정적 판단은 구분한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 변경되지 않은 gate 및 page query와 복원 test의 정적 대응을 확인했다. 과거 browser 복원 관찰을 새 login source의 실행 성공으로 표현하지 않는다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py; server의 같은 process route wiring은 유지된다. auth server 139146과 163169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 발급 및 삭제 책임은 Dashboard 내부에 있고 외부 인증 middleware로 이전하지 않았다. 새 source의 실제 browser 실행 확인은 D6-22에서 별도 판정한다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication의 Host prefix와 auth server 139146 및 163169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: cookie 보안 속성은 정적 source와 test 대응으로 확인했다. 과거 browser 기록을 현재 source 실행으로 승계하지 않는다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py; authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 152169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166180의 logout response test가 대응한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 기존 logout 및 보호 gate는 변경되지 않았다. 과거 browser 관찰과 현재 head 실행은 구분한다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: invalid cookie 거부와 matching deletion은 source와 test로 확인했다. 과거 browser malformed cleanup은 과거 관찰만으로 남기며 현재 head browser 실행 증명은 D6-22에서 별도로 판정한다.
D6-16 — satisfied
계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001의 AC 15개와 baseline 전체의 인수 검증 의미를 문서 전문과 source 및 test에 재대응했다. 직전 복원된 성공 무지연 및 counter와 lock state 부재 AC는 유지된다. 현재 rework는 auth server와 route test 두 파일뿐이며 요구사항과 추적성 문서는 직전 승인 head와 동일하다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
D6-17 — satisfied
계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
테스트 근거: 없음
판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
테스트 근거: 없음
판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; 두 TTL 경계와 미연장 및 새 session 복원과 logout 및 rotation 및 설정 오류 test가 유지된다. 새 route test는 delay offload 및 4096 byte 경계와 초과 후 chunk 미소비를 검증한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 필수 test의 존재와 정적 계약 대응은 확인했다. 새 head 실행 결과는 D6-23에서 미확인으로 판정하고 CI나 모든 실패 종류의 별도 browser case를 요구하지 않는다.
D6-22 — satisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 기존 HTTPS browser 관찰은 source 7bb959859a84f1d1293afd1f0270cca18f4cd576에 결합되어 있다. 전체 source to head 변경을 대조하면 실행 코드 변경은 login의 stream 수신과 실패 지연 offload이며 token 발급과 cookie 보안 속성 및 복원과 logout 및 cleanup 동작은 유지된다. 기존 실제 browser 관찰에 현재 source와 route 및 AppTest의 정적 대응을 함께 적용한다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: Review 50의 browser 발급 및 복원 및 삭제 전체 재실행 요구를 철회한다. 변경되지 않은 동작은 기존 관찰과 변경 영향 분석으로 충족을 판단하며 과거 관찰을 현재 head의 browser 실행으로 표시하지 않는다. 변경된 정상 login 수신 호환성과 실패 지연의 실행 기록 확인은 D6-23으로 한정한다.
D6-23 — indeterminate
계약: 승인 Discovery 포함 범위의 단위 및 통합 인증 검증과 제외 범위 및 REQ 001 기존 실패 지연 계약을 보존한다. 정상 로그인과 cookie 발급을 유지하고 실패 로그인 최소 1초 지연을 변경하지 않는다.
구현 근거: dashboard/auth_server.py, dashboard/authentication.py; 현재 head의 source와 회귀 test는 stream 수신 및 실패 지연 offload와 기존 정상 로그인 cookie 발급에 대응한다. PR body와 구현자 comment 1128은 head와 command 및 exit code와 116 passed 및 4 skipped와 focused 29 passed 및 동시 probe 요약을 제공하지만 실제 command output 및 실행 환경의 조회 가능한 기록은 확인되지 않았다. 기존 browser 발급과 복원 및 삭제 관찰은 D6-22에서 인정한다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication.py
판정 근거: 남은 요청은 변경된 login 경로의 정상 credential 처리 및 cookie 발급 호환성과 기존 실패 최소 지연 보존을 판정할 수 있는 실행 기록 확인뿐이다. 이미 수행한 focused route 검증 및 delay 검증 또는 probe의 명령과 exit code 및 관련 output과 실행 환경 및 source 결합을 확인 가능한 형태로 제시하면 된다. 기존 기록 보충으로 충분하면 새 artifact 형식이나 재실행을 요구하지 않는다. 전체 test 또는 browser 일괄 재실행 및 CI 성공은 요구하지 않으며 새 제품 결함을 발견했다는 뜻이 아니다.
범위 판정
누락: 변경된 login 경로의 정상 인증 및 cookie 발급 호환성과 기존 실패 최소 지연 보존을 판단할 조회 가능한 실행 기록
범위 초과: 없음
명세 모순: 없음
결론
Review 50의 과도한 browser 전체 재실행 요구를 철회하고 변경 영향별로 evidence를 재판정했다. D6-22는 기존 실제 browser 관찰과 변경되지 않은 source 및 현재 회귀 test 대응을 함께 인정해 satisfied로 정정한다. 남은 D6-23만 변경된 login 경로의 정상 인증 호환성과 기존 실패 지연 보존에 관한 실행 기록 확인으로 축소한다. 기존 focused 검증 및 probe 기록의 부족한 provenance를 보충하면 되며 새 artifact 형식이나 일괄 재실행을 요구하지 않는다. 제품 계약 위반을 새로 발견하지 않았고 정적 계약 판정은 유지된다. 승인 판정은 잔여 필수 검증의 실행 기록 확인 이후에 가능하다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `e7242a89f7f1faacbe984713a5ad211bd7bcac2b`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `request_changes`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: dashboard/authentication.py, dashboard/app.py; app 28~32에서 gate 실패 시 반환하고 authentication 297~316에서 검증 성공 때만 보호 기능을 허용한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 공통 gate와 no call test가 유지된다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: dashboard/authentication.py, .env.example; authentication 74~108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302~306에서 접근을 차단한다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111~147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123~131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 실패당 최소 지연과 성공 무지연 및 무잠금 계약의 정적 구현은 유지된다. 구현자가 주장한 현재 head 실행 성공과 reviewer의 정적 판단은 구분한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173~190 및 auth server 128~142에서 기본 86400초 token과 cookie를 발급한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173~190 및 auth server 128~142에서 선택 시 2592000초를 적용한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py
- 판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
- 테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 변경되지 않은 gate 및 page query와 복원 test의 정적 대응을 확인했다. 과거 browser 복원 관찰을 새 login source의 실행 성공으로 표현하지 않는다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: dashboard/auth_server.py, dashboard/server.py; server의 같은 process route wiring은 유지된다. auth server 139~146과 163~169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 발급 및 삭제 책임은 Dashboard 내부에 있고 외부 인증 middleware로 이전하지 않았다. 새 source의 실제 browser 실행 확인은 D6-22에서 별도 판정한다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication의 Host prefix와 auth server 139~146 및 163~169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: cookie 보안 속성은 정적 source와 test 대응으로 확인했다. 과거 browser 기록을 현재 source 실행으로 승계하지 않는다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: dashboard/authentication.py; authentication 194~239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297~316은 session boolean을 사용하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 152~169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166~180의 logout response test가 대응한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 기존 logout 및 보호 gate는 변경되지 않았다. 과거 browser 관찰과 현재 head 실행은 구분한다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179~185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152~165가 관리자 ID 원문의 부재를 확인한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302~312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168~176은 matching deletion과 redirect를 반환한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: invalid cookie 거부와 matching deletion은 source와 test로 확인했다. 과거 browser malformed cleanup은 과거 관찰만으로 남기며 현재 head browser 실행 증명은 D6-22에서 별도로 판정한다.
- `D6-16` — `satisfied`
- 계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
- 구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001의 AC 15개와 baseline 전체의 인수 검증 의미를 문서 전문과 source 및 test에 재대응했다. 직전 복원된 성공 무지연 및 counter와 lock state 부재 AC는 유지된다. 현재 rework는 auth server와 route test 두 파일뿐이며 요구사항과 추적성 문서는 직전 승인 head와 동일하다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
- `D6-17` — `satisfied`
- 계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
- 구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
- 테스트 근거: 없음
- 판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 23~46 및 66~83에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
- 테스트 근거: 없음
- 판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
- 테스트 근거: tests/test_dashboard_app.py
- 판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; 두 TTL 경계와 미연장 및 새 session 복원과 logout 및 rotation 및 설정 오류 test가 유지된다. 새 route test는 delay offload 및 4096 byte 경계와 초과 후 chunk 미소비를 검증한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
- 판정 근거: 필수 test의 존재와 정적 계약 대응은 확인했다. 새 head 실행 결과는 D6-23에서 미확인으로 판정하고 CI나 모든 실패 종류의 별도 browser case를 요구하지 않는다.
- `D6-22` — `satisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 기존 HTTPS browser 관찰은 source 7bb959859a84f1d1293afd1f0270cca18f4cd576에 결합되어 있다. 전체 source to head 변경을 대조하면 실행 코드 변경은 login의 stream 수신과 실패 지연 offload이며 token 발급과 cookie 보안 속성 및 복원과 logout 및 cleanup 동작은 유지된다. 기존 실제 browser 관찰에 현재 source와 route 및 AppTest의 정적 대응을 함께 적용한다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
- 판정 근거: Review 50의 browser 발급 및 복원 및 삭제 전체 재실행 요구를 철회한다. 변경되지 않은 동작은 기존 관찰과 변경 영향 분석으로 충족을 판단하며 과거 관찰을 현재 head의 browser 실행으로 표시하지 않는다. 변경된 정상 login 수신 호환성과 실패 지연의 실행 기록 확인은 D6-23으로 한정한다.
- `D6-23` — `indeterminate`
- 계약: 승인 Discovery 포함 범위의 단위 및 통합 인증 검증과 제외 범위 및 REQ 001 기존 실패 지연 계약을 보존한다. 정상 로그인과 cookie 발급을 유지하고 실패 로그인 최소 1초 지연을 변경하지 않는다.
- 구현 근거: dashboard/auth_server.py, dashboard/authentication.py; 현재 head의 source와 회귀 test는 stream 수신 및 실패 지연 offload와 기존 정상 로그인 cookie 발급에 대응한다. PR body와 구현자 comment 1128은 head와 command 및 exit code와 116 passed 및 4 skipped와 focused 29 passed 및 동시 probe 요약을 제공하지만 실제 command output 및 실행 환경의 조회 가능한 기록은 확인되지 않았다. 기존 browser 발급과 복원 및 삭제 관찰은 D6-22에서 인정한다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication.py
- 판정 근거: 남은 요청은 변경된 login 경로의 정상 credential 처리 및 cookie 발급 호환성과 기존 실패 최소 지연 보존을 판정할 수 있는 실행 기록 확인뿐이다. 이미 수행한 focused route 검증 및 delay 검증 또는 probe의 명령과 exit code 및 관련 output과 실행 환경 및 source 결합을 확인 가능한 형태로 제시하면 된다. 기존 기록 보충으로 충분하면 새 artifact 형식이나 재실행을 요구하지 않는다. 전체 test 또는 browser 일괄 재실행 및 CI 성공은 요구하지 않으며 새 제품 결함을 발견했다는 뜻이 아니다.
### 범위 판정
- 누락: 변경된 login 경로의 정상 인증 및 cookie 발급 호환성과 기존 실패 최소 지연 보존을 판단할 조회 가능한 실행 기록
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
Review 50의 과도한 browser 전체 재실행 요구를 철회하고 변경 영향별로 evidence를 재판정했다. D6-22는 기존 실제 browser 관찰과 변경되지 않은 source 및 현재 회귀 test 대응을 함께 인정해 satisfied로 정정한다. 남은 D6-23만 변경된 login 경로의 정상 인증 호환성과 기존 실패 지연 보존에 관한 실행 기록 확인으로 축소한다. 기존 focused 검증 및 probe 기록의 부족한 provenance를 보충하면 되며 새 artifact 형식이나 일괄 재실행을 요구하지 않는다. 제품 계약 위반을 새로 발견하지 않았고 정적 계약 판정은 유지된다. 승인 판정은 잔여 필수 검증의 실행 기록 확인 이후에 가능하다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=e7242a89f7f1faacbe984713a5ad211bd7bcac2b contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=4fe1a188161ba2af6d0598205c9375e871d55ab2ef83501568dde329517febc0 correlation=75afe4505954e17f0faad92d3a2dd1af7f983ca654d41d1ce368525bdf219ec8 -->
Source/environment binding → source_revision, environment, hashes_sha256: product/test source remains bound to e7242a89f7f1faacbe984713a5ad211bd7bcac2b on the evidence-only descendant
Secret safety → redaction: credentials, signing secret, token/cookie values, request body, raw Set-Cookie were not recorded
Browser boundary: this artifact does not claim a fresh browser run; D6-22 remains separately supported by docs/evidence/issue-6-persistent-auth-https.json and Review #51's change-impact analysis.
검토 및 원격 상태
Independent evidence review: PASS, blocker 없음
Commit status: pending (status context 0)
Actions runs: 0
Review #51의 기존 REQUEST_CHANGES는 이 comment로 해제되지 않습니다. Reviewer가 evidence-only head를 새 snapshot으로 재검토해야 합니다.
## Review #51 재작업 결과
- Source Issue: #6
- Pull Request: #7
- Source Review: #51
- Reviewed head: `e7242a89f7f1faacbe984713a5ad211bd7bcac2b`
- Evidence-only rework head: `755c26865c8e111aede8bcbe150a03e305a0ec60`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Result: `rework_delivered`
### D6-23 — resolved (`evidence_gap`)
Reviewer가 직접 조회 가능한 source-bound evidence를 추가했습니다.
- Evidence: [`docs/evidence/issue-6-d6-23-login-runtime.json`](https://gitea.jsfamily.quest/jsfamily/jarca-ledger-mcp/src/commit/755c26865c8e111aede8bcbe150a03e305a0ec60/docs/evidence/issue-6-d6-23-login-runtime.json)
- Artifact SHA-256: `5af59f867e4972b44c51d011f8348f124b2c5dc42fb3dcc3c020095c9ec1b939`
- Focused authentication claim → `commands[0]`: exact command, exit `0`, `29 passed`, warning output
- Real-delay concurrency claim → `commands[1]`: runnable probe source, exit `0`, event-loop probe `0.10056s`, successful-login latency `0.001267s`, two failed logins `1.00614s`, statuses `303/303`
- Source/environment binding → `source_revision`, `environment`, `hashes_sha256`: product/test source remains bound to `e7242a89f7f1faacbe984713a5ad211bd7bcac2b` on the evidence-only descendant
- Secret safety → `redaction`: credentials, signing secret, token/cookie values, request body, raw `Set-Cookie` were not recorded
- Browser boundary: this artifact does not claim a fresh browser run; D6-22 remains separately supported by `docs/evidence/issue-6-persistent-auth-https.json` and Review #51's change-impact analysis.
### 검토 및 원격 상태
- Independent evidence review: PASS, blocker 없음
- Commit status: `pending` (status context `0`)
- Actions runs: `0`
Review #51의 기존 `REQUEST_CHANGES`는 이 comment로 해제되지 않습니다. Reviewer가 evidence-only head를 새 snapshot으로 재검토해야 합니다.
<!-- gitea-agent:implementation-rework/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 review=51 reviewed-head=e7242a89f7f1faacbe984713a5ad211bd7bcac2b head=755c26865c8e111aede8bcbe150a03e305a0ec60 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe artifact-sha256=5af59f867e4972b44c51d011f8348f124b2c5dc42fb3dcc3c020095c9ec1b939 result-sha256=4a815f374e1164489a699abb01196c0cd40aff14d612d4cf290c8cf021361759 correlation=1ff376aadd602fcd0e4d3fd633d8591e232a949db92264be065570e1f92974fb -->
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py; app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 기존 공통 gate와 no call test가 유지된다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example; authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 실패당 최소 지연과 성공 무지연 및 무잠금 계약의 정적 구현은 유지된다. 구현자가 주장한 현재 head 실행 성공과 reviewer의 정적 판단은 구분한다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 미선택 기본값을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 변경되지 않은 gate 및 page query와 복원 test의 정적 대응을 확인했다. 과거 browser 복원 관찰을 새 login source의 실행 성공으로 표현하지 않는다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py; server의 같은 process route wiring은 유지된다. auth server 139146과 163169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 발급 및 삭제 책임은 Dashboard 내부에 있고 외부 인증 middleware로 이전하지 않았다. 새 source의 실제 browser 실행 확인은 D6-22에서 별도 판정한다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication의 Host prefix와 auth server 139146 및 163169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: cookie 보안 속성은 정적 source와 test 대응으로 확인했다. 과거 browser 기록을 현재 source 실행으로 승계하지 않는다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py; authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 152169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166180의 logout response test가 대응한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 기존 logout 및 보호 gate는 변경되지 않았다. 과거 browser 관찰과 현재 head 실행은 구분한다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: invalid cookie 거부와 matching deletion은 source와 test로 확인했다. 과거 browser malformed cleanup은 과거 관찰만으로 남기며 현재 head browser 실행 증명은 D6-22에서 별도로 판정한다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
D6-16 — satisfied
계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001의 AC 15개와 baseline 전체의 인수 검증 의미를 문서 전문과 source 및 test에 재대응했다. 직전 복원된 성공 무지연 및 counter와 lock state 부재 AC는 유지된다. 현재 rework는 auth server와 route test 두 파일뿐이며 요구사항과 추적성 문서는 직전 승인 head와 동일하다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
D6-17 — satisfied
계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
테스트 근거: 없음
판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
테스트 근거: 없음
판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; 두 TTL 경계와 미연장 및 새 session 복원과 logout 및 rotation 및 설정 오류 test가 유지된다. 새 route test는 delay offload 및 4096 byte 경계와 초과 후 chunk 미소비를 검증한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
판정 근거: 필수 test의 존재와 정적 계약 대응은 확인했다. 새 head 실행 결과는 D6-23에서 미확인으로 판정하고 CI나 모든 실패 종류의 별도 browser case를 요구하지 않는다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
D6-22 — satisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 기존 HTTPS browser 관찰은 source 7bb959859a84f1d1293afd1f0270cca18f4cd576에 결합되어 있다. 전체 source to head 변경을 대조하면 실행 코드 변경은 login의 stream 수신과 실패 지연 offload이며 token 발급과 cookie 보안 속성 및 복원과 logout 및 cleanup 동작은 유지된다. 기존 실제 browser 관찰에 현재 source와 route 및 AppTest의 정적 대응을 함께 적용한다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: 기존 browser의 발급 및 복원 및 삭제 관찰과 변경 영향 분석을 계속 인정한다. 현재 head는 실행 입력 변경 없는 evidence-only descendant다. 새 browser 실행을 주장하거나 일괄 재실행을 요구하지 않는다.
D6-23 — satisfied
계약: 승인 Discovery 포함 범위의 단위 및 통합 인증 검증과 제외 범위 및 REQ 001 기존 실패 지연 계약을 보존한다. 정상 로그인과 cookie 발급을 유지하고 실패 로그인 최소 1초 지연을 변경하지 않는다.
구현 근거: dashboard/auth_server.py, dashboard/authentication.py, docs/evidence/issue-6-d6-23-login-runtime.json; D6-23 보완 artifact는 source e7242a89f7f1faacbe984713a5ad211bd7bcac2b와 command 및 exit code 및 관련 raw output과 ARM64 Python 및 dependency 환경을 보존한다. 전체 source to reviewed head diff는 이 evidence JSON 추가뿐이며 모든 실행 입력과 선언된 source hashes 및 uv lockfile의 동일성을 확인했다. 구현자 focused 인증 검증은 29 passed 및 exit 0이다. 실제 지연 probe는 성공 응답 303과 hardened cookie를 확인했고 두 실패 응답 303 및 cookie 미발급과 총 1.00614초를 기록한다. 성공 응답 지연 0.001267초와 event loop probe 0.10056초도 기록돼 있다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication.py
판정 근거: 정상 login 수신과 cookie 발급 호환성 및 기존 실패 최소 지연 보존을 확인할 실행 기록이 reviewer에게 조회 가능해졌다. 기록된 source와 실행 입력이 동일한 evidence-only descendant의 연결을 검증해 기존 최소 보완 요청이 해소됐다. 이는 구현자 실행 evidence의 검토이며 reviewer의 독립 실행 또는 새 browser 실행을 의미하지 않는다.
범위 판정
누락: 없음
범위 초과: 없음
명세 모순: 없음
결론
승인된 Discovery와 baseline AC 전체의 인수 검증 의미 및 필수 문서와 구현 및 test 대응을 재확인했다. 현재 rework는 D6-23 실행 기록 추가 한 파일뿐이며 source와 test 및 설정과 dependency 실행 입력은 동일하다. 기존 browser 관찰의 유효 범위를 유지하고 보완된 focused test 및 실제 지연 probe를 source와 환경에 결합된 구현자 evidence로 인정한다. 모든 필수 criterion을 충족하므로 승인한다. Reviewer의 독립 runtime 및 browser 실행이나 CI 성공을 주장하지 않으며 독립 code review와 merge 및 배포 승인은 별도다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `755c26865c8e111aede8bcbe150a03e305a0ec60`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `approve`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: dashboard/authentication.py, dashboard/app.py; app 28~32에서 gate 실패 시 반환하고 authentication 297~316에서 검증 성공 때만 보호 기능을 허용한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 공통 gate와 no call test가 유지된다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: dashboard/authentication.py, .env.example; authentication 74~108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302~306에서 접근을 차단한다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 환경변수 전용 설정과 fail closed 구현 및 관련 test를 확인했다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 111~147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123~131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 실패당 최소 지연과 성공 무지연 및 무잠금 계약의 정적 구현은 유지된다. 구현자가 주장한 현재 head 실행 성공과 reviewer의 정적 판단은 구분한다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 173~190 및 auth server 128~142에서 기본 86400초 token과 cookie를 발급한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 기본 발급과 HTTP 응답 test가 24시간 규칙에 대응한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 173~190 및 auth server 128~142에서 선택 시 2592000초를 적용한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 발급 test와 새 30일 만료 경계 test 및 browser 기록이 대응한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 미선택 기본값을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py
- 판정 근거: 활동과 페이지 이동 및 새 session에서 만료를 연장하는 경로가 없다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.
- 테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 변경되지 않은 gate 및 page query와 복원 test의 정적 대응을 확인했다. 과거 browser 복원 관찰을 새 login source의 실행 성공으로 표현하지 않는다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: dashboard/auth_server.py, dashboard/server.py; server의 같은 process route wiring은 유지된다. auth server 139~146과 163~169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 발급 및 삭제 책임은 Dashboard 내부에 있고 외부 인증 middleware로 이전하지 않았다. 새 source의 실제 browser 실행 확인은 D6-22에서 별도 판정한다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; authentication의 Host prefix와 auth server 139~146 및 163~169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: cookie 보안 속성은 정적 source와 test 대응으로 확인했다. 과거 browser 기록을 현재 source 실행으로 승계하지 않는다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: dashboard/authentication.py; authentication 194~239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297~316은 session boolean을 사용하지 않는다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 만료와 무효화된 증거로 복원 상태를 유지할 수 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: dashboard/authentication.py; authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 현재 설정 변경 후 기존 cookie의 접근 차단을 검증하는 회귀 test가 존재한다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 152~169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166~180의 logout response test가 대응한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 기존 logout 및 보호 gate는 변경되지 않았다. 과거 browser 관찰과 현재 head 실행은 구분한다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 34 및 179~185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152~165가 관리자 ID 원문의 부재를 확인한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 이전 관리자 ID 원문 payload 결함은 해소됐다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 302~312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168~176은 matching deletion과 redirect를 반환한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: invalid cookie 거부와 matching deletion은 source와 test로 확인했다. 과거 browser malformed cleanup은 과거 관찰만으로 남기며 현재 head browser 실행 증명은 D6-22에서 별도로 판정한다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
- `D6-16` — `satisfied`
- 계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
- 구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; REQ 001의 AC 15개와 baseline 전체의 인수 검증 의미를 문서 전문과 source 및 test에 재대응했다. 직전 복원된 성공 무지연 및 counter와 lock state 부재 AC는 유지된다. 현재 rework는 auth server와 route test 두 파일뿐이며 요구사항과 추적성 문서는 직전 승인 head와 동일하다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 직전 D6-16의 기존 인수 검증 의미 누락이 해소됐다. 정적 source 검사와 기존 unit 및 HTTP integration test를 함께 인정한다. 동일 prose 반복이나 새 test 작성 및 이전 AC ID와 개수 동일성을 요구하지 않는다.
- `D6-17` — `satisfied`
- 계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
- 구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.
- 테스트 근거: 없음
- 판정 근거: 직전 문서 변경 요청의 모든 항목을 승인 계약과 source 전문에 대조했다. 기존 cleanup 및 보호 실행 전 차단도 유지한다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project 23~46 및 66~83에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 별도 금융 gateway 의존이나 외부 인증 middleware 도입 없이 배치 계약을 충족한다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.
- 테스트 근거: 없음
- 판정 근거: 실제 secret은 저장하지 않고 별도 주입 경계를 문서화한다. 특정 Compose native secrets 형식을 추가 요구하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: dashboard/app.py, docs/project-structure.md, README.md; 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.
- 테스트 근거: tests/test_dashboard_app.py
- 판정 근거: 승인 제외 범위를 침범하지 않는 supporting 변경이다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; 두 TTL 경계와 미연장 및 새 session 복원과 logout 및 rotation 및 설정 오류 test가 유지된다. 새 route test는 delay offload 및 4096 byte 경계와 초과 후 chunk 미소비를 검증한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py
- 판정 근거: 필수 test의 존재와 정적 계약 대응은 확인했다. 새 head 실행 결과는 D6-23에서 미확인으로 판정하고 CI나 모든 실패 종류의 별도 browser case를 요구하지 않는다. 이번 evidence-only 후속 변경에서 제품 source와 test는 동일하다. D6-23의 source bound 구현자 실행 기록을 인정하며 reviewer 독립 실행과 구분한다.
- `D6-22` — `satisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 기존 HTTPS browser 관찰은 source 7bb959859a84f1d1293afd1f0270cca18f4cd576에 결합되어 있다. 전체 source to head 변경을 대조하면 실행 코드 변경은 login의 stream 수신과 실패 지연 offload이며 token 발급과 cookie 보안 속성 및 복원과 logout 및 cleanup 동작은 유지된다. 기존 실제 browser 관찰에 현재 source와 route 및 AppTest의 정적 대응을 함께 적용한다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 browser의 발급 및 복원 및 삭제 관찰과 변경 영향 분석을 계속 인정한다. 현재 head는 실행 입력 변경 없는 evidence-only descendant다. 새 browser 실행을 주장하거나 일괄 재실행을 요구하지 않는다.
- `D6-23` — `satisfied`
- 계약: 승인 Discovery 포함 범위의 단위 및 통합 인증 검증과 제외 범위 및 REQ 001 기존 실패 지연 계약을 보존한다. 정상 로그인과 cookie 발급을 유지하고 실패 로그인 최소 1초 지연을 변경하지 않는다.
- 구현 근거: dashboard/auth_server.py, dashboard/authentication.py, docs/evidence/issue-6-d6-23-login-runtime.json; D6-23 보완 artifact는 source e7242a89f7f1faacbe984713a5ad211bd7bcac2b와 command 및 exit code 및 관련 raw output과 ARM64 Python 및 dependency 환경을 보존한다. 전체 source to reviewed head diff는 이 evidence JSON 추가뿐이며 모든 실행 입력과 선언된 source hashes 및 uv lockfile의 동일성을 확인했다. 구현자 focused 인증 검증은 29 passed 및 exit 0이다. 실제 지연 probe는 성공 응답 303과 hardened cookie를 확인했고 두 실패 응답 303 및 cookie 미발급과 총 1.00614초를 기록한다. 성공 응답 지연 0.001267초와 event loop probe 0.10056초도 기록돼 있다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication.py
- 판정 근거: 정상 login 수신과 cookie 발급 호환성 및 기존 실패 최소 지연 보존을 확인할 실행 기록이 reviewer에게 조회 가능해졌다. 기록된 source와 실행 입력이 동일한 evidence-only descendant의 연결을 검증해 기존 최소 보완 요청이 해소됐다. 이는 구현자 실행 evidence의 검토이며 reviewer의 독립 실행 또는 새 browser 실행을 의미하지 않는다.
### 범위 판정
- 누락: 없음
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
승인된 Discovery와 baseline AC 전체의 인수 검증 의미 및 필수 문서와 구현 및 test 대응을 재확인했다. 현재 rework는 D6-23 실행 기록 추가 한 파일뿐이며 source와 test 및 설정과 dependency 실행 입력은 동일하다. 기존 browser 관찰의 유효 범위를 유지하고 보완된 focused test 및 실제 지연 probe를 source와 환경에 결합된 구현자 evidence로 인정한다. 모든 필수 criterion을 충족하므로 승인한다. Reviewer의 독립 runtime 및 browser 실행이나 CI 성공을 주장하지 않으며 독립 code review와 merge 및 배포 승인은 별도다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=755c26865c8e111aede8bcbe150a03e305a0ec60 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=6195e107f0ce94934dd8061566d6f782c527eb09f12cb696db636e5e68dfcfed correlation=aa56407d5a9f3eb45a934557583232734d09c7228c73e2bcc8763b0f07c21cc2 -->
F-001 해결:dashboard/auth_server.py:127-129가 실제 실패 지연을 asyncio.to_thread()로 offload한다. 실제 Uvicorn/HTTPS에서 실패 로그인 6건이 약 1.09초에 끝났고, 진행 중 health는 200 / 약 0.017초였다. 동기 sleep에 의한 기존 공유 event-loop 차단은 재현되지 않았다.
F-002 해결:dashboard/auth_server.py:95-100가 stream 수신 중 누적 크기를 검사하고 초과 chunk를 bytearray에 넣기 전에 거부한다. 실제 HTTPS chunked 요청에서 4097바이트만 보내고 나머지 body와 종료 chunk를 보내지 않았는데도 400을 받았다. 직접 Request 검증에서도 4096바이트는 허용하고, 다음 1바이트에서 거부하며 뒤 chunk를 소비하지 않았다.
확정 결함
F-003 — Medium: 비ASCII credential 입력이 예외로 빠져 HTTP 500과 실패 지연 우회를 일으킨다.
발생 조건: same-origin HTTPS form에 한글 ID 또는 비밀번호를 제출한다. 크기·필드 수·UTF-8 조건을 만족하는 정상 form 값도 해당한다. admin_password=%FF 역시 percent-decoding 후 같은 경로로 들어간다.
원인: form 값은 str로 파싱되지만 verify_credentials()가 hmac.compare_digest(str, str)에 그대로 전달한다. 이 API는 비ASCII 문자열에 TypeError: comparing strings with non-ASCII characters is not supported를 발생시킨다. 새 HTTP login adapter는 이 예외를 정상 실패로 처리하지 않아 enforce_failed_login_delay와 login_failed 경로에 도달하지 않는다.
직접 재현: 현재 head의 dashboard.server:app를 실제 Uvicorn/HTTPS로 실행했다. 비ASCII 비밀번호, 비ASCII ID, %FF의 응답은 모두 500, 처리 시간은 각각 약 0.015초 / 0.042초 / 0.030초였다. Redirect와 인증 cookie는 없었다. ASGI 예외 전파 실행에서도 위 TypeError를 확인했다. 한글로 구성한 합성 관리자 ID·비밀번호는 require_auth_settings()에 통과하지만 정확한 값을 제출해도 500, cookie 없음이었다.
영향: 정상적인 비ASCII 자격증명을 사용할 수 없고, 잘못된 입력에 대한 generic 로그인 실패 UI와 기존 최소 1초 실패 지연이 유지되지 않는다. 인증 우회나 cookie 발급은 확인하지 않았다.
계약 관계: Discovery는 기존 credential 검증·실패 지연을 유지하고 그 정책 변경을 제외한다. 현재 docs/interface-contract.md:55-78은 bounded string credential, mismatch 시 generic error + delay, malformed body 시 400을 정의하며 ASCII-only 제한은 없다. 구현 결함으로 판단하며 새 요구사항을 추가하지 않는다.
범위 구분: compare_digest(str, str) 제약은 pinned base의 기존 helper에도 있었다. 따라서 이번 thread/stream 재작업이 새로 만든 회귀라고 주장하지 않는다. 이번 PR이 추가한 공개 HTTP 로그인 경로가 그 제약을 처리하지 못하는 현행 결함이다. 이전 리뷰 #49에서 발견하지 못한 추가 finding이다.
제한된 수정 방향: 허용된 Unicode credential을 일관된 UTF-8 bytes로 constant-time 비교하는 등 지원하는 string 입력에서 예외가 나지 않도록 처리한다. 잘못된 percent-encoded UTF-8은 명시적으로 거부한다. 유효한 비ASCII 설정의 성공 로그인, 비ASCII mismatch의 generic 응답·실제 지연, malformed 입력의 400·cookie 미발급을 실제 adapter 경로의 회귀 테스트로 확인한다. ASCII-only로 요구를 몰래 축소하거나 예외만 삼켜 정상 credential도 거부하지 않는다.
검토 범위와 실행 근거
Base/head 변경 파일 21개를 원격 blob 및 로컬 Git diff와 대조했다. #49 이후 변경/추가된 3파일(dashboard/auth_server.py, auth-server tests, 새 runtime evidence)을 직접 읽고 검토했다. 나머지 18파일은 이전 독립 리뷰 snapshot과 SHA-256이 동일함을 확인하고 이전 검토를 재사용했다. 새 HTTP 입력 경로와 관련 credential helper는 추가로 직접 검토했다.
전체 테스트 직접 실행: PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -o addopts= -q -p no:cacheprovider --basetemp=<review-run>/pytest-tmp --junitxml=<review-run>/pytest.xml → exit 0, 115 passed / 4 skipped / 0 failed. JUnit에서 119개 testcase를 집계했다. 기존 테스트가 F-003을 검출하지 못했다.
리뷰어의 실제 HTTPS 실행: 정상 로그인, 새 Streamlit WebSocket cookie 인증 복원, 로그아웃 통과; 실패 지연을 mock하지 않은 동시 6건/health 및 body 조기 거부 확인. Browser automation이나 실제 Compose/Kubernetes 배포로 표현하지 않는다.
구현자 evidence의 e7242a89f7f1faacbe984713a5ad211bd7bcac2b는 현재 head의 ancestor이고 후속 diff는 evidence JSON만이다. 기록된 5개 입력 hash가 현재 파일과 일치한다. 이 기록은 재사용 근거이며 리뷰어 독립 실행과 구분한다.
초기 ASGI 동시성 probe는 전체 테스트와 병행되어 heartbeat gap 약 1.48초가 기록됐다. 이를 무조건적인 responsiveness pass로 사용하지 않고 보존한 뒤 전체 테스트 종료 후 실제 HTTPS probe를 단독 실행했다.
비차단/잔여 범위: 이전 S-001의 직접 Uvicorn 실행용 proxy 환경변수 문서 불일치는 유지된다. 기존 다른 탭 WebSocket의 logout lifecycle, 실제 browser cookie jar 및 ingress/Compose 환경은 이번에 새로 검증하지 않았으며 추가 확정 결함으로 표현하지 않는다.
기존 PR에서 gitea-issue-rework로 F-003을 수정한 뒤, 새 head의 spec pass → 독립 code 재리뷰를 진행한다. 기존 F-001/F-002의 해결 상태도 새 head에서 회귀 확인한다. 이 판정은 merge/deploy 승인이 아니다.
<!-- gitea-agent:code-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=755c26865c8e111aede8bcbe150a03e305a0ec60 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe -->
## 독립 code 재리뷰: REQUEST_CHANGES
- Target: `jsfamily/jarca-ledger-mcp`, Issue #6 / PR #7
- Reviewed head: `755c26865c8e111aede8bcbe150a03e305a0ec60`
- Base: `7bea8b368e02e98afd4b2dc463fb270b0e6eb297`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Current-head spec pass: native review **#52 APPROVED**, stale/dismissed 아님
- Verdict: `request_changes`
### 이전 지적 해결 확인
- **F-001 해결:** `dashboard/auth_server.py:127-129`가 실제 실패 지연을 `asyncio.to_thread()`로 offload한다. 실제 Uvicorn/HTTPS에서 실패 로그인 6건이 약 1.09초에 끝났고, 진행 중 health는 200 / 약 0.017초였다. 동기 sleep에 의한 기존 공유 event-loop 차단은 재현되지 않았다.
- **F-002 해결:** `dashboard/auth_server.py:95-100`가 stream 수신 중 누적 크기를 검사하고 초과 chunk를 `bytearray`에 넣기 전에 거부한다. 실제 HTTPS chunked 요청에서 4097바이트만 보내고 나머지 body와 종료 chunk를 보내지 않았는데도 400을 받았다. 직접 Request 검증에서도 4096바이트는 허용하고, 다음 1바이트에서 거부하며 뒤 chunk를 소비하지 않았다.
### 확정 결함
**F-003 — Medium: 비ASCII credential 입력이 예외로 빠져 HTTP 500과 실패 지연 우회를 일으킨다.**
- 위치: `dashboard/auth_server.py:117-131`(특히 124-126), `dashboard/authentication.py:111-118`(116-117).
- 발생 조건: same-origin HTTPS form에 한글 ID 또는 비밀번호를 제출한다. 크기·필드 수·UTF-8 조건을 만족하는 정상 form 값도 해당한다. `admin_password=%FF` 역시 percent-decoding 후 같은 경로로 들어간다.
- 원인: form 값은 `str`로 파싱되지만 `verify_credentials()`가 `hmac.compare_digest(str, str)`에 그대로 전달한다. 이 API는 비ASCII 문자열에 `TypeError: comparing strings with non-ASCII characters is not supported`를 발생시킨다. 새 HTTP login adapter는 이 예외를 정상 실패로 처리하지 않아 `enforce_failed_login_delay`와 `login_failed` 경로에 도달하지 않는다.
- 직접 재현: 현재 head의 `dashboard.server:app`를 실제 Uvicorn/HTTPS로 실행했다. 비ASCII 비밀번호, 비ASCII ID, `%FF`의 응답은 모두 **500**, 처리 시간은 각각 약 **0.015초 / 0.042초 / 0.030초**였다. Redirect와 인증 cookie는 없었다. ASGI 예외 전파 실행에서도 위 `TypeError`를 확인했다. 한글로 구성한 합성 관리자 ID·비밀번호는 `require_auth_settings()`에 통과하지만 정확한 값을 제출해도 **500**, cookie 없음이었다.
- 영향: 정상적인 비ASCII 자격증명을 사용할 수 없고, 잘못된 입력에 대한 generic 로그인 실패 UI와 기존 최소 1초 실패 지연이 유지되지 않는다. 인증 우회나 cookie 발급은 확인하지 않았다.
- 계약 관계: Discovery는 기존 credential 검증·실패 지연을 유지하고 그 정책 변경을 제외한다. 현재 `docs/interface-contract.md:55-78`은 bounded string credential, mismatch 시 generic error + delay, malformed body 시 400을 정의하며 ASCII-only 제한은 없다. 구현 결함으로 판단하며 새 요구사항을 추가하지 않는다.
- 범위 구분: `compare_digest(str, str)` 제약은 pinned base의 기존 helper에도 있었다. 따라서 **이번 thread/stream 재작업이 새로 만든 회귀라고 주장하지 않는다.** 이번 PR이 추가한 공개 HTTP 로그인 경로가 그 제약을 처리하지 못하는 현행 결함이다. 이전 리뷰 #49에서 발견하지 못한 추가 finding이다.
- 제한된 수정 방향: 허용된 Unicode credential을 일관된 UTF-8 bytes로 constant-time 비교하는 등 지원하는 string 입력에서 예외가 나지 않도록 처리한다. 잘못된 percent-encoded UTF-8은 명시적으로 거부한다. 유효한 비ASCII 설정의 성공 로그인, 비ASCII mismatch의 generic 응답·실제 지연, malformed 입력의 400·cookie 미발급을 실제 adapter 경로의 회귀 테스트로 확인한다. ASCII-only로 요구를 몰래 축소하거나 예외만 삼켜 정상 credential도 거부하지 않는다.
### 검토 범위와 실행 근거
- Base/head 변경 파일 21개를 원격 blob 및 로컬 Git diff와 대조했다. #49 이후 변경/추가된 3파일(`dashboard/auth_server.py`, auth-server tests, 새 runtime evidence)을 직접 읽고 검토했다. 나머지 18파일은 이전 독립 리뷰 snapshot과 SHA-256이 동일함을 확인하고 이전 검토를 재사용했다. 새 HTTP 입력 경로와 관련 credential helper는 추가로 직접 검토했다.
- 전체 테스트 직접 실행: `PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -o addopts= -q -p no:cacheprovider --basetemp=<review-run>/pytest-tmp --junitxml=<review-run>/pytest.xml` → **exit 0, 115 passed / 4 skipped / 0 failed**. JUnit에서 119개 testcase를 집계했다. 기존 테스트가 F-003을 검출하지 못했다.
- Ruff `--no-cache`, base/head `git diff --check`, `uv lock --check --offline` 통과.
- 리뷰어의 실제 HTTPS 실행: 정상 로그인, 새 Streamlit WebSocket cookie 인증 복원, 로그아웃 통과; 실패 지연을 mock하지 않은 동시 6건/health 및 body 조기 거부 확인. Browser automation이나 실제 Compose/Kubernetes 배포로 표현하지 않는다.
- 구현자 evidence의 `e7242a89f7f1faacbe984713a5ad211bd7bcac2b`는 현재 head의 ancestor이고 후속 diff는 evidence JSON만이다. 기록된 5개 입력 hash가 현재 파일과 일치한다. 이 기록은 재사용 근거이며 리뷰어 독립 실행과 구분한다.
- 초기 ASGI 동시성 probe는 전체 테스트와 병행되어 heartbeat gap 약 1.48초가 기록됐다. 이를 무조건적인 responsiveness pass로 사용하지 않고 보존한 뒤 전체 테스트 종료 후 실제 HTTPS probe를 단독 실행했다.
- 비차단/잔여 범위: 이전 S-001의 직접 Uvicorn 실행용 proxy 환경변수 문서 불일치는 유지된다. 기존 다른 탭 WebSocket의 logout lifecycle, 실제 browser cookie jar 및 ingress/Compose 환경은 이번에 새로 검증하지 않았으며 추가 확정 결함으로 표현하지 않는다.
- Evidence run: `/opt/data/.review-runs/jarca-pr7-rereview-20261004T010756Z/` (`pytest.xml`, `focused-verification.json`, `https-runtime.json`, `unusual-input-results.json`, `configured-unicode-results.json`). 서버는 종료·reap했고 TLS private key를 삭제했다. Source/test/contract/state/refs는 수정하지 않았다.
### 다음 단계
기존 PR에서 `gitea-issue-rework`로 F-003을 수정한 뒤, **새 head의 spec pass → 독립 code 재리뷰**를 진행한다. 기존 F-001/F-002의 해결 상태도 새 head에서 회귀 확인한다. 이 판정은 merge/deploy 승인이 아니다.
계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
구현 근거: dashboard/authentication.py, dashboard/app.py; dashboard/app.py 2832와 authentication 301320이 현재 cookie 검증을 DB 경로 취득과 page dispatch보다 먼저 적용한다. AppTest 6787과 105117의 DB sentinel 및 Import query 차단은 유지된다.
테스트 근거: tests/test_dashboard_authentication_app.py
판정 근거: 공통 gate와 no call 경계를 source와 실패 test에 직접 대조했다.
D6-02 — satisfied
계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
구현 근거: dashboard/authentication.py, .env.example; authentication 74~108은 exact 관리자 ID와 password 및 signing 환경변수만 읽고 누락과 빈 값 및 읽기 오류와 짧은 secret을 거부한다. 설정 test와 AppTest 및 HTTP 설정 실패 test가 대응한다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 환경변수 전용 설정과 설정 오류 fail closed가 유지된다.
D6-03 — satisfied
계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-f003-unicode-credentials.json; authentication 111122는 ID와 password 각각을 UTF-8 bytes로 변환해 두 compare_digest 호출을 모두 수행한다. auth server 101133은 strict UTF-8 form 해석과 실패별 지연 및 일반 오류를 유지한다. test authentication 6485와 auth server 90131 및 151~165가 Unicode 성공과 mismatch 지연 및 malformed 거부를 검증한다. F003 artifact commands의 focused 32 passed와 source hashes를 현재 head까지 연결했다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 code review F-003의 비ASCII 비교 예외와 실패 지연 경로 누락은 해소됐다. 성공 무지연과 application counter 및 lock 부재도 유지되며 구현자 실행 evidence와 reviewer의 정적 검토를 구분한다.
D6-04 — satisfied
계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 170195 및 auth server 135149는 기본 TTL 86400초와 로그인 성공 시각의 iat 및 exp를 고정한다. token 발급과 HTTP cookie test가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 24시간 절대 만료 계약을 충족한다.
D6-05 — satisfied
계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 170195 및 auth server 135149는 선택 시 TTL 2592000초를 적용한다. persistent test 6574와 168179 및 auth server test 71~87이 선택과 exact 만료 경계를 검증한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: 30일 선택과 만료 경계 계약을 충족한다.
D6-06 — satisfied
계약: 로그인 유지 checkbox의 기본값은 미선택이다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication login form 271~278에는 checkbox checked 속성이 없고 auth server 135는 on 값만 30일 선택으로 처리한다. form AppTest와 route tests가 대응한다.
테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 기본 미선택을 유지한다.
D6-07 — satisfied
계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 177188의 iat와 exp는 발급 때만 설정하고 198243의 복원 검증은 이를 변경하지 않는다. 보호 gate와 logout 및 cleanup에 재발급 경로가 없고 non sliding test가 유지된다.
테스트 근거: tests/test_dashboard_persistent_authentication.py
판정 근거: 활동과 페이지 이동 및 새 session 복원으로 만료를 연장하지 않는다.
D6-08 — satisfied
계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; dashboard/common.py 358364의 동일 query navigation과 authentication 301320 gate를 유지한다. core page AppTest는 동일 cookie로 새 session들을 열고 기존 source 7bb959859a browser 기록은 새 탭 Import 복원을 관찰했다. 전체 후속 diff에서 page query와 cookie 복원 및 삭제는 변경되지 않았다.
테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
판정 근거: 기존 browser 관찰과 현재 변경 영향 및 test의 정적 대응을 함께 인정한다. 현재 head의 신규 browser 재실행을 주장하지 않는다.
D6-09 — satisfied
계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
구현 근거: dashboard/auth_server.py, dashboard/server.py; dashboard.server 510은 같은 process의 Streamlit App에 auth route를 결합한다. auth server 141149와 165~183은 실제 response에서 cookie를 발급하고 삭제한다. strict UTF-8 rework 후 정상 Unicode login과 cookie 발급 route test 및 source bound focused 실행 기록이 존재한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: Dashboard 내부 adapter가 발급과 검증 및 삭제 책임을 유지한다. 외부 인증 middleware로 책임을 이전하지 않았다.
D6-10 — satisfied
계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 141149와 165172는 Host prefix cookie를 Secure 및 HttpOnly 및 SameSite lax와 path root 및 no Domain으로 발급하고 matching deletion한다. HTTP test 5068 및 227259와 기존 실제 browser 관찰이 보안 속성을 확인한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: HTTPS와 최소 host scope 및 JavaScript 비노출 속성을 유지한다. 기존 browser 관찰은 과거 source에 결합된 evidence로만 사용한다.
D6-11 — satisfied
계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
구현 근거: dashboard/authentication.py; authentication 198243은 엄격한 claim set과 서버 시각 및 absolute TTL과 서명을 검증하며 301320에서 session boolean만으로 인증하지 않는다. tamper와 expiry 및 invalid cookie AppTest가 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 현재 cookie 검증을 우회하는 session-only 인증 경로가 없다.
D6-12 — satisfied
계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
구현 근거: dashboard/authentication.py; authentication 154167의 key binding은 현재 secret과 ID 및 password를 서버에서만 사용한다. persistent rotation test 94122와 AppTest 145~183이 password 및 signing rotation 뒤 보호 화면 차단을 검증한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
판정 근거: 기존 token과 복원된 session은 현재 설정 binding 실패 시 접근을 유지할 수 없다.
D6-13 — satisfied
계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 154172의 explicit logout은 matching deletion과 root redirect를 반환하고 금융 DB에 의존하지 않는다. test 227241 및 기존 browser logout 기록은 삭제와 다음 미인증 접근을 확인한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: session-only 인증 boolean을 제거했고 다음 보호 실행은 cookie 재검증을 통과해야 한다. 금융 DB 또는 token registry를 변경하지 않는다.
D6-14 — satisfied
계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 183189는 v와 고정 비민감 sub 및 iat와 exp 및 jti만 payload에 기록한다. 154159의 credential binding은 server-only다. persistent test 152~165와 AppTest 및 log helper tests가 원문 credential과 secret 및 cookie의 비보존을 확인한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 이전 원문 관리자 ID payload 결함은 계속 해소돼 있으며 이번 UTF-8 비교 수정도 credential을 저장하거나 log에 추가하지 않는다.
D6-15 — satisfied
계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 306316은 설정 오류와 invalid cookie에 cleanup navigation을 표시하고 auth server 175183은 matching deletion 뒤 미인증 root로 전이한다. invalid cookie AppTest와 cleanup route test 및 기존 browser cleanup 관찰이 대응한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
판정 근거: 변조와 만료 및 secret 오류는 보호 실행 전에 거부된다. cleanup 거부 상황도 gate를 열지 않는다.
D6-16 — satisfied
계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; canonical REQ-001 revision 2.0의 정본 H2 및 frontmatter와 Issue 1 및 6과 PR 3 및 7 추적성을 검증했다. Baseline AC 전체를 문서 전문과 현재 AC 및 참조된 tests에 대응했다. 기존 query 우회 금지는 REQ 001.01과 현재 AC01 및 AppTest 8287에, exact 환경변수와 fallback 배제는 REQ 001.02 7078과 AC02 및 기존 unit test 1761에 보존된다. 비문자열은 invalid 설정 판정 및 source 79와 unit test 3940에 대응한다. 기존 핵심 page 다섯 개는 baseline 376과 현재 453 및 AppTest 56~62가 동일하다. Constant-time과 성공 무지연 및 counter와 lock 부재와 MCP 분리도 보존되고 문서는 직전 승인 head와 byte 동일하다.
테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
판정 근거: baseline AC 전체의 판정과 검증 및 불변조건 의미를 보존하며 session-only 계약은 승인된 persistent 계약으로 대체됐다. 독립 AC 반복 부재를 누락으로 취급하지 않는다. 위임된 문서 누락 후보 네 건은 실제 원문과 참조 검증의 대응으로 배제했다.
D6-17 — satisfied
계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface 1341은 설정과 오류, 4378은 login 입력과 고정 claim 및 두 TTL, 80137은 cleanup과 logout, 139177은 현재 binding 및 gate와 미연장을 정의한다. runtime 3640과 5165 및 7696은 발급과 복원 및 실패 폐기와 DB 보호 및 동시 요청과 rotation을 정의하고 98113에 비민감 관찰 및 검증을 둔다.
테스트 근거: 없음
판정 근거: Discovery의 interface와 runtime 필수 조항 전체를 section별로 대조했다. 과거 D6-17 누락은 해소 상태이며 이번 수정으로 문서 의미가 사라지지 않았다.
D6-18 — satisfied
계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project structure 2346은 source와 test 책임을, 6683은 의존 방향과 금지 경계를 정의한다. server와 auth server는 금융 gateway나 page view로 역의존하지 않는다. pyproject dashboard extra와 uv lock에 Streamlit ASGI 및 signing dependency가 있고 Dockerfile 23과 32는 해당 extra를 설치한다.
테스트 근거: tests/test_dashboard_auth_server.py
판정 근거: 내부 adapter의 supporting 모듈 및 dependency와 test 배치는 승인 범위다.
D6-19 — satisfied
계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; env example은 값 없는 세 변수와 proxy allowlist를 제공한다. Compose 3035는 별도 signing 값 주입, deployment 124140은 세 secretKeyRef와 proxy 기본값을 둔다. README 3454와 92103 및 deploy README 13~43은 secret 비보존과 HTTPS 및 absolute expiry와 rotation과 rate limit 운영 조건을 설명한다.
테스트 근거: 없음
판정 근거: 배포 설정과 운영 조건을 정적 검증했다. 실제 배포 또는 ingress 실행 성공을 주장하지 않는다.
D6-20 — satisfied
계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
구현 근거: dashboard/app.py, docs/project-structure.md, README.md; changed-file 23개를 직접 diff와 immutable blob에 대조했다. 금융 schema와 MCP source 및 기존 page dispatch와 query navigation은 변경되지 않았고 문서와 dependency 및 evidence 파일은 승인 인증 변경의 supporting 또는 검증 산출물이다.
테스트 근거: tests/test_dashboard_app.py
판정 근거: DB session과 sliding 만료 및 MCP 인증이나 외부 인증 middleware와 다중 사용자 구현은 없다. 설명할 수 없는 범위 초과가 없다.
D6-21 — satisfied
계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json, docs/evidence/issue-6-f003-unicode-credentials.json; persistent 및 AppTest와 auth route test는 두 TTL 경계와 미연장 및 변조와 복원과 logout 및 rotation과 설정 오류를 검증한다. F003 artifact의 complete regression 118 passed와 4 skipped 및 focused 32 passed는 source d84ed8ff2a53cd2735eceaaf0c54cb40c7c2c3ad의 실행 기록이다. 전체 source-to-head diff가 evidence 한 파일뿐이며 기록된 source와 test 및 dependency hashes가 모두 일치한다.
테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py, tests/test_dashboard_authentication.py
판정 근거: 필수 unit 및 integration 검증에 source와 환경 및 command와 output 및 exit code가 결합돼 있다. 구현자 기록 검토이지 reviewer 독립 테스트 또는 CI 성공이 아니다.
D6-22 — satisfied
계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 기존 ARM64 Chromium HTTPS browser 기록은 source 7bb959859a84f1d1293afd1f0270cca18f4cd576에 결합돼 server-set HttpOnly 발급과 새 탭 복원 및 logout과 invalid cleanup 삭제를 관찰했다. 전체 후속 실행 입력 변경은 bounded stream과 실패 지연 offload 및 UTF-8 비교와 strict form decode이며 cookie 속성과 token 및 복원과 logout과 cleanup은 동일하다. 새 focused route 실행 기록과 현재 source 및 test의 정적 대응을 함께 검토했다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
판정 근거: 기존 실제 browser 관찰의 유효 범위를 영향 분석으로 인정한다. 해당 기록을 현재 head의 신규 browser 실행으로 표시하지 않으며 기존 변경된 login 경로의 보완 실행 근거는 D6-23에서 별도로 판정한다.
D6-23 — satisfied
계약: 승인 Discovery 포함 범위의 단위 및 통합 인증 검증과 제외 범위 및 REQ 001 기존 실패 지연 계약을 보존한다. 정상 로그인과 cookie 발급을 유지하고 실패 로그인 최소 1초 지연을 변경하지 않는다.
구현 근거: dashboard/auth_server.py, dashboard/authentication.py, docs/evidence/issue-6-d6-23-login-runtime.json, docs/evidence/issue-6-f003-unicode-credentials.json; 새 F003 evidence는 source d84ed8ff2a53cd2735eceaaf0c54cb40c7c2c3ad와 command 및 exit code와 관련 output 및 ARM64 Python 3.13.5와 dependency 환경을 보존한다. 여섯 source hash와 blob OID가 현재 head에 exact-match하고 후속 변경은 evidence JSON 추가뿐이다. Focused 32 passed는 Unicode 성공 및 cookie 발급과 mismatch 일반 오류 및 실제 sleep 경로와 malformed percent UTF-8 400 및 no cookie test를 포함한다. Full suite는 118 passed와 4 skipped 및 exit 0이다. 기존 실제 두 실패 최소 지연과 event loop probe 관찰은 과거 source 근거로 구분하며 delay offload와 stream 경계 회귀 tests는 새 focused suite에도 포함된다.
테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication.py
판정 근거: F-003의 현행 string credential 검증 결함은 지원 범위를 축소하지 않고 해소됐다. 기존 F-001과 F-002 경계가 보존됐고 새 실행 입력의 focused 및 full 기록을 source에 결합해 인정한다. reviewer 독립 runtime 및 browser 실행과 별도의 code 재리뷰를 대신하지 않는다.
범위 판정
누락: 없음
범위 초과: 없음
명세 모순: 없음
결론
승인 Discovery의 모든 필수 계약과 baseline AC 검증 의미 및 23개 changed file의 범위 대응을 재확인했다. 이번 F-003 재작업의 UTF-8 credential 비교 및 strict form decode와 회귀 test 및 source bound 실행 evidence를 인정하며 모든 criterion을 충족한다. 기존 browser 관찰은 변경 영향 분석으로 제한적으로 재사용하고 새 head에서 browser를 다시 실행한 것으로 표현하지 않는다. 따라서 명세 적합성을 승인한다. 구현자 실행 evidence와 reviewer의 정적 분석은 구분하며 CI 성공과 독립 code 재리뷰 및 merge와 배포 승인은 별도다.
## 구현 명세 적합성 검토
- Source Issue: #6
- Pull Request: #7
- Reviewed head: `6a35775cca96dfe0168e96c882c8e240b1f1e7b4`
- Contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Verdict: `approve`
### Acceptance criteria 판정
- `D6-01` — `satisfied`
- 계약: 지속 인증 판정은 DB 경로 취득과 gateway 및 page dispatch보다 먼저 수행한다.
- 구현 근거: dashboard/authentication.py, dashboard/app.py; dashboard/app.py 28~32와 authentication 301~320이 현재 cookie 검증을 DB 경로 취득과 page dispatch보다 먼저 적용한다. AppTest 67~87과 105~117의 DB sentinel 및 Import query 차단은 유지된다.
- 테스트 근거: tests/test_dashboard_authentication_app.py
- 판정 근거: 공통 gate와 no call 경계를 source와 실패 test에 직접 대조했다.
- `D6-02` — `satisfied`
- 계약: 관리자 ID와 password 및 독립 signing secret은 environment에서만 읽고 설정 오류를 fail closed한다.
- 구현 근거: dashboard/authentication.py, .env.example; authentication 74~108은 exact 관리자 ID와 password 및 signing 환경변수만 읽고 누락과 빈 값 및 읽기 오류와 짧은 secret을 거부한다. 설정 test와 AppTest 및 HTTP 설정 실패 test가 대응한다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 환경변수 전용 설정과 설정 오류 fail closed가 유지된다.
- `D6-03` — `satisfied`
- 계약: 기존 관리자 credential 비교와 실패별 최소 1초 지연 및 비민감 인증 log를 유지한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-f003-unicode-credentials.json; authentication 111~122는 ID와 password 각각을 UTF-8 bytes로 변환해 두 compare_digest 호출을 모두 수행한다. auth server 101~133은 strict UTF-8 form 해석과 실패별 지연 및 일반 오류를 유지한다. test authentication 64~85와 auth server 90~131 및 151~165가 Unicode 성공과 mismatch 지연 및 malformed 거부를 검증한다. F003 artifact commands의 focused 32 passed와 source hashes를 현재 head까지 연결했다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 이전 code review F-003의 비ASCII 비교 예외와 실패 지연 경로 누락은 해소됐다. 성공 무지연과 application counter 및 lock 부재도 유지되며 구현자 실행 evidence와 reviewer의 정적 검토를 구분한다.
- `D6-04` — `satisfied`
- 계약: 기본 로그인은 성공 시각부터 24시간 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 26과 170~195 및 auth server 135~149는 기본 TTL 86400초와 로그인 성공 시각의 iat 및 exp를 고정한다. token 발급과 HTTP cookie test가 대응한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 기본 24시간 절대 만료 계약을 충족한다.
- `D6-05` — `satisfied`
- 계약: 로그인 유지 선택 시 성공 시각부터 30일 후 절대 만료한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 27과 170~195 및 auth server 135~149는 선택 시 TTL 2592000초를 적용한다. persistent test 65~74와 168~179 및 auth server test 71~87이 선택과 exact 만료 경계를 검증한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: 30일 선택과 만료 경계 계약을 충족한다.
- `D6-06` — `satisfied`
- 계약: 로그인 유지 checkbox의 기본값은 미선택이다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication login form 271~278에는 checkbox checked 속성이 없고 auth server 135는 on 값만 30일 선택으로 처리한다. form AppTest와 route tests가 대응한다.
- 테스트 근거: tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 기본 미선택을 유지한다.
- `D6-07` — `satisfied`
- 계약: 활동과 페이지 이동 및 인증 복원으로 만료 시각을 연장하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py; authentication 177~188의 iat와 exp는 발급 때만 설정하고 198~243의 복원 검증은 이를 변경하지 않는다. 보호 gate와 logout 및 cleanup에 재발급 경로가 없고 non sliding test가 유지된다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py
- 판정 근거: 활동과 페이지 이동 및 새 session 복원으로 만료를 연장하지 않는다.
- `D6-08` — `satisfied`
- 계약: 동일 browser profile의 sidebar 이동과 새 탭 및 browser 재접속에서 유효 인증을 복원한다.
- 구현 근거: dashboard/authentication.py, dashboard/common.py, docs/evidence/issue-6-persistent-auth-https.json; dashboard/common.py 358~364의 동일 query navigation과 authentication 301~320 gate를 유지한다. core page AppTest는 동일 cookie로 새 session들을 열고 기존 source 7bb959859a84f1d1293afd1f0270cca18f4cd576 browser 기록은 새 탭 Import 복원을 관찰했다. 전체 후속 diff에서 page query와 cookie 복원 및 삭제는 변경되지 않았다.
- 테스트 근거: tests/test_dashboard_app.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 browser 관찰과 현재 변경 영향 및 test의 정적 대응을 함께 인정한다. 현재 head의 신규 browser 재실행을 주장하지 않는다.
- `D6-09` — `satisfied`
- 계약: Dashboard server side adapter가 실제 HTTP response에서 HttpOnly cookie를 발급하고 삭제한다.
- 구현 근거: dashboard/auth_server.py, dashboard/server.py; dashboard.server 5~10은 같은 process의 Streamlit App에 auth route를 결합한다. auth server 141~149와 165~183은 실제 response에서 cookie를 발급하고 삭제한다. strict UTF-8 rework 후 정상 Unicode login과 cookie 발급 route test 및 source bound focused 실행 기록이 존재한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: Dashboard 내부 adapter가 발급과 검증 및 삭제 책임을 유지한다. 외부 인증 middleware로 책임을 이전하지 않았다.
- `D6-10` — `satisfied`
- 계약: 인증 cookie는 HTTPS와 Secure 및 HttpOnly와 적절한 SameSite 및 최소 host scope를 사용한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 141~149와 165~172는 Host prefix cookie를 Secure 및 HttpOnly 및 SameSite lax와 path root 및 no Domain으로 발급하고 matching deletion한다. HTTP test 50~68 및 227~259와 기존 실제 browser 관찰이 보안 속성을 확인한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: HTTPS와 최소 host scope 및 JavaScript 비노출 속성을 유지한다. 기존 browser 관찰은 과거 source에 결합된 evidence로만 사용한다.
- `D6-11` — `satisfied`
- 계약: 변조와 만료 및 설정 binding 실패를 server에서 검증하고 session boolean으로 우회하지 않는다.
- 구현 근거: dashboard/authentication.py; authentication 198~243은 엄격한 claim set과 서버 시각 및 absolute TTL과 서명을 검증하며 301~320에서 session boolean만으로 인증하지 않는다. tamper와 expiry 및 invalid cookie AppTest가 대응한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 현재 cookie 검증을 우회하는 session-only 인증 경로가 없다.
- `D6-12` — `satisfied`
- 계약: signing secret rotation과 관리자 password 변경은 기존 token과 복원된 인증을 무효화한다.
- 구현 근거: dashboard/authentication.py; authentication 154~167의 key binding은 현재 secret과 ID 및 password를 서버에서만 사용한다. persistent rotation test 94~122와 AppTest 145~183이 password 및 signing rotation 뒤 보호 화면 차단을 검증한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 token과 복원된 session은 현재 설정 binding 실패 시 접근을 유지할 수 없다.
- `D6-13` — `satisfied`
- 계약: 명시적 logout은 browser 인증 cookie를 폐기하고 금융 DB를 변경하지 않는다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json; auth server 154~172의 explicit logout은 matching deletion과 root redirect를 반환하고 금융 DB에 의존하지 않는다. test 227~241 및 기존 browser logout 기록은 삭제와 다음 미인증 접근을 확인한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: session-only 인증 boolean을 제거했고 다음 보호 실행은 cookie 재검증을 통과해야 한다. 금융 DB 또는 token registry를 변경하지 않는다.
- `D6-14` — `satisfied`
- 계약: 지속 인증 증거와 browser 저장소에 관리자 ID와 password 또는 재사용 가능한 credential 원문을 포함하지 않는다.
- 구현 근거: dashboard/authentication.py, docs/requirements/req-001_dashboard-administrator-authentication.md, docs/interface-contract.md; authentication 183~189는 v와 고정 비민감 sub 및 iat와 exp 및 jti만 payload에 기록한다. 154~159의 credential binding은 server-only다. persistent test 152~165와 AppTest 및 log helper tests가 원문 credential과 secret 및 cookie의 비보존을 확인한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 이전 원문 관리자 ID payload 결함은 계속 해소돼 있으며 이번 UTF-8 비교 수정도 credential을 저장하거나 log에 추가하지 않는다.
- `D6-15` — `satisfied`
- 계약: 만료와 변조 및 secret 누락 시 cookie를 삭제하고 미인증 상태로 전이한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/runtime-flow.md; authentication 306~316은 설정 오류와 invalid cookie에 cleanup navigation을 표시하고 auth server 175~183은 matching deletion 뒤 미인증 root로 전이한다. invalid cookie AppTest와 cleanup route test 및 기존 browser cleanup 관찰이 대응한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py
- 판정 근거: 변조와 만료 및 secret 오류는 보호 실행 전에 거부된다. cleanup 거부 상황도 gate를 열지 않는다.
- `D6-16` — `satisfied`
- 계약: 승인 Discovery의 REQ 001 갱신은 기존 인증 경계와 credential 검증 및 실패 지연과 보호 범위 요구사항과 관련 acceptance criteria를 보존하며 정본 구조 및 추적성을 함께 갱신해야 한다.
- 구현 근거: docs/requirements/req-001_dashboard-administrator-authentication.md; canonical REQ-001 revision 2.0의 정본 H2 및 frontmatter와 Issue 1 및 6과 PR 3 및 7 추적성을 검증했다. Baseline AC 전체를 문서 전문과 현재 AC 및 참조된 tests에 대응했다. 기존 query 우회 금지는 REQ 001.01과 현재 AC01 및 AppTest 82~87에, exact 환경변수와 fallback 배제는 REQ 001.02 70~78과 AC02 및 기존 unit test 17~61에 보존된다. 비문자열은 invalid 설정 판정 및 source 79와 unit test 39~40에 대응한다. 기존 핵심 page 다섯 개는 baseline 376과 현재 453 및 AppTest 56~62가 동일하다. Constant-time과 성공 무지연 및 counter와 lock 부재와 MCP 분리도 보존되고 문서는 직전 승인 head와 byte 동일하다.
- 테스트 근거: tests/test_dashboard_authentication.py, tests/test_dashboard_auth_server.py
- 판정 근거: baseline AC 전체의 판정과 검증 및 불변조건 의미를 보존하며 session-only 계약은 승인된 persistent 계약으로 대체됐다. 독립 AC 반복 부재를 누락으로 취급하지 않는다. 위임된 문서 누락 후보 네 건은 실제 원문과 참조 검증의 대응으로 배제했다.
- `D6-17` — `satisfied`
- 계약: 승인 Discovery의 인터페이스 및 런타임 산출물은 비민감 token 필드와 로그인 성공 기준 24시간 및 30일 절대 만료와 활동 복원 시 미연장 및 rotation 무효화와 실패 cookie 폐기 및 DB 보호 순서를 명시한다.
- 구현 근거: docs/interface-contract.md, docs/runtime-flow.md, docs/requirements/req-001_dashboard-administrator-authentication.md; interface 13~41은 설정과 오류, 43~78은 login 입력과 고정 claim 및 두 TTL, 80~137은 cleanup과 logout, 139~177은 현재 binding 및 gate와 미연장을 정의한다. runtime 36~40과 51~65 및 76~96은 발급과 복원 및 실패 폐기와 DB 보호 및 동시 요청과 rotation을 정의하고 98~113에 비민감 관찰 및 검증을 둔다.
- 테스트 근거: 없음
- 판정 근거: Discovery의 interface와 runtime 필수 조항 전체를 section별로 대조했다. 과거 D6-17 누락은 해소 상태이며 이번 수정으로 문서 의미가 사라지지 않았다.
- `D6-18` — `satisfied`
- 계약: 공통 인증 경계와 내부 adapter 및 source와 test의 배치를 문서화하고 필요한 dependency를 배포 설치 경로에 선언한다.
- 구현 근거: docs/project-structure.md, dashboard/server.py, dashboard/auth_server.py, pyproject.toml, uv.lock; project structure 23~46은 source와 test 책임을, 66~83은 의존 방향과 금지 경계를 정의한다. server와 auth server는 금융 gateway나 page view로 역의존하지 않는다. pyproject dashboard extra와 uv lock에 Streamlit ASGI 및 signing dependency가 있고 Dockerfile 23과 32는 해당 extra를 설치한다.
- 테스트 근거: tests/test_dashboard_auth_server.py
- 판정 근거: 내부 adapter의 supporting 모듈 및 dependency와 test 배치는 승인 범위다.
- `D6-19` — `satisfied`
- 계약: 별도 signing secret 주입과 HTTPS 및 expiry와 rotation 운영 조건을 env example과 Compose 및 Kubernetes와 README에 반영한다.
- 구현 근거: .env.example, docker-compose.yml, deploy/k8s/base/deployment.yaml, deploy/k8s/README.md, README.md; env example은 값 없는 세 변수와 proxy allowlist를 제공한다. Compose 30~35는 별도 signing 값 주입, deployment 124~140은 세 secretKeyRef와 proxy 기본값을 둔다. README 34~54와 92~103 및 deploy README 13~43은 secret 비보존과 HTTPS 및 absolute expiry와 rotation과 rate limit 운영 조건을 설명한다.
- 테스트 근거: 없음
- 판정 근거: 배포 설정과 운영 조건을 정적 검증했다. 실제 배포 또는 ingress 실행 성공을 주장하지 않는다.
- `D6-20` — `satisfied`
- 계약: 금융 SQLite schema와 MCP 인증 및 기존 page와 gateway 범위는 변경하지 않는다.
- 구현 근거: dashboard/app.py, docs/project-structure.md, README.md; changed-file 23개를 직접 diff와 immutable blob에 대조했다. 금융 schema와 MCP source 및 기존 page dispatch와 query navigation은 변경되지 않았고 문서와 dependency 및 evidence 파일은 승인 인증 변경의 supporting 또는 검증 산출물이다.
- 테스트 근거: tests/test_dashboard_app.py
- 판정 근거: DB session과 sliding 만료 및 MCP 인증이나 외부 인증 middleware와 다중 사용자 구현은 없다. 설명할 수 없는 범위 초과가 없다.
- `D6-21` — `satisfied`
- 계약: 단위와 통합 test는 두 TTL의 만료 경계와 미연장 및 복원과 logout 및 signing rotation과 password rotation 및 설정 오류를 검증한다.
- 구현 근거: dashboard/authentication.py, dashboard/auth_server.py, docs/evidence/issue-6-persistent-auth-https.json, docs/evidence/issue-6-f003-unicode-credentials.json; persistent 및 AppTest와 auth route test는 두 TTL 경계와 미연장 및 변조와 복원과 logout 및 rotation과 설정 오류를 검증한다. F003 artifact의 complete regression 118 passed와 4 skipped 및 focused 32 passed는 source d84ed8ff2a53cd2735eceaaf0c54cb40c7c2c3ad의 실행 기록이다. 전체 source-to-head diff가 evidence 한 파일뿐이며 기록된 source와 test 및 dependency hashes가 모두 일치한다.
- 테스트 근거: tests/test_dashboard_persistent_authentication.py, tests/test_dashboard_authentication_app.py, tests/test_dashboard_auth_server.py, tests/test_dashboard_app.py, tests/test_dashboard_authentication.py
- 판정 근거: 필수 unit 및 integration 검증에 source와 환경 및 command와 output 및 exit code가 결합돼 있다. 구현자 기록 검토이지 reviewer 독립 테스트 또는 CI 성공이 아니다.
- `D6-22` — `satisfied`
- 계약: 실제 browser test에서 server set HttpOnly cookie의 발급과 복원 및 삭제를 확인한다.
- 구현 근거: docs/evidence/issue-6-persistent-auth-https.json, dashboard/authentication.py, dashboard/auth_server.py, scripts/persistent_auth_http_evidence.py; 기존 ARM64 Chromium HTTPS browser 기록은 source 7bb959859a84f1d1293afd1f0270cca18f4cd576에 결합돼 server-set HttpOnly 발급과 새 탭 복원 및 logout과 invalid cleanup 삭제를 관찰했다. 전체 후속 실행 입력 변경은 bounded stream과 실패 지연 offload 및 UTF-8 비교와 strict form decode이며 cookie 속성과 token 및 복원과 logout과 cleanup은 동일하다. 새 focused route 실행 기록과 현재 source 및 test의 정적 대응을 함께 검토했다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication_app.py
- 판정 근거: 기존 실제 browser 관찰의 유효 범위를 영향 분석으로 인정한다. 해당 기록을 현재 head의 신규 browser 실행으로 표시하지 않으며 기존 변경된 login 경로의 보완 실행 근거는 D6-23에서 별도로 판정한다.
- `D6-23` — `satisfied`
- 계약: 승인 Discovery 포함 범위의 단위 및 통합 인증 검증과 제외 범위 및 REQ 001 기존 실패 지연 계약을 보존한다. 정상 로그인과 cookie 발급을 유지하고 실패 로그인 최소 1초 지연을 변경하지 않는다.
- 구현 근거: dashboard/auth_server.py, dashboard/authentication.py, docs/evidence/issue-6-d6-23-login-runtime.json, docs/evidence/issue-6-f003-unicode-credentials.json; 새 F003 evidence는 source d84ed8ff2a53cd2735eceaaf0c54cb40c7c2c3ad와 command 및 exit code와 관련 output 및 ARM64 Python 3.13.5와 dependency 환경을 보존한다. 여섯 source hash와 blob OID가 현재 head에 exact-match하고 후속 변경은 evidence JSON 추가뿐이다. Focused 32 passed는 Unicode 성공 및 cookie 발급과 mismatch 일반 오류 및 실제 sleep 경로와 malformed percent UTF-8 400 및 no cookie test를 포함한다. Full suite는 118 passed와 4 skipped 및 exit 0이다. 기존 실제 두 실패 최소 지연과 event loop probe 관찰은 과거 source 근거로 구분하며 delay offload와 stream 경계 회귀 tests는 새 focused suite에도 포함된다.
- 테스트 근거: tests/test_dashboard_auth_server.py, tests/test_dashboard_authentication.py
- 판정 근거: F-003의 현행 string credential 검증 결함은 지원 범위를 축소하지 않고 해소됐다. 기존 F-001과 F-002 경계가 보존됐고 새 실행 입력의 focused 및 full 기록을 source에 결합해 인정한다. reviewer 독립 runtime 및 browser 실행과 별도의 code 재리뷰를 대신하지 않는다.
### 범위 판정
- 누락: 없음
- 범위 초과: 없음
- 명세 모순: 없음
### 결론
승인 Discovery의 모든 필수 계약과 baseline AC 검증 의미 및 23개 changed file의 범위 대응을 재확인했다. 이번 F-003 재작업의 UTF-8 credential 비교 및 strict form decode와 회귀 test 및 source bound 실행 evidence를 인정하며 모든 criterion을 충족한다. 기존 browser 관찰은 변경 영향 분석으로 제한적으로 재사용하고 새 head에서 browser를 다시 실행한 것으로 표현하지 않는다. 따라서 명세 적합성을 승인한다. 구현자 실행 evidence와 reviewer의 정적 분석은 구분하며 CI 성공과 독립 code 재리뷰 및 merge와 배포 승인은 별도다.
<!-- gitea-agent:implementation-spec-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=6a35775cca96dfe0168e96c882c8e240b1f1e7b4 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe result-sha256=d4a7d95c6136ea680771c8bd969232cc1b6b9198c09e229ee7ae477ef91a4558 correlation=fcc51c4fc0b637a76deddf665c76c2ac0386f4e479e6cb468a2709f4b5ddb152 -->
전체 PR diff의 변경 파일 23개를 원격 diff/file 목록과 로컬 commit 내용으로 대조했다. 이전 독립 리뷰 #53의 head 755c26865c8e111aede8bcbe150a03e305a0ec60 이후 변경 5개 파일을 직접 검토했다. 나머지 18개 파일은 byte/hash 동일성을 검증하여 이전 전체 검토 근거를 재사용했다. 새 코드 경로의 surrounding code, 인증 gate, token/cookie 처리와 기존 인증 테스트를 다시 확인했다.
변경 파일: dashboard/authentication.py, dashboard/auth_server.py, 두 인증 테스트, docs/evidence/issue-6-f003-unicode-credentials.json. 한 bounded worker가 독립 정적 검토를 수행했고, 판정 및 모든 중요한 후보 검증은 main reviewer가 수행했다.
기존 finding 처리
F-001 — 해결됨. 실패 로그인 지연은 asyncio.to_thread로 offload된다. 전체 회귀 테스트 통과. 실제 HTTPS 병렬 요청에서도 실패 요청은 약 1.017초 지연되는 동안 정상 요청은 약 0.016초에 완료됐다.
F-002 — 해결됨. 4096-byte streaming body 경계와 초과 시 조기 종료 관련 회귀 테스트가 현재 head에서 통과했다. 관련 로직은 이전 수정과 동일하다.
F-003 — 해결됨.dashboard/authentication.py:111-122는 두 credential을 UTF-8 bytes로 비교한다. 유효한 비ASCII credential이 더 이상 hmac.compare_digest(str, str) TypeError로 500을 만들지 않는다. 실제 TLS 연결에서 ASCII 설정과 Unicode 설정 모두 정상 로그인 및 24시간/30일 token 발급이 성공했다. Unicode ID/비밀번호 불일치는 일반 실패 redirect, cookie 미발급, 최소 1초 지연을 유지했다.
dashboard/auth_server.py:101-112 strict UTF-8 form decoding은 %FF, raw invalid UTF-8 및 percent-encoded unpaired surrogate를 credential 비교 전에 400 / cookie 미발급으로 거부했다. malformed transport rejection은 credential mismatch와 구분했다.
새로운 승인 차단 finding은 확인되지 않았다. 늦게 발견되었다는 이유나 경미한 선택적 개선만으로 변경 요청을 생성하지 않았다.
직접 실행 근거
전체 pytest: 118 passed, 4 skipped, failure/error 0, exit 0. 122개 testcase를 JUnit으로 집계했다. 약 270초 소요.
AST parse, git diff --check, ruff check ., ruff format --check .: 모두 exit 0.
별도 loopback Uvicorn HTTPS 프로세스 + 검증된 self-signed localhost certificate에서 credential 정상/불일치, strict decoding, cookie flags/TTL, logout, 동시성, log redaction을 검증했다. 합성 credential만 사용했고 token/secret을 결과에 보존하지 않았다. 서버 프로세스 종료를 확인했다.
구현자 evidence의 source revision d84ed8ff2a53cd2735eceaaf0c54cb40c7c2c3ad는 현재 head의 조상이며 변경된 실행 코드/테스트 4개 파일이 동일함을 직접 검증했다. 판정은 이 제출 자료뿐 아니라 현재 head에서 실행한 reviewer 결과에 근거한다.
worker의 sleep-restoration 테스트 후보는 confirmed defect가 아니다. fixture 뒤 real_time.sleep이 patched reference라는 부분은 맞지만, enforce_failed_login_delay(..., sleep=time.sleep)의 기본 인자는 함수 정의 시 원래 callable을 저장한다 (dashboard/authentication.py:125-134). reviewer의 별도 callable/elapsed probe, 실제 pytest 및 HTTPS 지연 결과가 이를 확인했다.
helper를 직접 호출해 unpaired surrogate Python str를 주면 UnicodeEncodeError가 발생할 수 있다. 유효한 Unicode credential과 구분되는 미지원 helper 입력의 방어적 개선 여지다. 공개 HTTP 경로는 strict decoding으로 같은 입력을 400으로 차단함을 재현했으므로 이 승인 범위의 새 차단 finding으로 취급하지 않았다.
처음 두 전체 pytest 시도는 240초/150초 제한으로 timeout됐다. 단독 진단은 합성 DB fixture 생성의 SQLite migration stack을 확인했고 해당 테스트는 34.37초에 통과했다. 제한을 늘린 같은 현재 head 전체 재실행은 완주했다. 초기 timeout의 partial stdout은 보존하지 못했으며, 두 번째 이후 log/JUnit은 보존했다. timeout을 테스트 통과로 계산하지 않았다.
skip 4개는 기존 실제 은행/증권 PDF sample 또는 password 부재 사례이며 이 인증 변경의 필수 검증과 무관하다.
live deployed 서비스 및 실제 브라우저 재실행은 이번 delta 재리뷰에서 수행하지 않았다. 제출된 현재-head spec 승인, byte 동일한 기존 browser/gateway 검토와 현재 reviewer의 AppTest/HTTPS 실행 근거를 결합했다. 인증 게이트웨이는 지속 인증 구현이며 원 PR 전체에 대해 이전 검토의 명시된 한계를 유지한다.
최종 판단
현재 승인 계약 및 검토 범위 내에서 미해결 중요 결함 또는 승인에 필요한 필수 검증 공백을 확인하지 못했다. 현재 head를 승인한다. 이는 검토 범위에서 material finding을 발견하지 않았다는 뜻이지 무결함 증명이 아니다. 제품 코드 수정, commit/push, merge/deploy 및 Issue 종료는 수행하지 않았다.
<!-- gitea-agent:code-review/v1 owner=jsfamily repository=jarca-ledger-mcp issue=6 pr=7 head=6a35775cca96dfe0168e96c882c8e240b1f1e7b4 contract-sha256=4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe -->
# 독립 코드 재리뷰 — APPROVE
- Reviewer: `gitea-review-agent`
- Issue: #6 / PR: #7
- Reviewed head: `6a35775cca96dfe0168e96c882c8e240b1f1e7b4`
- Base: `7bea8b368e02e98afd4b2dc463fb270b0e6eb297`
- Approved contract SHA-256: `4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fe`
- Current-head spec approval: review #54 (`gitea-spec-agent`)
- Verdict: `approve`
## 범위와 검토 방식
전체 PR diff의 변경 파일 **23개**를 원격 diff/file 목록과 로컬 commit 내용으로 대조했다. 이전 독립 리뷰 #53의 head `755c26865c8e111aede8bcbe150a03e305a0ec60` 이후 변경 **5개 파일**을 직접 검토했다. 나머지 **18개 파일**은 byte/hash 동일성을 검증하여 이전 전체 검토 근거를 재사용했다. 새 코드 경로의 surrounding code, 인증 gate, token/cookie 처리와 기존 인증 테스트를 다시 확인했다.
변경 파일: `dashboard/authentication.py`, `dashboard/auth_server.py`, 두 인증 테스트, `docs/evidence/issue-6-f003-unicode-credentials.json`. 한 bounded worker가 독립 정적 검토를 수행했고, 판정 및 모든 중요한 후보 검증은 main reviewer가 수행했다.
## 기존 finding 처리
- **F-001 — 해결됨.** 실패 로그인 지연은 `asyncio.to_thread`로 offload된다. 전체 회귀 테스트 통과. 실제 HTTPS 병렬 요청에서도 실패 요청은 약 1.017초 지연되는 동안 정상 요청은 약 0.016초에 완료됐다.
- **F-002 — 해결됨.** 4096-byte streaming body 경계와 초과 시 조기 종료 관련 회귀 테스트가 현재 head에서 통과했다. 관련 로직은 이전 수정과 동일하다.
- **F-003 — 해결됨.** `dashboard/authentication.py:111-122`는 두 credential을 UTF-8 bytes로 비교한다. 유효한 비ASCII credential이 더 이상 `hmac.compare_digest(str, str)` TypeError로 500을 만들지 않는다. 실제 TLS 연결에서 ASCII 설정과 Unicode 설정 모두 정상 로그인 및 24시간/30일 token 발급이 성공했다. Unicode ID/비밀번호 불일치는 일반 실패 redirect, cookie 미발급, **최소 1초 지연**을 유지했다.
- `dashboard/auth_server.py:101-112` strict UTF-8 form decoding은 `%FF`, raw invalid UTF-8 및 percent-encoded unpaired surrogate를 credential 비교 전에 **400 / cookie 미발급**으로 거부했다. malformed transport rejection은 credential mismatch와 구분했다.
**새로운 승인 차단 finding은 확인되지 않았다.** 늦게 발견되었다는 이유나 경미한 선택적 개선만으로 변경 요청을 생성하지 않았다.
## 직접 실행 근거
- 전체 pytest: **118 passed, 4 skipped**, failure/error 0, exit 0. 122개 testcase를 JUnit으로 집계했다. 약 270초 소요.
- AST parse, `git diff --check`, `ruff check .`, `ruff format --check .`: 모두 exit 0.
- 별도 loopback Uvicorn **HTTPS** 프로세스 + 검증된 self-signed localhost certificate에서 credential 정상/불일치, strict decoding, cookie flags/TTL, logout, 동시성, log redaction을 검증했다. 합성 credential만 사용했고 token/secret을 결과에 보존하지 않았다. 서버 프로세스 종료를 확인했다.
- 구현자 evidence의 source revision `d84ed8ff2a53cd2735eceaaf0c54cb40c7c2c3ad`는 현재 head의 조상이며 변경된 실행 코드/테스트 4개 파일이 동일함을 직접 검증했다. 판정은 이 제출 자료뿐 아니라 현재 head에서 실행한 reviewer 결과에 근거한다.
- reviewer artifacts: `/opt/data/.review-runs/jarca-pr7-f003-rereview-20261004T054141Z` (`scope-coverage.json`, `pytest-full-extended.xml/.log`, `static-checks.json`, `reviewer-https-runtime-results.json`, `worker-corroboration.json`, `evidence-provenance.json`).
## 후보 검증·한계·잔여 위험
- worker의 sleep-restoration 테스트 후보는 **confirmed defect가 아니다**. fixture 뒤 `real_time.sleep`이 patched reference라는 부분은 맞지만, `enforce_failed_login_delay(..., sleep=time.sleep)`의 기본 인자는 함수 정의 시 원래 callable을 저장한다 (`dashboard/authentication.py:125-134`). reviewer의 별도 callable/elapsed probe, 실제 pytest 및 HTTPS 지연 결과가 이를 확인했다.
- helper를 직접 호출해 unpaired surrogate Python `str`를 주면 `UnicodeEncodeError`가 발생할 수 있다. 유효한 Unicode credential과 구분되는 미지원 helper 입력의 방어적 개선 여지다. 공개 HTTP 경로는 strict decoding으로 같은 입력을 400으로 차단함을 재현했으므로 이 승인 범위의 새 차단 finding으로 취급하지 않았다.
- 처음 두 전체 pytest 시도는 240초/150초 제한으로 timeout됐다. 단독 진단은 합성 DB fixture 생성의 SQLite migration stack을 확인했고 해당 테스트는 34.37초에 통과했다. 제한을 늘린 같은 현재 head 전체 재실행은 완주했다. 초기 timeout의 partial stdout은 보존하지 못했으며, 두 번째 이후 log/JUnit은 보존했다. timeout을 테스트 통과로 계산하지 않았다.
- skip 4개는 기존 실제 은행/증권 PDF sample 또는 password 부재 사례이며 이 인증 변경의 필수 검증과 무관하다.
- live deployed 서비스 및 실제 브라우저 재실행은 이번 delta 재리뷰에서 수행하지 않았다. 제출된 현재-head spec 승인, byte 동일한 기존 browser/gateway 검토와 현재 reviewer의 AppTest/HTTPS 실행 근거를 결합했다. 인증 게이트웨이는 지속 인증 구현이며 원 PR 전체에 대해 이전 검토의 명시된 한계를 유지한다.
## 최종 판단
현재 승인 계약 및 검토 범위 내에서 미해결 중요 결함 또는 승인에 필요한 필수 검증 공백을 확인하지 못했다. **현재 head를 승인한다.** 이는 검토 범위에서 material finding을 발견하지 않았다는 뜻이지 무결함 증명이 아니다. 제품 코드 수정, commit/push, merge/deploy 및 Issue 종료는 수행하지 않았다.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Closes #6
구현 내용
Secure/HttpOnlycookie로 복원하도록 변경했습니다.로그인 상태 유지선택 시 30일의 절대 만료를 사용하며 일반 요청·페이지 이동·재접속으로 만료를 연장하지 않습니다.sub를 비민감 고정 roledashboard-admin으로 사용하고 관리자 ID·password·signing secret은 server-only key binding에만 사용합니다.App의 same-process login/cleanup/logout route가 실제 HTTP response에서 cookie를 발급·삭제합니다.REQ-001revision 2.0과 interface/project/runtime/deployment 문서를 harness 정본 구조로 복원했습니다.Review #44 재작업
Review #45 재작업
docs/interface-contract.md에 exact token claim, 24시간/30일 absolute TTL, 미연장과 rotation 무효화 계약을 명시했습니다.docs/runtime-flow.md의 발급·복원·cleanup 순서와iat/exp불변조건을 동일 계약으로 보완했습니다.Review #46 재작업
REQ-001.03-AC02에 ID/password 각각의 constant-time 비교와 non-short-circuit 계측 검증을 복원했습니다.REQ-001.10-AC02에 인증 후 핵심 Dashboard page/query 회귀 계약과 기존 test 근거를 복원했습니다.REQ-001.10-AC03에 Dashboard와 MCP 인증 범위 분리 및 MCP 진입점·transport·port 불변 계약을 복원했습니다.Review #47 재작업
REQ-001.04-AC02에 정상 credential의 실패 지연 미사용과 애플리케이션 failure counter·lock state 미생성 acceptance contract를 복원했습니다.Review #49 재작업
asyncio.to_thread로 offload하여 기존 지연 semantics를 유지하면서 ASGI event loop 차단을 제거했습니다.Content-Length가 아닌 실제request.stream()byte를 기준으로 4096-byte 상한을 적용하고 초과 chunk에서 즉시 400을 반환합니다.Review #51 재작업
755c26865c8e111aede8bcbe150a03e305a0ec60docs/evidence/issue-6-d6-23-login-runtime.json29 passed/exit0은commands[0]; real-delay concurrency 수치와303/303은commands[1]; environment/source binding은environment,source_revision,hashes_sha256; secret 비보존은redaction에 각각 대응합니다.검증
33118664316707e19146d803fb21d400073c5206e7242a89f7f1faacbe984713a5ad211bd7bcac2buv run --extra dashboard --extra dev pytest -q: 116 passed, 4 skipped, exit 0uv run --extra dashboard --extra dev ruff check dashboard tests scripts: 통과, exit 0uv run --extra dashboard --extra dev python -m compileall -q dashboard scripts: 통과, exit 0git diff --check: 통과, exit 0제한사항
REQUEST_CHANGES는 구현 comment로 해제되지 않으며 새 head에 대한 spec-agent pass와 독립 code 재리뷰가 필요합니다.구현 명세 적합성 검토
91e106d75c32905e0b9d6c89d04f537c72a7c4f84f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changesAcceptance criteria 판정
D6-01—satisfieddashboard/authentication.py,dashboard/app.py— app 2832의 gate 실패 시 즉시 반환과 authentication 295313의 cookie 재검증을 확인했다.tests/test_dashboard_authentication_app.pyD6-02—satisfieddashboard/authentication.py,.env.example— authentication 73107에서 누락과 빈 값 및 읽기 오류를 거부하고 295303에서 보호 기능 진입을 중단한다.tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_persistent_authentication.pyD6-03—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 110146과 auth server 96102 및 변경하지 않은 기존 credential test를 읽었다.tests/test_dashboard_authentication.py,tests/test_dashboard_auth_server.pyD6-04—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 26 및 172189와 auth server 104118에서 86400초 token과 cookie를 발급한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-05—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 27 및 172189와 auth server 104118에서 2592000초 발급 규칙을 확인했다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-06—satisfieddashboard/authentication.py— authentication 270~276의 form checkbox에 checked 속성이 없고 30일 선택값은 제출 시 on으로 판정한다.tests/test_dashboard_authentication_app.pyD6-07—satisfieddashboard/authentication.py,dashboard/auth_server.py— token 발급은 성공 login route에서만 호출되고 gate는 검증만 수행하며 authentication 230~235가 server 시각으로 만료를 거부한다.tests/test_dashboard_persistent_authentication.pyD6-08—indeterminatedashboard/authentication.py,dashboard/app.py,dashboard/common.py— common 358~364의 query link는 유지된다. AppTest는 cookie와 query를 주입하고 protocol script는 단일 새 WebSocket 연결만 검사한다.tests/test_dashboard_app.py,tests/test_dashboard_authentication_app.py,scripts/persistent_auth_http_evidence.py,docs/evidence/issue-6-persistent-auth-https.jsonD6-09—satisfieddashboard/auth_server.py,dashboard/server.py,pyproject.toml— server 510에서 Streamlit App의 동일 process route를 조립하고 auth server 109118 및 128~135에서 response cookie를 설정한다.tests/test_dashboard_auth_server.py,scripts/persistent_auth_http_evidence.pyD6-10—satisfieddashboard/authentication.py,dashboard/auth_server.py— auth server 110118과 129135에서 Secure와 HttpOnly 및 SameSite lax와 Path slash를 적용하고 Domain을 지정하지 않는다.tests/test_dashboard_auth_server.pyD6-11—satisfieddashboard/authentication.py— authentication 193242가 서명과 strict claim 및 TTL과 만료를 검증하고 295313의 gate에는 이전 인증 boolean 우회가 없다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-12—satisfieddashboard/authentication.py— authentication 149~162에서 현재 secret과 password로 signing key를 파생하며 매 gate에서 현재 설정을 다시 읽는다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-13—satisfieddashboard/auth_server.py,dashboard/authentication.py— auth server 123~137은 matching cookie deletion response를 반환하고 금융 DB를 호출하지 않는다. 인증 권한을 가진 session boolean도 저장하지 않는다.tests/test_dashboard_auth_server.py,scripts/persistent_auth_http_evidence.pyD6-14—unsatisfieddashboard/authentication.py,dashboard/auth_server.py,docs/requirements/req-001_dashboard-administrator-authentication.md,docs/interface-contract.md— authentication 178~186은 sub claim에 현재 관리자 ID 원문을 넣어 서명한다. 고정된 itsdangerous 2.2.0의 URL safe payload는 가역적 base64와 선택적 압축이며 암호화가 아니다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-15—unsatisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 299~309는 로그인 화면과 false만 반환한다. 전체 변경 Python 파일의 delete cookie 호출은 auth server 129의 명시적 logout 한 곳뿐이다.tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-16—unsatisfieddocs/requirements/req-001_dashboard-administrator-authentication.md— 요구 문서 103~159에서 필수 제약과 불변조건 및 하위 호환성 섹션을 제거했고 AC 001부터 AC 017의 checklist로 요구사항별 criterion 구조를 대체했다.D6-17—unsatisfieddocs/interface-contract.md,docs/runtime-flow.md,docs/requirements/req-001_dashboard-administrator-authentication.md— interface 27은 관리자 ID를 민감정보로 분류하지만 requirement 85는 이를 token sub에 넣도록 규정한다. runtime 58~66의 실패 복구에는 cookie 삭제가 없다.D6-18—satisfieddocs/project-structure.md,dashboard/server.py,dashboard/auth_server.py,dashboard/authentication.py,pyproject.toml,uv.lock— project structure 918과 pyproject 1928 및 locked dependency를 확인했다. 기존 Dockerfile은 dashboard extra와 Dashboard source를 설치한다.tests/test_dashboard_auth_server.py,tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-19—satisfied.env.example,docker-compose.yml,deploy/k8s/base/deployment.yaml,deploy/k8s/README.md,README.md— env example 49와 Compose 3247 및 Kubernetes 124~140에 별도 signing 설정과 proxy trust 경계가 있고 README가 HTTPS와 rotation 및 rate limit을 설명한다.D6-20—satisfieddashboard/app.py,dashboard/common.py,pyproject.toml,uv.lock— authoritative diff의 변경 경로 20개를 두 immutable tree와 대조했다. 금융 schema 및 MCP source와 page dispatch 목록 및 sidebar link는 변경하지 않았다.tests/test_dashboard_app.pyD6-21—unsatisfieddashboard/authentication.py,dashboard/auth_server.py— persistent test 7792의 만료 경계는 기본 TTL만 검사하고 6575의 30일 case는 발급 값만 확인한다. AppTest 138~155는 password rotation만 통합 검사한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.py,scripts/persistent_auth_http_evidence.pyD6-22—unsatisfieddashboard/auth_server.py,dashboard/authentication.py— 저장된 evidence 6~7은 HTTP와 Streamlit WebSocket protocol client라고 명시한다. PR 본문도 Chromium UI 자동화를 실행하지 못했다고 명시한다.scripts/persistent_auth_http_evidence.py,docs/evidence/issue-6-persistent-auth-https.json,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-23—indeterminatedocs/evidence/issue-6-persistent-auth-https.json,docs/requirements/req-001_dashboard-administrator-authentication.md— evidence 5의 source revision은 PR base다. 그 revision에는 evidence script와 신규 adapter가 존재하지 않는다. evidence에는 exit code와 실행 환경 및 실행된 source tree binding이 없고 현재 head status는 결과 0개인 pending이다.scripts/persistent_auth_http_evidence.py범위 판정
결론
승인 계약에 대한 명확한 구현 및 문서 위반과 필수 검증 누락이 있으므로 변경을 요청한다. 확인된 정적 구현과 미확인 실행 결과를 구분하며 승인 계약을 구현에 맞춰 완화하지 않는다.
구현 명세 적합성 검토
91e106d75c32905e0b9d6c89d04f537c72a7c4f84f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changesAcceptance criteria 판정
D6-01—satisfieddashboard/authentication.py,dashboard/app.py— app 2832의 gate 실패 시 즉시 반환과 authentication 295313의 cookie 재검증을 확인했다.tests/test_dashboard_authentication_app.pyD6-02—satisfieddashboard/authentication.py,.env.example— authentication 73107에서 누락과 빈 값 및 읽기 오류를 거부하고 295303에서 보호 기능 진입을 중단한다.tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_persistent_authentication.pyD6-03—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 110146과 auth server 96102 및 변경하지 않은 기존 credential test를 읽었다.tests/test_dashboard_authentication.py,tests/test_dashboard_auth_server.pyD6-04—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 26 및 172189와 auth server 104118에서 86400초 token과 cookie를 발급한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-05—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 27 및 172189와 auth server 104118에서 2592000초 발급 규칙을 확인했다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-06—satisfieddashboard/authentication.py— authentication 270~276의 form checkbox에 checked 속성이 없고 30일 선택값은 제출 시 on으로 판정한다.tests/test_dashboard_authentication_app.pyD6-07—satisfieddashboard/authentication.py,dashboard/auth_server.py— token 발급은 성공 login route에서만 호출되고 gate는 검증만 수행하며 authentication 230~235가 server 시각으로 만료를 거부한다.tests/test_dashboard_persistent_authentication.pyD6-08—indeterminatedashboard/authentication.py,dashboard/app.py,dashboard/common.py— common 358~364의 query link는 유지된다. AppTest는 cookie와 query를 주입하고 protocol script는 단일 새 WebSocket 연결만 검사한다.tests/test_dashboard_app.py,tests/test_dashboard_authentication_app.py,scripts/persistent_auth_http_evidence.py,docs/evidence/issue-6-persistent-auth-https.jsonD6-09—satisfieddashboard/auth_server.py,dashboard/server.py,pyproject.toml— server 510에서 Streamlit App의 동일 process route를 조립하고 auth server 109118 및 128~135에서 response cookie를 설정한다.tests/test_dashboard_auth_server.py,scripts/persistent_auth_http_evidence.pyD6-10—satisfieddashboard/authentication.py,dashboard/auth_server.py— auth server 110118과 129135에서 Secure와 HttpOnly 및 SameSite lax와 Path slash를 적용하고 Domain을 지정하지 않는다.tests/test_dashboard_auth_server.pyD6-11—satisfieddashboard/authentication.py— authentication 193242가 서명과 strict claim 및 TTL과 만료를 검증하고 295313의 gate에는 이전 인증 boolean 우회가 없다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-12—satisfieddashboard/authentication.py— authentication 149~162에서 현재 secret과 password로 signing key를 파생하며 매 gate에서 현재 설정을 다시 읽는다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-13—satisfieddashboard/auth_server.py,dashboard/authentication.py— auth server 123~137은 matching cookie deletion response를 반환하고 금융 DB를 호출하지 않는다. 인증 권한을 가진 session boolean도 저장하지 않는다.tests/test_dashboard_auth_server.py,scripts/persistent_auth_http_evidence.pyD6-14—unsatisfieddashboard/authentication.py,dashboard/auth_server.py,docs/requirements/req-001_dashboard-administrator-authentication.md,docs/interface-contract.md— authentication 178~186은 sub claim에 현재 관리자 ID 원문을 넣어 서명한다. 고정된 itsdangerous 2.2.0의 URL safe payload는 가역적 base64와 선택적 압축이며 암호화가 아니다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-15—unsatisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 299~309는 로그인 화면과 false만 반환한다. 전체 변경 Python 파일의 delete cookie 호출은 auth server 129의 명시적 logout 한 곳뿐이다.tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-16—unsatisfieddocs/requirements/req-001_dashboard-administrator-authentication.md— 요구 문서 103~159에서 필수 제약과 불변조건 및 하위 호환성 섹션을 제거했고 AC 001부터 AC 017의 checklist로 요구사항별 criterion 구조를 대체했다.D6-17—unsatisfieddocs/interface-contract.md,docs/runtime-flow.md,docs/requirements/req-001_dashboard-administrator-authentication.md— interface 27은 관리자 ID를 민감정보로 분류하지만 requirement 85는 이를 token sub에 넣도록 규정한다. runtime 58~66의 실패 복구에는 cookie 삭제가 없다.D6-18—satisfieddocs/project-structure.md,dashboard/server.py,dashboard/auth_server.py,dashboard/authentication.py,pyproject.toml,uv.lock— project structure 918과 pyproject 1928 및 locked dependency를 확인했다. 기존 Dockerfile은 dashboard extra와 Dashboard source를 설치한다.tests/test_dashboard_auth_server.py,tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-19—satisfied.env.example,docker-compose.yml,deploy/k8s/base/deployment.yaml,deploy/k8s/README.md,README.md— env example 49와 Compose 3247 및 Kubernetes 124~140에 별도 signing 설정과 proxy trust 경계가 있고 README가 HTTPS와 rotation 및 rate limit을 설명한다.D6-20—satisfieddashboard/app.py,dashboard/common.py,pyproject.toml,uv.lock— authoritative diff의 변경 경로 20개를 두 immutable tree와 대조했다. 금융 schema 및 MCP source와 page dispatch 목록 및 sidebar link는 변경하지 않았다.tests/test_dashboard_app.pyD6-21—unsatisfieddashboard/authentication.py,dashboard/auth_server.py— persistent test 7792의 만료 경계는 기본 TTL만 검사하고 6575의 30일 case는 발급 값만 확인한다. AppTest 138~155는 password rotation만 통합 검사한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.py,scripts/persistent_auth_http_evidence.pyD6-22—unsatisfieddashboard/auth_server.py,dashboard/authentication.py— 저장된 evidence 6~7은 HTTP와 Streamlit WebSocket protocol client라고 명시한다. PR 본문도 Chromium UI 자동화를 실행하지 못했다고 명시한다.scripts/persistent_auth_http_evidence.py,docs/evidence/issue-6-persistent-auth-https.json,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-23—indeterminatedocs/evidence/issue-6-persistent-auth-https.json,docs/requirements/req-001_dashboard-administrator-authentication.md— evidence 5의 source revision은 PR base다. 그 revision에는 evidence script와 신규 adapter가 존재하지 않는다. evidence에는 exit code와 실행 환경 및 실행된 source tree binding이 없고 현재 head status는 결과 0개인 pending이다.scripts/persistent_auth_http_evidence.py범위 판정
결론
승인 계약에 대한 명확한 구현 및 문서 위반과 필수 검증 누락이 있으므로 변경을 요청한다. 확인된 정적 구현과 미확인 실행 결과를 구분하며 승인 계약을 구현에 맞춰 완화하지 않는다.
구현 재작업 결과
91e106d75c32905e0b9d6c89d04f537c72a7c4f89bc64699cb22380b3557ab5c41952017a21ccc694f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferework_delivered지적 처리
D6-14—resolvedvalid_fixdashboard/authentication.py,tests/test_dashboard_persistent_authentication.py, 인증 계약 문서D6-15—resolvedvalid_fixdashboard/authentication.py,dashboard/auth_server.py, auth tests와 runtime/interface 문서D6-16—resolvedvalid_fixdocs/requirements/req-001_dashboard-administrator-authentication.mdD6-17—resolvedvalid_fixdocs/interface-contract.md,docs/project-structure.md,docs/runtime-flow.md,README.md,deploy/k8s/README.mdD6-21—resolvedevidence_gapD6-08—resolvedevidence_gapdocs/evidence/issue-6-persistent-auth-https.jsonImport이동 후 인증 복원D6-22—resolvedevidence_gapdocs/evidence/issue-6-persistent-auth-https.jsonD6-23—resolvedevidence_gapdocs/evidence/issue-6-persistent-auth-https.json7bb959859a84f1d1293afd1f0270cca18f4cd576에 결합하고 evidence-only descendant를 명시했습니다.최종 검증
uv run --extra dashboard --extra dev pytest -qexit 0 (111 passed, 4 skipped); ruff exit 0; compileall exit 0pending(status context 0개)다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존
REQUEST_CHANGES를 해제하거나 승인을 의미하지 않는다.구현 명세 적합성 검토
9bc64699cb22380b3557ab5c41952017a21ccc694f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changesAcceptance criteria 판정
D6-01—satisfieddashboard/authentication.py,dashboard/app.py— app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.tests/test_dashboard_authentication_app.pyD6-02—satisfieddashboard/authentication.py,.env.example— authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.tests/test_dashboard_authentication.py,tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-03—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 111147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.tests/test_dashboard_authentication.py,tests/test_dashboard_auth_server.pyD6-04—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-05—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-06—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-07—satisfieddashboard/authentication.py,dashboard/auth_server.py— 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.tests/test_dashboard_persistent_authentication.pyD6-08—satisfieddashboard/authentication.py,dashboard/common.py,docs/evidence/issue-6-persistent-auth-https.json— 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.tests/test_dashboard_app.py,tests/test_dashboard_authentication_app.pyD6-09—satisfieddashboard/auth_server.py,dashboard/server.py— server 510은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.tests/test_dashboard_auth_server.pyD6-10—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— authentication 25의 Host prefix와 auth server 134142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.tests/test_dashboard_auth_server.pyD6-11—satisfieddashboard/authentication.py— authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-12—satisfieddashboard/authentication.py— authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-13—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— auth server 147165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.tests/test_dashboard_auth_server.pyD6-14—satisfieddashboard/authentication.py,docs/requirements/req-001_dashboard-administrator-authentication.md,docs/interface-contract.md— authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-15—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/runtime-flow.md— authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-16—satisfieddocs/requirements/req-001_dashboard-administrator-authentication.md— REQ 001 revision 2.0과 정본 H2 11개 및 요구사항 11개와 요구사항별 AC 11개를 파싱했고 기존 인증 경계와 보호 범위 및 추적성을 대조했다.D6-17—unsatisfieddocs/interface-contract.md,docs/runtime-flow.md,docs/requirements/req-001_dashboard-administrator-authentication.md— interface 4376과 137172 및 213218에는 기본 24시간과 로그인 성공 시각 기준 절대 만료 및 미연장과 전체 token claim 계약이 정의되지 않았다. runtime 2753은 발급 및 검증 흐름을 적지만 두 TTL과 로그인 성공 기준 및 활동 복원 미연장을 정의하지 않는다.D6-18—satisfieddocs/project-structure.md,dashboard/server.py,dashboard/auth_server.py,pyproject.toml,uv.lock— project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.tests/test_dashboard_auth_server.pyD6-19—satisfied.env.example,docker-compose.yml,deploy/k8s/base/deployment.yaml,deploy/k8s/README.md,README.md— 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.D6-20—satisfieddashboard/app.py,docs/project-structure.md,README.md— 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.tests/test_dashboard_app.pyD6-21—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— persistent test 168179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.pyD6-22—satisfieddocs/evidence/issue-6-persistent-auth-https.json,dashboard/authentication.py,dashboard/auth_server.py,scripts/persistent_auth_http_evidence.py— browser 기록 1023 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.tests/test_dashboard_auth_server.py,tests/test_dashboard_authentication_app.pyD6-23—satisfieddocs/evidence/issue-6-persistent-auth-https.json— 기록 source 7bb959859a84f1d1293afd1f0270cca18f4cd576은 current head의 ancestor이고 이후 변경은 evidence JSON 하나뿐이다. 실행 대상 hash 다섯 개 및 commit provenance와 명령 및 exit code 및 환경 및 결과를 검증했다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.py범위 판정
결론
재작업으로 관리자 ID 원문 payload와 실패 cookie 폐기 및 REQ 001 구조와 추가 검증 누락은 해소됐다. 다만 승인된 인터페이스 및 런타임 문서 산출물의 만료와 미연장 등의 정의가 누락되어 변경을 요청한다. 정적 source와 source bound 구현자 실행 기록을 검토했으며 spec agent가 runtime test를 새로 실행하거나 CI 성공을 확인한 것은 아니다.
구현 재작업 결과
9bc64699cb22380b3557ab5c41952017a21ccc697056e012a7c51eab4405d2e380f44f20e885170e4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferework_delivered지적 처리
D6-17—resolvedvalid_fixdocs/interface-contract.md,docs/runtime-flow.md최종 검증
uv run --extra dashboard --extra dev pytest -qexit 0 (111 passed, 4 skipped); ruff exit 0; compileall exit 0;git diff --checkexit 0pending(status context 0개)다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존
REQUEST_CHANGES를 해제하거나 승인을 의미하지 않는다.구현 명세 적합성 검토
7056e012a7c51eab4405d2e380f44f20e885170e4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changesAcceptance criteria 판정
D6-01—satisfieddashboard/authentication.py,dashboard/app.py— app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.tests/test_dashboard_authentication_app.pyD6-02—satisfieddashboard/authentication.py,.env.example— authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.tests/test_dashboard_authentication.py,tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-03—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 111147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.tests/test_dashboard_authentication.py,tests/test_dashboard_auth_server.pyD6-04—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-05—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-06—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-07—satisfieddashboard/authentication.py,dashboard/auth_server.py— 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.tests/test_dashboard_persistent_authentication.pyD6-08—satisfieddashboard/authentication.py,dashboard/common.py,docs/evidence/issue-6-persistent-auth-https.json— 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.tests/test_dashboard_app.py,tests/test_dashboard_authentication_app.pyD6-09—satisfieddashboard/auth_server.py,dashboard/server.py— server 510은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.tests/test_dashboard_auth_server.pyD6-10—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— authentication 25의 Host prefix와 auth server 134142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.tests/test_dashboard_auth_server.pyD6-11—satisfieddashboard/authentication.py— authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-12—satisfieddashboard/authentication.py— authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-13—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— auth server 147165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.tests/test_dashboard_auth_server.pyD6-14—satisfieddashboard/authentication.py,docs/requirements/req-001_dashboard-administrator-authentication.md,docs/interface-contract.md— authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-15—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/runtime-flow.md— authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-16—unsatisfieddocs/requirements/req-001_dashboard-administrator-authentication.md— 현재 정본 구조와 식별자 및 revision 및 추적성은 충족한다. 그러나 기존 REQ 001.03-AC01의 두 constant time 비교 계측 검증에 대응하는 현재 AC는 없고 현재 03-AC01은 비노출만 검증한다. 기존 06-AC01 인증 후 핵심 페이지 회귀 및 06-AC02 MCP 범위 분리 검증도 현재 10-AC01의 배포 문서 검증으로 대체되어 해당 인수 기준이 빠졌다.D6-17—satisfieddocs/interface-contract.md,docs/runtime-flow.md,docs/requirements/req-001_dashboard-administrator-authentication.md— interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.D6-18—satisfieddocs/project-structure.md,dashboard/server.py,dashboard/auth_server.py,pyproject.toml,uv.lock— project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.tests/test_dashboard_auth_server.pyD6-19—satisfied.env.example,docker-compose.yml,deploy/k8s/base/deployment.yaml,deploy/k8s/README.md,README.md— 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.D6-20—satisfieddashboard/app.py,docs/project-structure.md,README.md— 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.tests/test_dashboard_app.pyD6-21—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— persistent test 168179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.pyD6-22—satisfieddocs/evidence/issue-6-persistent-auth-https.json,dashboard/authentication.py,dashboard/auth_server.py,scripts/persistent_auth_http_evidence.py— browser 기록 1023 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.tests/test_dashboard_auth_server.py,tests/test_dashboard_authentication_app.pyD6-23—satisfieddocs/evidence/issue-6-persistent-auth-https.json— 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 current head까지 변경은 evidence와 두 계약 문서뿐이다. 전체 diff와 현재 blob 및 실행 코드 설정 test fixture dependency lockfile의 불변성과 문서 입력 참조 부재를 확인했다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.py범위 판정
결론
직전 TTL과 token 및 rotation 문서 변경 요청은 해소됐다. 다만 기존 REQ 001의 credential 비교와 인증 후 페이지 호환성 및 MCP 범위 분리에 대한 인수 검증 계약 누락을 확인하여 변경을 요청한다. 이는 앞선 검토에서 충분히 발견하지 못한 기존 산출물 누락이며 새 요구사항이 아니다. 구현자 실행 기록과 reviewer 독립 실행을 구분하며 native 게시 여부와 절차 전체 상태는 별도 execution report에서 판정한다.
구현 재작업 결과
7056e012a7c51eab4405d2e380f44f20e885170e8544ea30f4c4cea5489b7a136d6c53a8a538341a4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferework_delivered지적 처리
D6-16—resolvedvalid_fixdocs/requirements/req-001_dashboard-administrator-authentication.md최종 검증
uv run --extra dashboard --extra dev pytest -qexit 0 (111 passed, 4 skipped); ruff exit 0; compileall exit 0;git diff --checkexit 0pending(status context 0개)다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존
REQUEST_CHANGES를 해제하거나 승인을 의미하지 않는다.구현 명세 적합성 검토
8544ea30f4c4cea5489b7a136d6c53a8a538341a4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changesAcceptance criteria 판정
D6-01—satisfieddashboard/authentication.py,dashboard/app.py— app 2832에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.tests/test_dashboard_authentication_app.pyD6-02—satisfieddashboard/authentication.py,.env.example— authentication 74108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.tests/test_dashboard_authentication.py,tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-03—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 111147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.tests/test_dashboard_authentication.py,tests/test_dashboard_auth_server.pyD6-04—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 26과 173190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-05—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 27과 173190 및 auth server 128142에서 선택 시 2592000초를 적용한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_auth_server.pyD6-06—satisfieddashboard/authentication.py,dashboard/auth_server.py— authentication 272에 checked 속성이 없고 auth server 128은 on으로 제출된 경우만 선택한다.tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-07—satisfieddashboard/authentication.py,dashboard/auth_server.py— 발급은 auth server login에만 있고 authentication verifier는 iat와 exp를 고정 검증하며 복원에서 token을 재발급하지 않는다.tests/test_dashboard_persistent_authentication.pyD6-08—satisfieddashboard/authentication.py,dashboard/common.py,docs/evidence/issue-6-persistent-auth-https.json— 동일 query link와 gate를 유지하며 browser 기록 57~62는 새 탭 Import 복원을 확인하고 core page AppTest는 같은 cookie로 새 session들을 검증한다.tests/test_dashboard_app.py,tests/test_dashboard_authentication_app.pyD6-09—satisfieddashboard/auth_server.py,dashboard/server.py— server 510은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.tests/test_dashboard_auth_server.pyD6-10—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— authentication 25의 Host prefix와 auth server 134142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.tests/test_dashboard_auth_server.pyD6-11—satisfieddashboard/authentication.py— authentication 194239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-12—satisfieddashboard/authentication.py— authentication 150~162의 server only key binding 및 verifier에 unit rotation test와 password 및 signing rotation AppTest가 대응한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.pyD6-13—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— auth server 147165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.tests/test_dashboard_auth_server.pyD6-14—satisfieddashboard/authentication.py,docs/requirements/req-001_dashboard-administrator-authentication.md,docs/interface-contract.md— authentication 34 및 179185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-15—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/runtime-flow.md— authentication 302312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.pyD6-16—unsatisfieddocs/requirements/req-001_dashboard-administrator-authentication.md— 현재 req 문서 269287 및 429467에서 직전 두 credential 비교 계측과 핵심 page 회귀 및 MCP 범위 분리 AC가 복원됐다. Baseline AC 15개를 의미별로 대조한 결과 기존 REQ 001.04-AC02의 성공 시 실패 지연 함수 미사용 및 반복 실패 후 failure counter와 lock state 미생성 검증에 대응하는 AC가 없다. 현재 289~307의 04-AC01은 두 credential 실패 지연과 일반 오류 및 cookie DB gateway 불변성만 검증한다. 성공 무지연은 요구사항 96에 남아 있으나 인수 검증 대응은 누락됐다.tests/test_dashboard_authentication.py,tests/test_dashboard_auth_server.pyD6-17—satisfieddocs/interface-contract.md,docs/runtime-flow.md,docs/requirements/req-001_dashboard-administrator-authentication.md— interface login 출력 뒤 token claim과 두 TTL 및 미연장을 정의하고 cookie restoration 뒤 현재 설정 binding과 rotation 무효화를 정의한다. runtime login 04와 05 및 gate 02와 만료 및 rotation 불변조건이 같은 계약을 명시한다.D6-18—satisfieddocs/project-structure.md,dashboard/server.py,dashboard/auth_server.py,pyproject.toml,uv.lock— project 2346 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.tests/test_dashboard_auth_server.pyD6-19—satisfied.env.example,docker-compose.yml,deploy/k8s/base/deployment.yaml,deploy/k8s/README.md,README.md— 별도 signing 환경변수와 Kubernetes secretKeyRef 및 README 34~54와 배포 문서의 HTTPS와 rotation 운영 조건을 확인했다.D6-20—satisfieddashboard/app.py,docs/project-structure.md,README.md— 변경 경로에는 금융 schema 또는 MCP 인증 구현이 없으며 기존 page AppTest는 같은 gateway 및 query 동작을 확인한다.tests/test_dashboard_app.pyD6-21—satisfieddashboard/authentication.py,dashboard/auth_server.py,docs/evidence/issue-6-persistent-auth-https.json— persistent test 168179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.pyD6-22—satisfieddocs/evidence/issue-6-persistent-auth-https.json,dashboard/authentication.py,dashboard/auth_server.py,scripts/persistent_auth_http_evidence.py— browser 기록 1023 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.tests/test_dashboard_auth_server.py,tests/test_dashboard_authentication_app.pyD6-23—satisfieddocs/evidence/issue-6-persistent-auth-https.json— 실행 source 7bb959859a84f1d1293afd1f0270cca18f4cd576부터 current head까지 변경은 evidence 및 두 계약 문서 및 REQ 001뿐이다. 전체 diff와 blob hash 및 실행 코드 설정 test fixture dependency lockfile의 동일성과 실행 입력의 문서 참조 부재를 검증했다.tests/test_dashboard_persistent_authentication.py,tests/test_dashboard_authentication_app.py,tests/test_dashboard_auth_server.py,tests/test_dashboard_app.py범위 판정
결론
직전 두 credential 비교 계측과 핵심 page 회귀 및 MCP 범위 분리 AC는 복원됐다. Baseline AC 전체를 의미별로 재검토하여 기존 성공 무지연 및 애플리케이션 잠금 미도입의 인수 검증 대응 누락을 확인했으므로 변경을 요청한다. 구현 동작의 회귀나 새 요구사항이 아니라 앞선 검토가 놓친 기존 문서 계약 누락이다. 구현자 실행 기록과 reviewer 독립 실행 및 native 게시와 전체 절차 상태를 구분한다.
구현 재작업 결과
8544ea30f4c4cea5489b7a136d6c53a8a538341a33118664316707e19146d803fb21d400073c52064f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferework_delivered지적 처리
D6-16—resolvedvalid_fixdocs/requirements/req-001_dashboard-administrator-authentication.mdREQ-001.04-AC02로 복원했습니다.최종 검증
uv run --extra dashboard --extra dev pytest -qexit 0 (111 passed, 4 skipped); targeted suite exit 0 (25 passed); ruff exit 0; compileall exit 0;git diff --checkexit 0dashboard/auth_server.py=6dc7186ef0a1bc162575962cc6399038c6494aef,dashboard/authentication.py=fe09bcd93fe98db86c9be254c7c465e99c947edb,tests/test_dashboard_authentication.py=47e85ee4cfcbfed51c529dca07b98e09b0b44d71,tests/test_dashboard_auth_server.py=f90de4e6811ff736dda853101c5367a1567110d8,docs/evidence/issue-6-persistent-auth-https.json=c03677f982837414830592021b4e09eb21c4d2a1이며 기존 ARM64 Chromium HTTPS evidence를 재사용했습니다.pending(status context 0개)다음 단계
Reviewer가 rework head를 새 snapshot으로 다시 검토해야 하며 이 comment는 기존
REQUEST_CHANGES를 해제하거나 승인을 의미하지 않는다.구현 명세 적합성 검토
33118664316707e19146d803fb21d400073c52064f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1feapproveAcceptance criteria 판정
D6-01—satisfied32에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.D6-02—satisfied108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.D6-03—satisfied147의 비교와 지연 및 event log를 auth server 120126에서 호출한다.D6-04—satisfied190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.D6-05—satisfied190 및 auth server 128142에서 선택 시 2592000초를 적용한다.D6-06—satisfiedD6-07—satisfiedD6-08—satisfiedD6-09—satisfied10은 Streamlit App에 같은 process auth routes를 연결하고 auth server 134142 및 158~165는 실제 응답 cookie를 제어한다.D6-10—satisfied142 및 158165의 보안 속성이 browser 기록 4455 및 7279에 대응한다.D6-11—satisfied239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.D6-12—satisfiedD6-13—satisfied165의 matching deletion과 browser 기록 7279의 cookie 제거 및 미인증 전이를 확인했다.D6-14—satisfied185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.D6-15—satisfied312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.D6-16—satisfied327에서 두 credential 비교 계측과 성공 지연 및 잠금 없음 AC를 확인했다. 449487에서 핵심 page 회귀와 MCP 범위 분리 AC가 유지된다. Baseline AC 15개 전체를 문서 전문 및 참조 test에 의미 대응했고 승인된 session 대체와 병합을 확인했다. 새 04 AC02는 auth server 120~132의 실패 분기 전용 지연 호출과 authentication source의 failure counter 및 lock state 부재와 기존 지연 helper 및 성공 login test를 연결한다.D6-17—satisfiedD6-18—satisfied46 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.D6-19—satisfiedD6-20—satisfiedD6-21—satisfied179의 30일 경계와 AppTest 165183의 signing rotation을 추가했고 browser 기록은 logout 후 cookie 부재와 미인증 전이를 확인한다.D6-22—satisfied23 및 4280은 ARM64 Chromium과 HTTPS 및 synthetic DB에서 발급과 HttpOnly 비노출 및 새 탭 복원과 cleanup 및 logout 삭제를 기록한다. HTTP protocol script는 보조 probe이며 실제 browser 기록을 대체하지 않는다.D6-23—satisfied범위 판정
결론
승인된 Issue 6 계약의 전체 항목과 baseline AC 전체의 의미를 현재 문서 및 구현과 검증 근거에 대조했다. 직전 D6-16의 성공 무지연 및 잠금 상태 미생성 인수 검증 계약은 복원됐으며 필수 누락과 범위 초과 및 명세 모순은 없다. 구현자의 source bound 실행 기록을 실행 입력 동일성으로 연결했으며 reviewer 독립 runtime 실행이나 CI 성공으로 표현하지 않는다. 명세 적합성을 승인한다.
독립 코드 리뷰 — jsfamily/jarca-ledger-mcp Issue #6 / PR #7
33118664316707e19146d803fb21d400073c52067bea8b368e02e98afd4b2dc463fb270b0e6eb2974f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1feAPPROVEDgitea-review-agent(stable account ID 7), 독립 코드 리뷰request_changes검토 범위
변경 파일 20개 전체 diff, 인증 gate와 기존 호출자, 관련 unit/AppTest/HTTP tests, dependency/lockfile, Compose/Kubernetes 및 운영 문서, 실제 Uvicorn/Streamlit 경로를 확인했다. 요구사항을 추가하거나 변경하지 않았다. 두 결함은 승인된 인증 방식 자체가 아니라 새 ASGI adapter의 실행·자원 경계 문제다.
확정 결함
F-001 — High: 실패 로그인 지연이 공유 event loop를 차단한다
dashboard/auth_server.py:120-126, 특히124; 호출되는dashboard/authentication.py:121-130./api/auth/login에 잘못된 ID/password를 제출한다. 관리자 credential이나 cookie가 없어도 가능하며 비브라우저 client가 Origin을 설정할 수 있다.async def login()이 동기enforce_failed_login_delay()를 직접 실행하여time.sleep()으로 Uvicorn event loop를 멈춘다. 이전 Streamlit 실행 경로의 helper를 ASGI coroutine으로 옮기면서 요청 간 독립 처리가 깨졌다.dashboard.server:app을 loopback HTTPS/Uvicorn으로 실행했다. baseline health는0.016754406038671732초였다. 실패 login 6건을 동시에 제출하자 모두 303을 반환하는 데6.093019616790116초가 걸렸고, 별도 health 요청은5.932137297000736초 지연됐다. 첫 재현에서도 health 지연6.160729866940528초가 확인됐다. 지연을 mock하지 않았다.https-runtime.json,https-runtime-first-attempt.json,reproductions.json, 해당 source 위치. 기존 테스트 111개가 통과해도 이 동시성 회귀를 검출하지 못한다.docs/runtime-flow.md:89-95의 독립 요청 처리에 맞아야 한다.F-002 — Medium: 4096바이트 제한이 전체 body 수신 후 적용되어 자원 상한이 아니다
dashboard/auth_server.py:94-97.application/x-www-form-urlencoded로 oversized 또는 chunked login body를 보낸다. credential 검증 전에 실행된다.await request.body()가 body 전체를 누적한 다음에야len(body) > 4096을 검사한다. 그러므로 명시된 상한은 응답 검증값일 뿐 수신·메모리 사용을 제한하지 않는다.dashboard.server:app의 ASGI 경로에 8192바이트 chunk 16개를 전달했다. 제한은 4096바이트인데 16개 chunk, 총131072바이트를 모두 소비한 후 400을 반환했다. credential 검증 전의 거부였다.reproductions.json의body_limit및 해당 source 위치.비차단 제안·잔여 위험
README.md:52와.env.example:8-9의JARCA_DASHBOARD_FORWARDED_ALLOW_IPS는 Compose가docker-compose.yml:35에서 실제FORWARDED_ALLOW_IPS로 매핑할 때만 적용된다. README의 직접 Uvicorn 명령에서는 자동 매핑되지 않는다. non-Compose 실행에는 실제 변수 또는 CLI flag를 안내하면 좋다. 실험에서 Compose 없이 prefixed 변수를 설정하면 effective allowlist가 loopback에 남아 비loopback proxy 경로가 403이고, 실제 Uvicorn allowlist를 설정하면 303이었다. Kubernetes 문서deploy/k8s/README.md:43는 이미 올바른 변수를 사용한다. 기본 loopback trust와 HTTPS-only 요구 자체를 결함으로 보지 않는다.uvicorn dashboard.server:app인데 기존scripts/ci_dashboard_http_smoke.py:79-93와.gitea/release.yaml:72-92는streamlit run dashboard/app.py를 검증한다. 기존 파일 자체의 결함으로 단정하지 않지만 새 adapter와 auth routes의 packaging/runtime 회귀를 놓칠 수 있다. 실제 배포 진입점을 사용하는 focused smoke로 정렬할 것을 제안한다.직접 실행 결과와 provenance
3.13.5, Streamlit1.58.0, Uvicorn0.49.0, Starlette1.3.1, HTTPX0.28.1, itsdangerous2.2.0, websockets16.0.PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -q -p no:cacheprovider --basetemp=/opt/data/.review-runs/jarca-pr7-resume-20261002T110307Z/pytest-tmp: exit 0, 111 passed, 4 skipped. 출력은 project addopts와 겹친 quiet mode여서 progress 문자를 프로그램으로 집계했다. Starlette TestClient의 httpx deprecation warning 1건.PYTHONDONTWRITEBYTECODE=1 .venv/bin/ruff check --no-cache dashboard tests scripts: exit 0,All checks passed!.git diff --check 7bea8b368e02e98afd4b2dc463fb270b0e6eb297 33118664316707e19146d803fb21d400073c5206: exit 0.PYTHONDONTWRITEBYTECODE=1 uv lock --check --offline: exit 0, 72 packages resolved; install이나 source 수정은 하지 않았다.PYTHONDONTWRITEBYTECODE=1 .venv/bin/python /opt/data/.review-runs/jarca-pr7-resume-20261002T110307Z/reproduce.py: exit 0, concurrency/body/proxy 재현.PYTHONDONTWRITEBYTECODE=1 .venv/bin/python /opt/data/.review-runs/jarca-pr7-resume-20261002T110307Z/https_runtime.py: 최종 exit 0. 실제 HTTPS login 303, reconnect HTTP 200, 새 Streamlit WebSocket에서 인증된 화면 복원(53 messages), logout deletion 303. HTTP/framework/protocol 실행이며 browser 실행으로 주장하지 않는다.https-protocol-first-attempt.log와 첫 run 결과를 보존한다. 이 timeout 자체는 확정 제품 결함으로 채택하지 않았다.재작업과 재리뷰
같은 PR에서 F-001/F-002를 수정하고 source·test 변경을 새 head로 제출한다. 구현 재작업 → 새 head의 spec-agent pass → 독립 code 재리뷰 순서다. 이전 spec 승인 #48을 새 head에 승계하지 않는다. 본 결과는 merge·release·deployment 승인이 아니다.
구현 재작업 결과
33118664316707e19146d803fb21d400073c5206e7242a89f7f1faacbe984713a5ad211bd7bcac2b4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferework_delivered지적 처리
F-001—resolvedvalid_fixdashboard/auth_server.py,tests/test_dashboard_auth_server.pyasyncio.to_thread로 실행하여 최소 지연을 보존하면서 event loop를 차단하지 않습니다.F-002—resolvedvalid_fixdashboard/auth_server.py,tests/test_dashboard_auth_server.pyContent-Length를 신뢰하지 않고 실제 request stream을 누적하며 4096 bytes 초과를 감지한 즉시 400을 반환합니다.최종 검증
git diff --checkexit 0다음 단계
새 head에 대한 spec-agent pass와 독립 code 재리뷰가 필요합니다. 이 comment는 Review #49의
REQUEST_CHANGES를 해제하거나 승인을 의미하지 않습니다.구현 명세 적합성 검토
e7242a89f7f1faacbe984713a5ad211bd7bcac2b4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changesAcceptance criteria 판정
D6-01—satisfied32에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.D6-02—satisfied108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.D6-03—satisfied147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.D6-04—satisfied190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.D6-05—satisfied190 및 auth server 128142에서 선택 시 2592000초를 적용한다.D6-06—satisfiedD6-07—satisfiedD6-08—satisfiedD6-09—satisfied146과 163169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.D6-10—satisfied146 및 163169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.D6-11—satisfied239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.D6-12—satisfiedD6-13—satisfied169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166180의 logout response test가 대응한다.D6-14—satisfied185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.D6-15—satisfied312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.D6-16—satisfiedD6-17—satisfiedD6-18—satisfied46 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.D6-19—satisfiedD6-20—satisfiedD6-21—satisfiedD6-22—indeterminate107과 123129의 stream 수신 및 delay 실행 코드가 바뀌어 기록된 auth server hash와 current head blob이 다르다. 구현자 PR 본문과 재작업 comment 1128도 기존 browser evidence를 새 head 실행으로 주장하지 않는다고 명시한다.D6-23—indeterminate범위 판정
결론
정적 검토에서 F-001의 지연 offload와 F-002의 streaming 상한 수정은 승인 계약에 부합하며 baseline AC의 의미와 문서 계약은 유지된다. 그러나 새 실행 source 및 test에 대해 필수 browser 관찰과 command 실행 결과를 신뢰 가능한 provenance로 연결할 수 없어 변경을 요청한다. 이전 문서 전용 후속 변경과 달리 이번에는 실행 input이 변경됐다. 제품 기능 추가나 과거 인수 기준 재번복이 아니라 현재 head 검증 근거의 보완 요청이며 독립 code 재리뷰와 merge 승인은 별도다.
구현 명세 적합성 검토
e7242a89f7f1faacbe984713a5ad211bd7bcac2b4f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changesAcceptance criteria 판정
D6-01—satisfied32에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.D6-02—satisfied108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.D6-03—satisfied147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.D6-04—satisfied190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.D6-05—satisfied190 및 auth server 128142에서 선택 시 2592000초를 적용한다.D6-06—satisfiedD6-07—satisfiedD6-08—satisfiedD6-09—satisfied146과 163169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.D6-10—satisfied146 및 163169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.D6-11—satisfied239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.D6-12—satisfiedD6-13—satisfied169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166180의 logout response test가 대응한다.D6-14—satisfied185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.D6-15—satisfied312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.D6-16—satisfiedD6-17—satisfiedD6-18—satisfied46 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.D6-19—satisfiedD6-20—satisfiedD6-21—satisfiedD6-22—satisfiedD6-23—indeterminate범위 판정
결론
Review 50의 과도한 browser 전체 재실행 요구를 철회하고 변경 영향별로 evidence를 재판정했다. D6-22는 기존 실제 browser 관찰과 변경되지 않은 source 및 현재 회귀 test 대응을 함께 인정해 satisfied로 정정한다. 남은 D6-23만 변경된 login 경로의 정상 인증 호환성과 기존 실패 지연 보존에 관한 실행 기록 확인으로 축소한다. 기존 focused 검증 및 probe 기록의 부족한 provenance를 보충하면 되며 새 artifact 형식이나 일괄 재실행을 요구하지 않는다. 제품 계약 위반을 새로 발견하지 않았고 정적 계약 판정은 유지된다. 승인 판정은 잔여 필수 검증의 실행 기록 확인 이후에 가능하다.
Review #51 재작업 결과
e7242a89f7f1faacbe984713a5ad211bd7bcac2b755c26865c8e111aede8bcbe150a03e305a0ec604f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferework_deliveredD6-23 — resolved (
evidence_gap)Reviewer가 직접 조회 가능한 source-bound evidence를 추가했습니다.
docs/evidence/issue-6-d6-23-login-runtime.json5af59f867e4972b44c51d011f8348f124b2c5dc42fb3dcc3c020095c9ec1b939commands[0]: exact command, exit0,29 passed, warning outputcommands[1]: runnable probe source, exit0, event-loop probe0.10056s, successful-login latency0.001267s, two failed logins1.00614s, statuses303/303source_revision,environment,hashes_sha256: product/test source remains bound toe7242a89f7f1faacbe984713a5ad211bd7bcac2bon the evidence-only descendantredaction: credentials, signing secret, token/cookie values, request body, rawSet-Cookiewere not recordeddocs/evidence/issue-6-persistent-auth-https.jsonand Review #51's change-impact analysis.검토 및 원격 상태
pending(status context0)0Review #51의 기존
REQUEST_CHANGES는 이 comment로 해제되지 않습니다. Reviewer가 evidence-only head를 새 snapshot으로 재검토해야 합니다.구현 명세 적합성 검토
755c26865c8e111aede8bcbe150a03e305a0ec604f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1feapproveAcceptance criteria 판정
D6-01—satisfied32에서 gate 실패 시 반환하고 authentication 297316에서 검증 성공 때만 보호 기능을 허용한다.D6-02—satisfied108이 세 환경변수와 설정 오류 및 짧은 secret을 판정하고 302306에서 접근을 차단한다.D6-03—satisfied147의 두 credential 비교와 remaining time 지연 및 비민감 event log가 유지된다. auth server 123131은 실패 분기에만 기존 helper를 thread offload하며 성공에는 호출하지 않는다. 새 test 263~293은 event loop probe가 delay 종료 전 실행되는 경계를 검증하도록 작성됐다.D6-04—satisfied190 및 auth server 128142에서 기본 86400초 token과 cookie를 발급한다.D6-05—satisfied190 및 auth server 128142에서 선택 시 2592000초를 적용한다.D6-06—satisfiedD6-07—satisfiedD6-08—satisfiedD6-09—satisfied146과 163169의 응답 cookie 제어와 route test가 존재하며 login 95~100은 실제 stream에서 상한을 적용하고 초과 이후 읽기를 중단한다.D6-10—satisfied146 및 163169의 Secure 및 HttpOnly와 SameSite 및 host scope가 유지되며 HTTP cookie 속성 test가 존재한다.D6-11—satisfied239는 엄격한 claim과 서버 시각 및 서명을 검증하며 297316은 session boolean을 사용하지 않는다.D6-12—satisfiedD6-13—satisfied169의 logout matching deletion과 금융 DB 비의존성이 유지되며 166180의 logout response test가 대응한다.D6-14—satisfied185는 고정 비민감 role subject를 쓰며 token payload 비노출 test 152165가 관리자 ID 원문의 부재를 확인한다.D6-15—satisfied312는 설정 오류 및 invalid cookie에 cleanup을 렌더링하고 auth server 168176은 matching deletion과 redirect를 반환한다.D6-16—satisfiedD6-17—satisfiedD6-18—satisfied46 및 6683에 adapter와 source 및 test 경계가 있고 dependency는 dashboard extra에 선언돼 있다.D6-19—satisfiedD6-20—satisfiedD6-21—satisfiedD6-22—satisfiedD6-23—satisfied범위 판정
결론
승인된 Discovery와 baseline AC 전체의 인수 검증 의미 및 필수 문서와 구현 및 test 대응을 재확인했다. 현재 rework는 D6-23 실행 기록 추가 한 파일뿐이며 source와 test 및 설정과 dependency 실행 입력은 동일하다. 기존 browser 관찰의 유효 범위를 유지하고 보완된 focused test 및 실제 지연 probe를 source와 환경에 결합된 구현자 evidence로 인정한다. 모든 필수 criterion을 충족하므로 승인한다. Reviewer의 독립 runtime 및 browser 실행이나 CI 성공을 주장하지 않으며 독립 code review와 merge 및 배포 승인은 별도다.
독립 code 재리뷰: REQUEST_CHANGES
jsfamily/jarca-ledger-mcp, Issue #6 / PR feat(dashboard): persist administrator login (#7)755c26865c8e111aede8bcbe150a03e305a0ec607bea8b368e02e98afd4b2dc463fb270b0e6eb2974f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1ferequest_changes이전 지적 해결 확인
dashboard/auth_server.py:127-129가 실제 실패 지연을asyncio.to_thread()로 offload한다. 실제 Uvicorn/HTTPS에서 실패 로그인 6건이 약 1.09초에 끝났고, 진행 중 health는 200 / 약 0.017초였다. 동기 sleep에 의한 기존 공유 event-loop 차단은 재현되지 않았다.dashboard/auth_server.py:95-100가 stream 수신 중 누적 크기를 검사하고 초과 chunk를bytearray에 넣기 전에 거부한다. 실제 HTTPS chunked 요청에서 4097바이트만 보내고 나머지 body와 종료 chunk를 보내지 않았는데도 400을 받았다. 직접 Request 검증에서도 4096바이트는 허용하고, 다음 1바이트에서 거부하며 뒤 chunk를 소비하지 않았다.확정 결함
F-003 — Medium: 비ASCII credential 입력이 예외로 빠져 HTTP 500과 실패 지연 우회를 일으킨다.
dashboard/auth_server.py:117-131(특히 124-126),dashboard/authentication.py:111-118(116-117).admin_password=%FF역시 percent-decoding 후 같은 경로로 들어간다.str로 파싱되지만verify_credentials()가hmac.compare_digest(str, str)에 그대로 전달한다. 이 API는 비ASCII 문자열에TypeError: comparing strings with non-ASCII characters is not supported를 발생시킨다. 새 HTTP login adapter는 이 예외를 정상 실패로 처리하지 않아enforce_failed_login_delay와login_failed경로에 도달하지 않는다.dashboard.server:app를 실제 Uvicorn/HTTPS로 실행했다. 비ASCII 비밀번호, 비ASCII ID,%FF의 응답은 모두 500, 처리 시간은 각각 약 0.015초 / 0.042초 / 0.030초였다. Redirect와 인증 cookie는 없었다. ASGI 예외 전파 실행에서도 위TypeError를 확인했다. 한글로 구성한 합성 관리자 ID·비밀번호는require_auth_settings()에 통과하지만 정확한 값을 제출해도 500, cookie 없음이었다.docs/interface-contract.md:55-78은 bounded string credential, mismatch 시 generic error + delay, malformed body 시 400을 정의하며 ASCII-only 제한은 없다. 구현 결함으로 판단하며 새 요구사항을 추가하지 않는다.compare_digest(str, str)제약은 pinned base의 기존 helper에도 있었다. 따라서 이번 thread/stream 재작업이 새로 만든 회귀라고 주장하지 않는다. 이번 PR이 추가한 공개 HTTP 로그인 경로가 그 제약을 처리하지 못하는 현행 결함이다. 이전 리뷰 #49에서 발견하지 못한 추가 finding이다.검토 범위와 실행 근거
dashboard/auth_server.py, auth-server tests, 새 runtime evidence)을 직접 읽고 검토했다. 나머지 18파일은 이전 독립 리뷰 snapshot과 SHA-256이 동일함을 확인하고 이전 검토를 재사용했다. 새 HTTP 입력 경로와 관련 credential helper는 추가로 직접 검토했다.PYTHONDONTWRITEBYTECODE=1 .venv/bin/python -m pytest -o addopts= -q -p no:cacheprovider --basetemp=<review-run>/pytest-tmp --junitxml=<review-run>/pytest.xml→ exit 0, 115 passed / 4 skipped / 0 failed. JUnit에서 119개 testcase를 집계했다. 기존 테스트가 F-003을 검출하지 못했다.--no-cache, base/headgit diff --check,uv lock --check --offline통과.e7242a89f7f1faacbe984713a5ad211bd7bcac2b는 현재 head의 ancestor이고 후속 diff는 evidence JSON만이다. 기록된 5개 입력 hash가 현재 파일과 일치한다. 이 기록은 재사용 근거이며 리뷰어 독립 실행과 구분한다./opt/data/.review-runs/jarca-pr7-rereview-20261004T010756Z/(pytest.xml,focused-verification.json,https-runtime.json,unusual-input-results.json,configured-unicode-results.json). 서버는 종료·reap했고 TLS private key를 삭제했다. Source/test/contract/state/refs는 수정하지 않았다.다음 단계
기존 PR에서
gitea-issue-rework로 F-003을 수정한 뒤, 새 head의 spec pass → 독립 code 재리뷰를 진행한다. 기존 F-001/F-002의 해결 상태도 새 head에서 회귀 확인한다. 이 판정은 merge/deploy 승인이 아니다.구현 명세 적합성 검토
6a35775cca96dfe0168e96c882c8e240b1f1e7b44f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1feapproveAcceptance criteria 판정
D6-01—satisfied32와 authentication 301320이 현재 cookie 검증을 DB 경로 취득과 page dispatch보다 먼저 적용한다. AppTest 6787과 105117의 DB sentinel 및 Import query 차단은 유지된다.D6-02—satisfiedD6-03—satisfied122는 ID와 password 각각을 UTF-8 bytes로 변환해 두 compare_digest 호출을 모두 수행한다. auth server 101133은 strict UTF-8 form 해석과 실패별 지연 및 일반 오류를 유지한다. test authentication 6485와 auth server 90131 및 151~165가 Unicode 성공과 mismatch 지연 및 malformed 거부를 검증한다. F003 artifact commands의 focused 32 passed와 source hashes를 현재 head까지 연결했다.D6-04—satisfied195 및 auth server 135149는 기본 TTL 86400초와 로그인 성공 시각의 iat 및 exp를 고정한다. token 발급과 HTTP cookie test가 대응한다.D6-05—satisfied195 및 auth server 135149는 선택 시 TTL 2592000초를 적용한다. persistent test 6574와 168179 및 auth server test 71~87이 선택과 exact 만료 경계를 검증한다.D6-06—satisfiedD6-07—satisfied188의 iat와 exp는 발급 때만 설정하고 198243의 복원 검증은 이를 변경하지 않는다. 보호 gate와 logout 및 cleanup에 재발급 경로가 없고 non sliding test가 유지된다.D6-08—satisfied364의 동일 query navigation과 authentication 301320 gate를 유지한다. core page AppTest는 동일 cookie로 새 session들을 열고 기존 source7bb959859abrowser 기록은 새 탭 Import 복원을 관찰했다. 전체 후속 diff에서 page query와 cookie 복원 및 삭제는 변경되지 않았다.D6-09—satisfied10은 같은 process의 Streamlit App에 auth route를 결합한다. auth server 141149와 165~183은 실제 response에서 cookie를 발급하고 삭제한다. strict UTF-8 rework 후 정상 Unicode login과 cookie 발급 route test 및 source bound focused 실행 기록이 존재한다.D6-10—satisfied149와 165172는 Host prefix cookie를 Secure 및 HttpOnly 및 SameSite lax와 path root 및 no Domain으로 발급하고 matching deletion한다. HTTP test 5068 및 227259와 기존 실제 browser 관찰이 보안 속성을 확인한다.D6-11—satisfied243은 엄격한 claim set과 서버 시각 및 absolute TTL과 서명을 검증하며 301320에서 session boolean만으로 인증하지 않는다. tamper와 expiry 및 invalid cookie AppTest가 대응한다.D6-12—satisfied167의 key binding은 현재 secret과 ID 및 password를 서버에서만 사용한다. persistent rotation test 94122와 AppTest 145~183이 password 및 signing rotation 뒤 보호 화면 차단을 검증한다.D6-13—satisfied172의 explicit logout은 matching deletion과 root redirect를 반환하고 금융 DB에 의존하지 않는다. test 227241 및 기존 browser logout 기록은 삭제와 다음 미인증 접근을 확인한다.D6-14—satisfied189는 v와 고정 비민감 sub 및 iat와 exp 및 jti만 payload에 기록한다. 154159의 credential binding은 server-only다. persistent test 152~165와 AppTest 및 log helper tests가 원문 credential과 secret 및 cookie의 비보존을 확인한다.D6-15—satisfied316은 설정 오류와 invalid cookie에 cleanup navigation을 표시하고 auth server 175183은 matching deletion 뒤 미인증 root로 전이한다. invalid cookie AppTest와 cleanup route test 및 기존 browser cleanup 관찰이 대응한다.D6-16—satisfied87에, exact 환경변수와 fallback 배제는 REQ 001.02 7078과 AC02 및 기존 unit test 1761에 보존된다. 비문자열은 invalid 설정 판정 및 source 79와 unit test 3940에 대응한다. 기존 핵심 page 다섯 개는 baseline 376과 현재 453 및 AppTest 56~62가 동일하다. Constant-time과 성공 무지연 및 counter와 lock 부재와 MCP 분리도 보존되고 문서는 직전 승인 head와 byte 동일하다.D6-17—satisfied41은 설정과 오류, 4378은 login 입력과 고정 claim 및 두 TTL, 80137은 cleanup과 logout, 139177은 현재 binding 및 gate와 미연장을 정의한다. runtime 3640과 5165 및 7696은 발급과 복원 및 실패 폐기와 DB 보호 및 동시 요청과 rotation을 정의하고 98113에 비민감 관찰 및 검증을 둔다.D6-18—satisfied46은 source와 test 책임을, 6683은 의존 방향과 금지 경계를 정의한다. server와 auth server는 금융 gateway나 page view로 역의존하지 않는다. pyproject dashboard extra와 uv lock에 Streamlit ASGI 및 signing dependency가 있고 Dockerfile 23과 32는 해당 extra를 설치한다.D6-19—satisfied35는 별도 signing 값 주입, deployment 124140은 세 secretKeyRef와 proxy 기본값을 둔다. README 3454와 92103 및 deploy README 13~43은 secret 비보존과 HTTPS 및 absolute expiry와 rotation과 rate limit 운영 조건을 설명한다.D6-20—satisfiedD6-21—satisfiedD6-22—satisfiedD6-23—satisfied범위 판정
결론
승인 Discovery의 모든 필수 계약과 baseline AC 검증 의미 및 23개 changed file의 범위 대응을 재확인했다. 이번 F-003 재작업의 UTF-8 credential 비교 및 strict form decode와 회귀 test 및 source bound 실행 evidence를 인정하며 모든 criterion을 충족한다. 기존 browser 관찰은 변경 영향 분석으로 제한적으로 재사용하고 새 head에서 browser를 다시 실행한 것으로 표현하지 않는다. 따라서 명세 적합성을 승인한다. 구현자 실행 evidence와 reviewer의 정적 분석은 구분하며 CI 성공과 독립 code 재리뷰 및 merge와 배포 승인은 별도다.
독립 코드 재리뷰 — APPROVE
gitea-review-agent6a35775cca96dfe0168e96c882c8e240b1f1e7b47bea8b368e02e98afd4b2dc463fb270b0e6eb2974f734169ec896747fe39fd7f3ab75d1fc0d8cdd4903045e665beba40d80eb1fegitea-spec-agent)approve범위와 검토 방식
전체 PR diff의 변경 파일 23개를 원격 diff/file 목록과 로컬 commit 내용으로 대조했다. 이전 독립 리뷰 #53의 head
755c26865c8e111aede8bcbe150a03e305a0ec60이후 변경 5개 파일을 직접 검토했다. 나머지 18개 파일은 byte/hash 동일성을 검증하여 이전 전체 검토 근거를 재사용했다. 새 코드 경로의 surrounding code, 인증 gate, token/cookie 처리와 기존 인증 테스트를 다시 확인했다.변경 파일:
dashboard/authentication.py,dashboard/auth_server.py, 두 인증 테스트,docs/evidence/issue-6-f003-unicode-credentials.json. 한 bounded worker가 독립 정적 검토를 수행했고, 판정 및 모든 중요한 후보 검증은 main reviewer가 수행했다.기존 finding 처리
asyncio.to_thread로 offload된다. 전체 회귀 테스트 통과. 실제 HTTPS 병렬 요청에서도 실패 요청은 약 1.017초 지연되는 동안 정상 요청은 약 0.016초에 완료됐다.dashboard/authentication.py:111-122는 두 credential을 UTF-8 bytes로 비교한다. 유효한 비ASCII credential이 더 이상hmac.compare_digest(str, str)TypeError로 500을 만들지 않는다. 실제 TLS 연결에서 ASCII 설정과 Unicode 설정 모두 정상 로그인 및 24시간/30일 token 발급이 성공했다. Unicode ID/비밀번호 불일치는 일반 실패 redirect, cookie 미발급, 최소 1초 지연을 유지했다.dashboard/auth_server.py:101-112strict UTF-8 form decoding은%FF, raw invalid UTF-8 및 percent-encoded unpaired surrogate를 credential 비교 전에 400 / cookie 미발급으로 거부했다. malformed transport rejection은 credential mismatch와 구분했다.새로운 승인 차단 finding은 확인되지 않았다. 늦게 발견되었다는 이유나 경미한 선택적 개선만으로 변경 요청을 생성하지 않았다.
직접 실행 근거
git diff --check,ruff check .,ruff format --check .: 모두 exit 0.d84ed8ff2a53cd2735eceaaf0c54cb40c7c2c3ad는 현재 head의 조상이며 변경된 실행 코드/테스트 4개 파일이 동일함을 직접 검증했다. 판정은 이 제출 자료뿐 아니라 현재 head에서 실행한 reviewer 결과에 근거한다./opt/data/.review-runs/jarca-pr7-f003-rereview-20261004T054141Z(scope-coverage.json,pytest-full-extended.xml/.log,static-checks.json,reviewer-https-runtime-results.json,worker-corroboration.json,evidence-provenance.json).후보 검증·한계·잔여 위험
real_time.sleep이 patched reference라는 부분은 맞지만,enforce_failed_login_delay(..., sleep=time.sleep)의 기본 인자는 함수 정의 시 원래 callable을 저장한다 (dashboard/authentication.py:125-134). reviewer의 별도 callable/elapsed probe, 실제 pytest 및 HTTPS 지연 결과가 이를 확인했다.str를 주면UnicodeEncodeError가 발생할 수 있다. 유효한 Unicode credential과 구분되는 미지원 helper 입력의 방어적 개선 여지다. 공개 HTTP 경로는 strict decoding으로 같은 입력을 400으로 차단함을 재현했으므로 이 승인 범위의 새 차단 finding으로 취급하지 않았다.최종 판단
현재 승인 계약 및 검토 범위 내에서 미해결 중요 결함 또는 승인에 필요한 필수 검증 공백을 확인하지 못했다. 현재 head를 승인한다. 이는 검토 범위에서 material finding을 발견하지 않았다는 뜻이지 무결함 증명이 아니다. 제품 코드 수정, commit/push, merge/deploy 및 Issue 종료는 수행하지 않았다.