자율 개선 일일 보고 — 2026-09-10
요약. 2026-09-10에 자율 개선 에이전트가 26개 프로젝트에서 62회차를 돌려 33건을 릴리즈하고 7건은 머지만 했으며 0건은 변경이 없었고 실패는 1건이다. 에이전트 시간 15시간 32분, 추정 비용 $356.91. 릴리즈: kanpic v0.241.0, pii-masker v1.0.19, ptium v1.69.32, releasedock v0.5.14, Clustara v0.9.280, AgentHub v0.241.0, Invenqor v0.2.30, ReSSO v0.9.76….
- 62회차
- 26프로젝트
- 32배포 준비 완료
- 1릴리즈 진행 중
- 7병합 완료
- 13검토 대기
- 3검증 실패
- 0변경 없음
- 6실행 오류
- $356.91비용
- 15시간 32분에이전트 시간
회차
| 시각 | 프로젝트 | 결과 |
|---|---|---|
| 00:13 | moyro | 검토 대기 guarded files, PR open PR #9 |
| 00:15 | moina | 검증 실패 CI failed, PR open PR #13 |
| 00:27 | kanpic | 배포 준비 완료 merged PR #14, released v0.241.0 |
| 00:39 | pii-masker | 배포 준비 완료 merged PR #16, released v1.0.19 |
| 00:43 | muni | 검토 대기 guarded files, PR open PR #10 |
| 00:51 | ptium | 배포 준비 완료 merged PR #16, released v1.69.32 |
| 01:10 | relio | 병합 완료 merged PR #16 |
| 01:17 | releasedock | 배포 준비 완료 merged PR #15, released v0.5.14 |
| 08:59 | weekly | 검증 실패 verify failed: build artifacts committed |
| 09:11 | Clustara | 배포 준비 완료 merged PR #19, released v0.9.280 |
| 09:36 | AgentHub | 배포 준비 완료 merged PR #20, released v0.241.0 |
| 10:01 | Vendra | 검토 대기 review held, PR open PR #117 |
| 10:07 | Invenqor | 배포 준비 완료 merged PR #15, released v0.2.30 |
| 10:16 | ReSSO | 배포 준비 완료 merged PR #16, released v0.9.76 |
| 11:18 | aiportal-front-admin | 검토 대기 guarded files, PR open PR #15 |
| 11:23 | aiportal-front | 병합 완료 merged PR #13, release skipped |
| 11:34 | ai-admin | 배포 준비 완료 merged PR #18, released v1.2.17 |
| 11:54 | aiportal-py | 병합 완료 merged PR #16, release skipped |
| 11:56 | dataworks | 배포 준비 완료 merged PR #15, released v0.9.50 |
| 12:07 | appstore | 배포 준비 완료 merged PR #12, released v2.5.6 |
| 12:22 | git-ctx | 검토 대기 review held, PR open PR #27 |
| 12:31 | jikim | 배포 준비 완료 merged PR #23, released v0.2.8 |
| 12:50 | igame | 릴리즈 진행 중 merged PR #12, released v0.7.9, ASSETS MISSING (release workflow FAILED), queued for fix |
| 13:41 | igame | 배포 준비 완료 fix-round: merged PR #13, released v0.7.10 |
| 14:09 | jupiq | 배포 준비 완료 merged PR #10, released v1.4.12 |
| 14:19 | kanpic | 배포 준비 완료 merged PR #15, released v0.242.0 |
| 14:22 | moina | 배포 준비 완료 merged PR #16, released v0.1.27 |
| 14:39 | pii-masker | 배포 준비 완료 merged PR #17, released v1.0.20 |
| 15:01 | muni | 배포 준비 완료 merged PR #11, released v0.36.0 |
| 15:22 | moyro | 배포 준비 완료 merged PR #10, released v0.2.27 |
| 15:41 | ptium | 검토 대기 review held, PR open PR #17 |
| 15:43 | relio | 검토 대기 needs approval (risk=medium, files=3), PR open PR #17 |
| 15:54 | releasedock | 배포 준비 완료 merged PR #16, released v0.5.15 |
| 16:01 | vibe-coders | 실행 오류 error: fetch |
| 16:01 | vibe-coders | 실행 오류 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 ) |
| 16:13 | umm | 검토 대기 review held, PR open PR #149 |
| 17:24 | weekly | 배포 준비 완료 merged PR #12, released v0.295.0 |
| 17:50 | Clustara | 배포 준비 완료 merged PR #20, released v0.9.281 |
| 17:58 | Invenqor | 배포 준비 완료 merged PR #16, released v0.2.31 |
| 18:10 | AgentHub | 배포 준비 완료 merged PR #21, released v0.242.0 |
| 18:35 | Vendra | 검토 대기 guarded files, PR open PR #118 |
| 18:42 | ai-admin | 배포 준비 완료 merged PR #19, released v1.2.18 |
| 18:58 | ReSSO | 배포 준비 완료 merged PR #17, released v0.9.77 |
| 19:12 | aiportal-front-admin | 병합 완료 merged PR #16, release skipped |
| 19:13 | aiportal-py | 병합 완료 merged PR #17, release skipped |
| 19:15 | aiportal-front | 병합 완료 merged PR #14, release skipped |
| 19:34 | git-ctx | 검토 대기 guarded files, PR open PR #28 |
| 19:36 | dataworks | 배포 준비 완료 merged PR #16, released v0.9.51 |
| 19:46 | appstore | 배포 준비 완료 merged PR #13, released v2.5.7 |
| 20:08 | jupiq | 배포 준비 완료 merged PR #11, released v1.4.13 |
| 20:10 | jikim | 배포 준비 완료 merged PR #24, released v0.2.9 |
| 20:33 | igame | 배포 준비 완료 merged PR #14, released v0.7.11 |
| 20:41 | kanpic | 실행 오류 hold: budget |
| 20:41 | moina | 실행 오류 hold: budget |
| 20:41 | moyro | 실행 오류 hold: budget |
| 20:48 | moina | 검토 대기 review held, PR open PR #17 |
| 21:02 | kanpic | 검토 대기 review held, PR open PR #16 |
| 21:21 | moyro | 병합 완료 merged PR #11, release hold (budget) |
| 21:54 | AgentHub | 검토 대기 review held, PR open PR #22 |
| 22:37 | Invenqor | 검증 실패 verify failed: secrets in diff |
| 22:58 | AgentHub | 실행 오류 fix-round: error: pr create |
| 00:02 | ReSSO | 배포 준비 완료 merged PR #18, released v0.9.78 |
비용·사용량
| 시각 | 프로젝트 | 단계 | 시간 | 턴 | 비용 | 토큰 입력/출력 | 종료 |
|---|---|---|---|---|---|---|---|
| 00:05 | kanpic | 개선 | 4분 | 35 | $1.94 | 1.8M / 16K | success |
| 00:07 | kanpic | review | 2분 | 13 | $0.70 | 465K / 7K | success |
| 00:08 | moina | 개선 | 7분 | 47 | $2.88 | 2.8M / 26K | success |
| 00:10 | moina | review | 2분 | 13 | $0.62 | 296K / 9K | success |
| 00:12 | moyro | 개선 | 12분 | 72 | $3.83 | 3.9M / 35K | success |
| 00:16 | kanpic | 릴리즈 | 2분 | 20 | $0.69 | 456K / 7K | success |
| 00:35 | pii-masker | 개선 | 4분 | 29 | $1.78 | 1.4M / 16K | success |
| 00:37 | pii-masker | review | 2분 | 12 | $0.60 | 283K / 8K | success |
| 00:38 | ptium | 개선 | 7분 | 30 | $2.12 | 1.5M / 27K | success |
| 00:39 | pii-masker | 릴리즈 | 1분 | 15 | $0.44 | 237K / 5K | success |
| 00:40 | ptium | review | 2분 | 18 | $0.74 | 553K / 8K | success |
| 00:42 | muni | 개선 | 12분 | 74 | $5.18 | 6.0M / 41K | success |
| 00:46 | ptium | 릴리즈 | 3분 | 24 | $1.09 | 819K / 9K | success |
| 01:06 | relio | 개선 | 5분 | 36 | $2.28 | 1.9M / 25K | success |
| 01:06 | releasedock | 개선 | 6분 | 44 | $2.24 | 2.1M / 20K | success |
| 01:07 | umm | 개선 | 6분 | 36 | $1.98 | 1.7M / 20K | success |
| 01:09 | relio | review | 3분 | 16 | $0.85 | 563K / 10K | success |
| 01:09 | releasedock | review | 3분 | 25 | $0.84 | 618K / 9K | success |
| 01:10 | umm | review | 3분 | 19 | $1.01 | 676K / 12K | success |
| 01:12 | releasedock | 릴리즈 | 2분 | 21 | $0.64 | 492K / 6K | success |
| 01:23 | umm | 릴리즈 | 3분 | 24 | $1.01 | 793K / 9K | success |
| 08:58 | Clustara | 개선 | 8분 | 48 | $3.34 | 3.2M / 29K | success |
| 08:59 | weekly | 개선 | 9분 | 57 | $4.46 | 4.9M / 34K | success |
| 09:03 | AgentHub | 개선 | 13분 | 90 | $6.87 | 8.6M / 47K | success |
| 09:05 | Clustara | review | 5분 | 17 | $0.97 | 642K / 11K | success |
| 09:09 | Clustara | 릴리즈 | 4분 | 28 | $1.39 | 1.2M / 10K | success |
| 09:10 | AgentHub | review | 7분 | 35 | $2.57 | 2.1M / 25K | success |
| 09:13 | AgentHub | 릴리즈 | 2분 | 20 | $0.85 | 627K / 6K | success |
| 09:46 | Invenqor | 개선 | 6분 | 43 | $2.25 | 2.2M / 20K | success |
| 09:50 | Invenqor | review | 4분 | 24 | $1.20 | 1000K / 12K | success |
| 09:52 | ReSSO | 개선 | 11분 | 78 | $6.07 | 7.4M / 41K | success |
| 09:54 | Vendra | 개선 | 14분 | 94 | $7.12 | 8.6M / 55K | success |
| 09:56 | ReSSO | review | 4분 | 22 | $1.15 | 912K / 12K | success |
| 10:01 | Vendra | review | 6분 | 40 | $2.38 | 2.3M / 22K | success |
| 10:01 | Invenqor | 릴리즈 | 9분 | 65 | $3.21 | 3.7M / 23K | success |
| 10:05 | ReSSO | 릴리즈 | 6분 | 32 | $1.52 | 1.3M / 10K | success |
| 10:29 | git-ctx | 릴리즈 | 8분 | 41 | $2.15 | 2.2M / 14K | success |
| 10:56 | jikim | 릴리즈 | 6분 | 36 | $1.74 | 1.8M / 10K | success |
| 11:05 | jupiq | 릴리즈 | 2분 | 22 | $0.90 | 692K / 7K | success |
| 11:17 | ai-admin | 개선 | 5분 | 40 | $1.98 | 1.9M / 16K | success |
| 11:17 | aiportal-front-admin | 개선 | 5분 | 30 | $2.02 | 1.7M / 19K | success |
| 11:17 | aiportal-front | 개선 | 5분 | 47 | $2.35 | 2.3M / 20K | success |
| 11:21 | ai-admin | review | 3분 | 25 | $1.11 | 736K / 13K | success |
| 11:21 | aiportal-front | review | 4분 | 24 | $1.23 | 873K / 14K | success |
| 11:23 | aiportal-front | 릴리즈 | 1분 | 10 | $0.39 | 227K / 4K | success |
| 11:26 | ai-admin | 릴리즈 | 2분 | 21 | $0.91 | 801K / 6K | success |
| 11:45 | dataworks | 개선 | 4분 | 39 | $1.91 | 1.8M / 16K | success |
| 11:48 | dataworks | review | 2분 | 17 | $0.70 | 559K / 6K | success |
| 11:49 | aiportal-py | 개선 | 9분 | 62 | $4.51 | 4.8M / 42K | success |
| 11:49 | appstore | 개선 | 9분 | 76 | $5.59 | 7.2M / 34K | success |
| 11:52 | aiportal-py | review | 3분 | 15 | $1.09 | 633K / 11K | success |
| 11:53 | appstore | review | 3분 | 23 | $1.03 | 700K / 12K | success |
| 11:53 | dataworks | 릴리즈 | 5분 | 38 | $2.05 | 2.2M / 12K | success |
| 11:54 | aiportal-py | 릴리즈 | 1분 | 16 | $0.52 | 260K / 6K | success |
| 11:59 | appstore | 릴리즈 | 4분 | 40 | $1.76 | 1.8M / 10K | success |
| 12:17 | git-ctx | 개선 | 6분 | 24 | $1.78 | 1.3M / 19K | success |
| 12:18 | jikim | 개선 | 7분 | 63 | $3.84 | 4.3M / 29K | success |
| 12:18 | igame | 개선 | 8분 | 72 | $3.88 | 4.4M / 32K | success |
| 12:21 | jikim | review | 4분 | 20 | $1.17 | 803K / 14K | success |
| 12:22 | git-ctx | review | 3분 | 20 | $1.14 | 805K / 14K | success |
| 12:24 | jikim | 릴리즈 | 2분 | 22 | $1.05 | 843K / 6K | success |
| 12:25 | igame | review | 6분 | 38 | $2.37 | 2.3M / 22K | success |
| 12:28 | igame | 릴리즈 | 3분 | 32 | $1.74 | 1.6M / 9K | success |
| 13:09 | igame | 개선 | 9분 | 58 | $3.01 | 3.2M / 26K | success |
| 13:14 | igame | review | 5분 | 32 | $1.05 | 823K / 14K | success |
| 13:20 | igame | 릴리즈 | 5분 | 33 | $1.70 | 1.6M / 12K | success |
| 13:56 | moina | 개선 | 6분 | 45 | $2.37 | 2.3M / 21K | success |
| 13:57 | jupiq | 개선 | 6분 | 50 | $3.56 | 3.8M / 24K | success |
| 13:58 | kanpic | 개선 | 8분 | 56 | $3.56 | 3.7M / 32K | success |
| 13:59 | jupiq | review | 2분 | 12 | $0.60 | 331K / 9K | success |
| 14:01 | moina | review | 5분 | 27 | $1.34 | 878K / 17K | success |
| 14:03 | kanpic | review | 4분 | 26 | $1.28 | 1.1M / 14K | success |
| 14:03 | jupiq | 릴리즈 | 2분 | 22 | $0.99 | 885K / 6K | success |
| 14:07 | moina | 릴리즈 | 3분 | 28 | $1.19 | 1.1M / 8K | success |
| 14:10 | kanpic | 릴리즈 | 2분 | 27 | $1.02 | 880K / 9K | success |
| 14:34 | pii-masker | 개선 | 4분 | 31 | $1.56 | 1.3M / 15K | success |
| 14:36 | moyro | 개선 | 5분 | 41 | $1.96 | 1.9M / 18K | success |
| 14:36 | pii-masker | review | 2분 | 16 | $0.68 | 489K / 8K | success |
| 14:39 | pii-masker | 릴리즈 | 2분 | 22 | $0.63 | 507K / 6K | success |
| 14:41 | moyro | review | 5분 | 39 | $1.79 | 1.8M / 17K | success |
| 14:43 | muni | 개선 | 13분 | 91 | $6.75 | 8.7M / 44K | success |
| 14:49 | muni | review | 7분 | 25 | $2.22 | 1.6M / 24K | success |
| 14:52 | moyro | 릴리즈 | 3분 | 25 | $1.11 | 979K / 7K | success |
| 14:53 | muni | 릴리즈 | 3분 | 32 | $1.22 | 1.1M / 11K | success |
| 15:37 | ptium | 개선 | 7분 | 42 | $2.52 | 2.3M / 26K | success |
| 15:38 | relio | 개선 | 8분 | 42 | $2.97 | 2.6M / 35K | success |
| 15:39 | releasedock | 개선 | 9분 | 62 | $4.12 | 4.5M / 33K | success |
| 15:41 | ptium | review | 4분 | 23 | $1.31 | 977K / 15K | success |
| 15:43 | relio | review | 5분 | 26 | $1.56 | 1.3M / 17K | success |
| 15:43 | releasedock | review | 4분 | 25 | $1.30 | 939K / 14K | success |
| 15:49 | releasedock | 릴리즈 | 6분 | 34 | $1.33 | 1.2M / 13K | success |
| 16:01 | vibe-coders | 개선 | 0분 | 0 | $0.00 | 0 / 0 | unknown |
| 16:08 | umm | 개선 | 8분 | 50 | $3.17 | 3.3M / 26K | success |
| 16:12 | umm | review | 4분 | 23 | $1.39 | 1.1M / 15K | success |
| 16:40 | weekly | 개선 | 42분 | 80 | $5.85 | 7.2M / 40K | success |
| 16:47 | weekly | review | 6분 | 52 | $2.46 | 2.5M / 20K | success |
| 17:10 | weekly | 릴리즈 | 5분 | 28 | $1.29 | 1.1M / 10K | success |
| 17:37 | Invenqor | 개선 | 7분 | 46 | $2.35 | 2.2M / 23K | success |
| 17:39 | AgentHub | 개선 | 9분 | 57 | $3.90 | 4.2M / 29K | success |
| 17:40 | Clustara | 개선 | 10분 | 65 | $4.01 | 4.2M / 38K | success |
| 17:42 | Invenqor | review | 5분 | 25 | $1.28 | 1.0M / 15K | success |
| 17:43 | Clustara | review | 2분 | 18 | $0.94 | 740K / 8K | success |
| 17:45 | AgentHub | review | 6분 | 34 | $1.57 | 1.1M / 20K | success |
| 17:48 | Clustara | 릴리즈 | 5분 | 30 | $1.32 | 1.1M / 12K | success |
| 17:48 | AgentHub | 릴리즈 | 3분 | 25 | $1.03 | 881K / 8K | success |
| 17:52 | Invenqor | 릴리즈 | 9분 | 65 | $2.96 | 3.3M / 23K | success |
| 18:27 | ai-admin | 개선 | 7분 | 48 | $2.75 | 2.7M / 23K | success |
| 18:30 | ai-admin | review | 2분 | 12 | $0.77 | 418K / 8K | success |
| 18:34 | ReSSO | 개선 | 14분 | 56 | $4.08 | 4.4M / 32K | success |
| 18:35 | Vendra | 개선 | 15분 | 95 | $8.05 | 10.0M / 57K | error_max_budget_usd |
| 18:35 | ai-admin | 릴리즈 | 2분 | 23 | $0.97 | 854K / 6K | success |
| 18:37 | ReSSO | review | 3분 | 16 | $0.96 | 642K / 10K | success |
| 18:47 | ReSSO | 릴리즈 | 6분 | 33 | $1.62 | 1.5M / 11K | success |
| 19:08 | aiportal-front | 개선 | 8분 | 54 | $3.57 | 3.7M / 33K | success |
| 19:08 | aiportal-front-admin | 개선 | 8분 | 62 | $4.35 | 4.8M / 34K | success |
| 19:08 | aiportal-py | 개선 | 8분 | 42 | $3.35 | 3.0M / 37K | success |
| 19:11 | aiportal-front-admin | review | 2분 | 14 | $0.84 | 540K / 9K | success |
| 19:11 | aiportal-py | review | 3분 | 20 | $0.95 | 601K / 10K | success |
| 19:12 | aiportal-front-admin | 릴리즈 | 1분 | 13 | $0.48 | 282K / 4K | success |
| 19:13 | aiportal-front | review | 5분 | 20 | $1.48 | 964K / 19K | success |
| 19:13 | aiportal-py | 릴리즈 | 1분 | 15 | $0.47 | 265K / 6K | success |
| 19:15 | aiportal-front | 릴리즈 | 1분 | 14 | $0.44 | 262K / 5K | success |
| 19:25 | dataworks | 개선 | 5분 | 35 | $1.66 | 1.6M / 13K | success |
| 19:29 | dataworks | review | 3분 | 21 | $0.92 | 742K / 9K | success |
| 19:29 | appstore | 개선 | 9분 | 84 | $5.35 | 6.8M / 32K | success |
| 19:32 | git-ctx | 개선 | 12분 | 49 | $3.94 | 3.8M / 41K | success |
| 19:34 | dataworks | 릴리즈 | 5분 | 30 | $1.46 | 1.4M / 11K | success |
| 19:34 | appstore | review | 6분 | 29 | $1.64 | 982K / 22K | success |
| 19:39 | appstore | 릴리즈 | 5분 | 33 | $1.49 | 1.4M / 11K | success |
| 19:56 | jupiq | 개선 | 6분 | 42 | $2.48 | 2.3M / 23K | success |
| 19:57 | igame | 개선 | 7분 | 42 | $2.49 | 2.1M / 31K | success |
| 19:58 | jikim | 개선 | 7분 | 54 | $3.92 | 4.3M / 29K | success |
| 19:59 | jupiq | review | 2분 | 13 | $0.67 | 296K / 10K | success |
| 20:01 | jikim | review | 3분 | 14 | $0.95 | 529K / 13K | success |
| 20:01 | igame | review | 3분 | 28 | $1.23 | 865K / 12K | success |
| 20:02 | jupiq | 릴리즈 | 2분 | 21 | $0.86 | 744K / 6K | success |
| 20:04 | jikim | 릴리즈 | 2분 | 23 | $1.17 | 944K / 7K | success |
| 20:08 | igame | 릴리즈 | 5분 | 33 | $1.82 | 1.7M / 12K | success |
| 20:45 | moina | 개선 | 4분 | 29 | $1.74 | 1.5M / 17K | success |
| 20:46 | kanpic | 개선 | 6분 | 42 | $2.39 | 2.3M / 23K | success |
| 20:48 | moina | review | 2분 | 12 | $0.63 | 367K / 8K | success |
| 21:02 | kanpic | review | 0분 | 0 | $0.00 | 0 / 0 | unknown |
| 21:08 | moyro | 개선 | 29분 | 84 | $5.77 | 7.2M / 43K | success |
| 21:12 | moyro | review | 4분 | 23 | $1.36 | 1.0M / 15K | success |
| 21:48 | AgentHub | 개선 | 19분 | 85 | $8.06 | 9.4M / 58K | error_max_budget_usd |
| 21:54 | AgentHub | review | 5분 | 41 | $2.51 | 2.4M / 20K | success |
| 22:36 | Invenqor | 개선 | 39분 | 162 | $15.14 | 21.6M / 76K | success |
| 22:58 | AgentHub | 개선 | 19분 | 147 | $12.99 | 19.0M / 64K | success |
| 23:32 | ReSSO | 개선 | 34분 | 160 | $15.61 | 22.1M / 83K | success |
| 23:37 | ReSSO | review | 6분 | 43 | $3.16 | 3.3M / 20K | success |
| 23:49 | ReSSO | 릴리즈 | 10분 | 45 | $2.43 | 2.5M / 18K | success |
무엇을 왜 바꿨나 (원장 발췌)
moyro
- 선택: 검색어가 LIKE 패턴으로 해석되던 문제 수정 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약: 사용자·채널·팀·파일·PAT·감사로그 검색이 모두 클라이언트가 보낸 term 을 그대로
LIKE/ILIKE패턴에 이어붙여,%는 “전부”,_는 “아무 글자 하나”,\는 “다음 글자 이스케이프” 로 새어 나갔다 — 맨%하나면 볼 수 있는 계정·채널·팀·파일 목록이 통째로 나오고(이메일 포함),kim_lead검색이kimxlead를,@team_notes가teamxnotes를 함께 잡았으며, 반대로 이름에 진짜%가 들어간 채널(“50% 할인”)은 이름을 그대로 쳐도 찾을 수 없었다.emojis는 예전 회차에 strpos 로 피해 갔지만 이 경로들은 대소문자 무시 prefix/contains 매칭이라 strpos 로 바꿀 수 없어 ILIKE 를 유지하고 term 을 이스케이프했다. 12개 call site 가 각자 와일드카드를 손으로 붙이던 것이 누락이 번진 원인이라store.EscapeLike와LikeContains/LikePrefix로 한 곳에 모았다(PostgreSQL 기본 LIKE 이스케이프가 백슬래시라 ESCAPE 절은 불필요). 파일 검색 핸들러 2곳은 SQL 안에서'%' || $1 || '%'로 붙이던 것을 완성된 패턴 바인딩으로 바꿨고,audit.List는 한 파라미터가 패턴 베이스와 “필터 없음” 센티널을 겸하고 있어 이스케이프된 패턴만 별도 파라미터로 분리했다. 이스케이퍼 표 테스트 2건과 채널 검색·멘션 자동완성·사용자 디렉터리에 대한 PostgreSQL 통합 테스트 3건을 추가했다(각각 와일드카드 해석이었다면 함께 걸렸을 미끼 행을 심어 둔다). 로컬 postgres:16-alpine 컨테이너로go vet ./...,MOYRO_TEST_POSTGRES_DSN설정 후go test -race -p 1 ./...(전 패키지 통과),scripts/check-source-sizes.sh로 검증했고, 이스케이퍼를 빈 Replacer 로 무력화해 새 테스트 5건이 모두 실제로 실패하는 것(맨%가 채널 4개·멤버 3명·계정 3개를 반환)까지 확인했다. 웹 변경이 없어 webapp 빌드는 손대지 않았다. - 보류 아이디어: (1)
webapp/e2e와vite.config.ts가include: ["src"]밖이라 어떤 타입체크도 받지 않음 — 별도 tsconfig 와@types/node명시가 필요. (2)getPreferenceByName이 모든 오류를 404로 뭉개 DB 장애를 “설정 없음”으로 위장 — 미머지 브랜치 auto/2026-09-07-0240 이 같은 파일을 손대므로 그 뒤에. (3)post_reminders에 사용자당 상한이 없어 무한 적재 가능 — 미머지 브랜치 auto/2026-09-08-2109 가 같은 파일을 크게 고침. (4)sidebar.Update가 사용자가 멤버가 아닌 채널 ID도 카테고리에 기록하도록 허용. (5)postacks/savedposts통합 테스트 보강 — 명확한 결함은 없고 순수 회귀 방지.
moina
- 선택: 업로드한 WebP 이미지의 실제 픽셀 크기를 저장·응답 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
imageDimensionsFrom이image.DecodeConfig만 쓰는데 표준 라이브러리에는 WebP 디코더가 없어, allowlist가 허용하고 웹 앱도 첨부를 받는image/webp만 언제나width=0,height=0으로 저장됐습니다(model.Media의omitempty때문에 media·Moin 응답에서 크기 필드 자체가 빠졌고, OpenAPI가mimeType예시로 쓰는 형식이 바로 WebP였습니다) — API client는 그림이 도착하기 전 자리를 잡을 수 없었습니다. 새 의존성 없이 이미 읽어 둔 sniff 앞부분에서 RIFF 컨테이너의 첫 chunk를 읽는webpDimensions를 추가해 VP8(lossy, key frame의 sync code 확인 후 14비트 크기), VP8L(서명 뒤 14비트 크기-1), VP8X(flags·reserved 뒤 24비트 canvas 크기-1, 알파·애니메이션) 세 종류를 모두 처리하고, WebP가 아니거나 헤더가 잘린 데이터는 기존처럼 표준 디코더 경로로 넘겨 0,0을 유지합니다. 검증은 libwebp가 만든 실제 WebP 3개(23x17 lossy·31x13 lossless·45x29 알파)로 크기를 대조하고 잘린 헤더·다른 RIFF·PNG 등 7케이스가 거절되는지 확인하는 새 테스트 3개(11케이스) 포함go test -race ./...전체 통과,make fmt·make check·go vet·staticcheck 통과(이 환경에 PostgreSQL이 없어 integration test는 skip 상태, frontend 무변경이라 ESLint·vitest 생략). - 보류 아이디어:
updatePost가 DB 오류를 409not_editable로 보고해 원인을 감춤(가치 2 / 위험 1 / S) ·safeFilename이 확장자와 판정한 MIME의 불일치를 그대로 둬 JPEG이photo.png로 저장·다운로드됨(가치 2 / 위험 2 / S) · Makefiletest가 CI와 달리-race미사용(가치 2 / 위험 1 / S) · 웹 앱이 API가 주는 미디어width·height를 쓰지 않아 Flow에서 이미지 로드 중 레이아웃이 밀림(가치 3 / 위험 2 / M)
kanpic
- 선택: 내보낸 CSV·TSV 가 스스로 UTF-8 이라고 밝힌다 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약: 엑셀은 UTF-8 표시(BOM)가 없는 CSV 를 그 컴퓨터의 기본 코드 페이지로 읽는데, 내려받은 표를 여는 길은 대개 두 번 누르는 것이고 그 길에는 인코딩을 물어보는 자리가 없어 한글 칸이 통째로 깨져 보였다 — 관리자 로그(
/api/v1/admin/logs.csv)와 AI 기록을 내보내는 자리는 이미 같은 이유로 표시를 붙이고 있었으므로, 정작 한글이 가장 많이 담기는 워크북 내보내기만 빠져 같은 제품이 내주는 두 파일이 엑셀에서 다르게 보이던 셈이다.exportDelimited가 csv 와 tsv 모두 앞에 표시를 붙이게 하고, 이미 그것을 떼고 있던parseDelimited와 같은utf8BOM을 쓰게 해 붙이는 자리와 떼는 자리를 한 값으로 묶었다(IMPORTDATA 의internal/external도 이미 뗀다). 검증: 새 테스트TestDelimitedExportMarksItselfAsUTF8(csv·tsv 두 갈래 — 표시가 붙는지와, 되돌려 읽은 첫 칸이 표시를 이고 있지 않은지를 함께 본다)와 표시가 붙으면서 깨진 기존TestCSVExportEscapesFormulaInjection수정,gofmt -l,go vet ./...,go build ./...,go test ./...(전체 통과),scripts/check-release-docs.sh,scripts/check-commit-identities.sh. 커밋 1개(b4eaa51). 서버가 내주는 바이트라 웹·문서는 손대지 않았고(문서는 내보내기의 인코딩을 말한 적이 없다) npm 검사도 돌리지 않았다. -
보류 아이디어:
csvNumber자리(formula.DecimalNumber)가 IMPORTDATA 의"1,200"·"12%"·통화 기호를 글자로 남긴다 / 서버formatValue는 글자 “123” 을 수로 읽고 격자는 글자로 보아 숫자처럼 생긴 글자 값에서 두 곳이 갈린다 / IMPORTDATA 의 CSV 파싱 실패(#VALUE!)도cache_seconds만큼 담겨 원격이 파일을 고쳐도 회복이 늦다 / 파이프(|)로 가른 파일을 후보에 넣을지 — 로그·DB 덤프가 흔히 쓰는데 지금은 한 열로 들어온다 / 가져오기가 UTF-16 로 저장한 CSV 를 “CSV must be UTF-8 encoded” 로 거절한다(엑셀의 ‘유니코드 텍스트’ 저장이 그 꼴이고 BOM 으로 알아볼 수 있다) /?의 자리 맞추기 빈칸을 그리지 않아# ??/??의 한 자리 분자가 자릿수를 맞추지 못한다 - 릴리즈: v0.241.0 (2026-09-10, run 2026-09-10-000118-kanpic-improve)
pii-masker
- 선택: 재기동이 job 파일 보존 기한을 밀어내던 문제 수정 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
jobs.Store.load()가 하드 킬로queued/running상태로 남은 job을job_interruptedfailed로 바꾸면서UpdatedAt을 현재 시각으로 올리고 그 변경을 디스크에 쓰지 않아서, 다음 기동 때 다시 디스크의 원래 상태를 읽고 또 시각을 올리는 일이 반복됐습니다. 보존 기간(기본 24시간)이UpdatedAt기준이므로, 보존 기간보다 자주 재시작하는 서버에서는 중단된 job의 업로드 원본(=마스킹해 달라고 받은 개인정보 원문)이 영구히 지워지지 않았습니다. 이제 중단 표시는 job이 마지막으로 바뀐 시각을 그대로 유지하고, 그 전환을persistLocked로 한 번 디스크에 반영해(실패해도 무해) 다음 기동에서 되풀이되지 않게 했습니다.load()가 맵을 직접 채우므로s.mu를 명시적으로 잡도록 바꿨습니다. 검증은internal/jobs테스트 2개(48시간 전runningjob이 재로드 후 시각을 유지하고DeleteExpired(now-24h)로 실제 삭제됨 / 재로드 후 디스크의job.json이failed+job_interrupted이고 시각도 그대로)를 추가했고,gofmt -l(무출력)·go vet ./...·go build ./...·go test -count=1 ./...·go test -race -count=3 ./...전부 통과, 이전 동작(시각 갱신 + 미영속)으로 임시 되돌려 새 테스트 2개가 실제로 실패하는 것까지 확인했습니다. README의 종료 동작 문단에도 보존 기한이 밀리지 않는다는 점을 적었습니다. -
보류 아이디어: 동기 슬롯 대기열의 메모리 상한(대기 요청이 이미 읽은 업로드 바이트를 보유) / 업스트림 응답 8MB 절단이
decode_failed로 잘못 보고됨(별도 오류 코드 필요) /internal/config의 나머지 순수 함수(normalizeAllowHosts,normalizeEndpointURL,normalizePIILang/Schema,envInt/envNonNegativeInt/envBool) 단위 테스트 //v1/jobs/{id}/result가GET만 라우팅되어HEAD프로브가 405를 받음 / 업로드 파일명 유니코드 정규화(NFC) 부재로 macOS의 NFD 한글 이름이 그대로 디스크·메타데이터에 기록됨 - 릴리즈: v1.0.19 (2026-09-10, run 2026-09-10-003120-pii-masker-improve)
muni
- 선택:
.hwpx인용문·코드 블록 왕복 (가치 3 / 위험 2 / 작업량 M) - 결과: 성공
- 요약: 한글에는 인용문도 코드 블록도 없어서 쓰기는 인용을 들여쓴 문단으로, 코드를 고정폭 문단 여러 개로 흘려보내고
blockContext.quote는 아무도 읽지 않는 죽은 값이었고, 읽는 쪽에는 되살릴 단서가 없었습니다 — 들여쓰고 고정폭으로 그린 문단은 리더에게 그저 문단입니다..docx쓰기가 하는 것과 같이 머리(Contents/header.xml)에 스타일 둘을 심고(인용/Quote,코드/Code) 문단이 번호로 가리키게 했으며, 이름을 무엇으로 읽을지는hangul.BlockStyle한 곳에 두어 워드에서 옮겨온 이름(Source Code따위)도 받습니다. 인용문은 목록처럼 시작도 끝도 표시가 없는 문단의 줄이라 한 블록으로 모으고, 코드 블록은 한 문단 안에 줄바꿈으로 넣습니다 — 줄마다 문단이면 끝을 알릴 것이 없어 나란한 코드 블록 둘이 하나로 돌아옵니다. 목록 항목·제목은 스타일을 받지 않고(항목의 줄이 통째로 인용이 됩니다), 인용이 여백에서 한 칸 물러난 것은 그리는 방법이지 글쓴이의 들여쓰기가 아니므로 읽을 때 덜어냅니다(그러지 않으면 왕복마다 한 칸씩 밀립니다). 검증은 변경 전에 실패하는 것을 확인한 새 테스트 셋(손으로 지은 파일의 인용 묶기·코드 줄바꿈, 왕복)과gofmt -l·go vet ./...·go test ./...전체 통과. 덤으로 여덟 회차 내리 손으로 돌린gofmt·go vet을 CI 단계로 넣었습니다(YAML 을 파싱해 단계 차례를 확인). 커밋 59cb89f, 7f6ac57. - 보류 아이디어:
.hwpx머리글 칸의 기본 음영 (2/3/S) —.docx는 F3F4FA 를 까는데.hwpx는 굵게뿐 /.hwpx코드 블록의 언어가 왕복에서 사라짐 (2/2/S) — mermaid 코드 블록이 도형으로 다시 그려지지 않음. 스타일 이름에 언어를 담을 수 있는지 / 그림의 그린 크기를 markdown 왕복에서도 지키기 (2/2/S) — 붙임말이나 HTML 태그로 남길 수 있는지 /.hwp표 캡션의 위치(위/아래) 읽기 (2/2/S) — 지금은 언제나 표 뒤에 붙임 /.hwp/.hwpx표 행 높이 읽기 (2/2/S) — 편집기가 행 높이를 들지 않아 값어치가 낮음
ptium
- 선택: 시트가 한도에 걸렸을 때 잘린 시트 이름을 말하지 않고 슬라이드 수를 시트 수처럼 말하는 문제 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
internal/docs/workbook.go의readWorkbook이 슬라이드 한도(maximumSlides30)에 걸리면 “시트가 많아 앞 30개만 가져왔습니다” 한 줄만 남기고break했다 — 빠진 시트 이름을 하나도 대지 않고, 30은 시트 수가 아니라 슬라이드 수라서 시트마다 3장씩 차는 통합 문서는 열두 시트 중 열 시트만 읽고도 “앞 30개(시트)를 가져왔다”고 말했다. 이제 한도를 넘긴 뒤에도 시트를 계속 훑어 빠진 이름을 모으고, 숨긴 시트 경고와 같은 말투로 “슬라이드가 많아 시트(…)는 가져오지 않았습니다. 나눠서 올리면 전부 가져옵니다”(docx 경로가 이미 하는 안내)라고 말한다. 이름은 다섯 개까지 대고 나머지는 “외 n개”로 세며(sheetsNamed), 그 목록 함수를 숨긴 시트 경고·오류와도 공유한다. 빈 시트는 애초에 슬라이드가 될 수 없었으므로trimGrid로 걸러 이름을 대지 않는다. 검증:sheetlimit_test.go(통합 문서 4가지 — 시트 34개·시트마다 3장씩 12개·이름이 넘치는 40개·한도 뒤의 빈 시트 + 한도에 딱 맞는 문서가 경고하지 않는지 1가지 +sheetsNamed6가지)를 추가하고go vet ./...와go test -race ./...22개 패키지 전부 통과.make test의 웹 단계(tsc/vite)는 이 워크트리에 node_modules 가 없어 실행하지 못했고, 변경은 Go 코드에만 있다. 커밋 bb8efaf. 버전·릴리스 노트는 이번 세션 범위가 아니라 손대지 않았다. -
보류 아이디어: 분류기가 평범한 공백(“1 200”)을 숫자로 보아 렌더러가 1 로 읽는 오래된 불일치 — amountOf 브랜치(auto/2026-09-09-1131) 머지 뒤에 (3/2/S) ·
seriesFromRows가 표의 값을 쉼표로 다시 쪼개 “1,200” 을 1 과 200 두 점으로 읽는 문제 (3/3/M) · 회계 음수를 막대가math.Abs로 그려 부호가 사라지는 문제 (3/3/M) ·gridOf가 행의r을 보지 않고trimGrid이 빈 행을 지워서 출처 행 번호가 어긋나는 문제 — 미머지 브랜치 auto/2026-09-07-0330 머지 뒤에 (3/3/M) · 숨긴 행·열(hidden="1")을 그대로 표에 넣는 문제 (2/2/M) - 릴리즈: v1.69.32 (2026-09-10, run 2026-09-10-003125-ptium-improve)
relio
- 선택: 금액·확률 급락 규칙을 설정된 기간 안에서만 세기 (가치 3 / 위험 1 / 작업량 M)
- 결과: 성공
- 요약: 보류 아이디어였던 “DealHealth 규칙을 순수 함수로 떼어내기”를 하다가 그 규칙 안에서 실제 버그를 찾았습니다. 시드 데이터는
AMOUNT_DROP에{"percent":30,"days":90},PROBABILITY_DROP에{"points":20,"days":90}로 기간을 선언하고 관리자 콘솔은 그 JSON 을 편집하게 하며 “임계값을 변경하면 화면, API 와 MCP 분석에 즉시 반영됩니다” 라고 적혀 있지만, 두 규칙의days를 읽는 코드가 없어 질의가 돌려준 1년치 이력 전부를 훑었습니다(같은 이력을 쓰는CLOSE_DATE_SLIPPAGE만 기간을 지켰습니다). 그래서 열 달 전에 금액이 반토막 났다가 그 뒤로 안정된 딜은 오늘도 “금액 급감” 으로 남았고, 콘솔에서 기간을 좁혀도 아무 변화가 없었습니다. 이제 두 규칙이 자기 기간 안의 변경만 세고, 이력 조회 자체도 규칙들이 요구하는 가장 넓은 기간까지 물러나므로(기존 365일이 하한이라 좁아지는 경우는 없음) 1년보다 긴 기간이 조용히 잘리지 않습니다. 기간을 한정할 수 없는 값(0·음수·숫자가 아닌 값)은 규칙을 꺼버리는 대신 시드 기본값으로 물러납니다. 규칙 switch 는DealHealth밖으로 나와(rule, facts)의 순수 함수evaluateHealthRule이 되었고,healthFacts가 규칙이 읽는 나머지(이력·의사결정자 수·챔피언 수·단계 상한·now·today)를 모읍니다. 검증은 새 테스트 11개(규칙 8종 전부의 발화·침묵 경계, 단계 상한이 기본값을 이기고 0 은 이기지 못함, 앞당긴 날짜는 지연이 아님, 기간 밖 변경 무시와 좁힌 기간 반영, 알 수 없는 규칙 유형,windowDays폴백 4종, 이력 조회 기간이 가장 넓은 설정을 덮는지)를 옛 구현으로 되돌리면 실제로 실패하는지 확인 — 전체 이력 훑기·365일 고정·폴백 제거로 되돌리자 3건 실패, 리팩터링 회귀 확인용으로Before(facts.Today)→Before(now)와 단계 상한 조건을 망가뜨리자 2건 실패 — 한 뒤go test -race ./...,go vet ./...,gofmt -l, web typecheck·build,check-env-contract.sh,check-static-assets.sh,previous-release-tag-test.sh전체 통과. 커밋 574c081. - 보류 아이디어: 네 intelligence 필터의
Cursor필드는 어떤 질의도 쓰지 않고 어떤 핸들러도 채우지 않는 죽은 코드 — 상위 200건 뒤를 볼 방법이 없음 (가치 3 / 위험 1 / M) ·list_activities·get_contracts·list_quotations등 나머지 MCP 목록 도구는 페이징 자체가 없음 (가치 3 / 위험 2 / M) ·DealsAtRisk는 영업기회 200건 각각에DealHealth를 돌려 딜마다 질의 4번을 쳐 최대 800회 왕복 — 이번에 뺀 순수 함수 덕에 팩트를 한 번에 모아 넣는 구조가 가능해짐 (가치 3 / 위험 2 / M) · 로컬 로그인이 꺼져 있을 때/auth/login이 403 과 401 을 갈라 돌려주는 두 번째 열거 통로 (가치 3 / 위험 2 / S) · CI 에gofmt -l검사 추가 — 지금은 포맷 위반이 CI 를 통과함 (가치 2 / 위험 1 / S)
releasedock
- 선택: 실행 기록 INSERT 실패를 전부 “이미 실행 중”으로 보고하던 문제 수정 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
createSimpleRun은 업로드를 대상 디렉터리에 안착시킨 뒤simple_runs에 행을 넣는데, 그Exec이 어떤 이유로 실패하든409 simple_run_active+ “이 대상에서 이미 실행 중인 작업이 있습니다” 로 답했습니다. 부분 유니크 인덱스 위반(23505)만이 업로더가 실제로 기다리면 풀리는 상황이고, 사용자 행이 사라진 actor 의 외래 키 위반·연결 끊김·요청 취소는 모두 서버 쪽 실패입니다 — 그런데도 같은 문구가 나가고 에러는 아무 데도 기록되지 않아, 폐쇄망 운영자는 실행 목록에 아무것도 없는 대상을 두고 존재하지 않는 실행이 끝나기를 기다리게 됩니다.errors.As로*pgconn.PgError를 꺼내 코드가 23505 일 때만 409 를 유지하는isUniqueViolation을 두고(전체 모드releases.go의isSerializationFailure와 같은 방식), 나머지는s.log.Error("could not record a simple run", ...)로 남긴 뒤500 database_error/ “실행 기록을 저장하지 못했습니다” 로 나누었습니다. 두 경로 모두 기존 defer 가 그대로 동작해 스테이징 파일은 지워지고 동시 실행 슬롯도 반납됩니다. 새 테스트 3건을simple_conflict_test.go에 추가했는데, 순수 단위 테스트는 23505·래핑된 23505 만 참이고 23503·23514·40001·context.Canceled·일반 오류·nil 은 거짓임을 확인하고, 스키마 격리 통합 테스트 두 건은 실제 핸들러로 멀티파트를 올려 (1) RUNNING 실행이 있으면 409simple_run_active, (2) users 에 없는 actor 로는 500database_error가 나가는지, 그리고 두 경우 모두 대상 디렉터리가 비고 슬롯이 0 이며 실행 행이 남지 않는지 검사합니다. 고치기 전 코드로 되돌려 (2) 가status = 409로 실패하는 것도 확인했습니다. 로컬 도커 PostgreSQL 16 으로TEST_POSTGRES_DSN을 채워 backend/runnergo vet·go test ./...(통합 테스트 포함),npm ci,npm test -- --run(90건),npm run build(tsc -b 포함) 을 모두 통과했고web/dist는 커밋 전에 지웠습니다. 사용자에게 보이는 동작 설명이 바뀌지 않아 docs 는 손대지 않았고, VERSION 은 릴리즈 세션의 몫이라 건드리지 않았습니다. - 보류 아이디어: 전체 모드의 릴리즈 업로드(
releases.go)와 프리셋 업로드(presets.go)도ParseMultipartForm을 써서 같은 임시 사본이 생깁니다 (가치 3 / 위험 3 / M). - 보류 아이디어: CI 와 Makefile 에
go vet(또는 golangci-lint) 단계가 없어 정적 검사가 매 세션 수동입니다 (가치 3 / 위험 1 / S). - 보류 아이디어: 진행 중 실행의 SSE 중복 제거가 줄마다 전체 배열을 훑어 긴 로그에서 O(n²) 이 됩니다 — 마지막 id 비교로 충분합니다 (가치 2 / 위험 1 / S).
- 보류 아이디어:
simpleRunLogger.append가 빈 payload 를 저장하지 않아 스크립트 출력의 빈 줄(문단 구분)이 로그에서 사라집니다 (가치 2 / 위험 1 / S). -
보류 아이디어: 로그 한도 도달을 알리는 system 행이 system 예산을 차감하지 않아, 예산 회계에서 벗어난 유일한 행입니다 (가치 1 / 위험 1 / S).
- 릴리즈: v0.5.14 (2026-09-10, run 2026-09-10-010116-releasedock-improve)
weekly
- 선택: 완료 숨기기가 상황판에 시키던 거짓말 두 가지 수정 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약: 담당자 보기는
byAssignee(tasks, …)에 이미 걸러진 목록을 넘기고doneRatio(lane.tasks)로 완료율을 냈습니다 —완료 숨기기를 켜면 레인에 완료 줄이 하나도 남지 않으므로 모든 사람이 언제나0/n · 0%, 막대는 0 이었습니다. 끝낸 일을 감췄더니 아무도 아무것도 끝내지 못한 것으로 그려지는 것이고, 벽에 걸린 판이 사람 이름 옆에서 하는 말이라 더 나쁩니다. 같은 필터가 만들던 두 번째 거짓말도 함께 고쳤습니다: 그 기간의 일을 모두 끝내면 목록·담당자 보기가이 기간에 등록된 업무 일정이 없습니다, 주 보기의 각 칸이일정 없음이라고 말했습니다 — 계획이 없는 달과 계획을 다 끝낸 달은 정반대인데 같은 문장이었고, 그것은 v0.294.0 이 “읽지 못한 것을 ‘계획 없음’ 으로 그리지 않는다” 로 막은 바로 그 문장입니다.byAssignee가 사람의 창 전체(tasks)와 그릴 줄(visible)을 따로 들도록 바꿔 막대와n/m은 창 전체로 재고 줄만 걸렀으며, 다 끝낸 사람의 레인은0%로 남기지 않고 판에서 내립니다(그것이완료 숨기기가 하겠다고 한 일입니다). 비어 있는 이유는emptyBoardText·emptyDayText가모두 완료와없습니다로 구분합니다. 검증: 이전 동작(걸러진 목록으로 레인을 만들고 빈 화면은 늘 ‘없습니다’)을 되돌려 새 시험 2개가[2] ≠ [1,2]·0% ≠ 50%·'없습니다' 에 '모두 완료' 없음으로 실패하는 것을 확인 → frontend lint(tsc -b)·build·test 144개가 두 시간대(Asia/Seoul, America/New_York) 모두 통과(새 시험 3개), 실제 DB(WEEKLY_TEST_POSTGRES_DSN)로go test ./...전체 통과,go vet ./..., modal-close·paging·openapi·version 검사 통과. Go 는 손대지 않아 guard-check·mutation-check 는 돌리지 않았고(대상 변경 없음), a11y·failstate 검사는 배포와 브라우저가 필요해 이 세션에서 실행하지 못했습니다(CI·수동 점검에서 실행). 사용자 안내서의 상황판 절에 “감추는 것은 줄뿐” 한 줄을 더하고 HTML·PDF 를 다시 생성했습니다. 빌드 산출물(frontend/dist,node_modules)은.gitignore에 있어 커밋에 들어가지 않았습니다. - 보류 아이디어:
updateScheduleTask에 시험이 하나도 없습니다 — 쓰기 넷 중 전체 치환 계약을 가진 하나만 비어 있습니다 (3/1/S) · 상황판 편집이workItemId를 담지 않아 업무 링크를 조용히 끊습니다 (2/1/S) ·listScheduleTasks에 상한이 없고/api/v1/schedule이 scale-check PATHS 에 없습니다 — 원장의 2026-09-09 2회차 작업이 이 체크아웃(main=v0.294.0)에는 없으니 착수 전에scheduleRowLimit존재부터 확인 (3/2/M) · 월 격자의 하루 칸이 담는 줄 수에 상한이 없어 한 날에 몰리면 나머지 칸을 밀어냅니다 (2/2/S) · 월 격자가 이웃 달 며칠까지 조회해 요약 숫자가 ‘이 달’ 보다 큽니다 (2/2/S) · [기각]/api/v1/schedule이 authz-check 스윕에 빠졌다는 지난 회차 메모는 전제가 틀렸습니다 — authz-check.py 에는 경로 목록이 없고internal/app/*.go의 403 거부를 전부 자동으로 훑습니다
Clustara
- 선택: Pod 레벨
securityContext.runAsUser: 0이 root 로 판정되지 않던 결함 4종 (가치 4 / 위험 1 / 작업량 M) - 결과: 성공
- 요약:
spec.securityContext.runAsUser: 0를 Pod 레벨에 적고 컨테이너는 아무것도 적지 않는 것이 워크로드를 root 로 돌리는 가장 흔한 형태인데(Pod 값은 덮어쓰지 않은 모든 컨테이너의 기본값), 세 보안 화면이 컨테이너 securityContext 만 읽어 그 워크로드를 root 아님으로 보고했다 — ① SEC-01classifyPodSecurity는runAsUser=0위반을 아예 만들지 않았고 ② Runtime Security Profile(CLU-OCP-03)의podSecurityInput은 Pod 레벨을 읽긴 하지만 무조건 적용해 컨테이너가 실제 UID 로 덮어쓴 Pod 까지 root 로 점수를 매겼으며(반대 방향 오탐), 동시에containers만 순회해 privileged init 컨테이너·그 추가 capability·지금 붙어 있는 privileged 디버그(ephemeral) 컨테이너가 위험 설정이 하나도 없는 것으로 채점됐다(같은 Pod 를 정책 엔진과 SEC-01 은 이미 위반으로 적고 있어 제품의 두 부분이 반대 판정) ③ Workspace 건강도의podHasRuntimeSecurityRisk는 Pod 레벨을 통째로 무시하고 역시containers만 봐서 같은 Pod 를 두고 런타임 보안 화면과 답이 달랐다. 판정을 한 곳으로 모았다 —analyzer.EffectiveRunAsUser가 컨테이너 → Pod 우선순위를 양방향으로 적용하고(명시적 null 은 미설정),PodRunsAsRoot가 세 호출자 모두에게 한 번만 답하며, 두 핸들러는 포스처·정책 엔진이 이미 쓰던analyzer.SecurityRelevantContainers(regular+init+ephemeral)를 순회한다. 검증: 신규 테스트 8개를 고치기 전 코드에 되돌려 붙여 6개가 각 결함을 지목하며 실패함을 확인했고(나머지 2개는 컨테이너가 Pod 의 0 을 덮어쓴 경우와 Pod 의 non-root 를 컨테이너가 덮어쓴 경우를 지키는 오탐 회귀),go build ./...·go vet ./...·go test ./...전부 통과(20 패키지). 이번 세션 규칙에 따라 버전·changelog·docs 마커는 건드리지 않았다. -
보류 아이디어: ①
.github에 CI 워크플로 없음 — build/vet/test 게이트 추가 (가치 3 / 위험 1 / S) ② PSS Restricted 검사에 seccompProfile 항목이 없어RuntimeDefault/Localhost미설정 Pod 가 Restricted 로 남음 (가치 3 / 위험 2 / S) ③ 취약점 import 가 파싱 못 한 아티팩트를 ‘취약점 0건 완료’ 로 저장 — 400 거절 또는parse_failed상태 재검토 (가치 3 / 위험 3 / S) ④PodSecurityResult에cluster_id가 없어 다중 클러스터 포스처 표가 어느 클러스터인지 말하지 못함 (가치 2 / 위험 1 / S) ⑤capacity.podRequestGPU는 nvidia 만,node_monitoring.podGPURequests는 amd·intel 도 세어 화면마다 GPU 요청량이 다름 (가치 2 / 위험 1 / S) - 릴리즈: v0.9.280 (2026-09-10, run 2026-09-10-085117-Clustara-improve)
AgentHub
- 선택: 스캔 한도에 잘린 페이로드가 잘렸다는 사실을 어디에도 남기지 않던 문제 수정 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공 — 커밋 9f84183 (auto/2026-09-10-0851)
- 요약:
dlp.Result.Truncated는 “깨끗한 결과가 완전한 결과로 오해되지 않도록” 존재한다고 주석에 적혀 있는데, 네 경계 전부가len(result.Findings) == 0이면 그 자리에서 돌아가므로 스캐너가 유일하게 보증할 수 없는 페이로드 — 한도까지만 읽고 뒷부분은 보지 못한 것 — 이 로그 한 줄도 감사 항목 하나도 남기지 않았습니다. 그것은 끝까지 읽고 아무것도 없었던 페이로드가 남기는 것과 정확히 같아서, 트레일을 읽는 운영자는 둘을 구분할 수 없었고, 큰 도구 결과 비용을 줄이려 MaxBytes 를 낮춘 배포에서는 조용한 쪽이 오히려 일상이 됩니다. 한도는valueEnd가 존재하는 이유이기도 한데, 그 함수 주석 자체가 “흔적이라고는 다른 점은 깨끗한 항목에 붙은 truncated 플래그뿐” 인 사고를 설명합니다 — 다른 것이 발견되지 않으면 그 항목은 애초에 쓰이지 않습니다. 이제 잘린 채 깨끗한 스캔은Incomplete()이고, 경계는Reportable()일 때 기록하며, 트레일은 그것을audited(아무도 하지 않은 발견을 주장하는 말)가 아니라 자기 이름unscanned/ 일부 미검사 로 파일합니다 — 모델 호출·흐름 실행·결정 기록 전송·리뷰 코멘트, 그리고 발견을 보고하는 것과 같은 방식으로 컨트롤 플레인에 보고하는 Pod 안 도구 게이트웨이까지 네 경계 모두입니다. 이 경로에서 정책은 일부러 묻지 않습니다(판정할 등급이 없고, 데이터 등급 선택자가 빈 규칙은 “모든 등급”이라 한도를 넘긴 프롬프트를 전부 거절하게 됩니다). 아무것도 막지 않고 아무 텍스트도 다시 쓰지 않습니다 — 게이트웨이 호출자는 예전과 같은 nil 을 받습니다. 한도 안에 들어오는 전송은 그대로 조용합니다. 검증: 새 live 테스트TestAPayloadPastTheScanLimitLeavesATrail이 Postgres 16 컨테이너에서 프로덕션 생성자(guard.NewModel,Dispatcher.exportDecision)를 그대로 통과해 실제 감사 행을 읽어 오며, 수정 전으로 두 게이트를 되돌리면 dlp.model·dlp.export 두 방향 모두 “흔적이 없다”로 실패하는 것을 확인했습니다. 게이트웨이는TestAToolPayloadPastTheScanLimitIsReported이 컨트롤 플레인 대역 서버로 실제 보고(truncated=true, findings 0, blocked=false)를 받고 호출 자체는 손대지 않음을 확인하고, 스캐너·두 게시 경계의 단위 테스트가 “끝까지 읽고 깨끗한 것은 계속 조용하다”를 함께 고정합니다.go build ./...,go vet ./...,go test -race ./cmd/... ./internal/...(DSN 없이/있이 모두), webnpm ci+lint+build,release-catalog-images.sh check-versions·validate,kubectl kustomize통과. internal/dlp·cmd/runtime-proxy 가 런타임 base 이미지 소스라 BASE_VERSION 0.25.0 으로 상향(5곳, 릴리즈 VERSION 은 건드리지 않음). -
보류 아이디어: 정책 시뮬레이터가 ‘이 규칙이 Pod에서 어떻게 컴파일되는가’를 보여주지 않아 순서 실수를 저장 전에 볼 수 없음 (3/1/M) / 결정 기록 내보내기가 정책에 거절당해도 provenance 화면이 그 건수를 세어 보여주지 않음 (3/2/S) / dlp.tool 보고 엔드포인트만 테스트가 하나도 없어 Pod 가 보낸 값이 감사에 어떻게 들어가는지 아무도 지켜보지 않음 (3/1/M) / 감사 결과(outcome) 값 목록이 서버와 콘솔에 각각 하드코딩돼 드리프트 가드가 없음 (2/1/S)
- 릴리즈: v0.241.0 (2026-09-10, run 2026-09-10-085112-AgentHub-improve)
Vendra
- 선택: 견적을 그 요청이 나간 통화로 매기고, 모든 금액이 어떤 통화인지 말하게 하기 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공 (commit 2e1abb1)
- 요약: 리스크등급·이메일·공급업체 거래 상태·참가자 상태·연락처 스윕과 같은 기준(철자가 아니라 연산 — 요청 값이 currency 컬럼에 도달)으로 훑었다. 통화 코드를 보는 문은 다섯인데 어느 것도 코드를 보지 않았고 전부 글자 수만 재고 있었으며, 이 상자에 들어가는 잘못된 값은 짧다. 나쁜 쪽은 낙찰이다: recalculateSourcing이 가격을
100*min_amount/total_amount로 제출된 견적 전체에 걸쳐 매기는데 두 통화를 한 척도에 올릴 환율이 애플리케이션 어디에도 없다(spend.currency가 base를 적어 두었지만 읽는 문이 없다). 그래서 68,000,000 KRW 견적들 옆에 선 50,000 USD 견적은 다른 통화의 견적이 아니라 그냥 가장 작은 숫자로 읽혀, 다섯 가중치 중 가장 큰 가격 배점을 통째로 가져가고 비교표 맨 위에 앉았다 — 그리고 그 표는 money()가 저장된 통화와 무관하게 전부 원화로 찍어서 ₩50,000으로 보였다. 무슨 일이 일어났는지 화면 어디에도 없고, 낙찰이 그대로 따라간다. 포털 견적 폼이 KRW/USD/EUR/JPY 넷을 제안하고 있었으므로 이건 API 전용 경로가 아니다. rows.go에 currencyCodes를 riskGrades·supplierStatuses 옆에 두고 다섯 문 전부에 validCurrencyFields/validCurrency를 세웠다(대문자화 — 주소를 소문자로, 웹사이트에 스킴을 붙여 하나의 모양으로 저장하는 것과 같다). 견적의 통화는 요청의 통화로 정하고 다른 코드는 400 currency_mismatch로 무엇으로 내야 하는지 말하며 거절한다. business_objects.currency를 편집이 되쓰는 컬럼에 넣었다 — 생성은 코드를 받는데 그 뒤 어떤 문도 쓰지 않아 잘못 들어간 통화가 정정 요청마다 200을 받고 그대로 남았다. money()는 레코드가 지닌 코드로 찍고 Intl이 모르는 코드에서는 패널을 내리는 대신 숫자와 코드를 같이 답한다. 포털 견적 폼은 넷을 제안하는 대신 그 요청의 통화를 말한다. 검증: docker postgres:16-alpine으로 CI와 동일한 세 DSN을 새 DB에 걸고go test ./internal/... ./cmd/...전체 통과, gofmt·go vet 무결, 웹 테스트(17파일 72개)·tsc·eslint·빌드 통과. 가드는 다섯 — 패키지를 파싱해 statement의 컬럼 목록에 currency가 있는 문마다 검사를 요구하는 TestEveryStoredCurrencyIsOneTheApplicationCanPrice, 거절이 상자를 지목하고 저장 모양이 하나임을 고정하는 TestARejectedCurrencyNamesTheBox, 두 문을 API로 걷는 TestABidIsPricedInTheCurrencyTheTenderWasPutOutIn(USD 요청에 KRW 견적 400 → 무기재는 USD로 저장 → 대소문자 → 포털 목록이 통화를 보고)과 TestACurrencyOnABusinessObjectIsOneAndCanBeCorrected, 견적 폼을 실제로 렌더링하는 sourcing-currency.test.tsx 3개. 포털 핸들러의 통화 판단, updateObject의 currency 컬럼, Portal.tsx의 폼과 금액 표시를 각각 되돌리면 해당 가드가 모두 실패하는 것까지 확인했다. 남긴 것 하나: spend_transactions의 sum(amount)는 여전히 통화를 섞어 더한다 — 환율이 놓일 자리를 정하는 것이 먼저라 별건으로 남겼다. - 보류 아이디어: 초안 축출이 방금 저장한 초안 자신을 버릴 수 있음(putFormDraft의 ORDER BY updated_at DESC 동률) — 고치는 법은 AND draft_key<>$3 (3/1/S) / 공급업체 연간 지출과 지출 리포트가 통화를 섞어 더함 — 환율이 없어 합계 자체가 합계가 아님, 기준통화 정책이나 통화별 집계가 필요 (3/3/M) / 업무 객체 status가 여전히 임의 문자열 — 유형별 어휘가 없어 오타 하나가 집계에서 조용히 빠짐 (3/3/M) / 업무 객체의 data jsonb 블롭에 어떤 검증도 없음 — 폼이 쓰는 키가 API로는 무제한이고 네 키는 결재 라우팅에도 닿음 (3/2/M) / 구매자 화면에는 통화 선택이 없어 모든 업무 객체가 KRW로만 생성됨 (2/1/S)
Invenqor
- 선택: MCP
asset_relations가 관계 목록에 상한을 두지 않아 무한정 큰 응답을 돌려줌 (가치 3 / 위험 1 / 작업량 S) - 결과: 성공
- 요약:
mcpAssetRelations만 유일하게 LIMIT 이 없어 자산의 활성 관계를 전부 돌려줬다 — process 상관관계가 돈 서버에서runs_on자식을 수천 개 가진 host 하나면 도구 호출 한 번의 응답이 모델 context 를 통째로 차지하고, 잘라내는 쪽은 클라이언트였다. 다른 읽기 도구와 같은limit(기본 50, 최대 100)·offset을 받고, 한 행을 더 읽어 버리는 lookahead 로has_more·next_offset을 사실로 보고하게 했다. 기존relations키를 그대로 유지해 응답 호환은 깨지지 않는다. 겸사겸사 가이드의 MCP 도구 표를mcpTools스키마와 대조하는 테스트를 추가했고 (openapi.yaml 대 라우터를 대조하는TestOpenAPIRouteCoverage와 같은 방식), 그 테스트가 실제로 기존 드리프트인software_inventory의vendor누락을 잡아내 문서를 함께 고쳤다. 검증:mcp_pagination_test.go에 두 테스트 추가 — 3개 관계를 limit 3 으로 읽으면has_morefalse, limit 2 면 2건·has_moretrue·next_offset2 이고 그 offset 으로 나머지 1건이 오며 세 페이지 합이 각 edge 정확히 1회(lookahead 행이 섞이지 않음), 관계 55개에서 limit 미지정이면 50건·has_moretrue. 헛돌지 않는지 보려고 lookahead 와 비교식을 옛 방식으로 되돌려 두 테스트가 정확히 그 지점에서 실패하는 것을 확인한 뒤 복구했다. 문서 대조 테스트도 표를 고치기 전에asset_relations·software_inventory두 드리프트를 차례로 잡는 것을 확인했다.go test ./...를 SQLite fallback 과 실 PostgreSQL(scripts/test-postgres.sh) 양쪽에서 전 패키지 통과,go vet·go build·gofmt통과. web·Rust·openapi.yaml(MCP 는 도구별 스키마를 기술하지 않음)은 손대지 않아npm·cargo·redocly는 돌리지 않았다. 버전 범프·릴리즈 노트는 하지 않았다. -
보류 아이디어: Query DSL 실행에 offset 이 없어 상한 500 을 넘는 나머지를 받아낼 방법이 아예 없음 —
/api/v1/assets처럼 offset·total 을 주는 것이 대안 (가치 3 / 위험 2 / M) · Query DSL 에attributes.<키>존재/부재 연산자가 없어>= ""우회가 필요함 (가치 3 / 위험 2 / M) · MCPasset_search가 0건일 때 실제 존재하는 type·status 값을 함께 돌려주어 ‘자산 없음’과 ‘필터 값 없음’을 구분하게 함 (가치 3 / 위험 2 / M) ·listAgents·설정 목록/이력·자산 상세 루프가rows.Err()를 확인하지 않아 부분 결과를 200 으로 돌려줌 — storage 에 테스트용 공개 생성자가 없어 fault injection 불가 (가치 3 / 위험 1 / M) ·attributes.*의 배열·객체 값이 두 저장 모드에서 다른 텍스트로 렌더링됨 — 컨테이너를 가리키는 절을 아예 거절하는 편이 나을 수도 (가치 3 / 위험 2 / S) - 릴리즈: v0.2.30 (2026-09-10, run 2026-09-10-094107-Invenqor-improve)
ReSSO
- 선택: 로그인 화면이 이쪽 장애를 “로그인 요청이 만료됐다”로 답하던 문제 수정 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공 (커밋 9529746)
- 요약: RP에서 넘어온 로그인 화면은 폼을 그리기 전에
GET /api/v1/auth/challenge/{token}으로 park된 인가 요청을 먼저 읽는데, 그 읽기의 모든 실패에 한 문장으로 답했다 — “로그인 요청이 만료되었습니다. 연결한 서비스에서 다시 시작하세요.” 그 답이 맞는 경우는 하나뿐이다. 백엔드는 소진·만료·발급된 적 없는 request token에 404를 답하고(writeStoreError), 그것만이 토큰에 관한 사실이며 다시 시작하는 것이 유일한 출구다. 저장소가 답하지 않으면 500이고, 페이지가 재시작 중에 fetch하면api.ts가 status 0으로 만드는데, 그 둘은 이쪽 문제이고 사람이 들고 있는 요청은 소진되지 않은 채 그대로 남아 있다. 그래서 화면이 가진 그 한 문장은 설명이 아니라 지시였고, 되는 쪽에서 사람을 떼어놓았다: RP로 돌아가 새 request token을 받고 여기 도착해 같은 장애를 다시 만난다. 게다가 그 Alert이 이 화면의 마지막 말이었다 — 누를 것이 없고challenge.isError가 폼을 계속 막으므로, 장애가 걷힌 뒤에도 화면은 글자 그대로 같아 보였다. 근거는 한 디렉터리 옆에 이미 있었다: 콘솔의ErrorAlert(components/Feedback.tsx)는 status 0·429·5xx에만 재시도를 내주고 나머지에는 내주지 않으며, 각 상태에 무엇을 할지까지 적어 준다. 로그인 안 한 사람이 보는 유일한 화면만 그 구분을 버리고 있었다. 이제 404만 옛 문구를 유지하고, 나머지는 “요청은 그대로 남아 있으니 연결한 서비스에서 다시 시작하지 말고 잠시 후 다시 시도하세요” + 같은 challenge를 다시 부르는다시 시도버튼 + Trace ID다. 백엔드는 손대지 않았다 — 이 구분은writeStoreError가 이미 하고 있었고,login의 400expired_request가 저장소 장애를 함께 뭉개는 건은 미병합 브랜치와 겹치므로 그대로 두었다. 검증: 새 테스트 둘 — 소진된 토큰(404)은 여전히 RP로 돌려보내고 재시도 버튼을 내주지 않는 것, 500은 다른 문구와 Trace ID를 보이고 만료 문구를 보이지 않으며 재시도 뒤 Client 안내가 뜨고 폼이 다시 쓸 수 있게 되는 것. 두 번째가 수정 전 페이지에서 실제로 실패함을 확인했다(findByText(/로그인 요청을 확인하지 못했습니다/)타임아웃).make test전체 통과(exit 0) —go test -race ./...전 패키지 ok(httpserver 89s / store 82s), 연동 테스트 SKIP 0건,go vet,npm run test(22파일/106테스트, 디스크 파일 수와 일치),npm run build.make lint도 통과(golangci-lint 0 issues,govulncheck0,eslint --max-warnings 0). 빌드가 만든webui/dist/index.html변경은 되돌렸다.docs/operations.md에 두 문구를 원인·사용자가 할 일로 나눠 읽는 표와 “두 번째 문구가 보고되면 사용자를 RP로 돌려보내지 마세요”를 적었다. -
보류 아이디어:
oidcLogout이 hint의sub를 쿠키 세션과 대조하지 않아 다른 사람의 ID Token으로도 지금 로그인한 사람이 로그아웃된다(가치 2/위험 2/M) /id_token_hint의aud를 요청한 Client와 대조하지 않아 같은 Realm의 다른 Client에 발급된 ID Token도 hint로 통과한다(가치 2/위험 2/M) / 로그인 화면이account_mismatch(403)를 실패 카운터에 세어, 로그인은 성공했는데 “반복 실패하면 계정이 잠긴다”는 안내를 띄운다 — 서버는 그 직전에 실패 횟수를 초기화했다(가치 2/위험 1/S, 새 아이디어) / 로그인 화면이account_mismatch를 일반 오류로만 보여줘 “지정된 계정으로 다시 로그인하면 이어진다”는 사실이 드러나지 않는다(가치 2/위험 1/S) / UserInfo POST에서 form-encodedaccess_token수용, RFC 6750 §2.2(가치 2/위험 1/S) - 릴리즈: v0.9.76 (2026-09-10, run 2026-09-10-094110-ReSSO-improve)
aiportal-front-admin
- 선택: 세션 만료를 라우트 이동 없이 즉시 복구 — 잃어버린 커밋 e425b54 복구 (가치 4 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
git branch --no-merged main으로 확인한 대로 원장에 ‘성공’으로 적힌 e425b54가 브랜치auto/2026-09-06-0140에만 남고 main에 없어,setSessionExpiredHandler()와auth/guard.ts가 지금 코드에 없었다(src/auth에 session.ts·ssoRedirect.ts·storage.ts뿐,router.beforeEach도 e425b54 이전 형태 그대로). 그래서 세션 만료 응답은 캐시된 관리자 세션을 버리기만 하고 다시 확인하는 주체는 router guard뿐이라, 한 화면에 머무르며 검색·페이지 이동·자동 갱신만 하는 사용자는 라우트를 옮기기 전까지 모든 조회가 실패하는 화면에 갇혔다. 지금 코드 기준으로 재검토한 뒤(현재router.ts·session.ts가 e425b54의 base와 동일해 cherry-pick이 충돌 없이 적용됐고, ARCHITECTURE.md는 그 뒤 추가된 절들과 자동 병합됐다) 복구했다 —session.ts가 캐시를 버린 직후 만료 handler를 부르고, guard 본문을 새auth/guard.ts의resolveAdminAccess(force)(allow/redirected/denied)로 옮겨 router guard와 만료 복구가 같은 판단과 같은 SSO 왕복 제한을 공유하며,/sso/ssologin자체가 만료 응답을 받는 재귀는state.checking으로 걸러낸다. 여기에 더해, 이 변경이 두 번이나 조용히 사라져도 아무도 알아채지 못한 이유인router.ts배선(만료 handler 등록과 세 판단 처리)에 테스트가 전혀 없던 공백을src/app/router.test.ts8개로 덮었다(guard 세 갈래 + public 라우트, 만료 복구의 강제 재확인·화면 유지·access-denied 이동·SSO 이동 중 덧씌우지 않음). 검증은npm run build(typecheck + vitest 142개 통과, 기존 124개 + 복구 10개 + 신규 8개 → vite build → runtime-config 검증 → offline 검사 → integrity manifest 19개) 전체 통과, 그리고 만료 알림 호출·재귀 차단 가드·force전달을 각각 되돌리고 router 배선을 네 가지로 변형해(handler 등록 제거,redirected를 access-denied로 처리, 복구 실패 시 이동 안 함, 복구가 캐시 세션 사용) 해당 테스트가 정확히 실패하는지 모두 직접 확인했다. 커밋 c1a4d7b. - 보류 아이디어:
monitoringParser.toPod이 kubectl 스타일restarts(“3 (5d ago)”)에서 NaN을 표에 노출 (2/1/S) · 결과 0건인 탭을 누를 때마다 ContentAccessView·CatalogView·DirectoryView가 같은 조회를 되풀이 (2/1/S) · PolicyCenterView·AdvancedPolicyView·OverviewView가createRequestGuard없이 조회해 늦게 온 이전 응답이 최신 결과를 덮어씀 (2/1/S) · AdvancedPolicyView가 저장할 때마다load()로 패널 전체를 skeleton으로 되돌림 (2/1/S) · 다른 탭에서 로그아웃해도(storage이벤트) 이 탭은 라우트를 옮길 때까지 복구하지 않음 — 이번에 만든 만료 handler를 재사용하면 되는 후속 (2/2/S) · AdvancedPolicyView가snapshot.extensions를 v-model로 직접 변형 (2/2/S) · 루트 앱의crypto-jslocal tarball 의존성 제거로 clean install 복구 (4/4/M)
aiportal-front
- 선택: 대화저장 상세(ChatStorageDetail)의 기본앱 비동기 처리 오류 및 봇 식별자 파싱 공용화 (가치 3 / 위험 2 / 작업량 S)
- 결과: 성공
- 요약:
src/views/ChatStorage/ChatStorageDetail.vue가 async 함수readDefaultApp을computed(() => getDefaultBot())로 감싸 Promise 를 담고 있어,pickBotIds(Promise)가 항상 빈 식별자를 돌려주고appId/prjId의 기본앱 폴백이 죽어 있던 문제를 고쳤다 — 일반챗봇 히스토리(라우트에 appId 쿼리가 없는 진입)에서pInf.chat.getHistorySession(대화 불러오기)과inf.chat.feedbackAnswer(피드백 저장)가app_id/project_id를 빈 문자열로 보내고,isGeneralChat이 영원히 false 라 헤더가 항상 “전문가 AI와 대화중” 으로 표시됐다.defaultBot을 ref 로 바꾸고initByRoute가 세션 조회 전에ensureDefaultBot()으로 선로딩하도록 했다(readDefaultApp 은 캐시·inflight 합치기가 있어 추가 호출이 생기지 않는다). 함께 Chat/Main·Chat/Index·ChatStorageDetail 에 각각 복사돼 있던pickBotIds를appDefaultStorage.pickBotIds로 합쳐(app_id/appId/appID·project_id/prj_id/prjId 표기 모두 처리, 비객체·Promise 방어) Main.vue 의app_id단일 표기 사본을 제거했다. 검증은npm test(총 353건 통과, 신규 6건)와npm run build:dev(빌드 성공, dist 는 커밋 전 삭제)로 수행했다. 다만 이 저장소에는 컴포넌트 테스트 환경이 없어 .vue 수정 자체는 순수 헬퍼 테스트와 코드 리뷰로만 확인했다. - 보류 아이디어: mitt 이 package.json 직접 의존성에 없어 전이 의존성에 기대는 문제 (가치 3 / 위험 1 / S) · Header.vue 가 OCR 사용 여부와 무관하게 마운트 즉시 3초 폴링을 시작하는 문제 (가치 3 / 위험 3 / M) · globalLoading 이 참조 카운트 없이 boolean 이라 병렬 요청 중 하나만 끝나도 스피너가 사라지는 문제 (가치 3 / 위험 3 / S) · serviceCode 리터럴 표기 불일치(myNoteBook/MyNotebook/multiModal) 상수화 (가치 3 / 위험 2 / S) · 빌드 산출물 단일 청크 6.2MB 코드 스플리팅(manualChunks) (가치 3 / 위험 3 / M)
ai-admin
- 선택: 대시보드가 조회 실패를 “승인 대기 0건”·”공급자 없음”으로 감추던 문제 수정 (main 재착수) (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약: 보류 목록 1번의 재착수 전제였던 “df278de가 왜 병합되지 않았는지”를 먼저 확인했다 — 그 commit이 담긴
auto/2026-09-06-2320은 origin에 아예 push되지 않았고(원격 branch 목록에 없음)git merge-base --is-ancestor df278de main도 NO다. 즉 반려가 아니라 단순 미push였고, 버그는 main에 그대로 남아 있었다:dashboard는_ = s.db.Pool.QueryRow(...).Scan(&pending)로 승인 대기 건수 조회 오류를 버리고 0을 내려보냈고,dashboardProviders는 조회 실패를 빈 목록으로, scan 실패 행을 건너뛴 짧은 목록으로 반환해 화면의 “등록된 AI 공급자가 없습니다”와 구별되지 않았다. 두 값은 레거시 스키마가 아니라ai_admin자기 테이블에서 오므로 degradation으로 볼 이유가 없어, 조회가 실패하면 500dashboard_unavailable오류 봉투를 반환하고(DashboardPage.tsx:60이 이미ErrorState+재시도를 표시한다) 공급자 목록은 v1.2.9의 공통collectRows경로를 쓰도록 했다. 레거시 스키마 기반 지표(metrics[].available)의 degradation 계약은 그대로 두었다. 검증은 통과 확인에 그치지 않고, 공급자 상태 매핑·scan 실패 시 부분 목록 없음·연결 불가 pool을 향한 500 응답을 테스트로 덮은 뒤dashboard핸들러만 수정 전 형태로 되돌려TestDashboardReportsApplicationQueryFailure가 실제로 200과"pendingApprovals":0,"providerHealth":null로 실패하는 것을 확인하고 되돌렸다. Docker로 PostgreSQL 16을 띄워TEST_POSTGRES_DSN을 걸고go test -race -count=1 ./...(통합 테스트 포함,internal/server47s)·gofmt -l·go vet ./...·go build ./...·scripts/verify-version.sh·npm ci && npm test(64개)·npm run build를 모두 통과시켰다. 이번 세션 규칙대로 VERSION·CHANGELOG·릴리즈 메타데이터는 건드리지 않았고(df278de에 있던 버전 bump는 의도적으로 제외했다)docs/api.md에만 대시보드 실패 계약을 명시했으며, 커밋 전git status로 빌드 산출물이 섞이지 않았음을 확인했다(변경 4개). -
보류 아이디어:
loadGrants가 map 순회로 roles·permissions 순서를 무작위화해/api/v1/auth/me응답 순서가 요청마다 뒤바뀜 — 지금 코드(auth.go:250-256)에서 재확인 (가치 2 / 위험 1 / S) ·updatePreferences가locale(varchar 20)·timezone(varchar 80)에 길이 검증 없이 upsert해 긴 값이 400 대신 500이 되고 같은 요청의 다른 설정 변경도 함께 사라짐 (가치 2 / 위험 1 / S) ·listUsers의q에만 200자 상한이 없어(감사·레거시·MCP는 모두 있음) 매우 긴 검색어가 세 컬럼 ILIKE 스캔으로 들어감 (가치 2 / 위험 1 / S) ·safeCSVCell이value[0]한 byte에서"=+-@"만 검사해 OWASP가 함께 권고하는 tab(0x09)·CR(0x0D) 선행 문자를 중화하지 않음 (가치 2 / 위험 1 / S) · 새 아이디어:aiModels·aiCatalog가available_models의 JSON 파싱 실패를_ = json.Unmarshal로 무시해 모델 목록이 기본 모델 하나로 조용히 줄어듦 (가치 2 / 위험 1 / S) · 새 아이디어:dashboardProviders의LIMIT 10이 목록이 잘렸음을 알리지 않아 11개 이상 등록 시 일부 공급자가 미등록처럼 보임 (가치 2 / 위험 1 / S) - 릴리즈: v1.2.17 (2026-09-10, run 2026-09-10-111300-ai-admin-improve)
aiportal-py
- 선택: 백그라운드 루프의 마지막 성공 시각(heartbeat)을
/health로 노출 (감사 A-112) (가치 3 / 위험 2 / 작업량 S) - 결과: 성공
- 요약: A-003 으로 task 생존 여부는
/health에 드러났지만, 세 무한 루프는 모두 주기 안의 예외를 잡고 다음 주기로 넘어가므로 “살아 있으면서 매 주기 실패” 하는 상태가 여전히healthy로 보였다(token_store.fetch_external_token은 실패를None으로 삼키고,auto_refresh_question은 수집이 비면 기존 캐시를 유지한 채 계속 돌며,feedback_batch_loop도 주기 예외를 로그만 남긴다 — policy token 이 한 번도 발급되지 않은 경우만checks.m2m_token으로 드러났고, 발급 후 갱신이 계속 실패하는 경우는 보이지 않았다).util/task_supervisor.py에heartbeat(name)과register(..., max_silence=N)을 추가해, 마지막 성공 이후 N 초가 지난 task 를stale로 만들고stale()로 조회하게 했다 — 살아 있는 상태이므로 중단(stopped())과 분리해/health가 “중단된 task” 와 “지연된 task” 를 나눠 알리고, 전이 시점에만 로깅하는 기존 폴링 규칙은 그대로 쓴다. 경과는 시스템 시각 변경에 흔들리지 않도록 monotonic 시계로 재고(테스트 주입 가능), 첫 성공 전에는 등록 시각을 기준선으로 삼아 기동 후 한 번도 성공하지 못한 경우도 잡는다. 허용 간격을 주지 않은 task 의 응답 형태는 그대로다. heartbeat 는 성공한 주기에만 호출한다(추천 질문은 캐시를 실제로 교체한 주기, 토큰은 policy token 을 실제로 받은 주기, 피드백 배치는 예외 없이 끝난 주기). 허용 간격은 루프 주기의 두 배 + 여유(1800→3900, 600→1500)라 한 주기를 건너뛴 것만으로는 경보가 나지 않는다. task 이름은TASK_*상수로 모아 루프 본문의 오타를 막았다. 검증은 supervisor 를 가짜 시계로 직접 호출하는 단위 테스트 9건(stale판정·회복·기준선·죽은 task 를 감추지 않음·전이 판정)과, kiwipiepy·psycopg2 미설치로 import 할 수 없는 세 파일(api.py/util/token_store.py/service/feedbackservice.py)에 대한 AST 정적 검사 5건(세 루프의 heartbeat 호출·그 호출이 성공 분기 안인지·등록의max_silence·/health의stale노출)으로 했고, 옛 코드를 되돌려 넣어 정적 검사 5건이 실제로 실패하는지 확인했다.python3 -m pytest -q984 passed(기존 969),python -m pyflakes .undefined name 0건. docs 5종(CURRENT_STATE_AUDIT/OPERATIONS/API_REFERENCE/CODEBASE_MAP/TESTING) 갱신. 커밋277d90e. 남은 한계는 문서에 명시:fetch_unprocessed_feedback이 DB 조회 실패를 빈 목록으로 돌려주므로 조회가 계속 실패하는 상태는 피드백 배치의 성공으로 기록된다. - 보류 아이디어: A-105 후속 — 공통 오류 응답 model 도입과 traceback 노출 제거(A-106 연계) — 가치 4 / 위험 3 / L
- 보류 아이디어: Milvus 적재 실패가 조용히 성공으로 보고되는 문제(
insert_to_milvus_cf가 traceback 문자열을 return 하고 호출부가 무시) — 가치 3 / 위험 2 / S - 보류 아이디어:
fetch_unprocessed_feedback이 DB 조회 실패와 “처리할 피드백 없음” 을 구분하지 않는 문제(A-112 후속) — 가치 3 / 위험 1 / S - 보류 아이디어: bare except → 구체 예외로 범위 축소(
service/pipelineservice.confluence_pipeline_kcblaw,api.py의/run_confluence_pipeline_spacefile등 잔여) — 가치 3 / 위험 3 / M - 보류 아이디어: A-107 후속 — 재시도·circuit breaker·공용
requests.Session도입 — 가치 3 / 위험 3 / M
dataworks
- 선택: API Entitlement 쓰기 경로에도
status허용값 검증 추가(계약 쪽과 대칭) (가치 3 / 위험 1 / 작업량 S) - 결과: 성공
- 요약:
POST /admin/dataworks/products/{key}/entitlements는expires_at형식만 검사하고status는 그대로 저장해,enabled·actve같은 오타가 들어오면store.EntitlementActive가active만 인정하므로 발급 순간부터 고객 API 키가403 inactive_entitlement를 받는데 admin 목록에는 정상 발급된 접근권으로 보였다(계약 쪽invalid_contract_status와 같은 유형의 fail-closed 오설정).contractScopeStatusKnown과 대칭으로entitlementStatusKnown을 두고active(기본)·draft·suspended·revoked만 허용하며 그 외에는400 invalid_entitlement_status로 거부하고, 소문자·trim 정규화 후 빈 값은active로 채워 응답이 실제 저장 행과 일치하게 했다(감사용으로 남기는revoked행은 관례대로 계속 저장 가능). 검증: HTTP 회귀 테스트(오타 상태 거부·미저장, ` ACTIVE ` 정규화 저장,revoked허용, 생략 시 기본값이 응답·저장 행 모두active)와entitlementStatusKnown표 테스트를 추가하고 검사를 꺼서 회귀 테스트가 실제로 실패(200 으로status:"enabled"저장)하는 것을 확인,go build ./...·go vet ./...(0건)·go test ./...전체 통과,go run ./cmd/api-surface-auditgap 0, gofmt 상태는 변경 전과 동일(dataworks_runtime.go는 기존 CRLF 파일).docs/OPERATIONS.md5절에 문서화. web 변경이 없어 웹 체크는 미실행이고, 릴리즈 커밋은 남기지 않았다. -
보류 아이디어: action center 의 활성 판정이
ent.Status != "active"라 대소문자가 다른 레거시 행을 런타임과 반대로 판정(store.EntitlementActive재사용 필요) / 이미 지난expires_at으로 발급되는 죽은 엔타이틀먼트를 쓰기 경로에서 거부 /internal/dataworks도메인 함수(EvaluatePublishGateV2·EvaluateRetirementCandidate) 단위 테스트 보강 / 동일 API 키에 활성 엔타이틀먼트가 둘 이상일 때 운영 화면에서 경고 / action center 가 상품이 사라진 고아 Contract Scope·Entitlement 도 그대로 집계 - 릴리즈: v0.9.50 (2026-09-10, run 2026-09-10-114120-dataworks-improve)
appstore
- 선택: 반려된 앱을 소유자가 다시 검토로 올릴 수 있게 (가치 4 / 위험 2 / 작업량 S)
- 결과: 성공
- 요약: 소유자에게 제출 경로는
PUT /apps/{app}하나뿐인데(별도 submit endpoint가 없다)store.UpdateApp은 status를 전혀 건드리지 않고,updateApp·mcpUpdateApp의 재제출 분기는before.Status == published일 때만 돌았다. 그래서 반려된 앱은 소유자가 지적받은 내용을 그대로 고쳐도rejected에 머물고 pending review가 하나도 생기지 않아, 관리자가 손으로 상태를 바꿔 주지 않으면 영구히 카탈로그에 못 올라갔다 — 정작 사용자 가이드는 “반려 사유를 반영하고 다시 제출”하라고 안내한다(docs/guides/user/index.html:90). 로컬 postgres:16-alpine 컨테이너로 전체 수명주기를 직접 확인했다: 반려 뒤 소유자가 serviceUrl을 고쳐UpdateApp을 호출하면 status는rejected, pending review는 0개였고, 재제출을 끼우면pending_review+ level 1 review가 생겨 검토자가 승인해published까지 도달했다. 새 순수 함수resubmitAfterEdit를 REST·MCP 두 수정 경로가 함께 쓰게 해 workflow가 켜져 있으면 반려된 앱을 재제출하고, 게시된 앱은 기존대로 재승인 정책을 따르게 했다. workflow가 꺼져 있으면SubmitApp이 곧바로 published로 만들기 때문에 반려된 앱을 검토 없이 카탈로그로 밀어 넣지 않도록Enabled조건을 반드시 유지했다. MCPapp_updatetool 설명과 OpenAPIupdateApp에도 이 재제출 의미를 문서화했다. 검증: 상태·정책 조합 8개 테이블 테스트를 추가하고 store 통합 테스트에 “수정만으로는 rejected에서 벗어나지 못하고 재제출은 level 1로 되돌아온다”는 단언을 더한 뒤gofmt -l,go vet ./...,go test -race(CI와 같은 패키지 목록), DSN을 준go test -race ./internal/store ./internal/httpapi ./internal/database재실행,go build ./cmd/server,check-env-contract.sh·check-docs.sh모두 통과. 프런트엔드는 변경하지 않아 npm 검사는 생략(앱 수정 완료 화면이 이미 “승인 Workflow 설정에 따라 즉시 게시되거나 검토 대기 상태가 됩니다”라고 안내한다). 커밋 698b5e1. -
보류 아이디어: 소유자가 반려 사유를 볼 수 있는 화면이 없음 — 이번에 재제출 경로는 열었지만 /me/apps 응답에 reason이 없어 무엇을 고쳐야 하는지 추측이 된다(가치 3/위험 2/M) ·
clientAddress가 RemoteAddr만 보아 reverse proxy 뒤에서는 익명 사용자 전체가 하나의 rate limit bucket을 공유 — 신뢰 프록시 설정이 필요한데 환경변수 계약이 네 개로 고정(가치 3/위험 3/M) · pending_review 상태의 앱을 수정해도 검토가 level 1로 되돌아가지 않아 다단계 승인에서 level 2 검토자가 level 1이 본 적 없는 내용을 승인함 — 정책 결정이 먼저 필요(가치 2/위험 3/S) ·fetchBrandingSource의 오류 메시지에 upstream 상태 코드와 err 원문이 샘(가치 2/위험 2/S) · 검증 하드닝 계열(adminUpdateUser·개인 키 이름·roleRequest·관리자 write path param)은 사람이 같은 접근의 PR 6854375를 반려했으므로 모두 rejected로 내렸다. - 릴리즈: v2.5.6 (2026-09-10, run 2026-09-10-114115-appstore-improve)
git-ctx
- 선택: 파이썬 프로젝트가 실제로 갖는 락파일(uv.lock·pdm.lock·Pipfile.lock)을 읽도록 수정 (가치 4 / 위험 1 / 작업량 M)
- 결과: 성공 (commit 470ee9f)
- 요약:
internal/manifest/lockfile.go의RecognizeLock이 파이썬 락파일로poetry.lock하나만 알고 있어서, uv·PDM·pipenv로 해석하는 저장소는 인벤토리에pyproject.toml의 범위(>=2.31,~=4.2)만 남았습니다 — 범위는 권고 판정이 결정하지 못하는 쪽이고, 해결된 버전은 즉답할 수 있는 유일한 근거인데 그 파일이 아예 인식되지 않아 읽히지 않았습니다. uv와 PDM은 Cargo·Poetry와 같은[[package]]블록을 쓰므로 기존parseTOMLLock으로 보냈고(이름·버전이 열 0에 있어 uv 블록의requires-dist·[package.metadata]의 중첩name =은 들어오지 않습니다), JSON인Pipfile.lock은 별도 리더를 붙였습니다 —_meta는 패키지 집합이 아니고 값의 모양도 달라서 문서 전체를 한 map으로 디코드하면 그 키 하나 때문에 파일이 통째로 버려지므로default·develop만 이름으로 읽습니다. pipenv는 핀을 설치할 요구사항 형태(==2.31.0)로 저장해 다른 락파일이 쓰는 숫자와 다른 버전 그룹이 되므로 연산자를 떼고, VCS ref로 고정된 패키지는 버전이 없으므로 제외합니다. 검증: 새 테스트 2건(TestThePythonLockFilesAProjectActuallyHas— 세 형식의 인식·해결 버전·certifi/_meta/VCS 항목 비유입,TestPipfileLockStatesTheVersionAlone) 추가하고 수정 전 실패를 직접 확인.gofmt -l,go vet ./...,go build -tags sqlite_fts5 ./...,go test -tags sqlite_fts5 ./...전체 통과(exit 0), manifest·indexer·search는-race도 통과. docs/configuration.md의 지원 락파일 목록도 갱신. - 보류 아이디어: pipenv의 매니페스트
Pipfile([packages]·[dev-packages] TOML)은 여전히 인식되지 않아 범위 선언이 인벤토리에 없음 (가치 2 / 위험 1 / S).clampResponse의 truncation notice 예약치 320B보다 실제 공지가 길어 예산을 십수 바이트 초과 (가치 2 / 위험 2 / S).parsePOM이<dependencyManagement>선언까지 실제 의존성으로 집계 (가치 3 / 위험 3 / M). YAML 블록 스칼라(password: |)의 값은 어떤 마스킹 규칙도 닿지 않음 (가치 3 / 위험 2 / M).mcp.filterLibraries/cacheKey가 호출자 슬라이스를 제자리 변경 (가치 2 / 위험 1 / S).
jikim
- 선택: SessionByToken·Authenticate가 DB 장애를 인증 실패로 접는 문제 수정 (가치 3 / 위험 2 / 작업량 M)
- 결과: 성공
- 요약:
users.go의Authenticate와SessionByToken이QueryRow실패를 원인과 무관하게ErrUnauthorized로 바꿔, DB 장애가 “자격증명 거부”·”세션 만료”로 보고되고 인증 미들웨어는 401/403을, 로그인 rate limiter는 장애를 실패 시도로 계산해 5분 창 동안 계정을 잠갔습니다. v0.2.5~v0.2.7의 오류 매핑 정리와 같은 방식으로lookupFailed로pgx.ErrNoRows와 그 밖의 오류를 나눠 행 없음만 sentinel로 남기고,withAuth·sessionStatus는storeError로 500을,withBaoAuth는 새baoSessionFailure로 만료 토큰(403permission denied)과 조회 불가(500failed to look up token)를 갈랐으며,login·baoUserpassLogin은 장애를 실패 시도로 계산하지 않고 500을 반환하고 driver 문자열을 응답에 담지 않습니다. 검증은 hook 기반 단위 테스트 7개(auth_outage_test.go6개 + storelookupFailed1개)를 추가하고./scripts/verify.sh전체(Go test·vet·gofmt, React test·lint·build, docs, compose) 통과로 확인했습니다. 커밋d79383b. -
보류 아이디어: 로그인 성공 판정 전에 rate limiter를 succeeded로 초기화하는 순서 정리 (2/1/S) / settings GET이 주입하는 파생 필드가 PUT 왕복 시 workflow 설정에 저장되는 문제 정리 (2/1/S) / 감사 로그 보존(audit_retention_days) 자동 정리 구현 (3/3/M) / baoKVWrite가 SecretExistsByPath로 create·update capability를 고르면서 생기는 TOCTOU 정리 (2/2/S) / Transit rewrap 엔드포인트 추가 (2/3/M)
- 릴리즈: v0.2.8 (2026-09-10, run 2026-09-10-121123-jikim-improve)
igame
- 선택: 관리자 비밀번호 재설정이 대상 사용자의 세션을 지우지 않는 문제 (가치 4 / 위험 2 / 작업량 S)
- 결과: 성공
- 요약:
updateUser(internal/api/admin.go)는password_hash만 쓰고auth_sessions는 건드리지 않았다. 비밀번호 재설정은 관리자가 탈취된 계정을 되찾는 수단인데, 옛 비밀번호로 열린 세션이 남은 수명(최대 12시간) 동안 그대로 살아 있어서 훔친 쿠키는 계속 통했고 계정은 사실 회수되지 않았다 — 본인 변경 경로인changePassword는 처음부터 나머지 세션을 지우고 있었으므로 의도는 이미 확립돼 있었다. 이제 삭제가 같은 statement의 DELETE CTE로 UPDATE와 함께 일어나 “새 비밀번호 + 옛 세션” 상태가 생길 수 없고, 살아남는 것은 관리자가 이 화면에서 자기 비밀번호를 재설정할 때의 요청 자신의 세션 하나뿐이다(중간에 로그아웃시켜서 지켜지는 것은 없다). 역할·상태는authenticate가 매 요청 다시 읽으므로 쓸어낼 필요가 없다. 감사 항목에는 폐기한 세션 수를sessions_revoked로 남긴다. 판정 주체가 PostgreSQL이라 실제로 돌려서 검증했다:IGAME_TEST_DSN이 있을 때만 도는 테스트(internal/api/admin_pg_test.go, 4개)가 빈 DB에 마이그레이션을 적용하고 재설정·본인 재설정·프로필만 수정·없는 사용자 네 경우를 확인하며, 훔친 쿠키가 정말로 인증되지 않는지authenticate로 직접 확인한다.make test-db DSN=...타깃과 README·docs/security.md 안내를 함께 넣었다. 검증:gofmt -l,go vet ./cmd/... ./internal/... ./migrations/...,go build ./...,go test·go test -race전체 통과, dockerpostgres:17-alpine으로 새 테스트 4개 통과 및 폐기를 무력화하면 재설정 관련 2개만 실패하고 나머지 2개는 통과함을 확인,npm --prefix web run lint와npm --prefix web test221개 통과(프런트엔드 소스는 건드리지 않았고 감사 상세 렌더러가 이미 임의 키를 처리한다). -
보류 아이디어: 일일 플레이 제한이 진행 중인 세션을 세지 않는 문제가 main에 그대로 남아 있음 — 2026-09-07 회차의 수정은 원격에 올라간 적 없는 브랜치에만 있다 / 동점일 때 row_number()와 바깥 ORDER BY가 독립적으로 정렬돼 rank 번호와 행 순서가 어긋날 수 있음 / 잘린 감사 로그 CSV가 200과 완전한 헤더로 내려가고 audit.export가 잘린 개수를 전체인 양 기록함 / Migrate 체크섬 불일치·트랜잭션 경계 통합 테스트 — 이번에 들어온 IGAME_TEST_DSN 하네스로 이제 가능 / 게임에 묶이지 않은 전역 업적은 content.go의 JOIN이 NULL과 매치되지 않아 클라이언트에서 해금될 수 없음
- 릴리즈: v0.7.9 (2026-09-10, run 2026-09-10-121120-igame-improve)
jupiq
- 선택: 오류 메시지 자르기가 한글 문자를 쪼개지 않게 수정 (가치 3 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약:
store.truncate(1000바이트)와integration.safeConnectionError(500바이트)가 바이트 단위로 슬라이스해, 한글 오류 메시지가 상한을 넘으면 자르는 지점이 UTF-8 시퀀스 중간에 떨어졌다. 앞의 값은hubs.last_error·integration_health.last_error에 그대로 들어가는데 PostgreSQL이 잘못된 바이트 시퀀스를 거부하므로 실패를 기록하려는 UPDATE 자체가 실패해 망가진 Hub가 계속 healthy로 남고, 뒤의 값은 연결 검증 Drawer에 그대로 보이므로 JSON 인코더가 U+FFFD로 바꿔 관리자에게 깨진 글자가 보인다. 두 곳 모두 룬 경계까지 되돌아가 자르고 애초에 유효하지 않은 입력은 대체 문자로 치환하도록 고쳤으며, 상한 근처 모든 오프셋에서 결과가 유효한 UTF-8이고 원본의 접두사이며 한 룬(3바이트) 넘게 버리지 않는지 검증하는 테스트 4개를 추가했다. 수정 전 코드로 되돌린 변형에서 새 테스트가 실제로 실패하는 것을 확인했고,gofmt -l,go vet ./...,go test -race ./...,scripts/check-version.sh,scripts/check-screenshots.mjs,npm run lint,npm test(18파일 59개) 모두 통과했다. -
보류 아이디어: 로그인 리미터
succeeded가 ip 키를 의도적으로 유지하는 동작에 대한 테스트·문서화 /internal/store커버리지 중 DB 없이 테스트 가능한 순수 함수 경로 보강 /auth/oidc.go가RandomToken오류를 무시하고 state·nonce·verifier를 만드는 부분 정리 /internal/api통합 테스트를 로컬에서 돌리는 방법 문서화와make test-integration타깃 / 쿼리로만 GPU로 분류되는 별칭 지표가feature_enabled없이 빈 목록을 받는 잔여 간극 - 릴리즈: v1.4.12 (2026-09-10, run 2026-09-10-135106-jupiq-improve)
umm
- 선택: 내보내기가 쓰는 이름을 자기 생각에 붙인 사람 (가치 4 / 위험 1 / 작업량 S)
- 결과: 성공
- 요약: Markdown 내보내기는 백업이고 끝에
## Connections·## Lines of thinking두 절을 붙이는데, 가져오기가 그 둘을 제목만 보고 알아봤습니다. 그런데 “Connections”는 생각에 붙이기 아주 흔한 제목이고(가져오기 자신의 주석도 “an ordinary thing to title a thought”라고 적어 둠), umm의 파일 안에서는 제목만으로 사람의 생각과 내보내기의 목록을 구별할 수 없습니다. 목록으로 읽힌 그 절은 산문에서 연결을 하나도 뽑지 못한 채 통째로 버려져 생각이 복원에서 사라졌고,- id:도 등록되지 않으므로 그 생각으로 그어져 있던 연결은 답할 생각이 없는 id를 가리키게 되어 함께 사라졌습니다 — 사람이 가장 다시 쓸 법한 제목 하나가 가장 비싼 값을 치르던 자리입니다. 내보내기는 이미 그 차이를 적어 두고 있어서(생각 절은 언제나 id·type·source·color·canvas 메타데이터로 끝나고, 닫는 두 절은 결코 그렇지 않음 — 마지막 줄이 연결이거나 갈래) 절 꼬리로 판정하게 했습니다. v0.71.5가 메타데이터를 읽는 그 꼬리와 같은 자리라 형식은 그대로이고, 이미 받아 둔 백업 파일도 그대로 복원됩니다. 검증: 새 vitest 2개 추가 후 옛 판정(return exportSections.has(title))으로 되돌리면 둘 다 실패함을 확인, 실제 PostgreSQL 17(도커umm-test-pg, DSNpostgres://umm:umm@127.0.0.1:15433/umm)에 대한go test -p 1 ./...전체 통과 ·go vet ./...· tsc · oxlint/Prettier · i18n 994키 · vitest 158개 ·scripts/check-version.sh통과. 버전은 올리지 않았습니다(릴리스는 별도 세션). - 보류 아이디어: 재시도 초안(
formatImportedThoughts)은 메타데이터가 하나도 없는 생각에## Connections제목을 그대로 써서 같은 결함이 한 발짝 옆에 남음 — 붙여넣기 혼합 + 일부 실패에서만 닿음 / 업로드 라벨에 든 경로가safeFilename에서 구분자만 지워져C:\a\b.png가Cab.png로 붙음 — 마지막 조각만 취해야 함 / Markdown 백업에 그림이 담기지 않는 사실이 릴리스 노트에만 있고 내보내기 메뉴·user-guide·features.md 어디에도 없음 /usableSections가 서로 다른 부에 같은 제목이 오는 제안을 막지 않아 같은 이름의 부가 두 번 열림 /exportOutline의 내려받기 이름이 언제나umm-outline.md라 여러 공간의 개요를 받으면 구분할 수 없음
← 대시보드 · Atom 피드 · 원본 데이터 runs.jsonl