실행 오류 error: agent produced no result (/tmp/aidev-run.b47dYb.sh: line 97: cd: /home/hkjang/.cache/auto-improve-wt/vibe-coders: No such file or directory )
OPERATIONS.md·SAFETY_GUIDE.md·ROUTING_GUIDE.md 에도 docs/images/guide 캡처를 실어 문서군 통일
3/1/S
대기
캡처 스크립트가 있으니 shots 목록만 늘리면 됨
2026-09-12
MODEL_PRICING_KRW_PER_1M 키 소문자 정규화 부재로 lookupPrice 정확 매칭이 항상 실패하고 prefix 루프로만 걸림
3/1/S
대기
적재 시점에 strings.ToLower(TrimSpace) 일원화
2026-09-12
가이드 캡처 스크립트를 CI 선택 잡으로 돌려 빈 화면·오류 상태 회귀 감지
3/2/M
대기
드라이버가 page-state-error 를 실패로 보고함. workflow_dispatch 로 시작
2026-09-12
text2sql.ValidateSQL 이 MySQL `LIMIT n, m`·`FETCH FIRST n ROWS ONLY` 를 인식하지 못해 상한·기본값 규칙 우회
3/2/S
대기
limitRe 확장 + 회귀 테스트
2026-09-12
audit.InferLanguages.addSignal 이 더 높은 신뢰도 신호가 오면 기존 evidence 를 통째로 버림
2/1/S
대기
근거 누적 유실
2026-09-12
사용자·관리자 가이드를 실제 콘솔 캡처와 함께 GUIDE-STANDARD 구조로 재작성 (수정 과제: 캠페인 34회 fetch 실패)
5/1/M
완료
scripts/guide-screenshots.sh + web/scripts/guide-screenshots.mjs 로 /app 24화면 캡처, USER/ADMIN_GUIDE.md·PDF·README 링크 갱신. fetch 실패 원인은 러너 정책 base_branch=main → master 로 정정됨; 저장소 스크립트·워크플로는 이미 master 만 참조해 코드 변경 없음
2026-09-12
원장 (에이전트가 남긴 기록)
2026-09-02
선택: 모델 가격 prefix 매칭 비결정성 수정 (가치 5 / 위험 1 / 작업량 S)
결과: 성공
요약: audit.lookupPrice가 가격표 map을 range하며 첫 prefix 일치를 반환해, 내장 카탈로그에 서로의 prefix인 항목(gpt-4o vs gpt-4o-mini, claude-sonnet-4 vs claude-sonnet-4-5)이 많은 탓에 버전 접미사가 붙은 실제 모델 ID(gpt-4o-mini-2026-06-01)가 호출마다 최대 16.7배 다른 단가로 계산되던 버그를 고쳤다. 가장 긴(구체적인) prefix를 채택하도록 바꾸고, 같은 규칙을 손으로 복제해 동일 버그를 갖고 있던 admin_explain.lookupModelPrice는 새로 export한 audit.LookupPrice에 위임시켰다. 회귀 테스트를 추가해 옛 구현에서 실패(16250 vs 975)함을 확인했고, gofmt·go vet·go build·go test ./...·go test -race ./internal/audit ./internal/proxy·cmd/api-surface-audit 모두 통과했다.
보류 아이디어: redact.go IPv4 규칙의 “사설망 제외” 주석과 실제 동작(전부 마스킹) 불일치 정리 / internal/config 패키지 테스트 부재 보강 / EstimateTokens의 []rune(text) 전체 복사를 utf8.RuneCountInString으로 교체 / 가격표 키 정규화(소문자·trim)를 적재 시점에 일원화해 조회마다 재정규화 제거 / prefix 매칭에 경계 검사 추가로 gpt-4가 gpt-45에 잘못 매칭되는 것 방지
2026-09-03
선택: PROXY_API_KEYS 필드 트림 및 빈 시크릿 검증 (가치 4 / 위험 1 / 작업량 S)
결과: 성공
요약: config.parseProxyKeys가 CSV 항목 전체만 TrimSpace하고 : 분리 후 개별 필드는 트림하지 않아, PROXY_API_KEYS="dev: dev-proxy-key: alice"처럼 자연스럽게 띄어쓴 설정이 " dev-proxy-key"의 해시로 저장되던 버그를 고쳤다. 인증 경로의 bearerToken은 토큰을 트림해서 넘기므로 이 키는 어떤 요청으로도 절대 매칭되지 않는 무성 인증 실패였다. 아울러 시크릿이 빈 항목(dev:, dev::alice:team)이 sha256("")을 active 키 해시로 등록해 게이트웨이를 키 필수 모드로 뒤집던 것도 건너뛰도록 했다. 테스트가 전무하던 internal/config에 첫 테스트 파일을 추가해 문서화된 name:key:owner:team 형식과 두 회귀를 덮었고, 수정 전 코드에서 실패함을 확인했다. gofmt·go vet·go build·go test ./...·go test -race ./internal/config 모두 통과.
보류 아이디어: redact.go IPv4 규칙의 “사설망 제외” 주석과 실제 동작(전부 마스킹) 불일치 정리 / EstimateTokens의 []rune(text) 전체 복사를 utf8.RuneCountInString으로 교체 / 가격표 키 정규화(소문자·trim)를 적재 시점에 일원화해 조회마다 재정규화 제거 / databaseConfig의 DSN 우선순위·durationEnv/floatMapEnv 등 나머지 config 헬퍼 테스트 보강 / parseProxyKeys에서 중복 키가 같은 ID로 서로를 덮어쓰는 문제 경고 처리
릴리즈: v0.82.0 (2026-09-03)
2026-09-03
선택: 응답 분석기의 구조화 content 파트 누락 및 스트리밍 tool_call 순서 비결정성 수정 (가치 4 / 위험 1 / 작업량 S)
결과: 성공
요약: proxy.contentString이 문자열 content만 평탄화해, content 파트 배열([{"type":"text","text":"..."}])로 답하는 업스트림의 CompletionText가 빈 문자열이 되던 버그를 고쳤다. pipeline.go는 응답에 usage 블록이 없을 때 이 텍스트 기반 추정치로 폴백하므로, 해당 요청들은 completion 토큰 0 · 비용 0으로 기록되고 있었다(요청 측 flattenContent는 같은 형태를 이미 평탄화하고 있어 명백한 누락). 텍스트가 없는 파트(이미지 등)는 추정치를 부풀리지 않도록 raw JSON 대신 계속 빈 문자열로 둔다. 함께, 스트리밍 tool_call 이름을 index 키 map을 그대로 range하며 내보내 한 응답의 tool_invocations 행 순서가 실행마다 달라지던 것(조회 쿼리는 ORDER BY created_at, source뿐이라 표시 순서가 그대로 흔들림)을 index 정렬 방출로 바꿨다. 회귀 테스트 3개를 추가해 수정 전 코드에서 모두 실패함을 확인했고(순서 테스트는 20회 반복), gofmt·go vet·go build·go test ./...·go test -race ./internal/proxy 모두 통과.
보류 아이디어: POST /admin/pricing에 음수 단가 검증 부재(음수 비용이 그대로 저장됨) / audit.InferLanguages가 동점 신뢰도를 알파벳순으로만 깨서 대표 언어가 자의적으로 결정되는 문제를 근거 개수 기준으로 개선 / redact.go IPv4 규칙의 “사설망 제외” 주석과 실제 동작(전부 마스킹) 불일치 정리 / EstimateTokens의 []rune(text) 전체 복사를 utf8.RuneCountInString으로 교체 / databaseConfig의 DSN 우선순위·durationEnv/floatMapEnv 등 나머지 config 헬퍼 테스트 보강
참고: 2026-09-02 세션의 가격 prefix 수정(auto/2026-09-02-2250 브랜치)은 아직 master에 병합되지 않은 상태
2026-09-04
선택: 모델 단가 음수 검증 부재 수정 (POST /admin/pricing + MODEL_PRICING_KRW_PER_1M) (가치 4 / 위험 1 / 작업량 S)
결과: 성공
요약: 운영자가 단가를 넣는 두 경로 모두 음수 KRW를 그대로 받아들이고 있었다 — handlePricing의 POST는 필드 존재 여부만 검사했고, config.Load는 MODEL_PRICING_KRW_PER_1M을 범위 검사 없이 unmarshal했다. audit.EstimateCostKRW는 단가를 그대로 곱하므로 음수 단가는 음수 비용이 되고, 강제 지점이 전부 cost > limit 비교라서(키별 예산 pipeline.go:417, 비용 가드 pipeline.go:427) 해당 모델의 예산·비용 가드가 무조건 통과하고 쿼터 합계는 요청을 더하는 대신 빼게 된다. -1을 “미설정” 뜻으로 넣는 흔한 오타 하나로 강제가 무력화되는 구조라 두 경계에서 각각 400 응답·부팅 거부로 막았다. 0은 “과금하지 않는 모델” 표기 수단이라 계속 허용한다. 나머지 InsertPricingVersion 호출부 2곳은 정적 내장 카탈로그 시드라 대상 아님. 회귀 테스트 5개를 추가해 수정 전 코드에서 모두 실패함을 확인했고(잘못된 버전이 DB에 기록되지 않는 것까지 검증), gofmt·go vet·go build·go test ./...·go test -race ./internal/config ./internal/proxy -timeout=30m(CI와 동일, data race 0)·cmd/api-surface-audit 모두 통과.
보류 아이디어: OpenAPI PricingWriteRequest 스키마에 minimum: 0 반영(이번엔 생략 — 재생성에 Node 24·pnpm 11이 필요한데 환경엔 Node 22·pnpm 없음, 손으로 openapi.json을 고치면 pnpm openapi:check drift로 CI가 깨짐) / audit.InferLanguages가 동점 신뢰도를 알파벳순으로만 깨서 대표 언어가 자의적으로 결정되는 문제를 근거 개수 기준으로 개선 / redact.go IPv4 규칙의 “사설망 제외” 주석과 실제 동작(전부 마스킹) 불일치 정리 / EstimateTokens의 []rune(text) 전체 복사를 utf8.RuneCountInString으로 교체 / MODEL_PRICING_KRW_PER_1M 키가 소문자 정규화되지 않아 lookupPrice의 정확 매칭을 항상 놓치고 prefix 루프로만 걸리는 문제
참고: 2026-09-02(가격 longest-prefix 매칭)·2026-09-03(PROXY_API_KEYS 트림) 세션 수정 모두 아직 master에 병합되지 않아, master의 audit.lookupPrice는 여전히 첫 prefix 일치를 반환한다. 중복 작업하지 말 것.
릴리즈: v0.82.2 (2026-09-04)
2026-09-05
선택: Text2SQL LIMIT 규칙을 문장 자신의 결과 범위로 한정 (가치 4 / 위험 1 / 작업량 S)
결과: 성공
요약: text2sql.ValidateSQL의 두 LIMIT 규칙이 중첩을 전혀 고려하지 않고 문장 전체를 훑어, 서브쿼리·CTE 본문의 LIMIT이 바깥 쿼리를 대신 대답하고 있었다. (1) 상한 검사는 limitRe.FindString으로 텍스트 순서상 첫 매치만 봤는데 서브쿼리는 바깥 LIMIT보다 먼저 쓰이므로 ... (SELECT ... LIMIT 5) ... LIMIT 999999가 5로 측정돼 통과했다 → 이제 모든 LIMIT을 MaxLimit과 비교한다. (2) 기본 LIMIT 주입은 어디든 LIMIT이 있으면 건너뛰어, 바깥은 무제한인데 서브쿼리에만 LIMIT이 있는 쿼리(SELECT * FROM users WHERE id IN (SELECT ... LIMIT 5))에 아무 상한도 붙지 않았다 — 기본 상한이 존재하는 이유인 바로 그 쿼리에 무제한 스캔과 ORDER BY 전체 정렬을 넘긴 셈 → 괄호 밖(depth 0) LIMIT 유무로 판정하게 바꿨다. (3) 값을 fmt.Sscanf("limit %d")로 되읽었는데 포맷의 공백은 개행과 매치되지 않아 줄바꿈된 LIMIT\n999999가 0으로 파싱돼 상한이 무력화됐고, 오버플로하는 리터럴도 0이 됐다 → 정규식 캡처 그룹 + strconv.Atoi로 바꾸고 파싱 불가 값은 거부한다. 회귀 테스트 6개를 새 파일로 추가해 수정 전 코드에서 실패함을 확인했고(중첩 4건·개행 1건·오버플로 1건), gofmt·go vet·go build·go test ./... -count=1·go test -race ./internal/text2sql·cmd/api-surface-audit 모두 통과. Go 전용 변경이라 OpenAPI/프런트엔드 영향 없음.
보류 아이디어: audit.InferLanguages의 addSignal이 더 높은 신뢰도 신호가 오면 기존 evidence를 통째로 버리는 문제(근거 누적 유실) / redact.go IPv4 규칙의 “사설망 제외” 주석과 실제 동작(전부 마스킹) 불일치 정리 / EstimateTokens의 []rune(text) 전체 복사를 utf8.RuneCountInString으로 교체 / MODEL_PRICING_KRW_PER_1M 키가 소문자 정규화되지 않아 lookupPrice의 정확 매칭을 항상 놓치고 prefix 루프로만 걸리는 문제 / ValidateSQL이 MySQL LIMIT 5, 100 및 FETCH FIRST n ROWS ONLY 형식을 인식하지 못해 상한·기본값 규칙이 모두 우회되는 문제
참고: 2026-09-02(가격 longest-prefix 매칭)·2026-09-03(PROXY_API_KEYS 트림) 세션 수정은 여전히 master에 병합되지 않았다(internal/audit/usage.go의 lookupPrice가 아직 첫 prefix 일치를 반환). 중복 작업하지 말 것.
릴리즈: v0.83.0 (2026-09-05)
2026-09-12
선택: 수정 과제 — 가이드 캠페인: 사용자·관리자 가이드를 실제 콘솔 캡처와 함께 GUIDE-STANDARD 구조로 재작성 (가치 5 / 위험 1 / 작업량 M)
결과: 성공
요약: 34회 연속 fetch 실패의 원인은 러너 정책 기본값 base_branch=main이었고(이 저장소는 master), state/vibe-coders.policy.json에 base_branch: master가 적혀 이번 회차부터 master@f940400으로 정상 진행됐다. “릴리즈 워크플로 2회 실패”도 같은 원인인지 저장소 쪽을 점검했다: .github/workflows에는 CI·golden-regression만 있고 릴리즈 워크플로는 없으며, scripts/release.sh·release.ps1·gh_release.ps1·verify_release.sh·ci.yml은 모두 refs/heads/master·branches: [master]를 쓰고 main을 참조하는 곳이 한 군데도 없다(grep -rn '\bmain\b' .github scripts docs/RELEASE_GUIDE.md 로 확인). state/lessons.jsonl·fix-queue.tsv에도 vibe-coders의 release-workflow-failed 항목은 없고, 러너 로그의 실패는 전부 [improve] error — fetch 실패(base=main)였다. 즉 저장소 쪽에 고칠 코드가 없어 워크플로·스크립트는 손대지 않았고, 정책 파일 수정이 곧 수정이다. 본 과제인 가이드는 aidev/GUIDE-STANDARD.md를 따라 완성했다: scripts/guide-screenshots.sh가 임시 SQLite에 일회용 게이트웨이(AUTH_ENABLED·UI_APP_ENABLED)를 무작위 계정·비밀값으로 띄우고 가짜 업스트림으로 3사용자×4모델 트래픽을 흘린 뒤 web/scripts/guide-screenshots.mjs(Playwright, 시스템 Chrome)가 /app 24개 화면을 1440×900으로 찍어 docs/images/guide/*.png에 둔다(루프백 아니면 GUIDE_CAPTURE_DISPOSABLE=1 없이는 거부, 읽기만 하고 설정 변경 없음, 자격증명 리터럴 없음). docs/USER_GUIDE.md는 제품 소개·처음 5분·화면별 사용법·자주 하는 작업(기존 도구 연결 절 보존)·막혔을 때(pipeline.go의 실제 오류 코드·메시지 표)·용어로, docs/ADMIN_GUIDE.md는 구성 요소·설치(릴리즈 자산 명령)·환경 변수 전수 표(config.go에서 추출, 121개)·역할 표(auth.goroleScopes)·운영·장애 대응·보안 + 기존 /admin 탭 레퍼런스(2부)로 재구성했고 README 링크와 PDF(md2pdf.mjs) 둘 다 갱신해 정본이 하나다. 검증: 캡처 스크립트 2회 실행 성공(오류 화면 0), 참조 이미지 전부 존재 확인, corepack pnpm format:check·pnpm lint 통과(새 .mjs는 web/ 아래라 CI 대상), Go 변경 없음, git status에 빌드 산출물 없음(임베드 dist는 스크립트가 정리).
보류 아이디어: docs/OPERATIONS.md·SAFETY_GUIDE.md·ROUTING_GUIDE.md도 같은 캡처를 실어 문서군 통일 / 캡처 스크립트를 CI 선택 잡으로 돌려 화면 회귀(빈 화면·오류 상태) 감지 / MODEL_PRICING_KRW_PER_1M 키 소문자 정규화 부재(lookupPrice 정확 매칭 항상 실패) / audit.InferLanguages.addSignal이 높은 신뢰도 신호에 기존 evidence를 통째로 버리는 문제 / ValidateSQL이 MySQL LIMIT n, m·FETCH FIRST n ROWS ONLY를 인식하지 못해 상한 규칙 우회