인증과 첫 병렬 라운드
- 작성일: 2026-10-17
- 관련 문서: 분개 테스트 룰과 감사 용어, 시스템 구조와 데이터 모델, 디자인 시스템, HTML 디자인 시안, MVP 범위와 일정
목표
10/18부터 코드 워커를 병렬로 돌릴 수 있도록 인증을 붙이고, 도메인, 설계, 디자인 문서를 확정한다. 코드 워커는 이 세 문서를 계약으로 삼는다.
한 일
mix phx.gen.auth Accounts User users --live로 인증을 붙였다. Phoenix 1.8의 scope 방식(current_scope)을 그대로 쓰고, engagement 권한도 이 scope 위에 얹는다.mix precommit통과(테스트 112개).- 생성기가 AGENTS.md에 인증 가이드라인을 주입했다. 이 블록을
refs/phoenix-guidelines.md로 옮기고, 앞으로도 같은 방식으로 처리한다는 규칙을 AGENTS.md 6장에 적었다. - 첫 병렬 라운드: 워커 3명이 문서 3종을 동시에 썼다. 메인 세션은 그동안 스캐폴딩, 인증, 계약 검토를 맡았다.
- 디자인 문서를 HTML 시안으로도 만들었다. 색 토큰(라이트/다크), 글꼴, 컴포넌트별 상태, 화면 S1-S6을 도메인 문서 7장의 합성 데이터로 채웠다. 브라우저에서 375/768/1280px 폭으로 열어 페이지 가로 스크롤이 없는지 확인했고, 그 과정에서 넘치던 곳 5군데(grid 칸의 자동 폭, 감사 로그 payload의 UUID, 스크롤 영역 밖으로 빠진
sr-only요소, 키-값 목록의 키 열, 합계 카드)를 고쳤다. 원본은 md이고 HTML은 md를 따른다. - GitHub 원격 저장소(
basilry/kpmg-elixir)를 연결하고 main을 올렸다. 지금은 비공개다. 지원서에 링크를 넣기 전에 공개로 바꿔야 한다.
| 워커 | 산출물 | 공유 계약 |
|---|---|---|
| domain-writer | 룰 8종, 점수, AI 입출력, 상태 모델, 합성 데이터 명세 | MVP 문서 5장 GL 컬럼 |
| architect | 컨텍스트, 테이블 13개, Oban, PubSub, AI 클라이언트, 병렬 개발 계획 | MVP 문서 5장, 룰 플러그인 계약 |
| designer | 색, 타이포그래피, 컴포넌트, 화면 구성 | 도메인 문서의 상태값 이름 |
문서 간 충돌과 정리
두 워커가 같은 계약을 서로 다르게 해석한 곳이 7군데 나왔다. 테이블과 상태값은 설계 문서를, 감사 판단 근거는 도메인 문서를 원본으로 정하고 반대쪽을 고쳤다.
| 주제 | 도메인 문서 | 설계 문서 | 결정 | 이유 |
|---|---|---|---|---|
| 전표 점수 | 룰 가중치 합 | 적중 룰의 최대 점수(max_score) |
가중치 합, 상한 100 | 여러 위험 신호가 겹친 전표가 위로 와야 한다. 최대값이면 룰 1개 적중과 4개 적중이 같은 점수가 된다 |
| 검토 상태 | queued, in_review 등 8단계 |
open, concluded, approved, rejected |
설계 쪽 4단계 + 별도 triage_status |
AI 처리 상태와 사람의 판단 상태를 섞지 않는다. 결론 제출이 곧 검토 요청이라 in_review가 따로 필요 없다 |
| 판단 값 이름 | no_issue, approve |
no_exception, approved |
설계 쪽 | 감사 조서 용어(예외)와 맞고, 상태값과 결정값 표기가 일관된다 |
| AI 출력 | confidence 포함 |
없음 | 제외 | LLM이 스스로 매긴 확신도는 보정되지 않는다. 감사인이 근거 있는 확률로 오해할 수 있다 |
| 트리아지 시작 | 테스트 완료 후 자동 | 미정 | 작성자의 일괄 요청 버튼, 1회 100건 | 비용을 통제하고, 누가 AI를 불렀는지 감사 로그에 남긴다 |
| 무효 출력 | 1회 자동 재시도 | 미정 | 재시도 없이 실패, 건별 재요청 | 같은 입력에 같은 처리를 하도록 결정적으로 둔다. 일시 오류(429, 5xx) 재시도와는 구분한다 |
counterparty |
라인 필드 | 라인 필드 | 전표 헤더 필드 | MVP 문서 5장에서 같은 전표의 행은 거래처가 같아야 한다고 정했다 |
같이 고친 것:
- 도메인 문서의 중복 전표 주입(I8)은 "원본 +1-5 영업일"이라 추석 연휴에 걸리면 중복 판단 기간(7일)을 넘을 수 있었다. 달력일 7일 상한을 넣고, 넘치면 그 후보를 건너뛰게 했다.
- 설계 문서가 요청한 GL 검증 규칙 3개(같은 전표의 행 연속,
entered_at형식, 원 단위 금액)를 MVP 문서 5장에 넣었다. - HTML 시안을 그리면서 md에서 빠진 것을 찾아 md를 고쳤다.
score_meter의 채움 칸 수(ceil(score / 20))를 명시하고, S5 이력 목록 컴포넌트finding_history/1을 표에 추가했다. 실행 중 시안의test_progress는 동시 실행 상한(Obanrules: 4)을 넘지 않게 그렸다. - 도메인 문서 7장의 기대 건수를 다시 셌다. 트리아지 대상 52건(I5 30건 + 복합 22건), 경미 적중 213건(단일 룰 167건 + 정상인데 걸리는 46건), 전표 약 8,341건, 라인 약 19,794건으로 명세와 맞았다. 10/19 룰 테스트가 이 숫자를 정답으로 쓴다.
메인 세션이 기본값으로 정한 것
architect가 남긴 미결정 사항은 아래 기본값으로 진행한다. 바꾸려면 설계 문서 Changelog에 남긴다.
| 항목 | 기본값 |
|---|---|
| LLM 모델 | config의 claude-opus-5-5, ANTHROPIC_MODEL로 덮어씀. 기본 클라이언트는 Fake(키 없이 동작) |
| Anthropic server-side fallback | 끔 |
| 트리아지 일괄 요청 상한 | 100건 |
| CSV 적재 상한 | 20MB, 100,000행 |
| engagement 역할 | 사용자당 하나 |
| oban_web 대시보드 | 2단계 |
| 시각 표시 | KST 고정(+9시간). 사용자별 시간대는 두지 않는다 |
| 승인 단위 | 전표(finding)별. 감사 대상 단위 sign-off는 2단계 |
진행 방식 메모
- 고속 모드로 전환하려 했지만 계정의 추가 사용량 설정이 꺼져 있어 거부됐다. 대신 워커마다 작업 시간을 정해 두고, 디자인 문서를 세 번째 워커로 앞당겨 같은 라운드에 넣었다.
- 워커가 쓴 문서는 메인 세션이 검토한 뒤 상태를 "확정"으로 바꾸고 Changelog에 검토 내용을 남겼다. 다음 라운드부터 코드 워커는 확정 문서만 계약으로 본다.
Elixir 학습 포인트
NaiveDateTime.from_iso8601/1은+09:00같은 오프셋이 붙어도 오류 없이 무시한다. 계약에서 오프셋을 금지했으니 형식(길이 19자)을 먼저 확인해야 한다.- NimbleCSV가 돌려주는 필드는 원본 바이너리의 sub-binary다. 오래 들고 있는 문자열은
:binary.copy/1로 복사해야 원본 전체가 메모리에 남지 않는다. - Oban OSS에는 Pro의 Batch가 없다. 룰 job들이 모두 끝났는지는
test_runs행을FOR UPDATE로 잠그고 남은 룰 수를 세는 방식으로 판단한다. 마지막으로 끝난 job이 마무리를 맡는다. - 감사 로그의 append-only는 애플리케이션 규칙이 아니라 PostgreSQL 트리거로 강제한다.
Repo실수나 수동 SQL로도 UPDATE, DELETE가 막힌다. phx.gen.auth의 Scope는 "현재 누가 요청했는가"를 컨텍스트 함수의 첫 인자로 넘기는 패턴이다. 직무분리 검사도 이 scope를 받아 처리한다.
다음 할 일 (10/18)
- 메인 세션:
oban,nimble_csv의존성, Oban과 AI 설정, 마이그레이션 1-8, 스키마 모듈,AuditTrail.append/3,Engagements.Policy, 라우트.mix ecto.migrate와mix ecto.rollback --all이 양방향으로 도는지 확인한다. - 그다음 설계 문서 12장대로 코드 워커 3명(Engagements, CSV 적재, 합성 GL 생성기)을 worktree에서 병렬로 돌린다.