ci.yml 은 07a14c3 이후 push: branches: [main] 이다. 실행이 없다면 GITHUB_TOKEN 으로 한 푸시(워크플로 미트리거)나 [skip ci] 제목이 원인일 가능성이 크다 — 저장소 안에서 고칠 수 없고 러너(외부 스크립트) 쪽 확인 필요.
2026-09-12
가이드 캡처 스크립트를 CI 에서 릴리즈 tarball 로 돌려 화면 어긋남 검사
3/2/M
대기
scripts/guide 를 release 워크플로 뒤에 붙여 캡처 실패(셀렉터·문구 변경)를 알린다. 그림 자동 갱신은 하지 않음. 2026-09-12 현재 auto/2026-09-11-1901 미머지 — 머지 뒤에 가능.
2026-09-12
ci.yml 의 go test 단계 주석('약 90초')을 현재 비용으로 갱신하고 -json 으로 패키지별 소요를 남긴다
2/1/S
대기
주석은 8/27 기준이라 지금과 다르다. 패키지별 소요를 로그에 남기면 다음 회귀가 타임아웃이 되기 전에 보인다. 워크플로를 느슨하게 만드는 것이 아님.
2026-09-12
관리자 가이드 4.1 역할 표를 라우터 순회 테스트로 고정
2/1/S
대기
admin/orgAdmin 미들웨어 대상 경로 목록을 테스트가 출력해 문서 표와 diff. 1901 머지 뒤.
2026-09-12
기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크
2/1/S
대기
describeQueryError 가 canNarrowRange=false 일 때 /admin/governance 링크 제시. queryError.ts 를 2200 이 고쳤으므로 머지 뒤(2026-09-12 현재 미머지).
2026-09-12
visitor-search 의 검색 기간을 Empty 상태에 표시
2/1/S
대기
Empty 문구에 실제 조회 기간(policyRange 결과)을 적는다. policyRange 는 2200 에 있으므로 머지 뒤.
2026-09-12
관리 센터 홈의 재집계 조치 항목이 오래 이어지는 재시도 대기를 warning 으로 알린다
2/1/S
대기
AdminPage.tsx 의 actions 가 pending+error 로 created_at 1시간 넘은 작업을 warning 으로 Aggregate 탭에 연결. describeAggregateJobStatus(0011) 재사용. AdminPage.tsx 는 2200 도 고쳤으므로 두 브랜치 머지 뒤.
2026-09-12
Aggregate 탭 작업 목록에 started_at·finished_at 기반 소요 시간 열
2/1/S
대기
API 는 이미 내려줌. running 이면 경과, 끝났으면 소요 시간. 순수 함수 + node:test. EnterpriseAdminPage.tsx 를 0011 이 고쳤으므로 머지 뒤.
2026-09-12
데이터 계보가 goal 이 가리키는 metric 이 없을 때 끊긴 참조를 드러낸다
2/1/S
대기
metric_goals.metric_name 이 semantic_metrics 에 없으면 measures 간선의 from 노드가 없다. 'missing' kind 노드를 만든다. 0241 의 dataLineage 와 같은 함수이므로 0241 머지 뒤.
2026-09-12
Data Lineage 표가 id 대신 label 과 kind 칩을 보여준다
2/1/S
대기
API 는 nodes 에 kind/label 을 내려주지만 표는 edges 의 id 문자열만 보여준다. EnterpriseAdminPage.tsx 를 0011 이 고쳤으므로 머지 뒤.
2026-09-12
DataTable 의 CSV 내려받기가 format 으로 그린 표시값이 아니라 원시값을 내보낸다
2/1/S
대기
비율(0.123)·ms 등이 화면과 다른 단위로 나간다. 열마다 exportFormat 을 두거나(csvExport.ts 의 buildCSV 에 열별 변환 인자) 문서에 원시값임을 명시. 0331 의 csvExport.ts 위에 얹으므로 0331 머지 뒤.
2026-09-12
카탈로그 used_by 가 metric_ref 로 간접 참조하는 메트릭까지 센다
2/2/S
대기
지금은 정의 트리에 event_name 을 직접 적은 메트릭만 센다. 재귀 CTE 로 semantic_metrics 를 따라가며 validateMetricReferences 와 같은 5단계 제한. 0151 의 eventCatalog 와 같은 함수이므로 0151 머지 뒤.
2026-09-12
seed 를 set-based INSERT(generate_series)로 다시 써 서버 실행 시간까지 줄인다
2/3/M
대기
지금은 라운드트립을 없앤 것이고 서버 쪽 2,000 문장은 그대로 0.3초. TestTheFixtureIsWhatItSaysItIs 가 지키지만 fixture 재작성은 미묘한 차이 위험이 있어 필요할 때만.
2026-09-12
testPool 이 테스트마다 database.Migrate 를 돌려 17번 SELECT 를 하니 패키지당 한 번으로
1/1/S
대기
sync.Once 로 첫 호출만 Migrate. 테스트당 수십 ms 수준이라 급하지 않음.
2026-09-12
Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 남긴다
1/1/S
대기
maintenance.go:30 이 `_, _ =` 로 오류를 버림. 2330 이 maintenance.go 를 크게 고쳤으므로 머지 뒤.
2026-09-12
사용자 가이드 CSV 절에 수식 문자로 시작하는 셀은 아포스트로피가 붙어 글자로 열린다는 한 줄
1/1/S
대기
USER_GUIDE.md:550 근처. docs/ 는 1901 과 겹치고 동작은 0331 에 있으므로 두 브랜치 머지 뒤.
2026-09-12
[수정 과제] internal/httpapi 테스트 패키지가 CI 의 go test 기본 10분 제한을 넘기는 원인을 찾아 고친다
5/1/S
완료
seed 가 약 2,000개 INSERT 를 한 문장씩 autocommit 으로 보내 81개 테스트가 각 3.5초를 냈다. pgx.Batch 로 한 라운드트립·한 트랜잭션으로 보내 seed 0.3초, 패키지 335초→56초(-race). 멈추는 지점은 없었음. 커밋 9d3e06c.
2026-09-12
TestBehaviouralSegmentMatchesTheReferenceSemantics 의 참조(상관 서브쿼리) 5초를 줄인다
1/1/S
기각
참조 형태가 일부러 옛 상관 집계라 의미 비교의 근거다. 5초는 남은 가장 느린 테스트지만 의도된 비용이므로 손대지 않음.
2026-09-12
원장 (에이전트가 남긴 기록)
2026-09-11
선택: 사용자·관리자 가이드를 실제 화면 캡처가 실린 완성본으로 재구성 (가치 5 / 위험 1 / 작업량 M)
결과: 성공
요약: docs/USER_GUIDE.md·ADMIN_GUIDE.md 를 GUIDE-STANDARD 구성(처음 5분·화면별 사용법·막혔을 때 / 구성 요소·설치·환경 변수 전수 표·계정과 권한·운영·장애 대응·보안)으로 다시 쓰고, v0.34.39 를 로컬 Postgres 와 함께 실제로 띄워 seed.mjs 로 가짜 데이터(데모 포털, EMP0001, hong@example.com)를 넣은 뒤 headless Chrome 1440x900 으로 49장을 찍어 docs/assets/guide/ 에 실었다. 환경 변수 표는 internal/config·internal/database 에서, 역할 표는 라우터 미들웨어에서, 오류 문구는 writeError/queryError.ts 에서 읽었고, 옛 USER_GUIDE.html/ADMIN_GUIDE.html 은 삭제하고 docs/index*.html·README 링크를 새 문서로 옮겼다. 공용 md2pdf 로 두 PDF(47쪽/27쪽)를 생성해 표지·그림이 렌더링되는 것과 문서가 참조하는 그림이 모두 존재하는 것을 확인했다. scripts/guide 는 전용 환경 변수만 받고 로컬 호스트가 아니면 멈추며 –cleanup 으로 만든 것만 지운다; 비밀값은 글자로 적지 않았다.
보류 아이디어: User Explorer 타임라인이 365일을 요청해 기본 정책(180일)에서 RANGE_EXCEEDS_POLICY 로 막힘 — 사이트 정책 한도를 읽어 요청하도록 수정 (가치 4 / 위험 2 / S); 대량 과거 이벤트 반입 직후 aggregate maintenance 가 deadlock(40P01)으로 실패하는 재집계 작업 — 재시도 또는 잠금 순서 정리 (가치 3 / 위험 3 / M); compose.yml 의 build: 때문에 릴리즈 이미지로 띄울 때 –no-build 가 필요 — 문서화했으나 compose 파일에 주석 추가 (가치 2 / 위험 1 / S); 캡처 스크립트를 CI 에서 릴리즈 tarball 로 돌려 가이드 그림이 최신 화면과 어긋나는지 검사 (가치 3 / 위험 2 / M).
2026-09-11
선택: 고정 기간으로 조회하는 화면이 사이트 정책 한도(max_exact_days)를 넘겨 요청하지 않게 한다 (가치 4 / 위험 2 / 작업량 S)
결과: 성공
요약: 캠페인 목표(가이드 완성본)는 앞 회차 커밋 e73d357(브랜치 auto/2026-09-11-1901, 미머지)에 이미 완성되어 있어 이 브랜치에서 다시 만들면 충돌하는 중복이 되므로 손대지 않고, 그 회차가 캡처 중 발견한 실제 버그를 고쳤다. UserExplorerPage 가 타임라인을 365일 고정으로 요청해 기본 정책(180일)에서 “추적” 버튼이 항상 RANGE_EXCEEDS_POLICY 를 받았다. queryError.ts 에 policyRange(days, maxExactDays) 를 추가하고 페이지들의 고정 기간 rangeQuery 호출 10곳(7·30·90·365일)을 모두 이것을 거치게 했으며, periodOptions.test.mjs 에 단위 테스트와 rangeQuery(<숫자>, …) 가 policyRange 없이 남아 있으면 실패하는 소스 검사 테스트를 추가했다. web 에서 npm test(91 통과)·eslint·tsc -b && vite build 로 검증했다.
보류 아이디어: 대량 과거 이벤트 반입 직후 aggregate maintenance 재집계 작업이 deadlock(40P01)으로 failed — 잠금 순서 정리 또는 40P01 재시도 (가치 3 / 위험 3 / M); compose.yml 의 build: 때문에 릴리즈 이미지로 띄울 때 –no-build 가 필요하다는 주석을 compose 파일에 추가 (가치 2 / 위험 1 / S); scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 (가치 2 / 위험 1 / S); 서버의 RANGE_EXCEEDS_POLICY 오류 문구가 “기간을 줄이거나”라고 하지만 기간을 고를 수 없는 화면(추적·사용량)에서는 줄일 방법이 없으므로 ErrorState 가 정책 화면 링크를 함께 보여주게 한다 (가치 2 / 위험 1 / S).
2026-09-11
선택: 대량 과거 이벤트 반입 중 deadlock(40P01)으로 실패한 재집계 작업을 다시 큐에 넣는다 (가치 3 / 위험 2 / 작업량 M)
결과: 성공
요약: 캠페인 목표(캡처가 실린 가이드 완성본)는 미머지 브랜치 auto/2026-09-11-1901 의 e73d357 에 이미 완성되어 있어 이 브랜치에서 docs/ 를 다시 만지면 충돌하는 중복이 되므로 손대지 않고, 그 회차가 캡처 중 발견한 남은 실제 결함을 고쳤다. Maintenance.runNext 는 재집계가 어떤 이유로든 실패하면 status=’failed’ 로 끝내 다시 돌지 않았는데, 워커의 일별 upsert 와 잠금 순서가 달라 late_event 재집계가 deadlock_detected 로 지는 것은 반입 중에는 일상이고 배치가 커밋되면 같은 작업이 그대로 성공한다. 이제 40P01·40001 이면 attempts<10 인 동안 status=’pending’ 으로 되돌리고(사유는 error 에 기록) 그 패스를 끝내 다음 틱(15초)에 다시 잡게 했다. 로컬 Postgres 16 컨테이너로 실제 deadlock 을 만드는 통합 테스트(다른 트랜잭션이 metrics 행을 잡고, 재집계가 막힌 것을 pg_stat_activity 로 확인한 뒤 sessions 행을 요청해 재집계가 희생자가 되게 함)와 attempts 소진 시 failed 가 되는 테스트, 오류 분류 단위 테스트를 추가했고, 수정 없이 돌리면 40P01 로 실패하는 것을 확인했다. go vet 과 MOMENTO_TEST_POSTGRES_DSN 을 준 go test ./… (httpapi 통합 포함) 전부 통과, service 패키지는 -race 로도 통과.
보류 아이디어: compose.yml 의 build: 때문에 릴리즈 이미지로 띄울 때 –no-build 가 필요하다는 주석을 compose 파일에 추가 (가치 2 / 위험 1 / S); scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 — 1901 브랜치 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간(policyRange 결과)을 표시 (가치 2 / 위험 1 / S); 재집계 재시도 사유(error)가 pending 작업에도 남으므로 거버넌스 화면의 작업 목록이 pending+error 를 “재시도 대기”로 구분해 보여주기 (가치 2 / 위험 1 / S).
2026-09-12
선택: 거버넌스 Aggregate 탭의 작업 목록이 재시도 대기(pending + error)를 구분해 보여준다 (가치 2 / 위험 1 / 작업량 S)
결과: 성공
요약: 캠페인 목표(캡처가 실린 가이드 완성본)는 미머지 브랜치 auto/2026-09-11-1901 의 e73d357 에 이미 완성되어 있고 2200·2330 도 아직 미머지여서, 이 브랜치에서 docs/·queryError.ts·maintenance.go 를 만지면 충돌하는 중복이 되므로 그 파일들은 손대지 않았다. 대신 보류 목록에서 다른 브랜치와 파일이 겹치지 않는 항목을 골랐다: Maintenance.Run 은 main 에서도 30분 넘게 running 인 작업을 status=’pending’, error=’recovered after interrupted worker’ 로 되돌리므로(2330 머지 뒤에는 40P01 재시도도 같은 모양) Aggregate 탭에 “pending” 칩 옆에 오류 문구가 나란히 보여 실패인지 다시 돌 것인지 알 수 없었다. web/src/pages/aggregateJobStatus.ts 의 describeAggregateJobStatus 가 pending+error 를 “재시도 대기”(warning) 로, 나머지는 기존 색 그대로 돌려주게 하고 EnterpriseAdminPage 의 Status 열이 이것을 쓰며 Attempts 열을 추가했다. web/test/aggregateJobStatus.test.mjs 를 추가해 npm test(92 통과), eslint, prettier, tsc -b && vite build 로 검증했다.
보류 아이디어: compose.yml 의 build: 때문에 릴리즈 이미지로 띄울 때 –no-build 가 필요하다는 주석을 compose 파일에 추가 (가치 2 / 위험 1 / S); scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 — 1901 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 — 2200 머지 뒤 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간(policyRange 결과)을 표시 — 2200 머지 뒤 (가치 2 / 위험 1 / S); Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 남긴다 — 2330 머지 뒤 (가치 1 / 위험 1 / S); 관리 센터 홈의 “실패한 재집계 작업” 조치 항목이 attempts 소진으로 failed 가 된 작업과 재시도 대기 작업을 나눠 세어 재시도 대기가 오래(예: 1시간 넘게) 이어지면 warning 으로 알린다 (가치 2 / 위험 1 / S); Aggregate 탭 작업 목록에 started_at·finished_at 기반 소요 시간 열을 추가해 오래 걸리는 재집계를 한눈에 본다 (가치 2 / 위험 1 / S).
2026-09-12
선택: 세그먼트 contains/startsWith/endsWith 와 방문자 검색이 입력한 글자를 패턴이 아니라 글자 그대로 찾게 한다 (가치 3 / 위험 2 / 작업량 S)
결과: 성공
요약: 캠페인 목표(캡처가 실린 가이드 완성본)는 미머지 브랜치 auto/2026-09-11-1901 의 e73d357 에 이미 완성되어 있고 2200·2330·0011 도 미머지여서 docs/·queryError.ts·AdminPage.tsx·EnterpriseAdminPage.tsx·maintenance.go 는 손대지 않았다. 대신 어느 브랜치와도 겹치지 않는 실제 결함을 고쳤다: segment.go 의 텍스트 연산자와 visitor_trace.go 의 검색이 값을 ILIKE 패턴에 그대로 바인딩해 _ 가 아무 한 글자, % 가 아무 문자열로 읽혔다 — “report_daily” 가 “reportXdaily” 도 잡고, URL 인코딩된 한글 경로 “%EA%B0%80” 은 그 16진수가 순서대로 어디든 있는 페이지를 전부 잡아 세그먼트 모집단이 틀렸다. segment.LikeLiteral 이 \ % _ 를 이스케이프하고 두 경로가 이것을 거치며(=·!= 는 그대로), 단위 테스트(internal/segment/segment_test.go)와 로컬 Postgres 17 컨테이너로 컴파일된 predicate 와 /visitor-search 를 실제 데이터에 돌리는 통합 테스트를 추가했고, 이스케이프를 빼면 두 경로 모두 실패하는 것을 확인했다. go vet 과 MOMENTO_TEST_POSTGRES_DSN 을 준 go test ./cmd/… ./internal/… 전부 통과. 덤으로 보류 항목이던 compose.yml 의 build: 위에 릴리즈 이미지는 --no-build 로 띄운다는 주석을 별도 커밋으로 넣었다(compose config 로 YAML 확인).
보류 아이디어: scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 — 1901 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 — 2200 머지 뒤 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간(policyRange 결과)을 표시 — 2200 머지 뒤 (가치 2 / 위험 1 / S); Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 남긴다 — 2330 머지 뒤 (가치 1 / 위험 1 / S); 관리 센터 홈의 재집계 조치 항목이 1시간 넘게 재시도 대기 중인 작업을 warning 으로 알린다 — 0011·2200 머지 뒤 (가치 2 / 위험 1 / S); Aggregate 탭 작업 목록에 started_at·finished_at 기반 소요 시간 열 — 0011 머지 뒤 (가치 2 / 위험 1 / S); 세그먼트 in/not in 의 값이 fmt.Sprint 로 문자열화되어 JSON 숫자 1.0 이 “1” 로 바인딩되므로 화면이 숫자를 보내는 경우가 있는지 확인하고 있다면 원문 보존 (가치 1 / 위험 1 / S); enterprise_governance.go 의 dimension 사용 수 세기가 definition::text LIKE '%'||d.name||'%' 로 이름을 패턴으로 읽어 _ 가 든 dimension 이름의 사용 수가 과다 집계됨 — 같은 이스케이프 적용 (가치 2 / 위험 1 / S).
2026-09-12
선택: 이벤트 카탈로그의 used_by 가 이벤트 이름을 패턴이 아니라 정의가 실제로 가리키는 이름으로 센다 (가치 2 / 위험 1 / 작업량 S)
결과: 성공
요약: 캠페인 목표(캡처가 실린 가이드 완성본)는 미머지 브랜치 auto/2026-09-11-1901 의 e73d357 에 이미 완성되어 있고 2200·2330·0011·0101 도 아직 미머지여서, 이 브랜치에서 docs/·queryError.ts·maintenance.go·AdminPage.tsx·EnterpriseAdminPage.tsx·segment.go 를 만지면 충돌하는 중복이 되므로 손대지 않았다. 대신 보류 목록 중 어느 브랜치와도 겹치지 않는 결함을 고쳤다: enterprise_governance.go 의 eventCatalog 가 각 이벤트를 쓰는 메트릭·목표 수를 definition::text LIKE '%'||d.name||'%' 로 세어 “purchase” 가 “purchase_completed” 메트릭까지 잡고 _ 가 아무 한 글자로 읽혀 폐기 여부를 판단하는 사용 수가 과다 집계됐다. 정의 트리(최상위와 ratio 의 numerator/denominator)의 event_name 값과 정확히 비교하는 jsonpath($.**.event_name ? (@ == $name))로 바꿨다(0101 의 segment.LikeLiteral 에 의존하지 않도록 SQL 안에서 해결). 이름이 서로를 포함하는 이벤트 셋과 중첩 정의를 API 로 만들어 카탈로그를 읽는 통합 테스트(integration_catalog_usage_test.go)를 추가했고 수정 전에는 3/2/1 로 실패, 수정 후 통과를 확인했다. go vet, gofmt, 로컬 Postgres 17 컨테이너에 MOMENTO_TEST_POSTGRES_DSN 을 준 go test ./cmd/… ./internal/… 전부 통과. MCP get_event_catalog 는 used_by 를 내려주지 않아 해당 없음. 세그먼트 in/not in 의 fmt.Sprint 항목은 SegmentBuilder 가 쉼표 분리 문자열만 보내는 것을 확인해 rejected 로 정리했다.
보류 아이디어: scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 — 1901 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 — 2200 머지 뒤 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간(policyRange 결과)을 표시 — 2200 머지 뒤 (가치 2 / 위험 1 / S); Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 남긴다 — 2330 머지 뒤 (가치 1 / 위험 1 / S); 관리 센터 홈의 재집계 조치 항목이 1시간 넘게 재시도 대기 중인 작업을 warning 으로 알린다 — 0011·2200 머지 뒤 (가치 2 / 위험 1 / S); Aggregate 탭 작업 목록에 started_at·finished_at 기반 소요 시간 열 — 0011 머지 뒤 (가치 2 / 위험 1 / S); 카탈로그 used_by 가 metric_ref 로 간접 참조하는 메트릭(다른 메트릭을 가리키는 메트릭)까지 세지 않으므로 참조를 따라가 간접 사용도 포함 — 재귀 CTE 로 semantic_metrics 를 따라가며 5단계 제한 (가치 2 / 위험 2 / S); 데이터 계보(dataLineage)가 정의의 최상위 event_name·metric 만 읽어 ratio 의 numerator/denominator 가 가리키는 이벤트·메트릭이 계보 그래프에서 빠진다 — 카탈로그와 같은 트리 순회로 간선을 만들고 goalRows, _ := 로 버리는 오류도 보고 (가치 2 / 위험 1 / S).
2026-09-12
선택: 데이터 계보(dataLineage)가 ratio 의 numerator/denominator 안에 있는 이벤트·메트릭 참조까지 간선으로 그린다 (가치 2 / 위험 1 / 작업량 S)
결과: 성공
요약: 캠페인 목표(캡처가 실린 가이드 완성본)는 미머지 브랜치 auto/2026-09-11-1901 의 e73d357 에 이미 완성되어 있고 2200·2330·0011·0101·0151 도 아직 미머지여서(main 은 9f857c4), 이 브랜치에서 docs/·queryError.ts·maintenance.go·AdminPage.tsx·EnterpriseAdminPage.tsx·segment.go·eventCatalog 를 만지면 충돌하는 중복이 되므로 손대지 않았다. 대신 보류 목록 중 어느 브랜치와도 겹치지 않는 결함을 고쳤다: enterprise_governance.go 의 dataLineage 가 정의의 최상위 event_name·metric 만 읽어, 소스를 numerator/denominator 안에 두는 ratio 메트릭(전환율 등)은 Data Lineage 표에 소스 없는 메트릭으로 나왔고 metric_ref 로 다른 메트릭을 가리키는 ratio 도 formula 간선이 빠졌다(같은 이벤트를 카탈로그 used_by 는 세면서 계보는 안 그리는 불일치). appendLineageSources 가 numerator/denominator 를 validateSemanticDefinition 과 같은 5단계까지 따라가며 간선을 만들고 같은 소스에 두 번 닿으면 간선 하나만 그리며, goalRows, _ := 로 버리던 metric_goals 조회 오류도 QUERY_FAILED 로 보고한다. 이벤트를 각 변에 둔 ratio·metric_ref 양변 ratio·같은 이벤트가 두 단계 아래 두 번 나오는 중첩 ratio 를 API 로 만들어 /lineage 를 읽는 통합 테스트(integration_lineage_test.go)를 추가했고 수정 전에는 간선 6개·노드 1개가 빠져 실패, 수정 후 통과를 확인했다. gofmt, go vet ./…, 로컬 Postgres 17 컨테이너에 MOMENTO_TEST_POSTGRES_DSN 을 준 go test ./cmd/… ./internal/… 전부 통과(httpapi 통합 포함). 화면(EnterpriseAdminPage 의 Data Lineage 표)은 from/relation/to 를 그대로 보여주므로 web 변경은 없다.
보류 아이디어: scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 — 1901 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 — 2200 머지 뒤 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간(policyRange 결과)을 표시 — 2200 머지 뒤 (가치 2 / 위험 1 / S); Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 남긴다 — 2330 머지 뒤 (가치 1 / 위험 1 / S); 관리 센터 홈의 재집계 조치 항목이 1시간 넘게 재시도 대기 중인 작업을 warning 으로 알린다 — 0011·2200 머지 뒤 (가치 2 / 위험 1 / S); Aggregate 탭 작업 목록에 started_at·finished_at 기반 소요 시간 열 — 0011 머지 뒤 (가치 2 / 위험 1 / S); 카탈로그 used_by 가 metric_ref 로 간접 참조하는 메트릭까지 센다 — 0151 머지 뒤 (가치 2 / 위험 2 / S); 데이터 계보에 goal 이 가리키는 metric 이 semantic_metrics 에 없으면(삭제·이름 변경) measures 간선의 from 노드가 없으므로 끊긴 참조를 “missing” 종류 노드로 드러내 관리자가 고칠 수 있게 한다 — 이번 회차 파일(dataLineage)과 겹치므로 이 브랜치 머지 뒤 (가치 2 / 위험 1 / S); 계보 표가 노드 kind(event/metric/goal)와 label 을 내려받고도 id 문자열만 보여주므로 EnterpriseAdminPage 의 Data Lineage 열이 label 과 종류 칩을 쓰게 한다 — 0011 머지 뒤 (가치 2 / 위험 1 / S).
2026-09-12
선택: CSV 내보내기(서버 raw export·콘솔 표 CSV)의 셀이 스프레드시트에서 수식으로 실행되지 않게 한다 (가치 3 / 위험 1 / 작업량 S)
결과: 성공
요약: 캠페인 목표(캡처가 실린 가이드 완성본)는 미머지 브랜치 auto/2026-09-11-1901 의 e73d357 에 이미 완성되어 있고 2200·2330·0011·0101·0151·0241 도 아직 미머지여서(main 은 9f857c4), docs/·scripts/guide·queryError.ts·maintenance.go·AdminPage.tsx·EnterpriseAdminPage.tsx·segment.go·visitor_trace.go·enterprise_governance.go 는 손대지 않았다. 대신 어느 브랜치와도 겹치지 않는 보안 결함을 고쳤다: /export 의 CSV(analytics.go exportEvents)와 DataTable 의 CSV 내려받기가 page_url·campaign·event_name 등 방문자 브라우저가 쓴 값을 그대로 셀에 넣어, =·+·-·@ 로 시작하는 값(예: campaign 에 =HYPERLINK(...), DDE 호출)이 Excel·LibreOffice 에서 수식으로 실행됐다(CSV injection). 서버는 csvCell 이, 콘솔은 새 순수 모듈 web/src/components/csvExport.ts 의 csvValue/buildCSV 가 그런 문자열에만 아포스트로피를 붙이고 숫자·id·타임스탬프·JSON·보통 글자는 그대로 둔다. Go 단위 테스트와, 적대적 campaign 을 수집기로 보내고 CSV 리더로 export 를 되읽는 통합 테스트(export_cell_test.go), node:test 3건(csvExport.test.mjs)을 추가했고 가드를 빼면 통합 테스트가 실패하는 것을 확인했다. gofmt, go vet, 로컬 Postgres 17 컨테이너에 MOMENTO_TEST_POSTGRES_DSN 을 준 go test ./cmd/… ./internal/… 전부 통과, web 은 npm test(92 통과)·eslint·prettier·tsc -b && vite build 통과.
보류 아이디어: scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 — 1901 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 — 2200 머지 뒤 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간(policyRange 결과)을 표시 — 2200 머지 뒤 (가치 2 / 위험 1 / S); Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 남긴다 — 2330 머지 뒤 (가치 1 / 위험 1 / S); 관리 센터 홈의 재집계 조치 항목이 1시간 넘게 재시도 대기 중인 작업을 warning 으로 알린다 — 0011·2200 머지 뒤 (가치 2 / 위험 1 / S); Aggregate 탭 작업 목록에 started_at·finished_at 기반 소요 시간 열 — 0011 머지 뒤 (가치 2 / 위험 1 / S); 카탈로그 used_by 가 metric_ref 로 간접 참조하는 메트릭까지 센다 — 0151 머지 뒤 (가치 2 / 위험 2 / S); 데이터 계보에서 goal 이 가리키는 metric 이 없을 때 ‘missing’ 노드로 끊긴 참조를 드러낸다 — 0241 머지 뒤 (가치 2 / 위험 1 / S); Data Lineage 표가 id 대신 label 과 kind 칩을 보여준다 — 0011 머지 뒤 (가치 2 / 위험 1 / S); DataTable 의 CSV 내려받기가 format 으로 그린 표시값이 아니라 원시값을 내보내므로 비율(0.123)·ms 등이 화면과 다른 단위로 나간다 — 열마다 exportFormat 을 두거나 문서에 원시값임을 명시 (가치 2 / 위험 1 / S); 사용자 가이드 CSV 절(USER_GUIDE.md:550)에 =·+·-·@ 로 시작하는 셀은 아포스트로피가 붙어 글자로 열린다는 한 줄 — docs/ 는 1901 과 겹치므로 머지 뒤 (가치 1 / 위험 1 / S).
2026-09-12
선택: bcrypt 가 해시할 수 없는 비밀번호(72바이트 초과)를 빈 해시로 저장해 계정을 잠그는 대신 거부한다 (가치 4 / 위험 1 / 작업량 S)
결과: 성공
요약: 캠페인 목표(캡처가 실린 가이드 완성본)는 미머지 브랜치 auto/2026-09-11-1901 의 e73d357 에 이미 완성되어 있고 2200·2330·0011·0101·0151·0241·0331 도 아직 미머지여서(main 은 9f857c4), docs/·scripts/guide·queryError.ts·AdminPage.tsx·EnterpriseAdminPage.tsx·DataTable.tsx·csvExport.ts·maintenance.go·segment.go·visitor_trace.go·enterprise_governance.go·analytics.go·compose.yml 은 손대지 않았다. 대신 어느 브랜치와도 겹치지 않는 실제 결함을 고쳤다: golang.org/x/crypto v0.42 의 bcrypt 는 72바이트를 넘는 입력을 ErrPasswordTooLong 으로 거부하는데 admin.go 의 updateMe 가 newHash, _ := auth.HashPassword(...) 뒤 _, _ = UPDATE users SET password_hash=$2 로 빈 문자열을 기존 해시 위에 덮어써, 한글 25자(75바이트) 같은 긴 암호구로 프로필을 한 번 저장하면 updated:true 를 받고도 다시는 로그인할 수 없었다(관리자 재설정 경로가 없어 DB 직접 수정 외 복구 불가). createUser 도 같은 방식으로 아무도 들어갈 수 없는 계정을 만들고 이메일을 점유했다. internal/auth 에 MinPasswordLength/MaxPasswordBytes 와 PasswordProblem 을 두어 두 핸들러가 해시 전에 검사해 400 을 돌려주고, 해시·UPDATE 오류를 더는 버리지 않으며, 프로필과 비밀번호를 password_hash=COALESCE($5,password_hash) 한 문장으로 갱신하고 감사 로그 detail 에 password_changed 를 남긴다. auth 단위 테스트(72바이트는 통과·75바이트는 bcrypt 도 거부하는지 함께 고정)와 PATCH /me·POST /users 를 실제로 부르는 통합 테스트(integration_password_test.go)를 추가했고, 수정을 되돌리면 통합 테스트가 “75-byte new password answered 200” 으로 실패하는 것을 확인했다. gofmt, go vet ./cmd/… ./internal/…, 로컬 Postgres 17 컨테이너에 MOMENTO_TEST_POSTGRES_DSN 을 준 go test ./cmd/… ./internal/… 전부 통과(httpapi 통합 308초 포함). web 은 변경 없음(ProfilePage 와 AdminPage 의 “12자 이상” 도움말은 AdminPage 가 2200 과 겹쳐 함께 미룸).
보류 아이디어: scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 셀렉터·문구 변경으로 가이드 그림이 어긋나면 알린다 — 1901 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 admin/orgAdmin 미들웨어 순회 테스트 출력과 diff 해 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 — 2200 머지 뒤 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간(policyRange 결과)을 표시 — 2200 머지 뒤 (가치 2 / 위험 1 / S); Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 남긴다 — 2330 머지 뒤 (가치 1 / 위험 1 / S); 관리 센터 홈의 재집계 조치 항목이 1시간 넘게 재시도 대기 중인 작업을 warning 으로 알린다 — 0011·2200 머지 뒤 (가치 2 / 위험 1 / S); Aggregate 탭 작업 목록에 started_at·finished_at 기반 소요 시간 열 — 0011 머지 뒤 (가치 2 / 위험 1 / S); 카탈로그 used_by 가 metric_ref 로 간접 참조하는 메트릭까지 센다 — 0151 머지 뒤 (가치 2 / 위험 2 / S); 데이터 계보에서 goal 이 가리키는 metric 이 없을 때 ‘missing’ 노드로 끊긴 참조를 드러낸다 — 0241 머지 뒤 (가치 2 / 위험 1 / S); Data Lineage 표가 id 대신 label 과 kind 칩을 보여준다 — 0011 머지 뒤 (가치 2 / 위험 1 / S); DataTable 의 CSV 내려받기가 format 으로 그린 표시값이 아니라 원시값을 내보낸다 — 0331 머지 뒤 (가치 2 / 위험 1 / S); 사용자 가이드 CSV 절에 수식 문자로 시작하는 셀은 아포스트로피가 붙는다는 한 줄 — 1901·0331 머지 뒤 (가치 1 / 위험 1 / S); ProfilePage·AdminPage 의 비밀번호 도움말 “12자 이상”을 “12자 이상, 72바이트(한글 24자) 이하”로 맞추고 관리자 가이드 계정 절에도 한 줄 — AdminPage 는 2200, docs 는 1901 머지 뒤 (가치 2 / 위험 1 / S); 관리자가 다른 사용자의 비밀번호를 재설정하는 경로가 없어 잠긴 계정은 DB 직접 수정으로만 복구된다 — PATCH /users/{id} 에 password 필드(호출자 역할 이상 금지 규칙 그대로)와 감사 로그 (가치 3 / 위험 2 / M); 비밀번호 최소 길이 검사가 len() 바이트 기준이라 한글 4자(12바이트)가 통과한다 — utf8.RuneCountInString 으로 문자 수를 세고 테스트 (가치 2 / 위험 1 / S).
2026-09-12
선택: [수정 과제] internal/httpapi 테스트 패키지가 CI 의 go test 기본 10분 제한을 넘기는 원인을 찾아 고친다 (가치 5 / 위험 1 / 작업량 S)
결과: 성공
요약: 멈추는 지점은 없었다 — 채널 대기는 모두 5~10초 상한이 있고, -race -v 로 전 테스트 시간을 재 보니 seed 를 부르는 81개 테스트가 저마다 3.5초의 고정 비용을 내고 있었다(패키지 전체 5분 35초, CPU 40초, 나머지는 DB 대기). seed 가 약 2,000개의 INSERT 를 한 문장씩 autocommit 으로 보내 문장마다 라운드트립과 fsync 를 치렀기 때문이며, 8/27 에 -race 를 켠 뒤 seed 를 부르는 테스트가 계속 늘어 CI 러너에서 600초를 넘긴 것이다. seed 의 run 을 pgx.Batch 에 쌓아 한 라운드트립·한 암묵적 트랜잭션으로 보내고 행을 되읽기 전에 flush 하도록 바꿨다(SQL 문장은 그대로). 로컬 Postgres 17 컨테이너로 같은 측정을 반복해 seed 3.5초→0.3초, httpapi 패키지 335초→56초(-race), TestTheFixtureIsWhatItSaysItIs 포함 전부 통과를 확인했고 gofmt·go vet·go test -race ./cmd/... ./internal/... 도 통과했다. ci.yml 은 이미 push: branches: [main] 으로 main 푸시에서 돌게 되어 있어(07a14c3 이후) 워크플로는 손대지 않았다; main 푸시 실행이 실제로 없다면 GITHUB_TOKEN 으로 한 푸시(워크플로를 트리거하지 않음)나 [skip ci] 제목 등 저장소 밖 원인이므로 러너 쪽에서 확인이 필요하다. release.yml 은 Go 테스트를 돌리지 않으므로(빌드→스모크→게시) 릴리즈 실패는 같은 커밋의 CI 게이트를 통해 온 것이고 이 수정으로 함께 해소된다.
보류 아이디어: main 푸시가 워크플로를 트리거하지 않는 원인(GITHUB_TOKEN 푸시·[skip ci]) 을 러너 설정에서 확인 — 저장소 밖 (가치 3 / 위험 1 / S); ci.yml 의 go test 단계 주석(“약 90초”)을 현재 비용으로 갱신하고 -json 으로 패키지별 소요를 남겨 다음 회귀를 알아보게 한다 (가치 2 / 위험 1 / S); testPool 이 테스트마다 database.Migrate 로 17번 SELECT 를 도니 패키지당 한 번(sync.Once)으로 (가치 1 / 위험 1 / S); seed 를 set-based INSERT(generate_series)로 다시 써 서버 실행 시간까지 줄인다 — TestTheFixtureIsWhatItSaysItIs 가 지키지만 fixture 재작성 위험 (가치 2 / 위험 3 / M); scripts/guide 캡처를 CI 에서 릴리즈 tarball 로 돌려 가이드 그림 어긋남 검사 — 1901 머지 뒤 (가치 3 / 위험 2 / M); 관리자 가이드 4.1 역할 표를 라우터 순회 테스트로 고정 — 1901 머지 뒤 (가치 2 / 위험 1 / S); 기간을 고를 수 없는 화면의 RANGE_EXCEEDS_POLICY 안내에 정책 화면 링크 — 2200 머지 뒤 (가치 2 / 위험 1 / S); visitor-search 의 Empty 상태에 실제 조회 기간 표시 — 2200 머지 뒤 (가치 2 / 위험 1 / S); Maintenance.Run 의 30분 running 복구 UPDATE 실패를 로그에 — 2330 머지 뒤 (가치 1 / 위험 1 / S); 관리 센터 홈의 재집계 조치 항목이 오래 이어지는 재시도 대기를 warning 으로 — 0011·2200 머지 뒤 (가치 2 / 위험 1 / S); Aggregate 탭 작업 목록에 소요 시간 열 — 0011 머지 뒤 (가치 2 / 위험 1 / S); 카탈로그 used_by 가 metric_ref 간접 참조까지 센다 — 0151 머지 뒤 (가치 2 / 위험 2 / S); 데이터 계보의 끊긴 goal→metric 참조를 ‘missing’ 노드로 — 0241 머지 뒤 (가치 2 / 위험 1 / S); Data Lineage 표가 label 과 kind 칩을 보여준다 — 0011 머지 뒤 (가치 2 / 위험 1 / S); DataTable CSV 가 원시값을 내보내는 문제 — 0331 머지 뒤 (가치 2 / 위험 1 / S); 사용자 가이드 CSV 절에 아포스트로피 한 줄 — 1901·0331 머지 뒤 (가치 1 / 위험 1 / S).