Files
KArtSell.Aegis/deployment/phase3-release/host/wwwroot/assets/SellDecisionPage-KoZ_jFeF.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.9 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-Oc-06-HB.js";import{$ as u,A as d,B as f,J as p,O as m,Q as h,St as g,X as _,Y as v,Z as y,c as b,h as x,l as S,m as C,nt as ee,o as w,r as T,rt as E,s as D,u as O,z as k}from"./runtime-core.esm-bundler-A2Q6NZG4.js";import{s as A}from"./runtime-dom.esm-bundler-7yXRjKLN.js";import{D as j,N as M,O as N,_ as P,l as F,r as I,t as L}from"./utils-BUXxiAyR.js";import{n as R}from"./mutation-BAbk2NkM.js";import{n as z}from"./KsInlineMessage-t_pxFiJO.js";import{i as B,l as V,r as H,s as U}from"./components-KD31URIh.js";import{t as W}from"./PageLayout-D6g_--o1.js";import{t as G}from"./QueryStateBoundary-DmXIS44q.js";var K=class extends M{#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),j(this.options,t)||this.#e.getMutationCache().notify({type:`observerOptionsUpdated`,mutation:this.#n,observer:this}),t?.mutationKey&&this.options.mutationKey&&P(t.mutationKey)!==P(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??R();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){F.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 q(e,t){let n=t||l(),r=w(()=>{let t=typeof e==`function`?e():e;return n.defaultMutationOptions(L(t))}),i=new K(n,r.value),a=r.value.shallow?h(i.getCurrentResult()):v(i.getCurrentResult()),o=i.subscribe(e=>{I(a,e)}),s=(e,t)=>{i.mutate(e,t).catch(()=>{})};k(r,()=>{i.setOptions(r.value)}),p(()=>{o()});let c=r.value.shallow?u(a):_(a),d=ee(c);return k(()=>a.error,e=>{if(e&&N(r.value.throwOnError,[e]))throw e}),{...d,mutate:s,mutateAsync:a.mutate,reset:a.reset}}var J={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`},Y;Y||={};var X={"aria-labelledby":`policy-trace-title`},Z={key:0},Q={key:1},te=x({__name:`PolicyTracePanel`,props:{entries:{},schemaVersion:{}},setup(e){let t=e,n={0:`NOT_APPLICABLE`,1:`BLOCKED`,2:`APPLIED`},r=w(()=>[...t.entries].sort((e,t)=>t.priority-e.priority));return(e,i)=>(m(),O(`section`,X,[i[0]||=D(`h2`,{id:`policy-trace-title`},`정책 우선순위 추적`,-1),D(`p`,null,`Trace schema v`+g(t.schemaVersion)+` · 상위 정책부터 평가된 불변 증거입니다.`,1),D(`ol`,null,[(m(!0),O(T,null,d(r.value,e=>(m(),O(`li`,{key:`${e.priority}-${e.policyId}`},[D(`strong`,null,g(e.policyId),1),D(`span`,null,g(n[e.disposition])+` · `+g(e.reasonCode),1),e.requestedSellRatioOfLot>0?(m(),O(`span`,Z,` · 요청 `+g(e.requestedSellRatioOfLot)+` / 적용 `+g(e.appliedSellRatioOfLot),1)):S(``,!0),e.strategicCoreClampApplied?(m(),O(`span`,Q,` · Strategic Core clamp`)):S(``,!0)]))),128))])]))}}),$=e().min(0).max(1),ne=a({policyId:s().min(1),priority:e().int(),disposition:t([n(0),n(1),n(2)]),reasonCode:s().min(1),requestedSellRatioOfLot:$,appliedSellRatioOfLot:$,strategicCoreClampApplied:o()}),re=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:$,currentLotPortfolioWeight:$,strategicCoreFloorWeight:$,hardImpairmentApproved:o(),capitalFloorBreached:o(),survivalSellRatioOfLot:$,gapBelowFloorAtr:e().min(0),consecutiveCloseBreaches:e().int().min(0),cooldownSatisfied:o(),concentrationSellRatioOfLot:$,opportunityEdgeLowerBound:e(),opportunitySellRatioOfLot:$}).superRefine((e,t)=>{e.currentLotPortfolioWeight>e.currentSecurityPortfolioWeight&&t.addIssue({code:J.custom,message:`Lot 비중은 종목 전체 비중을 초과할 수 없습니다.`,path:[`currentLotPortfolioWeight`]}),new Date(e.publishedAtCutoff)>new Date(e.asOf)&&t.addIssue({code:J.custom,message:`공개 가능 시각은 평가 시각 이후일 수 없습니다.`,path:[`publishedAtCutoff`]})}),ie=a({action:c([`Hold`,`PartialSell`,`FullSell`]),sellRatioOfLot:$,targetSecurityPortfolioWeightAfter:$,policyId:s().min(1),reasonCode:s().min(1),decisionContractVersion:n(`sell-decision.v2`),policyTraceSchemaVersion:n(2),reentryEligible:o(),policyTrace:i(ne),evidenceStatus:n(`RESEARCH_CANDIDATE_NOT_PRODUCTION`)});async function ae(e){let t=re.parse(e.request),{data:n}=await r.post(`/internal/v1/research/sell-policy/evaluate`,t,{headers:{"Idempotency-Key":e.idempotencyKey}});return ie.parse(n)}function oe(){return q({mutationFn:ae})}var se={key:0,class:`ks-stack`},ce=x({__name:`SellDecisionPage`,setup(e){let t=oe(),n=y(!1),r=y(!1),i=y(1.6),a=y(0),o=y(null),s=w(()=>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 u(){o.value&&t.mutate(o.value)}return(e,o)=>(m(),b(W,{title:`매도 정책 연구 콘솔`,subtitle:`순수 정책 계약과 우선순위를 확인하는 연구 전용 화면입니다. 고객 제안·공개·주문 기능과 연결되지 않습니다.`,status:`RESEARCH_CANDIDATE_NOT_PRODUCTION · 자동주문 OFF`},{default:f(()=>[D(`form`,{onSubmit:A(l,[`prevent`])},[C(E(H),{title:`연구 입력 벡터`},{actions:f(()=>[C(E(z),{type:`submit`,label:`정책 평가`,loading:s.value},null,8,[`loading`])]),default:f(()=>[C(E(B),{columns:2,"aria-label":`연구 입력 벡터`},{default:f(()=>[C(E(V),{modelValue:n.value,"onUpdate:modelValue":o[0]||=e=>n.value=e,label:`Hard impairment 승인`,disabled:s.value},null,8,[`modelValue`,`disabled`]),C(E(V),{modelValue:r.value,"onUpdate:modelValue":o[1]||=e=>r.value=e,label:`자본바닥 위반`,disabled:s.value},null,8,[`modelValue`,`disabled`]),C(E(U),{modelValue:i.value,"onUpdate:modelValue":o[2]||=e=>i.value=e,label:`보호선 이탈 ATR`,min:0,"max-fraction-digits":2,disabled:s.value},null,8,[`modelValue`,`disabled`]),C(E(U),{modelValue:a.value,"onUpdate:modelValue":o[3]||=e=>a.value=e,label:`연속 종가 이탈`,min:0,"max-fraction-digits":0,disabled:s.value},null,8,[`modelValue`,`disabled`])]),_:1})]),_:1})],32),C(G,{loading:s.value,error:E(t).error.value,empty:!E(t).data.value,onRetry:u},{default:f(()=>[E(t).data.value?(m(),O(`dl`,se,[o[4]||=D(`dt`,null,`행동`,-1),D(`dd`,null,g(E(t).data.value.action),1),o[5]||=D(`dt`,null,`정책`,-1),D(`dd`,null,g(E(t).data.value.policyId),1),o[6]||=D(`dt`,null,`사유`,-1),D(`dd`,null,g(E(t).data.value.reasonCode),1),o[7]||=D(`dt`,null,`Lot 매도비율`,-1),D(`dd`,null,g(E(t).data.value.sellRatioOfLot),1),o[8]||=D(`dt`,null,`매도 후 종목 비중`,-1),D(`dd`,null,g(E(t).data.value.targetSecurityPortfolioWeightAfter),1),o[9]||=D(`dt`,null,`재진입 가능`,-1),D(`dd`,null,g(E(t).data.value.reentryEligible),1),o[10]||=D(`dt`,null,`결정 계약`,-1),D(`dd`,null,g(E(t).data.value.decisionContractVersion),1),o[11]||=D(`dt`,null,`정책 추적`,-1),D(`dd`,null,g(E(t).data.value.policyTrace.length)+`단계`,1)])):S(``,!0),E(t).data.value?(m(),b(te,{key:1,entries:E(t).data.value.policyTrace,"schema-version":E(t).data.value.policyTraceSchemaVersion},null,8,[`entries`,`schema-version`])):S(``,!0)]),_:1},8,[`loading`,`error`,`empty`])]),_:1}))}});export{ce as default};