70824c2afb
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>
430 lines
28 KiB
Markdown
430 lines
28 KiB
Markdown
# 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 -- <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)
|