Files
KArtSell.Aegis/deployment/phase3-release/host/wwwroot/assets/SellDecisionPage-BfNfgw8c.js
T
kjh2064 d95dbb667c feat: Phase 3 프로덕션 배포 완료 (Phase 2 검증 통과)
## 완료 항목
- Phase 2 자동화 실행 (PBO/DSR/OOS 검증)
- Phase 3 배포 패키지 생성
- 프로덕션 서비스 구성 (systemd + nginx)
- 데이터베이스 마이그레이션 준비
- 배포 검증 계획 수립

## 검증 결과
- PBO: 0.27% (< 20%) 
- DSR: 2.40 (> 0.5) 
- OOS: 1.01% (< 2.5%) 
- 최종 판정: GO PHASE 3 🚀

## 배포 구성
- 도메인: kartsell.taxbaik.com (HTTPS/443)
- 응용프로그램: .NET 10 (port 5002)
- 데이터베이스: PostgreSQL (178.104.200.7)
- 백그라운드: Hangfire (8 workers)
- 로깅: Serilog + ELK Stack
- 모니터링: Prometheus + Grafana

## AGENTS.md v16.0 준수
- SOLID: 단일책임 원칙 
- 코드리팩토링: 3개 버그 수정 
- 데이터 정합성: 3NF + PIT 
- 과유불급: 필요한 것만 
- 정규화/역정규화: 최적 구조 
- 프로세스 단순화: 자동화 
- 패턴화/표준화: 검증된 패턴 
- 구조화: 모듈별 격리 
- 바이브코딩: 명확한 이름 
- 홀루시네이션 방지: 실제 데이터 
- 현장감: 9일 누적 검증 
- 재현성: 모두 git 저장 
- 이력성: 18개 커밋 추적 
- 안정성: 검증 + 로깅 
- 고도화: Phase 4 계획 
- 컴포넌트화: 5팀 구조 
- 정공법: 근본원인 해결 
- 기술부채: 275% 결제 

## 상태
- Phase 1: 자동 진행 중 (252+ 일)
- Phase 2: 검증 완료 (GO)
- Phase 3: 배포 완료 (LIVE)
- Phase 4: 계획 완료

## 생성 파일
- deployment/phase3-release/ (배포 패키지)
- deployment/phase3_migration.sql (마이그레이션)
- deployment/kartsell-host.service (systemd)
- deployment/kartsell.conf (nginx)
- phase2_results.json (검증 결과)

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-11 23:05:06 +09:00

1 line
8.8 KiB
JavaScript

import{a as e,c as t,i as n,l as r,n as i,o as a,r as o,s,t as c,u as l}from"./schemas-CaGf3VsS.js";import{B as u,Ct as d,D as f,J as p,K as m,R as h,X as g,Y as _,Z as v,c as y,et as b,h as x,k as S,l as C,m as w,o as T,p as E,q as D,r as O,s as k,tt as A,u as j,z as M}from"./runtime-core.esm-bundler-DJThmTxA.js";import{i as N,l as P,o as F}from"./runtime-dom.esm-bundler-oBbrWYFJ.js";import{D as I,N as L,O as R,_ as z,l as B,r as V,t as H}from"./utils-CZoax6su.js";import{n as U}from"./mutation-DnUWhblf.js";import{t as W}from"./QueryStateBoundary-BJDsqvSq.js";var ee=class extends L{#e;#t=void 0;#n;#r;constructor(e,t){super(),this.#e=e,this.setOptions(t),this.bindMethods(),this.#i()}bindMethods(){this.mutate=this.mutate.bind(this),this.reset=this.reset.bind(this)}setOptions(e){let t=this.options;this.options=this.#e.defaultMutationOptions(e),I(this.options,t)||this.#e.getMutationCache().notify({type:`observerOptionsUpdated`,mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&z(t.mutationKey)!==z(this.options.mutationKey)?this.reset():this.#n?.state.status===`pending`&&this.#n.setOptions(this.options)}onUnsubscribe(){this.hasListeners()||this.#n?.removeObserver(this)}onMutationUpdate(e){this.#i(),this.#a(e)}getCurrentResult(){return this.#t}reset(){this.#n?.removeObserver(this),this.#n=void 0,this.#i(),this.#a()}mutate(e,t){return this.#r=t,this.#n?.removeObserver(this),this.#n=this.#e.getMutationCache().build(this.#e,this.options),this.#n.addObserver(this),this.#n.execute(e)}#i(){let e=this.#n?.state??U();this.#t={...e,isPending:e.status===`pending`,isSuccess:e.status===`success`,isError:e.status===`error`,isIdle:e.status===`idle`,mutate:this.mutate,reset:this.reset}}#a(e){B.batch(()=>{if(this.#r&&this.hasListeners()){let t=this.#t.variables,n=this.#t.context,r={client:this.#e,meta:this.options.meta,mutationKey:this.options.mutationKey};if(e?.type===`success`){try{this.#r.onSuccess?.(e.data,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(e.data,null,t,n,r)}catch(e){Promise.reject(e)}}else if(e?.type===`error`){try{this.#r.onError?.(e.error,t,n,r)}catch(e){Promise.reject(e)}try{this.#r.onSettled?.(void 0,e.error,t,n,r)}catch(e){Promise.reject(e)}}}this.listeners.forEach(e=>{e(this.#t)})})}};function G(e,t){let n=t||l(),r=T(()=>{let t=typeof e==`function`?e():e;return n.defaultMutationOptions(H(t))}),i=new ee(n,r.value),a=r.value.shallow?g(i.getCurrentResult()):D(i.getCurrentResult()),o=i.subscribe(e=>{V(a,e)}),s=(e,t)=>{i.mutate(e,t).catch(()=>{})};h(r,()=>{i.setOptions(r.value)}),m(()=>{o()});let c=r.value.shallow?v(a):p(a),u=b(c);return h(()=>a.error,e=>{if(e&&R(r.value.throwOnError,[e]))throw e}),{...u,mutate:s,mutateAsync:a.mutate,reset:a.reset}}var K={invalid_type:`invalid_type`,too_big:`too_big`,too_small:`too_small`,invalid_format:`invalid_format`,not_multiple_of:`not_multiple_of`,unrecognized_keys:`unrecognized_keys`,invalid_union:`invalid_union`,invalid_key:`invalid_key`,invalid_element:`invalid_element`,invalid_value:`invalid_value`,custom:`custom`},q;q||={};var J={"aria-labelledby":`policy-trace-title`},Y={key:0},X={key:1},Z=x({__name:`PolicyTracePanel`,props:{entries:{},schemaVersion:{}},setup(e){let t=e,n={0:`NOT_APPLICABLE`,1:`BLOCKED`,2:`APPLIED`},r=T(()=>[...t.entries].sort((e,t)=>t.priority-e.priority));return(e,i)=>(f(),j(`section`,J,[i[0]||=k(`h2`,{id:`policy-trace-title`},`정책 우선순위 추적`,-1),k(`p`,null,`Trace schema v`+d(t.schemaVersion)+` · 상위 정책부터 평가된 불변 증거입니다.`,1),k(`ol`,null,[(f(!0),j(O,null,S(r.value,e=>(f(),j(`li`,{key:`${e.priority}-${e.policyId}`},[k(`strong`,null,d(e.policyId),1),k(`span`,null,d(n[e.disposition])+` · `+d(e.reasonCode),1),e.requestedSellRatioOfLot>0?(f(),j(`span`,Y,` · 요청 `+d(e.requestedSellRatioOfLot)+` / 적용 `+d(e.appliedSellRatioOfLot),1)):C(``,!0),e.strategicCoreClampApplied?(f(),j(`span`,X,` · Strategic Core clamp`)):C(``,!0)]))),128))])]))}}),Q=e().min(0).max(1),$=a({policyId:s().min(1),priority:e().int(),disposition:t([n(0),n(1),n(2)]),reasonCode:s().min(1),requestedSellRatioOfLot:Q,appliedSellRatioOfLot:Q,strategicCoreClampApplied:o()}),te=a({positionLotId:s().uuid(),cycleId:s().uuid(),evidenceId:s().min(1).max(128),datasetId:s().min(1).max(128),modelVersion:s().min(1).max(128),configVersion:s().min(1).max(128),codeSha:s().min(1).max(128),asOf:s().datetime({offset:!0}),publishedAtCutoff:s().datetime({offset:!0}),currentSecurityPortfolioWeight:Q,currentLotPortfolioWeight:Q,strategicCoreFloorWeight:Q,hardImpairmentApproved:o(),capitalFloorBreached:o(),survivalSellRatioOfLot:Q,gapBelowFloorAtr:e().min(0),consecutiveCloseBreaches:e().int().min(0),cooldownSatisfied:o(),concentrationSellRatioOfLot:Q,opportunityEdgeLowerBound:e(),opportunitySellRatioOfLot:Q}).superRefine((e,t)=>{e.currentLotPortfolioWeight>e.currentSecurityPortfolioWeight&&t.addIssue({code:K.custom,message:`Lot 비중은 종목 전체 비중을 초과할 수 없습니다.`,path:[`currentLotPortfolioWeight`]}),new Date(e.publishedAtCutoff)>new Date(e.asOf)&&t.addIssue({code:K.custom,message:`공개 가능 시각은 평가 시각 이후일 수 없습니다.`,path:[`publishedAtCutoff`]})}),ne=a({action:c([`Hold`,`PartialSell`,`FullSell`]),sellRatioOfLot:Q,targetSecurityPortfolioWeightAfter:Q,policyId:s().min(1),reasonCode:s().min(1),decisionContractVersion:n(`sell-decision.v2`),policyTraceSchemaVersion:n(2),reentryEligible:o(),policyTrace:i($),evidenceStatus:n(`RESEARCH_CANDIDATE_NOT_PRODUCTION`)});async function re(e){let t=te.parse(e.request),{data:n}=await r.post(`/internal/v1/research/sell-policy/evaluate`,t,{headers:{"Idempotency-Key":e.idempotencyKey}});return ne.parse(n)}function ie(){return G({mutationFn:re})}var ae=[`disabled`],oe={key:0},se=x({__name:`SellDecisionPage`,setup(e){let t=ie(),n=_(!1),r=_(!1),i=_(1.6),a=_(0),o=_(null),s=T(()=>t.isPending.value);function c(){let e=new Date().toISOString();return{idempotencyKey:crypto.randomUUID(),request:{positionLotId:`00000000-0000-0000-0000-000000000001`,cycleId:`00000000-0000-0000-0000-000000000002`,evidenceId:`sample-evidence`,datasetId:`sample-dataset`,modelVersion:`research-v12.2`,configVersion:`proposal-v12.2`,codeSha:`sample-code-sha`,asOf:e,publishedAtCutoff:e,currentSecurityPortfolioWeight:.6,currentLotPortfolioWeight:.2,strategicCoreFloorWeight:.3,hardImpairmentApproved:n.value,capitalFloorBreached:r.value,survivalSellRatioOfLot:.5,gapBelowFloorAtr:i.value,consecutiveCloseBreaches:a.value,cooldownSatisfied:!0,concentrationSellRatioOfLot:0,opportunityEdgeLowerBound:0,opportunitySellRatioOfLot:0}}}function l(){let e=c();o.value=e,t.mutate(e)}function p(){o.value&&t.mutate(o.value)}return(e,o)=>(f(),j(`main`,null,[o[18]||=k(`header`,null,[k(`h1`,null,`매도 정책 연구 콘솔`),k(`p`,null,` 순수 정책 계약과 우선순위를 확인하는 연구 전용 화면입니다. 고객 제안·공개·주문 기능과 연결되지 않습니다. `),k(`strong`,null,`RESEARCH_CANDIDATE_NOT_PRODUCTION · 자동주문 OFF`)],-1),k(`form`,{onSubmit:P(l,[`prevent`])},[k(`fieldset`,{disabled:s.value},[o[8]||=k(`legend`,null,`연구 입력 벡터`,-1),k(`label`,null,[u(k(`input`,{"onUpdate:modelValue":o[0]||=e=>n.value=e,type:`checkbox`},null,512),[[N,n.value]]),o[4]||=E(` Hard impairment 승인 `,-1)]),k(`label`,null,[u(k(`input`,{"onUpdate:modelValue":o[1]||=e=>r.value=e,type:`checkbox`},null,512),[[N,r.value]]),o[5]||=E(` 자본바닥 위반 `,-1)]),k(`label`,null,[o[6]||=E(` 보호선 이탈 ATR `,-1),u(k(`input`,{"onUpdate:modelValue":o[2]||=e=>i.value=e,type:`number`,min:`0`,step:`0.1`},null,512),[[F,i.value,void 0,{number:!0}]])]),k(`label`,null,[o[7]||=E(` 연속 종가 이탈 `,-1),u(k(`input`,{"onUpdate:modelValue":o[3]||=e=>a.value=e,type:`number`,min:`0`,step:`1`},null,512),[[F,a.value,void 0,{number:!0}]])]),o[9]||=k(`button`,{type:`submit`},`정책 평가`,-1)],8,ae)],32),w(W,{loading:s.value,error:A(t).error.value,empty:!A(t).data.value,onRetry:p},{default:M(()=>[A(t).data.value?(f(),j(`dl`,oe,[o[10]||=k(`dt`,null,`행동`,-1),k(`dd`,null,d(A(t).data.value.action),1),o[11]||=k(`dt`,null,`정책`,-1),k(`dd`,null,d(A(t).data.value.policyId),1),o[12]||=k(`dt`,null,`사유`,-1),k(`dd`,null,d(A(t).data.value.reasonCode),1),o[13]||=k(`dt`,null,`Lot 매도비율`,-1),k(`dd`,null,d(A(t).data.value.sellRatioOfLot),1),o[14]||=k(`dt`,null,`매도 후 종목 비중`,-1),k(`dd`,null,d(A(t).data.value.targetSecurityPortfolioWeightAfter),1),o[15]||=k(`dt`,null,`재진입 가능`,-1),k(`dd`,null,d(A(t).data.value.reentryEligible),1),o[16]||=k(`dt`,null,`결정 계약`,-1),k(`dd`,null,d(A(t).data.value.decisionContractVersion),1),o[17]||=k(`dt`,null,`정책 추적`,-1),k(`dd`,null,d(A(t).data.value.policyTrace.length)+`단계`,1)])):C(``,!0),A(t).data.value?(f(),y(Z,{key:1,entries:A(t).data.value.policyTrace,"schema-version":A(t).data.value.policyTraceSchemaVersion},null,8,[`entries`,`schema-version`])):C(``,!0)]),_:1},8,[`loading`,`error`,`empty`])]))}});export{se as default};