# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## High-Level Architecture Overview **QuantEngine v0.1** — A hybrid quantitative analysis and data collection system for retirement asset portfolio management, transitioning from Python (legacy) to .NET 9 (primary). ### System Architecture Layers ``` ┌─────────────────────────────────────────────────────────────┐ │ Web UI Layer (Razor Pages) │ │ ├─ Admin Dashboard, Users, Collection, Monitoring, Ops │ │ └─ Server-side rendering + Cookie auth (no WASM) │ ├─────────────────────────────────────────────────────────────┤ │ .NET Web API Layer (FastEndpoints, Minimal APIs) │ │ ├─ Collection API: /api/collection/{state,runs,snapshots} │ │ ├─ Emergency password reset, auth endpoints │ │ └─ Portfolio/settings endpoints │ ├─────────────────────────────────────────────────────────────┤ │ Application Layer (QuantEngine.Application) │ │ ├─ ~22 orchestrators/services between Web and Infrastructure│ │ ├─ KisDataCollectionOrchestrator, PipelineOrchestrator, │ │ │ WorkspaceService, etc. │ │ └─ Owns cross-cutting workflows; Web/Pages call these, not │ │ Infrastructure directly │ ├─────────────────────────────────────────────────────────────┤ │ Repository Layer (Dapper ORM, SQL-driven) │ │ ├─ IWorkspaceRepository: workspace/settings CRUD │ │ ├─ ICollectionRepository: KIS runs, snapshots, errors │ │ ├─ IPortfolioRepository: asset/position data │ │ └─ Whitelist-only table access (enum-based security) │ ├─────────────────────────────────────────────────────────────┤ │ Infrastructure Layer (.NET) │ │ ├─ DbUp Migrations: src/dotnet/QuantEngine.Infrastructure/ │ │ ├─ PostgreSQL connection pooling, Npgsql 8.0 │ │ ├─ ITokenCache (KIS token management) │ │ ├─ PostProcessing: JSON normalization, schema validation │ │ └─ Contracts: Parity, Provenance, Scheduling, Normalization│ ├─────────────────────────────────────────────────────────────┤ │ KIS API Client Layer (.NET) │ │ ├─ IKisApiClient: GetCurrentPrice, AskingPrice, DailyChart │ │ ├─ Read-only mandate: quotations + ranking only │ │ ├─ Governance enforcement: AssertReadOnly(path, trId) │ │ ├─ Fallback chain: KIS → Naver → Yahoo → OpenDART │ │ └─ Token caching: PostgreSQL-backed with 10-min skew │ ├─────────────────────────────────────────────────────────────┤ │ Data Collection Pipeline (Python legacy + .NET new) │ │ ├─ Python (ops:data-collect): SQLite accumulator │ │ │ └─ tools/run_kis_data_collection_v1.py │ │ ├─ .NET: Async collection runner, PostgreSQL persistence │ │ ├─ Snapshot Admin (web UI): settings/account_snapshot edit │ │ └─ Calibration Backlog: priority + change ledger │ ├─────────────────────────────────────────────────────────────┤ │ Decision Engine & Reporting (.NET Canonical Renderer) │ │ ├─ QuantEngine.Tools: Final decision packet + report render │ │ ├─ JSON schema: schemas/operational_report.schema.json │ │ ├─ Outputs: Temp/operational_report.{json,md} │ │ └─ Contract validation via canonical JSON │ ├─────────────────────────────────────────────────────────────┤ │ Database Layer (PostgreSQL) │ │ ├─ Unified schema: quantenginedb / quantengine schema │ │ ├─ Key tables: kis_tokens, kis_collection_runs/snapshots │ │ │ workspaces, account_snapshots, settings │ │ ├─ Auto-migrations via DbUp (V1__Name.sql, V2__Name.sql...) │ │ └─ Documented in: docs/db/quantengine.dbml (DBML sync) │ ├─────────────────────────────────────────────────────────────┤ │ Validation & Quality Gates (Python) │ │ ├─ 30+ validators: specs, contracts, WBS, audits │ │ ├─ CI pipeline: 9 parallel jobs (core + 8 independent) │ │ └─ Release gates: full-gate (strict), ops:release (warn-ok) │ ├─────────────────────────────────────────────────────────────┤ │ CI/CD Orchestration (Gitea Actions) │ │ ├─ ci.yml: 10 jobs, core + contracts + reports + notify │ │ ├─ prepare-release.yml: tag + artifact + checksum │ │ ├─ deploy-prod.yml: SSH deploy + health checks │ │ ├─ kis_data_collection.yml: cron (00:30 KST) validation │ │ └─ Other workflows: snapshot admin, qualitative strategy │ └─────────────────────────────────────────────────────────────┘ ``` ### Key Design Decisions (SOLID + Domain-Driven) 1. **Razor Pages (Server-Rendered)** — No WASM; Admin UI uses server-side rendering with cookie auth. Why? Simpler security, faster initial load, team familiarity. 2. **Repository Pattern + Dapper ORM** — SQL-first, no EF. Why? Direct control over queries, performance, audit trail (raw SQL = explicit intent). 3. **Read-Only KIS Governance** — `AssertReadOnly()` blocks all /trading/ paths + TTTC*/VTTC* TR_IDs. Why? Hard mandate: data collection only, no order placement. 4. **PostgreSQL Single Source of Truth** — All collection runs, snapshots, tokens centralized. Why? Audit, reproducibility, real-time coordination. 5. **Hybrid Python-to-.NET Transition** — Python handles legacy ops:data-collect; .NET does web, API, orchestration. Why? Gradual migration reduces risk; Python stays until .NET collection fully validated. 6. **Contract-Driven Validation** — Parity, Provenance, Scheduler, Normalization contracts in CI. Why? Catch schema drift, data quality regressions before deployment. 7. **Canonical JSON Renderer** (.NET Tools) — Single source of operational_report.json schema. Why? One contract, all tools emit to it (no format wars). ## Project Overview **QuantEngine v0.1** — A comprehensive quantitative analysis and data collection system for retirement asset portfolio management. - **Architecture**: .NET 9 + C# (web UI + APIs, primary); Python (legacy data collection, gradual deprecation) - **Critical Path**: KIS API → PostgreSQL → Admin Dashboard (read-only quotations only, no trading APIs) - **Validation**: 30+ contract validators + 9 parallel CI jobs + manual release gates - **Web UI**: Razor Pages (Server-Rendered) + Bootstrap 5/Tabler + Cookie auth (Blazor/MudBlazor deprecated 2026-07-11 — see UI Design Principles below) - **Database**: PostgreSQL (Npgsql 8.0), single unified database - **Data Source**: KIS Open API (quotations/ranking read-only), with fallbacks - **Key Runtimes**: .NET 9, Python 3.9+, Node.js 16+ ### Migration Status Summary - **Phase 1** (Web UI → Razor Pages): ✅ Complete (2026-07-11) - **Phase 2** (KIS Data Collection Pipeline): 95% complete — pipeline orchestration (Python→.NET) pending - **Phase 3** (Node.js→.NET CLI Tools): Planned - **Phase 4** (CI/CD Pipeline Hardening): 80% complete — Gitea Actions runner has network limitations, manual-trigger release workflow active (see Deployment & Operations) - **Phase 5** (Admin UI & Deployment Optimization): ✅ Complete (2026-07-11) Full phase-by-phase history and detail: [docs/MIGRATION_STATUS.md](docs/MIGRATION_STATUS.md) ## Deployment & Operations **Production Server**: Hetzner Cloud `178.104.200.7` (kjh2064@178.104.200.7). Full runbook (release/deploy workflow, SSH key setup, health checks, rollback, troubleshooting): [docs/DEPLOYMENT_RUNBOOK.md](docs/DEPLOYMENT_RUNBOOK.md) ### ⚠️ CRITICAL: CI/CD-Only Deployment Mandate **Rule**: ALL production deployments MUST go through Gitea Actions CI/CD (`prepare-release.yml` → `deploy-prod.yml`, both manual `workflow_dispatch`). Manual SSH deployments are **FORBIDDEN** (audit trail, consistent health checks, rollback safety). ### ⚠️ CRITICAL: DB Secret Management (Incident 2026-07-12) **Incident**: `quant.taxbaik.com/login`이 `28P01 password authentication failed`로 장애 발생 — `appsettings.Production.json`에 하드코딩되어 배포된 DB 비밀번호가 로테이션 이후에도 옛날 값 그대로 배포되고 있었음. **Rule**: **DB 접속 문자열(`ConnectionStrings`)은 절대 `appsettings.Production.json`이나 워크플로우 파일에 하드코딩하지 않는다.** `prepare-release.yml`이 생성하는 아티팩트에는 `Logging` 설정만 있고 `ConnectionStrings`는 없다 (의도된 설계 — Gitea Release는 누구나 다운로드 가능하므로 시크릿을 담으면 안 됨). 실제 DB 비밀번호는 프로덕션 서버의 `/home/kjh2064/.config/quantengine.env` 파일에만 존재하며, `quantengine.service.d/env.conf`의 `EnvironmentFile=` 지시자로 systemd가 환경변수로 주입한다 (환경변수가 `appsettings.Production.json`을 오버라이드). **DB 비밀번호 로테이션 시**: `/home/kjh2064/.config/quantengine.env`만 갱신 + `sudo systemctl restart quantengine`. 워크플로우 파일/Gitea Secrets는 건드리지 않음. **배포 후 필수 확인**: `/Account/Login` 실제 HTTP 응답 + `journalctl -u quantengine`에서 `28P01`/`password authentication failed` 부재 확인 (단순 `active` 상태만으로는 DB 연결 실패를 못 잡음 — ASP.NET Core는 DB 없이도 기동은 되고 로그인 요청 시점에야 실패함). 6-point 헬스체크에 DB 인증 확인이 포함되어 있음. 상세: [docs/DEPLOYMENT_RUNBOOK.md](docs/DEPLOYMENT_RUNBOOK.md) ### Git Repository **Gitea Server** (동일 호스트): - **HTTP**: `https://gitea.taxbaik.com/kjh2064/QuantEngineByItz.git` - **SSH**: `ssh://git@gitea.taxbaik.com:2222/kjh2064/QuantEngineByItz.git` ## UI Design Principles - **Primary Framework**: ASP.NET Core Razor Pages (server-rendered, no WASM) + Bootstrap 5 + Tabler UI. Blazor Interactive WebAssembly, MudBlazor, SmartAdmin are **deprecated** (2026-07-11). The `.sln` has 6 projects: Core, Application, Infrastructure, Web, Tools, Core.Tests — there is no `QuantEngine.Web.Client` in the repo (fully removed, not just excluded from the solution; verified 2026-07-30). - **Authentication**: Cookie (`QuantEngine.Admin.Auth`, HttpOnly, SameSite=Lax, 12h sliding) + BCrypt + IP lockout (3 strikes/15min). Folder-level `[Authorize]` via `AuthorizeFolder("/Admin")` — no per-page repetition. Login: `/Account/Login`. - **Component rules**: PageModel constructor DI (repos/services directly, no API hop); `OnPostAsync()`/`OnPostDeleteAsync()` multi-handler pattern; validation failures return `Page()`; DataAnnotations + FluentValidation double-check. - Full component-to-Bootstrap/Tabler mapping table (buttons, inputs, tables, badges, modals, validation messages): [docs/UI_GUIDELINES.md](docs/UI_GUIDELINES.md) ## Development Commands ### .NET ```powershell cd src/dotnet dotnet restore dotnet build # Debug (0 errors, 0 warnings) dotnet build -c Release dotnet watch run --project QuantEngine.Web # Hot-reload → http://localhost:5265 dotnet test src/dotnet/QuantEngine.Core.Tests -c Release dotnet test src/dotnet/QuantEngine.sln --configuration Release ``` ### Python / Node.js (legacy & release gates) ```powershell npm install npm run ops:validate # Warn-only validation npm run full-gate # Strict validation (all gates PASS) npm run ops:data-collect # KIS collection (Python subprocess) npm run ops:release # Full release DAG npm run prepare-upload-zip # Package for distribution python3 -m pytest tests/unit/test_*.py -v ``` ### Local run (SSH tunnel required — never use Docker locally) ```powershell # Terminal 1: tunnel to remote PostgreSQL ssh -L 127.0.0.1:5432:localhost:5432 kjh2064@178.104.200.7 -N # Terminal 2: run the app cd src/dotnet dotnet watch run --project QuantEngine.Web # → http://localhost:5265/Account/Login ``` `appsettings.Development.json`: ```json { "ConnectionStrings": { "DefaultConnection": "Host=127.0.0.1;Database=quantenginedb;Username=quantengine_app;Password=quantengine_app;Search Path=quantengine;" } } ``` **⚠️ 2026-07-30 확인**: 위 `quantengine_app`/`quantengine_app` 비밀번호로 실제 프로덕션 DB에 접속을 시도하면 `password authentication failed`로 거부된다 (직접 psql 연결 테스트로 확인). 로컬 개발 시 실제 값은 `/home/kjh2064/.config/quantengine.env`의 `ConnectionStrings__DefaultConnection`을 참고할 것 — 이 문서의 예시 값을 그대로 복사해 쓰지 말 것. ### Collection pipeline testing ```powershell $env:KIS_APP_Key_TEST = "your_kis_test_key" $env:KIS_APP_Secret_TEST = "your_kis_test_secret" dotnet run --project QuantEngine.Web # → http://localhost:5265/collection — "Start Collection" triggers an async run curl http://localhost:5265/api/collection/state curl http://localhost:5265/api/collection/runs curl "http://localhost:5265/api/collection/latest/005930" ``` ### E2E / Playwright ```powershell npx playwright test npx playwright test tests/e2e/complete-admin-flow.spec.ts ``` ### Calibration & data tools ```powershell python tools/build_calibration_priority_v1.py python tools/build_calibration_change_ledger_v4.py python tools/build_calibration_review_report_v1.py python tools/validate_calibration_change_ledger_v1.py python tools/run_kis_data_collection_v1.py --input-json GatherTradingData.json --sqlite-db src/quant_engine/kis_data_collection.db --output-json Temp/kis_data_collection_v1.json --kis-account real python tools/run_snapshot_admin_server_v1.py --host 127.0.0.1 --port 8787 --db src/quant_engine/snapshot_admin.db --seed GatherTradingData.json ``` ### CI/CD monitoring ```powershell # https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/actions python3 tools/validate_gitea_ci_workflow_lint_v1.py ``` Full day-to-day scenarios (KIS credential validation, snapshot admin editing, release/deploy, CI debugging, DB schema changes, "when things break" table): [docs/DEV_WORKFLOWS.md](docs/DEV_WORKFLOWS.md) ## API Endpoints All endpoints prefixed with `/api/`. Full route tables (Workspace & History, Collection Pipeline) and Collection Run status/success-criteria definitions: [docs/API_REFERENCE.md](docs/API_REFERENCE.md) ## KIS API Client Security (Phase 2) ### Governance Enforcement - **Read-Only Mandate**: `AssertReadOnly(path, trId)` blocks all trading-related endpoints - **Forbidden Paths**: `/trading/` substring triggers 🚫 immediate exception - **Forbidden TR_IDs**: TTTC* / VTTC* prefixes (buy/sell order codes) blocked - **Source**: `governance/rules/06_no_direct_api_trading.yaml` ### Token Management - **ITokenCache** abstraction: PostgreSQL-backed in production - **Credential Loading**: - Windows environment variables: `KIS_APP_Key`, `KIS_APP_Secret`, `KIS_APP_Key_TEST`, `KIS_APP_Secret_TEST` - Fallback: `HKCU\Environment` registry (Windows only) - Account modes: `"real"` (prod) vs `"mock"` (sandbox) ### Quotation Methods (All Read-Only) 1. **GetCurrentPriceAsync** (FHKST01010100) — Current price inquiry 2. **GetAskingPrice10LevelAsync** (FHKST01010200) — Order book (10-level) 3. **GetDailyShortSaleAsync** (FHPST04830000) — Short-sale trends 4. **GetDailyItemChartPriceAsync** (FHKST03010100) — Daily OHLCV data 5. **GetInvestorTrendAsync** (FHKST01010900) — Investor sentiment (개인/외국인/기관) ## OpenDART Fallback API (2026-07-30) **Credential**: `DART_API_KEY` 환경변수 (KIS 키와 동일한 패턴 — 코드/문서/워크플로우 파일에 절대 하드코딩하지 않는다. 실제 키 값은 로컬 환경변수 또는 Gitea Secrets에만 존재해야 한다). 로딩 위치: `tools/ingest_fundamental_raw.py:55` (`os.environ.get("DART_API_KEY")`). **⚠️ CRITICAL: 일일 호출 한도 40,000건** — OpenDART는 계정당 하루 40,000건으로 제한된다. 이 한도를 넘으면 그날 나머지 호출이 전부 실패한다. DART를 호출하는 모든 코드(현재는 `tools/ingest_fundamental_raw.py`의 `_dart_fundamentals()` — 아직 스텁이며 실제 API 호출은 미구현)는 **구현 시점부터** 다음을 지켜야 한다: - 종목 수 × 종목당 호출 수(재무제표 조회, corpCode 매핑 등)가 하루 40,000건을 넘지 않도록 사전에 예산을 계산한다 (여유분 확보 — 재시도/오류 포함 실제 한도의 80% 이내 목표 권장). - `corpCode.xml` 매핑처럼 자주 바뀌지 않는 데이터는 반드시 캐싱한다 (기존 `DART_CORP_MAP_CACHE`가 7일 캐시로 이미 이 원칙을 따르고 있음 — 새 DART 호출도 동일하게 캐싱 우선으로 설계할 것). - 일일 호출 수를 실제로 추적하는 카운터가 아직 없다 — 실 데이터 호출을 구현할 때 호출 카운트를 파일 또는 DB에 기록하고 한도 근접 시 중단하는 가드를 함께 추가한다. ## Local Development & Testing ### Mandatory Pre-Deployment Checklist **EVERY code change must pass before creating a release:** 1. ✅ Local build (0 errors, 0 warnings): `dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release` 2. ✅ Local service starts cleanly via SSH tunnel (DbUp migrations succeed, no DB connection errors) 3. ✅ Login test (`/Account/Login` → 200, cookie set) 4. ✅ All Admin pages return 200 — Dashboard, Users, Collection, Monitoring, Operations (no 500 in response body) 5. ✅ Playwright E2E: `npx playwright test tests/e2e/complete-admin-flow.spec.ts` 6. ✅ `git status` clean, all changes pushed to main 7. ✅ Authorization Policy configured (if changes made to Program.cs) **Deployment failure is better than service outage.** Halt and investigate if any step fails. Full deployment runbook, SSH key setup, Gitea Actions API monitoring, secrets configuration: [docs/DEPLOYMENT_RUNBOOK.md](docs/DEPLOYMENT_RUNBOOK.md) ## Core Workflows & Common Scenarios Day-to-day dev loop, KIS credential validation, snapshot admin editing, multi-stage release/deploy, CI debugging, DB schema change process, and a "when things break" troubleshooting table: [docs/DEV_WORKFLOWS.md](docs/DEV_WORKFLOWS.md) --- ## Notes for Contributors (2026-07-11 — Updated 2026-07-24) ### Code Standards - **SQL Safety**: Whitelist-only table access (enum switch in Repository). Never raw concatenation. - **KIS API**: Read-only quotations/ranking; no order/trade endpoints. Enforced via `AssertReadOnly()` in client. - **Admin UI**: Server-rendered Razor Pages only; no WASM. PageModel constructor: DI repos directly, no API hops. - **Authentication**: Cookie-based (HttpOnly, SameSite=Lax). No Bearer tokens. Password reset via API endpoints only (no UI form). - **Password Hashing**: BCrypt (auto-migrates from SHA-256 on login). IP lockout: 3 failed attempts → 15 min ban. - **Database**: PostgreSQL as single source of truth. Dapper ORM + raw SQL (no Entity Framework). Auto-migrations via DbUp. - **Error Handling**: Validate only at system boundaries (user input, external APIs). Trust internal guarantees. - **Comments**: Minimal; only explain WHY for non-obvious constraints. Removed comments on refactor; code names explain WHAT. ### Repository Structure Rules - **Legacy Code**: No `QuantEngine.Web.Client` remains in the repo — fully removed (verified 2026-07-30), not just excluded from `.sln`. - **DBML Sync (2026-07-12)**: All DbUp migrations (`V*.sql`) must sync with `docs/db/quantengine.dbml` in same commit. Future schema reads use DBML. - **Migration naming (2026-07-30)**: Existing migrations mix two conventions — `V1__Name.sql` (double underscore, unpadded) and `V003_name.sql` (single underscore, zero-padded). DbUp sorts scripts alphabetically, so `V003_...` actually runs *before* `V1__...`. **Do not rename existing migration files** — DbUp journals applied scripts by filename/checksum, so a rename makes an already-applied migration look new and re-runs it. New migrations must use `V{next_number}__Name.sql` (double underscore, matching V1–V8, the majority convention) so filename order matches intended execution order. **Note**: verified against production (`quantengine.schemaversions`, 2026-07-30) that `V003`/`V004` have never actually run — only V1–V8 are journaled. Renumbering V003/V004 to `V9__`/`V10__` is therefore safe from a checksum standpoint, but changes their execution order relative to V1–V8 (they currently run *first*); that's a real design decision (open, not made here), not a blind rename. - **Diagrams**: Mermaid diagrams in `docs/diagrams/` for state machines, flows, sequences (e.g., collection-pipeline.md). - **WBS Evidence (2026-07-12)**: Task completion = `npm run verify:task -- ` PASS (BE=DB queries/logs/JSON, FE=Playwright+screenshot). Full check: `npm run verify:wbs`. ### Testing & Validation - **Local pre-deployment**: Build (0 errors), E2E tests, all admin pages return 200 (no 500), git status clean. - **CI auto-validation**: 9 parallel jobs (core + 8 independent). Expected time: ~15-20min (was ~40min, parallelized 2026-07-24). - core: CRITICAL tests + DB setup - wbs-audit, dotnet-contracts, ui-storage, database-schema, calibration, operational-reporting, security-validation, workflow-lint: parallel - notify-results: final PR summary - **Release gates**: `npm run ops:validate` (warn-ok), `npm run full-gate` (strict), `npm run ops:release` (warn-ok + full DAG). ### Deployment - **Mandatory checklist**: Local build, E2E pass, admin pages 200, git clean, pushed to main. - **Release creation**: Manual workflow_dispatch → prepare-release.yml (tag + artifact). - **Deployment**: Manual workflow_dispatch → deploy-prod.yml (SSH upload + health checks). - **DB secrets**: Never hardcode `ConnectionStrings` in artifacts. Runtime injection via systemd EnvironmentFile (production only). - **Health checks (6-point)**: HTTP 200, login page, CSS load, service active, release tag match, DB auth (no 28P01 errors). ### Known Issues & Tech Debt - **Newtonsoft.Json**: High-severity vuln (GHSA-5crp-9r3c-p9vr); update or replace when feasible. - **Python-to-.NET transition**: Legacy `ops:data-collect` remains until .NET collection validated in production (est. Q3 2026). - **Gitea Actions limitation**: Act runner ↔ Gitea network issues (workaround: manual SSH deployment active). - **도구 버전 관리 (2026-07-30)**: `_v1`/`_v2`... 접미사로 새 버전의 도구를 추가할 때, 이전 버전이 더 이상 쓰이지 않으면 삭제한다 — git 히스토리로 충분하다. 병행 존재 금지(예: `apply_engine_upgrade_v4.py`와 `_v7.py`가 동시에 남아있는 상태). 단, 다른 파일이 옛 버전을 이름으로 참조 중이면(lint 화이트리스트, 문서 등) 그 참조부터 정리한 뒤 삭제한다. ### Reliability & Data Quality - **Reproducibility**: All validation gates, WBS tasks, and CI runs must be reproducible. Use deterministic inputs (GatherTradingData.json seed). - **Audit trail**: PostgreSQL is source of truth for all collection runs/snapshots. Raw SQL = explicit intent (no ORM magic). - **Contract validation**: Parity, Provenance, Scheduler, Normalization contracts catch schema drift before deployment. - **Canonical rendering**: .NET Tools (QuantEngine.Tools) is single source for operational_report.json schema. No format divergence. ### When Making Changes 1. **Change type determines scope**: - Bug fix: No cleanup beyond fix itself. - Feature: Add only what's requested; no hypothetical abstractions. - Refactor: Break down large methods/classes; extract reusable patterns only if used 3+ times. - Architecture change: Must update CLAUDE.md and CI contracts simultaneously. 2. **Locality-first**: Prefer editing in place. Create new files only when genuinely new concept or isolation needed. 3. **Testing locality**: Unit tests verify code correctness; UI testing (Playwright) verifies feature correctness. If UI can't be tested, say so. 4. **Git commits**: New commits preferred over amend. Include co-author: `Claude Haiku 4.5 `. ## CI/CD Pipeline Structure 9-job parallel `ci.yml` pipeline (~15-20min, was single-job ~40min before 2026-07-24 refactor). `core` job blocks downstream (DB migrations, .NET/API tests); 7 jobs run in parallel (wbs-audit, dotnet-contracts, ui-storage, database-schema, calibration-pipeline→ operational-reporting, security-validation, workflow-lint); `notify-results` summarizes all jobs in the PR comment. Other workflows: `kis_data_collection.yml` (cron 00:30 KST M-F), `qualitative_sell_strategy.yml` (cron 00:15 KST M-F), `ci_lint.yml` (on push to `.gitea/workflows/`), `snapshot_admin.yml`, `ci-frontend.yml` (push/PR — 8-step src/frontend/ pipeline: typecheck, lint, unit, contract parity, build, E2E, audit), `t20_ledger.yml` (cron 17:00 KST M-F — builds the T+20 outcome ledger), `prepare-release.yml` (manual), `deploy-prod.yml` (manual). Full job dependency graph, per-job purpose table, trigger schedule, workflow maintenance checklist, troubleshooting: [docs/CICD_PIPELINE.md](docs/CICD_PIPELINE.md) --- ## OMS·WMS·ERP Commercialization Project (Phase 12) Enterprise Order/Warehouse/ERP platform commercialization, gated by QuantEngine Phase 2 (KIS integration) completion in production. 18 weeks, 12 phases, 13 FTE, $371K budget, target launch Q4 2026. Phase 0 (Requirements & Baseline) complete 2026-07-26 — **GO** decision for Phase 1 (Dev Environment & CI/CD), starting 2026-08-02. Ground-truth specs: `spec/60_oms_wms_erp_wbs.yaml` (WBS), `spec/61_strategic_execution_framework.yaml` (30 principles), `spec/63_oms_wms_erp_api_openapi.yaml` (OpenAPI), `spec/64_oms_wms_erp_database_schema.sql` (DB schema), `spec/65_adr_001_monolithic_spa_architecture.md` (ADR), `spec/66_component_taxonomy.md` (component taxonomy). Full 30-principle framework, 12-phase breakdown, Phase 1-4 dev guide (Vite/Storybook setup, component code templates, Pinia stores, Playwright E2E), team/budget/risk detail, Phase 1 Go/No-Go checklist: [docs/OMS_WMS_ERP_PLAYBOOK.md](docs/OMS_WMS_ERP_PLAYBOOK.md)