[{"data":1,"prerenderedAt":933},["ShallowReactive",2],{"doc:\u002Fhistory\u002Fhistory.20260616":3},{"id":4,"title":5,"body":6,"description":889,"extension":926,"meta":927,"navigation":928,"path":929,"seo":930,"stem":931,"__hash__":932},"docs\u002Fhistory\u002Fhistory.20260616.md","작업 이력 — 2026-06-16",{"type":7,"value":8,"toc":888},"minimark",[9,13,18,28,32,96,138,150,162,208,211,244,248,261,265,268,275,393,400,490,493,512,516,527,531,566,571,650,656,729,732,789,792,811,814,824,831,842,849,858,864,873,879],[10,11,5],"h1",{"id":12},"작업-이력-2026-06-16",[14,15,17],"h2",{"id":16},"기획-planner","기획 (planner)",[19,20,22,23,27],"h3",{"id":21},"a-요구사항-정의서-정본화-docsrequirementsmd-신규","A. 요구사항 정의서 정본화 — ",[24,25,26],"code",{},"docs\u002FREQUIREMENTS.md"," 신규",[29,30,31],"p",{},"분산돼 있던 요구사항(CLAUDE.md·ROADMAP.md·ADMIN-PLAN.md·PROJECT-INQUIRY-ANALYSIS.md)을 단일 정본으로 집약. 분산 문서는 대체하지 않고 요약·상호참조(링크).",[33,34,35,48,90,93],"ul",{},[36,37,38,39,43,44,47],"li",{},"기능 요구사항 ",[40,41,42],"strong",{},"FR-1~FR-12"," (각 수용기준 AC 포함), 비기능 요구사항 ",[40,45,46],{},"NFR-1~NFR-8",".",[36,49,50,57,58,61,62],{},[40,51,52,53,56],{},"안전가드(NFR-1)는 운영 MySQL ",[24,54,55],{},"hp_setting"," safety 시드값과 일치"," — 근거: ",[24,59,60],{},"malgn-helper-api\u002Fmigrations\u002F002_admin_console.sql"," INSERT 4-4 (라인 128~134). 값:\n",[33,63,64,76],{},[36,65,66,69,70,69,73],{},[24,67,68],{},"confidence_threshold=0.6"," · ",[24,71,72],{},"escalate_on_low=true",[24,74,75],{},"pii_masking=true",[36,77,78,81,82,85,86],{},[24,79,80],{},"pii_patterns","=주민번호·전화·이메일 정규식 3종(JSON) · ",[24,83,84],{},"blocked_words","=",[87,88,89],"span",{},"\"비방\",\"욕설\",\"스팸\",\"광고\"",[36,91,92],{},"Phase 1\u002F2 범위 경계, 표준답변 우선(FR-1)·출처 인용(FR-2)·\"모름\"\u002F에스컬레이션(FR-3)·비공개 자료 비노출(FR-10) 정책 명문화.",[36,94,95],{},"추적성 매트릭스(요구사항↔화면\u002F데이터\u002F로드맵), 정책 결정\u002F미정 TODO 5건(Q1~Q5), 후속 핸드오프.",[29,97,98,101,102,105,106,109,110,109,113,109,116,109,119,109,122,125,126,129,130,133,134,137],{},[40,99,100],{},"발견한 불일치(Q2)",": ",[24,103,104],{},"hp_service"," 시드 슬러그(",[24,107,108],{},"step","\u002F",[24,111,112],{},"lms-general",[24,114,115],{},"lms-mixed",[24,117,118],{},"lms-private",[24,120,121],{},"lms-public-security",[24,123,124],{},"lms-global",")와 ADMIN-PLAN §4-3-1·",[24,127,128],{},"use-bots.ts","(",[24,131,132],{},"lms-general\u002F-refund\u002F-public\u002F-security\u002F-hybrid\u002F-global",")가 ",[40,135,136],{},"불일치",". 정본 슬러그 세트 확정 필요(DBA+기획).",[19,139,141,142,145,146,149],{"id":140},"b-bots-화면-기획-결정-docsadmin-planmd-4-13-신설","B. ",[24,143,144],{},"bots\u002F"," 화면 기획 결정 — ",[24,147,148],{},"docs\u002FADMIN-PLAN.md"," §4-13 신설",[29,151,152,155,156,129,158,161],{},[24,153,154],{},"malgn-helper-admin\u002Fpages\u002Fbots\u002F{index,[id]}.vue"," + ",[24,157,128],{},[24,159,160],{},"use-admin-menu.ts:39"," 메뉴)로 기획 없이 선구현된 \"봇 관리\" 화면 검토.",[33,163,164,181,187],{},[36,165,166,169,170,109,173,176,177,180],{},[40,167,168],{},"결정: 정식 편입 + 백엔드 완성 전까지 메뉴 숨김."," 봇 단위 거버넌스(서비스별 말투·답변범위·\"모름\" 강도·에스컬레이션 임계값·가시성·학습소스)는 분류 정책·P2 다중 챗봇과 정합해 가치 있음. 단 현재 localStorage 데모뿐(",[24,171,172],{},"hp_bot",[24,174,175],{},"hp_bot_source","·",[24,178,179],{},"\u002Fadmin\u002Fbots"," 부재)이라 노출 시 오인 위험 → 백엔드 게이트.",[36,182,183,184,186],{},"ADMIN-PLAN 반영: IA 트리·§3-2 메뉴표(2.0, 숨김 표기)·§4-13 화면 명세·§5-3 API(",[24,185,179],{},")·§9 결정 로그.",[36,188,189,101,192,194,195,197,198,129,201,204,205,207],{},[40,190,191],{},"후속(api-developer·DBA)",[24,193,172],{},"(페르소나·답변범위·모델 파라미터)·",[24,196,175],{},"(bot↔material\u002Fstandard_answer 다대다) 신설 + ",[24,199,200],{},"GET\u002FPOST\u002FPATCH\u002FDELETE \u002Fadmin\u002Fbots",[24,202,203],{},"requireRole('admin','developer')","). ",[24,206,128],{},"가 이미 해당 스키마에 1:1 매핑.",[19,209,210],{"id":210},"핸드오프",[33,212,213,224,232,238],{},[36,214,215,218,219,109,221,223],{},[40,216,217],{},"DBA",": Q2 슬러그 정합, FR-12 편입 시 ",[24,220,172],{},[24,222,175],{}," DDL.",[36,225,226,101,229,231],{},[40,227,228],{},"api-developer",[24,230,179],{}," CRUD, safety 설정값 실제 적용 검증(NFR-1 AC).",[36,233,234,237],{},[40,235,236],{},"QA",": NFR-1(safety 화면=시드)·FR-2(출처 누락 차단)·FR-3(저신뢰 분기) 테스트.",[36,239,240,243],{},[40,241,242],{},"개인정보보호관리자",": Q3 비공개 자료·PII 노출 규칙 확정.",[19,245,247],{"id":246},"생성수정-파일","생성\u002F수정 파일",[33,249,250,255],{},[36,251,252,253],{},"신규: ",[24,254,26],{},[36,256,257,258,260],{},"수정: ",[24,259,148],{}," (IA 트리·메뉴표·§4-13·§5-3·§9)",[14,262,264],{"id":263},"보안-수정-security-reviewer-deployer","보안 수정 (security-reviewer → deployer)",[29,266,267],{},"무인증 노출 라우트 차단 + 디버그 라우트 제거. api\u002Fadmin 2개 레포 배포.",[19,269,271,274],{"id":270},"malgn-helper-api-cloudflare-workers",[24,272,273],{},"malgn-helper-api"," — Cloudflare Workers",[33,276,277,292,303,336,346,357],{},[36,278,279,101,282,285,286,101,289],{},[40,280,281],{},"커밋",[24,283,284],{},"41d6f6a"," \u002F ",[40,287,288],{},"Version ID",[24,290,291],{},"31dbb92a-f503-4e76-965a-3d60fe73a885",[36,293,294,101,297],{},[40,295,296],{},"URL",[298,299,300],"a",{"href":300,"rel":301},"https:\u002F\u002Fmalgn-helper-api.malgnsoft.workers.dev",[302],"nofollow",[36,304,305,306,109,309,312,313,176,316,176,319,176,322,325,326,329,330,325,333,335],{},"무인증 노출 라우트에 ",[24,307,308],{},"requireAuth",[24,310,311],{},"requireRole"," 가드 추가: ",[24,314,315],{},"\u002Fadmin\u002Fkpi",[24,317,318],{},"\u002Fadmin\u002Fcost",[24,320,321],{},"\u002Fadmin\u002Fevals",[24,323,324],{},"\u002Fimage-assets","(GET·",[24,327,328],{},"\u002F:id",")·",[24,331,332],{},"\u002Fstandard-answers",[24,334,328],{},"·PATCH·DELETE). 가드 정의 블록 TDZ 회피 위해 파일 상단으로 이전.",[36,337,338,339,342,343,47],{},"무인증 디버그\u002F일회용 라우트 6개 삭제: ",[24,340,341],{},"GET \u002Fdb\u002Fping·\u002Fdb\u002Fwhoami·\u002Fdb\u002Ftables·\u002Fdb\u002Fcolumns\u002F:table·\u002Fdb\u002Fsample\u002F:table",", ",[24,344,345],{},"POST \u002Fadmin\u002Fmigrate\u002Fhp_image_asset",[36,347,348,349,352,353,356],{},"빌드: ",[24,350,351],{},"tsc --noEmit"," 11 baseline(신규 0, working↔HEAD 동일 확인), ",[24,354,355],{},"wrangler deploy --dry-run"," 번들 정상.",[36,358,359,101,362,176,364,176,366,176,368,176,370,372,373,376,377,176,380,176,383,372,386,389,390,392],{},[40,360,361],{},"검증(무인증 curl)",[24,363,315],{},[24,365,321],{},[24,367,318],{},[24,369,324],{},[24,371,332],{}," → ",[40,374,375],{},"모두 401","(이전 200 차단). ",[24,378,379],{},"\u002Fdb\u002Fsample\u002Ftb_user",[24,381,382],{},"\u002Fdb\u002Ftables",[24,384,385],{},"\u002Fdb\u002Fping",[40,387,388],{},"모두 404","(제거 확인). 루트 ",[24,391,109],{}," → 200.",[19,394,396,399],{"id":395},"malgn-helper-admin-cloudflare-pages",[24,397,398],{},"malgn-helper-admin"," — Cloudflare Pages",[33,401,402,429,452,462],{},[36,403,404,101,406,285,409,101,412,285,416,101,419,285,423,101,426],{},[40,405,281],{},[24,407,408],{},"f1a4b20",[40,410,411],{},"production",[298,413,414],{"href":414,"rel":415},"https:\u002F\u002Fmalgn-helper-admin.pages.dev",[302],[40,417,418],{},"alias",[298,420,421],{"href":421,"rel":422},"https:\u002F\u002Fe9c060c8.malgn-helper-admin.pages.dev",[302],[40,424,425],{},"buildId",[24,427,428],{},"21f06fa3-6824-4219-a009-30b1865b479e",[36,430,431,176,434,176,437,176,440,443,444,447,448,451],{},[24,432,433],{},"index.vue",[24,435,436],{},"cost.vue",[24,438,439],{},"qa-evals.vue",[24,441,442],{},"images.vue"," fetch에 ",[24,445,446],{},"credentials:'include'"," 추가(api 가드와 정합). ",[24,449,450],{},"pages\u002Fforbidden.vue"," 신설.",[36,453,348,454,457,458,461],{},[24,455,456],{},"nuxt build"," 통과(",[24,459,460],{},"forbidden"," 신규 chunk 산출 확인).",[36,463,464,467,468,470,471,372,474,477,478,176,480,176,483,176,486,489],{},[40,465,466],{},"검증(curl)",": prod ",[24,469,109],{}," 200(buildId 마커 확인). ",[24,472,473],{},"\u002Fforbidden",[40,475,476],{},"200","(이전 404 해소, alias도 200). 연동 4페이지 ",[24,479,109],{},[24,481,482],{},"\u002Fcost",[24,484,485],{},"\u002Fqa-evals",[24,487,488],{},"\u002Fimages"," → 모두 200(SSR 셸).",[19,491,492],{"id":492},"정합",[33,494,495,509],{},[36,496,497,498,501,502,176,505,508],{},"두 레포 working tree clean, ",[24,499,500],{},"origin\u002Fmain"," 동기화 완료(미푸시였던 직전 배포 커밋 ",[24,503,504],{},"9cefd48",[24,506,507],{},"2d1acf9","도 함께 푸시되어 origin↔live 일치).",[36,510,511],{},"경계 준수: mng(docs)·pms 미변경.",[14,513,515],{"id":514},"서비스토큰-관찰모드-security-reviewer-deployer","서비스토큰 관찰모드 (security-reviewer → deployer)",[29,517,518,519,526],{},"PMS→API 서비스토큰 가드 + LLM rate limit ",[40,520,521,522,525],{},"관찰모드(",[24,523,524],{},"SERVICE_TOKEN_ENFORCE=0",")"," 점진 도입. api\u002Fpms 2개 레포 동시 배포 + 시크릿 양쪽 등록. 하드차단(1) 아님 — 회귀 없음.",[19,528,530],{"id":529},"시크릿환경-등록","시크릿\u002F환경 등록",[33,532,533,544,553],{},[36,534,535,536,539,540,543],{},"강한 랜덤 토큰(",[24,537,538],{},"openssl rand -hex 32",", 64 hex, prefix ",[24,541,542],{},"a999ed…",") 1개를 양쪽에 동일 등록(값 비노출).",[36,545,546,101,549,552],{},[40,547,548],{},"api",[24,550,551],{},"wrangler secret put PMS_SERVICE_TOKEN"," (Worker) — secret list 확인.",[36,554,555,101,558,561,562,565],{},[40,556,557],{},"pms",[24,559,560],{},"wrangler pages secret put NUXT_PMS_SERVICE_TOKEN"," — ",[40,563,564],{},"Production + Preview"," 양쪽 등록 확인(Value Encrypted).",[19,567,569,274],{"id":568},"malgn-helper-api-cloudflare-workers-1",[24,570,273],{},[33,572,573,585,592,606,629,641],{},[36,574,575,101,577,285,580,101,582],{},[40,576,281],{},[24,578,579],{},"85025ce",[40,581,288],{},[24,583,584],{},"aced5319-a03b-470c-87b1-8483a12e592d",[36,586,587,101,589],{},[40,588,296],{},[298,590,300],{"href":300,"rel":591},[302],[36,593,594,597,598,601,602,605],{},[24,595,596],{},"requireServiceToken","(관찰: ",[24,599,600],{},"X-Service-Token-Status: missing|mismatch"," 헤더만, ENFORCE≠1이면 통과) + ",[24,603,604],{},"rateLimitLlm","(route+IP 키) 미들웨어 신설.",[36,607,608,609,176,612,176,615,176,618,621,622,624,625,628],{},"적용: LLM generate 4종(",[24,610,611],{},"\u002Fpms\u002Fprojects\u002F:id\u002Fbriefing\u002Fgenerate",[24,613,614],{},"\u002Fpms\u002Fposts\u002F:id\u002Fannounce-eval\u002Fgenerate",[24,616,617],{},"\u002Fpms\u002Fposts\u002F:id\u002Feval\u002Fgenerate",[24,619,620],{},"\u002Fpms\u002Fprojects\u002F:id\u002Fstandard-answer-suggestions",")에 guard+ratelimit, ",[24,623,332],{},"(POST)·",[24,626,627],{},"\u002Fstandard-answers\u002F:id\u002Fuse","에 guard.",[36,630,631,101,634,637,638,47],{},[24,632,633],{},"wrangler.jsonc",[24,635,636],{},"[[ratelimits]] RL_LLM","(12\u002F60s), ",[24,639,640],{},"vars.SERVICE_TOKEN_ENFORCE=\"0\"",[36,642,643,644,342,647,47],{},"배포 바인딩 확인: ",[24,645,646],{},"env.RL_LLM (12 requests\u002F60s)",[24,648,649],{},"env.SERVICE_TOKEN_ENFORCE (\"0\")",[19,651,653,399],{"id":652},"malgn-helper-pms-cloudflare-pages",[24,654,655],{},"malgn-helper-pms",[33,657,658,683,700,724],{},[36,659,660,101,662,285,665,101,667,285,671,101,673,285,677,101,680],{},[40,661,281],{},[24,663,664],{},"e4b54f7",[40,666,411],{},[298,668,669],{"href":669,"rel":670},"https:\u002F\u002Fmalgn-helper-pms.pages.dev",[302],[40,672,418],{},[298,674,675],{"href":675,"rel":676},"https:\u002F\u002Fdb098a98.malgn-helper-pms.pages.dev",[302],[40,678,679],{},"deployment",[24,681,682],{},"db098a98-97b8-4f45-90af-7eeee4274b3d",[36,684,685,688,689,692,693,342,696,699],{},[24,686,687],{},"server\u002Futils\u002Fforward.ts","(서버 전용 투명 프록시, ",[24,690,691],{},"x-service-token"," 서버 주입) + 서버 프록시 라우트 5종(",[24,694,695],{},"\u002Fapi\u002Fstandard-answers",[24,697,698],{},"\u002Fapi\u002Fpms\u002F...generate"," 4종).",[36,701,702,705,706,709,710,713,714,176,717,176,720,723],{},[24,703,704],{},"nuxt.config.ts"," ",[24,707,708],{},"runtimeConfig.pmsServiceToken","(env ",[24,711,712],{},"NUXT_PMS_SERVICE_TOKEN",", public 아님). 호출부 6곳을 프록시 경유로 전환 + 안내글평가 UI(",[24,715,716],{},"AnnounceEvalCard.vue",[24,718,719],{},"eval.vue",[24,721,722],{},"types\u002Fannounce-eval.ts",").",[36,725,348,726,728],{},[24,727,456],{}," 통과, 서버 프록시 5 chunk 산출 sanity OK, project-name=malgn-helper-pms 일치.",[19,730,731],{"id":731},"검증",[33,733,734,757,780],{},[36,735,736,739,740,372,742,744,745,748,749,752,753,756],{},[40,737,738],{},"회귀 없음",": pms prod ",[24,741,109],{},[40,743,476],{},". pms 프록시 ",[24,746,747],{},"POST \u002Fapi\u002Fstandard-answers","(빈 바디) → ",[40,750,751],{},"400","(핸들러 validation 도달=토큰 일치, 가드 통과). 프록시는 ",[24,754,755],{},"x-service-token-status"," 미전파(정상, forward가 content-type\u002Fstatus\u002Fbody만 전달).",[36,758,759,762,763,176,766,372,769,771,772,775,776,779],{},[40,760,761],{},"관찰모드",": api 무인증 ",[24,764,765],{},"POST \u002Fstandard-answers",[24,767,768],{},"\u002Fpms\u002F...\u002Fbriefing\u002Fgenerate",[40,770,751],{},"(핸들러 validation, 401 아님=관찰 통과) + 헤더 ",[24,773,774],{},"X-Service-Token-Status: missing",". 잘못된 토큰 → ",[24,777,778],{},"mismatch",". 가드가 missing\u002Fmismatch 구분 + 둘 다 통과(ENFORCE=0).",[36,781,782,101,785,788],{},[40,783,784],{},"rate limit",[24,786,787],{},"RL_LLM(12\u002F60s)"," 바인딩 등록·active 확인. 단 합성 burst(순차 14·병렬 20)에서 429 미발생 — Cloudflare rate-limiting 바인딩은 best-effort\u002Feventually-consistent(버스트 언더카운트 가능). 미들웨어 배선(handler 앞)·바인딩은 정상. ENFORCE=1 전환 전 실트래픽 로그로 재확인 권장.",[19,790,492],{"id":791},"정합-1",[33,793,794,800],{},[36,795,796,797,799],{},"api·pms 두 레포 working tree clean, ",[24,798,500],{}," 동기화. mng은 이번 단계 비대상(이 history 항목만 별도 커밋 예정).",[36,801,802,803,806,807,810],{},"경계 준수: ",[24,804,805],{},"SERVICE_TOKEN_ENFORCE","는 ",[24,808,809],{},"0","(관찰) 유지 — 하드차단(1) 미전환.",[14,812,813],{"id":813},"배포",[815,816,817],"blockquote",{},[29,818,819,820,823],{},"배포 시각·커밋 로그(워크스페이스 ",[24,821,822],{},"deploy.sh"," 기록, helper repo에서 이관).",[19,825,827,828,830],{"id":826},"_2157-malgn-helper-api-cloudflare-workers","21:57 — ",[24,829,273],{}," → Cloudflare Workers",[33,832,833,839],{},[36,834,835,836,838],{},"커밋: ",[24,837,284],{}," (신규 커밋: yes)",[36,840,841],{},"메시지: fix(security): guard admin\u002Fkpi·cost·evals·image-assets·standard-answers + remove unauth \u002Fdb debug routes",[19,843,845,846,848],{"id":844},"_2158-malgn-helper-admin-cloudflare-pages","21:58 — ",[24,847,398],{}," → Cloudflare Pages",[33,850,851,855],{},[36,852,835,853,838],{},[24,854,408],{},[36,856,857],{},"메시지: fix(security): send credentials on kpi\u002Fcost\u002Fevals\u002Fimages + add \u002Fforbidden page",[19,859,861,862,830],{"id":860},"_2218-malgn-helper-api-cloudflare-workers","22:18 — ",[24,863,273],{},[33,865,866,870],{},[36,867,835,868,838],{},[24,869,579],{},[36,871,872],{},"메시지: feat(security): service-token guard + LLM rate limit on PMS routes (observe mode)",[19,874,876,877,848],{"id":875},"_2219-malgn-helper-pms-cloudflare-pages","22:19 — ",[24,878,655],{},[33,880,881,885],{},[36,882,835,883,838],{},[24,884,664],{},[36,886,887],{},"메시지: feat: service-token server proxy + announce-eval UI",{"title":889,"searchDepth":890,"depth":890,"links":891},"",3,[892,901,908,916],{"id":16,"depth":893,"text":17,"children":894},2,[895,897,899,900],{"id":21,"depth":890,"text":896},"A. 요구사항 정의서 정본화 — docs\u002FREQUIREMENTS.md 신규",{"id":140,"depth":890,"text":898},"B. bots\u002F 화면 기획 결정 — docs\u002FADMIN-PLAN.md §4-13 신설",{"id":210,"depth":890,"text":210},{"id":246,"depth":890,"text":247},{"id":263,"depth":893,"text":264,"children":902},[903,905,907],{"id":270,"depth":890,"text":904},"malgn-helper-api — Cloudflare Workers",{"id":395,"depth":890,"text":906},"malgn-helper-admin — Cloudflare Pages",{"id":492,"depth":890,"text":492},{"id":514,"depth":893,"text":515,"children":909},[910,911,912,914,915],{"id":529,"depth":890,"text":530},{"id":568,"depth":890,"text":904},{"id":652,"depth":890,"text":913},"malgn-helper-pms — Cloudflare Pages",{"id":731,"depth":890,"text":731},{"id":791,"depth":890,"text":492},{"id":813,"depth":893,"text":813,"children":917},[918,920,922,924],{"id":826,"depth":890,"text":919},"21:57 — malgn-helper-api → Cloudflare Workers",{"id":844,"depth":890,"text":921},"21:58 — malgn-helper-admin → Cloudflare Pages",{"id":860,"depth":890,"text":923},"22:18 — malgn-helper-api → Cloudflare Workers",{"id":875,"depth":890,"text":925},"22:19 — malgn-helper-pms → Cloudflare Pages","md",{},true,"\u002Fhistory\u002Fhistory.20260616",{"title":5,"description":889},"history\u002Fhistory.20260616","DMk44Minf9uzFmdNmUVzBUJN4a0FXFskmbn4YHE4hMs",1783932938622]