Files
QuantEngineByItz/CLAUDE.md
T
kjh2064 70824c2afb fix: security, data-integrity, and doc-drift findings from repo audit
Consolidates duplicate KIS API client implementations (governance tests
were exercising an unused class instead of the one actually running in
production), closes a SQL injection path in the DB admin page, fixes a
migration that used MySQL-only syntax and had never actually applied
(confirmed against production), resyncs docs/db/quantengine.dbml with
all migrations, and removes a duplicate OMS·WMS·ERP frontend tree in
favor of src/frontend/. Also corrects several unverifiable/inflated
claims in the OMS planning docs and realigns CI/CD and architecture
documentation with what's actually in the repo.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 11:20:02 +09:00

430 lines
28 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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 V1V8, 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 V1V8 are journaled. Renumbering V003/V004 to `V9__`/`V10__` is therefore safe from a checksum standpoint, but changes their execution order relative to V1V8 (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 -- <TASK_ID>` 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 <noreply@anthropic.com>`.
## 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)