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>
This commit is contained in:
@@ -0,0 +1,53 @@
|
||||
# QuantEngine API Reference
|
||||
|
||||
Full API endpoint tables, extracted from CLAUDE.md (2026-07-30) to keep the main file within
|
||||
the character budget.
|
||||
|
||||
## Workspace & History (Phase 1)
|
||||
All endpoints prefixed with `/api/`:
|
||||
|
||||
| Route | Purpose |
|
||||
|-------|---------|
|
||||
| `GET /state` | Full UI state snapshot |
|
||||
| `GET /tables` | Browsable tables list |
|
||||
| `GET /table-rows` | Paginated rows |
|
||||
| `POST /settings/save` | Save settings |
|
||||
| `POST /account-snapshot/save` | Save snapshots |
|
||||
| `POST /bootstrap` | Seed DB from JSON |
|
||||
| `POST /account-snapshot/import-tsv` | Import TSV |
|
||||
| `POST /autofix` | Auto-correct data |
|
||||
|
||||
## Collection Pipeline (Phase 2)
|
||||
| Route | Purpose |
|
||||
|-------|---------|
|
||||
| `GET /collection/state` | Dashboard summary (runs, snapshots, errors) |
|
||||
| `GET /collection/runs` | Recent collection runs (paginated) |
|
||||
| `GET /collection/runs/{runId}/snapshots` | Snapshots from a run |
|
||||
| `GET /collection/runs/{runId}/errors` | Errors from a run |
|
||||
| `GET /collection/latest/{ticker}` | Latest snapshots for ticker |
|
||||
| `POST /collection/run` | Start new collection run (async) |
|
||||
|
||||
## Collection Run Status Values
|
||||
| Status | Meaning | UI Badge | Transitions |
|
||||
|--------|---------|----------|------------|
|
||||
| `running` | Collection in progress | <span class="badge bg-warning">진행 중</span> | → completed or failed |
|
||||
| `completed` | Collection finished (may have errors) | <span class="badge bg-success">완료</span> | (final) |
|
||||
| `failed` | Collection crashed/aborted | <span class="badge bg-danger">실패</span> | (final) |
|
||||
| `pending` | Queued, not yet started | <span class="badge bg-secondary">대기 중</span> | → running |
|
||||
|
||||
## Collection Run Success Criteria
|
||||
**Success** is defined as:
|
||||
- Status = `completed` (not `failed`)
|
||||
- `TotalSnapshots > 0` (at least one snapshot captured)
|
||||
- `TotalErrors == 0` OR `TotalErrors < TotalSnapshots * 0.1` (error rate < 10%)
|
||||
|
||||
**Partial Success** (warning state):
|
||||
- Status = `completed`
|
||||
- `TotalSnapshots > 0` (some data captured)
|
||||
- `TotalErrors > 0` (has errors, but not total loss)
|
||||
|
||||
**Failure**:
|
||||
- Status = `failed` OR
|
||||
- Status = `completed` + `TotalSnapshots == 0` (no data captured)
|
||||
|
||||
UI: `Pages/Admin/Collection/Index.cshtml` — status 값에 따라 배지 색상 결정, 향후 TotalSnapshots/TotalErrors로 상세 상태 표시
|
||||
@@ -0,0 +1,107 @@
|
||||
# QuantEngine CI/CD Pipeline Structure
|
||||
|
||||
Full Gitea Actions workflow structure, extracted from CLAUDE.md (2026-07-30) to keep the main
|
||||
file within the character budget.
|
||||
|
||||
## Workflow Architecture Refactoring (2026-07-24)
|
||||
|
||||
**2026-07-24 refactoring**: Single-job ci.yml (30+ steps, ~40min runtime) → **9-job parallel pipeline** (~15-20min runtime).
|
||||
|
||||
## CI Pipeline Jobs (ci.yml)
|
||||
|
||||
| Job | Dependencies | Purpose | Parallelizable |
|
||||
|-----|--------------|---------|---|
|
||||
| **core** | — | CRITICAL: .NET tests, API trading gate, KIS creds, DB migrations | ✗ (blocks others) |
|
||||
| **wbs-audit** | core | WBS validation, platform migration, coverage audits | ✓ |
|
||||
| **dotnet-contracts** | core | .NET parity, provenance, scheduler, normalization contracts | ✓ |
|
||||
| **ui-storage** | — | Admin UI, storage backend, integration tests | ✓ |
|
||||
| **database-schema** | — | DB pipeline, PostgreSQL schema, history contracts | ✓ |
|
||||
| **calibration-pipeline** | core | Calibration priority, change ledger, qualitative sell strategy | ✓ |
|
||||
| **operational-reporting** | calibration | Decision packet, operational report, performance metrics | ✗ (depends on calibration) |
|
||||
| **security-validation** | — | Secrets contract, workflow validation | ✓ |
|
||||
| **workflow-lint** | — | CI workflow structure, secrets contract | ✓ |
|
||||
| **notify-results** | ALL | PR notification with job status summary | — |
|
||||
|
||||
**Dependency Graph**:
|
||||
```
|
||||
core ─┬─> wbs-audit ─────────────────────┐
|
||||
├─> dotnet-contracts ─────────────┤
|
||||
└─> calibration-pipeline ────────┤
|
||||
└─> operational-reporting ─┤
|
||||
└─> notify-results
|
||||
ui-storage ────────────────────────────────┘
|
||||
database-schema ──────────────────────────┘
|
||||
security-validation ───────────────────────┘
|
||||
workflow-lint ─────────────────────────────┘
|
||||
```
|
||||
|
||||
## Other Workflow Files
|
||||
|
||||
| File | Trigger | Purpose | Status |
|
||||
|------|---------|---------|--------|
|
||||
| **kis_data_collection.yml** | cron (00:30 KST M-F) + dispatch | Validate KIS credentials & PostgreSQL pipeline | ✓ 2026-07-24 |
|
||||
| **qualitative_sell_strategy.yml** | cron (00:15 KST M-F) + push + dispatch | Validate sell strategy pipeline & store | ✓ 2026-07-24 |
|
||||
| **ci_lint.yml** | push (.gitea/workflows/) + dispatch | Lint all workflow files, validate job dependencies, secrets contract | ✓ 2026-07-24 |
|
||||
| **snapshot_admin.yml** | push (snapshot_admin_*) + dispatch | Validate snapshot admin workflow & UI (2 jobs) | ✓ 2026-07-24 |
|
||||
| **ci-frontend.yml** | push (main/master/feature/**) + PR | 8-step `src/frontend/` pipeline: install, typecheck, import-boundary lint, unit test, enterprise CRUD contract parity, Vite build, Playwright E2E, npm audit | ✓ (undocumented until 2026-07-30) |
|
||||
| **t20_ledger.yml** | cron (17:00 KST M-F) + dispatch | Build `tools/build_operational_t20_outcome_ledger_v1.py` daily T+20 outcome ledger | ✓ (undocumented until 2026-07-30) |
|
||||
| **prepare-release.yml** | workflow_run (ci.yml success) + dispatch | Build, tag, create Gitea Release with artifact + checksums | — |
|
||||
| **deploy-prod.yml** | dispatch | Deploy release, run health checks, report status (3 jobs) | — |
|
||||
|
||||
**Note (2026-07-30)**: An earlier version of this table claimed `ci_lint.yml` had been renamed to
|
||||
`workflow_lint.yml`. That rename was never actually carried out — the file on disk is still
|
||||
`ci_lint.yml`. Corrected here after direct verification against `.gitea/workflows/`.
|
||||
|
||||
## Performance Improvements (2026-07-24)
|
||||
|
||||
**ci.yml refactoring results**:
|
||||
- **Before**: 1 job, 30+ sequential steps, ~40min runtime
|
||||
- **After**: 9 jobs, 7 in parallel, ~15-20min total runtime
|
||||
- **Speedup**: ~2-2.5x faster CI feedback (core branch blocks only downstream, others parallel)
|
||||
- **Fault isolation**: Single validation failure no longer blocks unrelated checks
|
||||
|
||||
**Key changes**:
|
||||
1. **Setup consolidation**: Database migrations, Python, .NET setup in `core` job only
|
||||
2. **Parallel validation groups**: 7 jobs run independently from core (ui-storage, database-schema, security-validation, workflow-lint, etc.)
|
||||
3. **Dependency clarity**: `needs:` explicitly defines blocking relationships
|
||||
4. **Error reporting**: `notify-results` summarizes all 9 job statuses in PR comment
|
||||
|
||||
## Workflow Maintenance Checklist
|
||||
|
||||
When modifying workflows (.gitea/workflows/*.yml):
|
||||
|
||||
1. ✅ Update `ci_lint.yml` if adding new triggers or job dependencies
|
||||
2. ✅ Test locally with `python3 tools/validate_gitea_ci_workflow_lint_v1.py`
|
||||
3. ✅ Verify all `needs:` references point to existing jobs
|
||||
4. ✅ Document new jobs in this section above
|
||||
5. ✅ Validate YAML syntax: `python3 -m yaml < .gitea/workflows/new.yml`
|
||||
6. ✅ Ensure no hardcoded secrets in workflow files (env vars only)
|
||||
|
||||
## Troubleshooting Workflows
|
||||
|
||||
**Symptom**: CI job timeout
|
||||
- **Check**: Does your job need PostgreSQL? Only `core` provides it; others must be independent.
|
||||
- **Fix**: Add `services: postgres:` block or restructure to parallel-safe job.
|
||||
|
||||
**Symptom**: Cascading failure (multiple jobs fail)
|
||||
- **Check**: Does your job have missing dependencies? Review `needs:` and dependency graph above.
|
||||
- **Fix**: Add explicit `needs: [job_name]` if job depends on another's output.
|
||||
|
||||
**Symptom**: "job not found" error in notify-results
|
||||
- **Check**: Job name typo in `notify-results.needs` list.
|
||||
- **Fix**: Match job name exactly (case-sensitive).
|
||||
|
||||
## Workflow Trigger Schedule (2026-07-24)
|
||||
|
||||
| Time (KST) | Workflow | Trigger | Purpose |
|
||||
|-----------|----------|---------|---------|
|
||||
| 00:15 | qualitative_sell_strategy.yml | cron (M-F) | Validate sell strategy before daily operations |
|
||||
| 00:30 | kis_data_collection.yml | cron (M-F) | Validate KIS API & DB pipeline before data collection |
|
||||
| Push | ci.yml | on:push (main) | Validate code on every push to main |
|
||||
| PR | ci.yml | on:pull_request | Gate PR merges with full validation suite |
|
||||
| Manual | prepare-release.yml | workflow_dispatch | Create release tag & artifact |
|
||||
| Manual | deploy-prod.yml | workflow_dispatch | Deploy release to production |
|
||||
|
||||
**Dependencies**:
|
||||
- Release creation (prepare-release.yml) is gated by ci.yml success (workflow_run trigger)
|
||||
- Deployment (deploy-prod.yml) is manual — only after release artifact exists
|
||||
@@ -0,0 +1,337 @@
|
||||
# QuantEngine Deployment Runbook
|
||||
|
||||
Full deployment procedure, extracted from CLAUDE.md (2026-07-30) to keep the main file within
|
||||
the character budget. CLAUDE.md keeps the CRITICAL rules (CI/CD-only mandate, DB secret
|
||||
management); this file has the complete step-by-step runbook.
|
||||
|
||||
**Production Server**: Hetzner Cloud `178.104.200.7` (kjh2064@178.104.200.7)
|
||||
|
||||
Projects on server:
|
||||
1. **TaxBaik** (홈페이지) — Nginx location `/taxbaik`
|
||||
2. **QuantEngine** (데이터 수집/분석) — Nginx location `/quantengine`
|
||||
|
||||
## ⚠️ CRITICAL: CI/CD-Only Deployment Mandate
|
||||
|
||||
**Rule**: ALL production deployments MUST go through Gitea Actions CI/CD. Manual SSH deployments are **FORBIDDEN**.
|
||||
|
||||
**Why**:
|
||||
- Automatic validation (build, health checks, version verification)
|
||||
- Audit trail (all deployments logged in Gitea Actions)
|
||||
- Consistent process (no manual errors)
|
||||
- Rollback safety (deployment history retained)
|
||||
- Release traceability (version control via git tags)
|
||||
|
||||
## ⚠️ CRITICAL: DB Secret Management (Incident 2026-07-12)
|
||||
|
||||
**Incident**: `quant.taxbaik.com/login`이 `28P01 password authentication failed`로 장애 발생.
|
||||
원인: `appsettings.Production.json`에 하드코딩되어 배포된 DB 비밀번호가, 실제 DB 비밀번호가
|
||||
로테이션된 이후에도 계속 옛날 값(심지어 이전 세션에서 검증 없이 넣은 placeholder였던 적도 있음)
|
||||
그대로 배포되고 있었음.
|
||||
|
||||
**Rule**: **DB 접속 문자열(`ConnectionStrings`)은 절대 `appsettings.Production.json`이나
|
||||
워크플로우 파일에 하드코딩하지 않는다.** `prepare-release.yml`이 생성하는
|
||||
`appsettings.Production.json`에는 `Logging` 설정만 있고 `ConnectionStrings`는 없다 —
|
||||
이는 의도된 설계다 (Gitea Release는 누구나 다운로드 가능한 아티팩트이므로 시크릿을
|
||||
담으면 안 됨).
|
||||
|
||||
**실제 DB 비밀번호의 출처**: 프로덕션 서버의 `/home/kjh2064/.config/quantengine.env`
|
||||
파일 (`ConnectionStrings__DefaultConnection=...` 형식) 하나뿐이며,
|
||||
`quantengine.service.d/env.conf` drop-in의 `EnvironmentFile=` 지시자로 systemd가
|
||||
이 값을 환경변수로 주입한다. ASP.NET Core 설정 우선순위상 **환경변수가
|
||||
`appsettings.Production.json`을 오버라이드**하므로, 배포되는 아티팩트 자체에는
|
||||
DB 정보가 없어도 서비스는 정상 동작한다.
|
||||
|
||||
**DB 비밀번호가 바뀌면** (로테이션 등): `/home/kjh2064/.config/quantengine.env` 파일만
|
||||
갱신하고 `sudo systemctl restart quantengine`. 워크플로우 파일이나 Gitea Secrets는
|
||||
건드릴 필요 없음 (배포 파이프라인은 DB 비밀번호를 모른 채로 동작해야 정상).
|
||||
|
||||
**배포 전 체크리스트에 추가**:
|
||||
- ✅ 새 릴리즈 배포 후 반드시 `/Account/Login` 실제 HTTP 응답 + `journalctl -u quantengine`에서
|
||||
`28P01`/`password authentication failed` 부재 확인 (단순 프로세스 `active` 상태만으로는
|
||||
DB 연결 실패를 못 잡음 — ASP.NET Core는 DB 없이도 기동은 되고 로그인 요청 시점에야 실패함)
|
||||
- ✅ `.config/quantengine.env`의 존재와 `quantengine.service.d/env.conf`의
|
||||
`EnvironmentFile=` 배선이 서버에 유지되고 있는지 (systemd unit 자체를 재생성/덮어쓰는
|
||||
배포 방식으로 전환할 경우 이 drop-in이 날아가지 않는지 확인 필요)
|
||||
|
||||
## Production Deployment Strategy (Release-Based)
|
||||
|
||||
**Architecture**: Two-Workflow System (Release Creation → Deployment)
|
||||
|
||||
### Workflow 1: prepare-release.yml (Release Creation)
|
||||
|
||||
**Purpose**: Create a release with built artifact
|
||||
|
||||
**Trigger**: Manual (`workflow_dispatch`)
|
||||
```bash
|
||||
# Visit Gitea Actions and select prepare-release.yml
|
||||
# Input version: v0.1.20260711 (or any semantic version)
|
||||
```
|
||||
|
||||
**What it does**:
|
||||
1. ✓ Build (restore, build, publish)
|
||||
2. ✓ Generate `appsettings.Production.json`
|
||||
3. ✓ Package artifact: `.tar.gz`
|
||||
4. ✓ Create git tag: `v0.1.20260711`
|
||||
5. ✓ Create Gitea Release with artifact attached
|
||||
6. ✓ Notify: Release ready for deployment
|
||||
|
||||
**Output**: Gitea Release with downloadable artifact
|
||||
|
||||
### Workflow 2: deploy-prod.yml (Deployment)
|
||||
|
||||
**Purpose**: Deploy a release to production
|
||||
|
||||
**Trigger**: Manual (`workflow_dispatch`)
|
||||
```bash
|
||||
# Visit Gitea Actions and select deploy-prod.yml
|
||||
# Input release: v0.1.20260711 (optional — uses latest if empty)
|
||||
```
|
||||
|
||||
**What it does**:
|
||||
1. ✓ Fetch Release (from Gitea Releases)
|
||||
2. ✓ Download artifact
|
||||
3. ✓ Verify SSH credentials
|
||||
4. ✓ Upload to production server
|
||||
5. ✓ Extract and symlink
|
||||
6. ✓ Restart service
|
||||
7. ✓ 6-point health checks
|
||||
8. ✓ Report deployment status
|
||||
|
||||
**Deployment Pipeline (5 Stages)**:
|
||||
|
||||
| Stage | Purpose | Timeout |
|
||||
|-------|---------|---------|
|
||||
| 1. Fetch Release | Query Gitea Releases, download artifact | 10min |
|
||||
| 2. Pre-Check | Verify SSH keys, secrets, release | 5min |
|
||||
| 3. Deploy | Upload, extract, symlink, restart service | 30min |
|
||||
| 4. Health Check | 6-point verification (HTTP, CSS, login, service, release, DB auth) | 10min |
|
||||
| 5. Report | Final deployment status | Auto |
|
||||
|
||||
**Health Checks (Automatic)**:
|
||||
- ✓ HTTP 200 on `/Account/Login`
|
||||
- ✓ Login page content verification
|
||||
- ✓ CSS file loads (`/css/admin.css`)
|
||||
- ✓ Service status (systemctl active)
|
||||
- ✓ Release verification (deployed release tag matches)
|
||||
- ✓ **DB authentication check** (`journalctl`에서 `28P01`/`password authentication failed`
|
||||
부재 확인 — GET `/Account/Login`은 DB가 끊겨도 200을 반환하므로 이 체크가 없으면
|
||||
DB 장애를 배포 파이프라인이 놓친다. 2026-07-12 사고 이후 추가됨)
|
||||
|
||||
**Complete Deployment Flow**:
|
||||
```
|
||||
1. Code committed to main branch
|
||||
2. Create release: prepare-release.yml workflow_dispatch (manual)
|
||||
→ Builds code
|
||||
→ Creates Gitea Release with artifact
|
||||
→ Tags repository
|
||||
3. Deploy release: deploy-prod.yml workflow_dispatch (manual)
|
||||
→ Selects release version
|
||||
→ Downloads artifact from Gitea Release
|
||||
→ Deploys to production server
|
||||
→ Runs health checks
|
||||
→ Reports status
|
||||
```
|
||||
|
||||
## Pre-Deployment Checklist
|
||||
|
||||
**Before creating a release**, verify:
|
||||
1. ✅ Local build: `dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release` (0 errors, 0 warnings)
|
||||
2. ✅ E2E tests pass: `npx playwright test`
|
||||
3. ✅ Admin pages verified (200 status, no 500 errors)
|
||||
4. ✅ All changes committed and pushed to main branch
|
||||
5. ✅ No uncommitted changes: `git status`
|
||||
|
||||
## Release & Deployment Workflow
|
||||
|
||||
**Step 1: Create Release (prepare-release.yml)**
|
||||
```bash
|
||||
# Visit Gitea Actions
|
||||
# https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/actions
|
||||
|
||||
# Run prepare-release.yml workflow
|
||||
# Input: version = v0.1.20260711
|
||||
|
||||
# Workflow will:
|
||||
# - Build and publish
|
||||
# - Package artifact
|
||||
# - Create git tag
|
||||
# - Create Gitea Release
|
||||
# - Attach artifact
|
||||
```
|
||||
|
||||
**Step 2: Deploy Release (deploy-prod.yml)**
|
||||
```bash
|
||||
# Visit Gitea Actions (same page)
|
||||
# Run deploy-prod.yml workflow
|
||||
# Input: release = v0.1.20260711 (leave empty for latest)
|
||||
|
||||
# Workflow will:
|
||||
# - Download artifact from release
|
||||
# - Deploy to production server
|
||||
# - Run health checks
|
||||
# - Report status
|
||||
```
|
||||
|
||||
## SSH Key Configuration (Required)
|
||||
|
||||
**Setup (One-time)**:
|
||||
1. Generate ED25519 key locally (or reuse existing):
|
||||
```bash
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/quantengine_deploy -C "QuantEngine CI/CD"
|
||||
```
|
||||
|
||||
2. Add public key to production server:
|
||||
```bash
|
||||
ssh-copy-id -i ~/.ssh/quantengine_deploy.pub kjh2064@178.104.200.7
|
||||
```
|
||||
|
||||
3. Get private key in base64 format:
|
||||
```bash
|
||||
# macOS/Linux
|
||||
base64 -w 0 ~/.ssh/quantengine_deploy > /tmp/key_b64.txt
|
||||
cat /tmp/key_b64.txt | pbcopy
|
||||
|
||||
# Or Windows PowerShell
|
||||
$key = Get-Content ~/.ssh/quantengine_deploy -Raw
|
||||
[Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes($key)) | Set-Clipboard
|
||||
```
|
||||
|
||||
4. Configure in Gitea:
|
||||
- URL: https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/settings/secrets
|
||||
- Add secret: `DEPLOY_SSH_KEY_B64` (base64-encoded private key)
|
||||
- Or: `DEPLOY_SSH_KEY` (raw PEM format)
|
||||
- Also add: `GITEA_TOKEN` (for release API access)
|
||||
- Generate at: https://gitea.taxbaik.com/user/settings/applications
|
||||
- Required permissions: `repo` + `read:actions`
|
||||
|
||||
## Deployment Monitoring
|
||||
|
||||
**During Deployment**:
|
||||
- Watch live in Gitea Actions UI
|
||||
- Jobs complete in order: Build → Pre-Check → Deploy → Health Check → Report
|
||||
|
||||
**After Deployment**:
|
||||
```bash
|
||||
# SSH into server
|
||||
ssh kjh2064@178.104.200.7
|
||||
|
||||
# Check active deployment
|
||||
readlink ~/quantengine_active
|
||||
|
||||
# View service status
|
||||
systemctl status quantengine
|
||||
|
||||
# Tail live logs
|
||||
journalctl -u quantengine -f
|
||||
|
||||
# Health check
|
||||
curl -I http://127.0.0.1:5000/Account/Login
|
||||
```
|
||||
|
||||
## Automatic Rollback (if health check fails)
|
||||
|
||||
If health check fails, deployment stops automatically:
|
||||
1. Service restart may fail
|
||||
2. Symlink update reverts to previous deployment
|
||||
3. Gitea Actions marks deployment as FAILED
|
||||
4. Logs include failure details
|
||||
|
||||
Manual rollback (if needed):
|
||||
```bash
|
||||
# List deployments
|
||||
ls -lht ~/deployments/quantengine_*
|
||||
|
||||
# Revert symlink to previous version
|
||||
ln -sfn /home/kjh2064/deployments/quantengine_YYYYMMDD_HHMMSS_COMMIT ~/quantengine_active
|
||||
|
||||
# Restart service
|
||||
sudo systemctl restart quantengine
|
||||
|
||||
# Verify
|
||||
curl http://127.0.0.1:5000/Account/Login
|
||||
```
|
||||
|
||||
## Troubleshooting Deployment Failures
|
||||
|
||||
**Issue**: Build fails
|
||||
- Check: `dotnet build` locally first
|
||||
- Ensure: No compilation errors, 0 warnings
|
||||
|
||||
**Issue**: Health check timeout
|
||||
- Check: Service logs: `journalctl -u quantengine -n 50`
|
||||
- Check: Port 5000 listening: `ss -tlnp | grep 5000`
|
||||
- Check: DB connectivity in appsettings.Production.json
|
||||
|
||||
**Issue**: SSH key error
|
||||
- Verify: `DEPLOY_SSH_KEY_B64` or `DEPLOY_SSH_KEY` in Gitea Secrets
|
||||
- Check: Public key added to `~/.ssh/authorized_keys` on server
|
||||
- Test: `ssh -i ~/.ssh/key_file kjh2064@178.104.200.7 echo OK`
|
||||
|
||||
## Git Repository
|
||||
|
||||
**Gitea Server** (동일 호스트):
|
||||
- **HTTP**: `https://gitea.taxbaik.com/kjh2064/QuantEngineByItz.git`
|
||||
- **SSH**: `ssh://git@gitea.taxbaik.com:2222/kjh2064/QuantEngineByItz.git`
|
||||
|
||||
## Active Gitea Workflows (summary)
|
||||
|
||||
1. **prepare-release.yml** — Release creation (workflow_dispatch only)
|
||||
- Build → Publish → Package → Tag → Gitea Release
|
||||
- Does NOT write ConnectionStrings into the artifact (see DB Secret Management above) —
|
||||
only `Logging` config ships in `appsettings.Production.json`
|
||||
2. **deploy-prod.yml** — Production deployment (workflow_dispatch only, takes a release tag)
|
||||
- 5 stages: Fetch Release → Pre-Check → Deploy → Health Check → Report
|
||||
- 6-point health checks (HTTP, login page, CSS, service, release, DB auth)
|
||||
- SSH-based deployment with artifact validation
|
||||
3. **ci.yml** — PR validation (on:pull_request), 29 validators, runs on every pull request
|
||||
|
||||
**Accessing Gitea Actions**:
|
||||
- Web UI: https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/actions
|
||||
- Runs API: https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs
|
||||
|
||||
## API Monitoring (CLI)
|
||||
|
||||
Monitor deployment status from command line:
|
||||
|
||||
```powershell
|
||||
# Setup (one-time)
|
||||
$env:GITEA_TOKEN_TAXBAIK = "your_gitea_personal_token"
|
||||
|
||||
# List recent deployment runs
|
||||
$token = $env:GITEA_TOKEN_TAXBAIK
|
||||
$response = Invoke-WebRequest `
|
||||
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs?limit=5" `
|
||||
-Headers @{ "Authorization" = "token $token" }
|
||||
($response.Content | ConvertFrom-Json).workflow_runs | ForEach-Object {
|
||||
Write-Host "Run #$($_.id): $($_.display_title) [$($_.conclusion)]"
|
||||
}
|
||||
|
||||
# Get specific run details
|
||||
$run_id = 1234 # Replace with actual run ID
|
||||
$response = Invoke-WebRequest `
|
||||
-Uri "https://gitea.taxbaik.com/api/v1/repos/kjh2064/QuantEngineByItz/actions/runs/$run_id" `
|
||||
-Headers @{ "Authorization" = "token $token" }
|
||||
$run = $response.Content | ConvertFrom-Json
|
||||
Write-Host "Commit: $($run.head_sha)"
|
||||
Write-Host "Status: $($run.status) / $($run.conclusion)"
|
||||
```
|
||||
|
||||
See `docs/GITEA_ACTIONS_API_GUIDE.md` for the complete API reference.
|
||||
|
||||
## Deployment Secrets Configuration
|
||||
|
||||
**Required Secrets** (Gitea Repository Settings → Secrets):
|
||||
|
||||
| Secret | Type | Purpose |
|
||||
|--------|------|---------|
|
||||
| `DEPLOY_SSH_KEY_B64` | Base64 (recommended) | ED25519 private key for SSH |
|
||||
| `DEPLOY_SSH_KEY` | PEM (alternative) | Raw private key format |
|
||||
| `DEPLOY_HOST` | Text | Production server IP (178.104.200.7) |
|
||||
| `DEPLOY_USER` | Text | SSH username (kjh2064) |
|
||||
|
||||
**How to add secrets**:
|
||||
1. Go to: https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/settings/secrets
|
||||
2. Click "Add Secret"
|
||||
3. Name: `DEPLOY_SSH_KEY_B64`
|
||||
4. Value: `base64 -w 0 ~/.ssh/deploy_key | pbcopy` (macOS) or `certutil -encode deploy_key deploy_key.b64` (Windows)
|
||||
5. Save
|
||||
@@ -0,0 +1,137 @@
|
||||
# QuantEngine Development Workflows & Common Scenarios
|
||||
|
||||
Full day-to-day workflow walkthroughs, extracted from CLAUDE.md (2026-07-30) to keep the main
|
||||
file within the character budget.
|
||||
|
||||
## Scenario 1: Day-to-Day Development (Code Change)
|
||||
|
||||
1. **Make code changes** (C# Razor Pages / .NET API / Python tools)
|
||||
2. **Local validation**:
|
||||
```powershell
|
||||
dotnet build src/dotnet/QuantEngine.Web/QuantEngine.Web.csproj -c Release
|
||||
dotnet test src/dotnet/QuantEngine.Core.Tests -c Release
|
||||
```
|
||||
3. **Test admin pages locally** (with SSH tunnel):
|
||||
```powershell
|
||||
ssh -L 127.0.0.1:5432:localhost:5432 kjh2064@178.104.200.7 -N &
|
||||
dotnet watch run --project QuantEngine.Web
|
||||
# Verify: /Admin/Dashboard, /Admin/Users, /Admin/Collection, etc. all return 200
|
||||
```
|
||||
4. **Commit & push**: Changes automatically trigger ci.yml
|
||||
- Core validators run first (blocking others)
|
||||
- Parallel validators (contracts, UI, DB, calibration) run independently
|
||||
- notify-results summarizes all 9 jobs in PR comment
|
||||
- Expected CI time: ~15-20min (was ~40min before 2026-07-24 refactor)
|
||||
|
||||
## Scenario 2: Data Collection Setup (KIS API Validation)
|
||||
|
||||
1. **Obtain KIS credentials** (real or mock account)
|
||||
2. **Validate with mock account**:
|
||||
```powershell
|
||||
$env:KIS_APP_Key_TEST="<test_key>"
|
||||
$env:KIS_APP_Secret_TEST="<test_secret>"
|
||||
python tools/validate_kis_api_credentials_v1.py --account mock --ticker 005930 --dry-run
|
||||
```
|
||||
3. **Run real collection** (if approved):
|
||||
```powershell
|
||||
$env:KIS_APP_Key="<real_key>"
|
||||
$env:KIS_APP_Secret="<real_secret>"
|
||||
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
|
||||
```
|
||||
4. **Verify database**:
|
||||
```sql
|
||||
SELECT COUNT(*) FROM kis_collection_runs;
|
||||
SELECT COUNT(*) FROM kis_collection_snapshots;
|
||||
```
|
||||
|
||||
## Scenario 3: Admin Data Editing (Snapshot Admin Web UI)
|
||||
|
||||
1. **Start snapshot admin server**:
|
||||
```powershell
|
||||
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
|
||||
```
|
||||
2. **Access web UI**: http://127.0.0.1:8787
|
||||
3. **Edit settings / account_snapshot** in browser (like Excel)
|
||||
4. **Manage changes**: Approval & Locks area handles change history, undo, approval workflow
|
||||
5. **Export for CI**: `/api/export` → JSON or "Export approval packet" button
|
||||
|
||||
## Scenario 4: Release & Deployment (Multi-Stage)
|
||||
|
||||
**Stage 1: Local validation**
|
||||
```powershell
|
||||
npm run ops:validate # Warn-only (allow some issues)
|
||||
npm run full-gate # Strict (all gates PASS)
|
||||
```
|
||||
|
||||
**Stage 2: Create release** (manual via Gitea Actions)
|
||||
```
|
||||
→ Visit https://gitea.taxbaik.com/kjh2064/QuantEngineByItz/actions
|
||||
→ Run "prepare-release.yml" workflow_dispatch
|
||||
- Builds and publishes .NET
|
||||
- Creates git tag (e.g., quant_20260724.0.abc1234)
|
||||
- Generates Gitea Release with artifact + checksums
|
||||
- Packages as .tar.gz
|
||||
```
|
||||
|
||||
**Stage 3: Deploy** (manual, only after release exists)
|
||||
```
|
||||
→ Run "deploy-prod.yml" workflow_dispatch
|
||||
- Downloads release artifact from Gitea
|
||||
- Validates checksums and manifest
|
||||
- Verifies upstream CI success
|
||||
- SSH uploads to production server (178.104.200.7)
|
||||
- Extracts and symlinks
|
||||
- Restarts systemd service
|
||||
- 6-point health checks (HTTP, login page, CSS, service, release tag, DB auth)
|
||||
- Reports final status
|
||||
```
|
||||
|
||||
**Pre-deployment checklist** (MANDATORY):
|
||||
- ✅ Local build: 0 errors, 0 warnings
|
||||
- ✅ E2E tests pass: `npx playwright test`
|
||||
- ✅ All admin pages tested locally (200 status, no 500)
|
||||
- ✅ `git status` clean (no uncommitted changes)
|
||||
- ✅ Commit pushed to main
|
||||
|
||||
Full runbook: [DEPLOYMENT_RUNBOOK.md](DEPLOYMENT_RUNBOOK.md)
|
||||
|
||||
## Scenario 5: CI Workflow Debugging
|
||||
|
||||
**Problem**: A specific validation fails in CI
|
||||
1. Identify failing job from PR comment (notify-results output)
|
||||
2. Reproduce locally:
|
||||
```powershell
|
||||
# For core, wbs-audit, dotnet-contracts: run relevant Python validators
|
||||
python tools/validate_dotnet_migration_execution_plan_v1.py
|
||||
python tools/validate_dotnet_parity_contract_v1.py
|
||||
# etc.
|
||||
```
|
||||
3. Fix and re-push (triggers ci.yml again)
|
||||
4. Monitor in Gitea Actions dashboard
|
||||
|
||||
**Problem**: Workflow syntax error
|
||||
1. Validate locally:
|
||||
```powershell
|
||||
python tools/validate_gitea_ci_workflow_lint_v1.py --workflow .gitea/workflows/ci.yml
|
||||
```
|
||||
2. Fix YAML and test again
|
||||
|
||||
## Scenario 6: Database Schema Changes
|
||||
|
||||
1. **Create migration**: `src/dotnet/QuantEngine.Infrastructure/Migrations/V003.sql`
|
||||
2. **Update DBML**: `docs/db/quantengine.dbml` (same commit)
|
||||
- DbUp auto-applies migrations on startup
|
||||
- DBML is reference documentation
|
||||
3. **Test locally** (with SSH tunnel): Migrations must apply cleanly
|
||||
4. **Commit both** (SQL + DBML) together
|
||||
5. **CI validates**: ci.yml applies migrations to test PostgreSQL service
|
||||
|
||||
## When Things Break
|
||||
|
||||
| Issue | Root Cause | Fix |
|
||||
|-------|-----------|-----|
|
||||
| Admin page returns 500 | Likely unhandled DB exception or auth issue | Check journalctl, verify ConnectionStrings in production env |
|
||||
| KIS API fails with "not found" | Ticker doesn't exist in KIS | Use fallback (Naver → Yahoo → OpenDART) |
|
||||
| Snapshot admin won't load | SQLite DB corrupted or missing | Delete and re-seed from GatherTradingData.json |
|
||||
| CI takes >25min | core job is slow or parallel jobs stalling | Profile individual job logs; likely DB migrations or large test suite |
|
||||
| Deployment health check fails (DB 28P01) | DB password rotated but not updated in production env | Update `/home/kjh2064/.config/quantengine.env` on server only (not in repo) |
|
||||
@@ -0,0 +1,101 @@
|
||||
# QuantEngine Migration Status (Historical Log)
|
||||
|
||||
Detailed phase-by-phase migration history, extracted from CLAUDE.md (2026-07-30) to keep the
|
||||
main file within the character budget. CLAUDE.md keeps a short summary; this file is the
|
||||
full historical record.
|
||||
|
||||
## Migration Phases Status (2026-07-11)
|
||||
|
||||
**Phase 1: Web UI Migration** ✅ 완료 (2026-07-11)
|
||||
- **새로운 표준**: Razor Pages (Server-Rendered) + Cookie Authentication + Tabler UI
|
||||
- **폐기 대상**: Blazor Interactive WebAssembly, MudBlazor, SmartAdmin
|
||||
- **완료 기준 — Phase 1 Success Criteria**:
|
||||
- ✅ Cookie 인증 구현 (AuthService + IpLockoutService + BCrypt)
|
||||
- ✅ Razor Pages 렌더링 (Admin 레이아웃 + 3개 이상 기본 페이지)
|
||||
- ✅ 공용 UI 컴포넌트 (4개 이상 shared partials)
|
||||
- ✅ 보안: 백도어 제거, 무솔트 해시 마이그레이션, IP 잠금
|
||||
- ✅ 빌드 성공: 0 errors, 0 warnings
|
||||
- ✅ CLAUDE.md 업데이트 (UI 기준 + 인증 정책)
|
||||
- **✅ 모든 기준 충족됨** (2026-07-11)
|
||||
- **구현 완료**:
|
||||
- ✅ Cookie 기반 인증 (AuthService + IpLockoutService)
|
||||
- ✅ Razor Pages CRUD 레이아웃 (_AdminLayout.cshtml, shared partials)
|
||||
- ✅ Admin 페이지: Dashboard, Collection, Users (기본 구조)
|
||||
- ✅ 공용 UI 컴포넌트: _ValidationSummary, _Pagination, _StatusBadge, _EmptyState
|
||||
- ✅ 보안 개선: BCrypt 해싱, IP 잠금, 하드코딩된 백도어 제거
|
||||
- ✅ 빌드: 0 errors, 0 warnings (Newtonsoft.Json 보안 경고 제외)
|
||||
- ✅ CLAUDE.md 완전 업데이트 (UI 기준, 인증, 상태 정의)
|
||||
- **구현 미완료 (향후 작업)**:
|
||||
- 🔄 Users 페이지: Create/Edit 폼 완성
|
||||
- 🔄 Collection 페이지: 스냅샷/에러 조회 상세화
|
||||
- 🔄 E2E 테스트: Playwright 스펙 업데이트
|
||||
|
||||
**Phase 2: KIS Data Collection Pipeline** ✅ 95% COMPLETE
|
||||
- ✅ KIS API Client: Full implementation complete
|
||||
- IKisApiClient interface (5 quotation methods)
|
||||
- KisApiClient with real HTTP implementation + token caching
|
||||
- All governance rules enforced (no trading APIs)
|
||||
- Windows env var + registry fallback for credentials
|
||||
- Build: 0 errors, 0 warnings
|
||||
- ✅ PostgreSQL Infrastructure: Complete
|
||||
- PostgresTokenCache (token management, 10-min skew)
|
||||
- CollectionRepository (full CRUD + dashboard aggregations)
|
||||
- Auto-creates kis_tokens, kis_collection_runs, kis_collection_snapshots, kis_collection_errors
|
||||
- Dapper ORM + parameterized SQL (injection-proof)
|
||||
- ✅ Web API Endpoints: Complete
|
||||
- CollectionEndpoints (6 endpoints: state, runs, snapshots, errors, latest, start)
|
||||
- ApiClient for Blazor consumption
|
||||
- ✅ Blazor UI: Complete
|
||||
- Collection.razor dashboard with real-time monitoring
|
||||
- Summary cards, recent errors table, runs history
|
||||
- Start/refresh functionality
|
||||
- FluentSkeleton loading states
|
||||
- 🔄 Pipeline Orchestration: Pending
|
||||
- Python `kis_data_collection_v1.py` → .NET (data fetching + validation)
|
||||
- Real KIS API data collection workflow integration
|
||||
- E2E test: API → DB → UI validation
|
||||
|
||||
**Phase 3: Node.js→.NET CLI Tools** 📋 PLANNED
|
||||
- Makefile created (npm → make mappings)
|
||||
- np operations documented
|
||||
|
||||
**Phase 4: CI/CD Pipeline Hardening** ✅ 80% COMPLETE (2026-07-11)
|
||||
- ✅ deploy-prod.yml (4-stage pipeline, 223 lines)
|
||||
- Build → Pre-Deployment Check → Deploy → Post-Deployment Reporting
|
||||
- SSH-based remote deployment (scp + ssh commands)
|
||||
- Comprehensive health checks (10-retry with 3s intervals)
|
||||
- Artifact management (.tar.gz)
|
||||
- ✅ Workflow consolidation (2 active files)
|
||||
- ci.yml: PR validation only (maintains 29 validators)
|
||||
- deploy-prod.yml: Production deployment
|
||||
- Deleted: merge-to-main.yml (non-functional), fast-validation.yml (redundant), archived/ directory
|
||||
- ✅ SSH credentials: SSH_KEY registered in Gitea Secrets
|
||||
- ⚠️ Gitea Actions limitation: Act runner ↔ Gitea network connectivity issues
|
||||
- Workflow trigger (on:push) works ✓
|
||||
- Job execution fails (network: dial tcp 172.18.0.2:3000 refused)
|
||||
- **Workaround**: Manual SSH-based deployment (see "Production Deployment" below)
|
||||
- 📚 Gitea API documentation: docs/GITEA_ACTIONS_API_GUIDE.md
|
||||
|
||||
**Phase 5: Admin UI & Deployment Optimization** ✅ COMPLETE (2026-07-11)
|
||||
- ✅ Admin UI redesign (Tabler framework)
|
||||
- Dashboard: stat cards, quick actions, system info
|
||||
- Responsive sidebar navigation
|
||||
- Professional layout (dark sidebar #2c3e50, white content)
|
||||
- ✅ Build output: 0 errors, 0 warnings
|
||||
- ✅ E2E tests: 8/8 passing (Playwright)
|
||||
- ✅ Production deployment: Active since 2026-07-11 21:00:55 KST
|
||||
- Commit: 30fb702
|
||||
- HTTP 200 health check
|
||||
- Service: active (running)
|
||||
|
||||
**Status Summary**:
|
||||
- Python codebase: Operational (1,140 files)
|
||||
- .NET 9 coverage: Core (✅), Infrastructure (✅), API (✅), Web UI (✅)
|
||||
- Database: PostgreSQL fully migrated
|
||||
- CI/CD: Manual SSH deployment (fully operational), Gitea Actions (limited by infrastructure)
|
||||
- Release gates: Python gates remain authority until Phase 2 integration testing complete
|
||||
|
||||
**Note (2026-07-30)**: Phase 4/5 above still describe the deploy-prod.yml pipeline as it existed
|
||||
2026-07-11. It has since evolved into the release-based two-workflow system (prepare-release.yml
|
||||
+ deploy-prod.yml with 6-point health checks including DB auth). See
|
||||
[DEPLOYMENT_RUNBOOK.md](DEPLOYMENT_RUNBOOK.md) for the current procedure.
|
||||
@@ -0,0 +1,725 @@
|
||||
# OMS·WMS·ERP Commercialization Project Playbook
|
||||
|
||||
Full strategic framework and Phase 1-4 development playbook, extracted from CLAUDE.md
|
||||
(2026-07-30) to keep the main file within the character budget. CLAUDE.md keeps a short
|
||||
summary and pointer to this file; this is the complete reference.
|
||||
|
||||
## OMS·WMS·ERP Commercialization Project: Strategic Execution Framework (2026-07-26)
|
||||
|
||||
**OFFICIAL PROJECT FOUNDATION** — 30-Year Senior Architect/PM/PL/Dev/UX/QA/User Perspective
|
||||
|
||||
**⚠️ CORRECTION (2026-07-26)**: Initial WBS was fabricated from filenames + general knowledge without reading PDFs. Post-advisor review, claimed to now be **based on actual PDF specifications** (5 documents, 179 pages). All numbers, team size, budget, timelines in previous version marked DRAFT.
|
||||
|
||||
**⚠️ SECOND CORRECTION (2026-07-30) — PDF sourcing claim is itself unverified**: A repo-wide search found zero PDF files anywhere in this repository. The "based on actual PDF specifications (not hallucinated)" claim in `spec/61_strategic_execution_framework.yaml` — and the ~40 inline `(PDF n...)` citations throughout that file — cannot be verified from this codebase. Treat every PDF citation as "source claimed, not confirmed" until the original PDFs are located and attached somewhere accessible.
|
||||
|
||||
**⚠️ THIRD CORRECTION (2026-07-30) — this was never a greenfield start**: Below, Phase 1 is described as beginning 2026-08-02 with `npm create vite@latest` "from scratch." In reality, OMS·WMS·ERP frontend code was already merged to `main` on 2026-07-27 (PR #16, commit `b34b0dd`) — the day *before* this document was written. At that point it existed as two separate, uncoordinated trees (`oms-wms-erp/` and `src/frontend/`) with overlapping component structure and at least one unreviewed generated file (`oms-wms-erp/src/components/composites/${component}.vue` — a literal unexpanded shell variable). As of 2026-07-30, `src/frontend/` has been confirmed as the canonical tree and `oms-wms-erp/` has been removed (`git rm -r`, recoverable from history). The "Phase 1 Go/No-Go" checklist below should be read as a checklist for *auditing what already exists in `src/frontend/`*, not a plan for starting from zero.
|
||||
|
||||
### Phase 0 Status: COMPLETE ✅ (2026-07-26)
|
||||
|
||||
**Phase 0 deliverables** (Requirements & Baseline):
|
||||
|
||||
| # | Deliverable | File | Status | Content |
|
||||
|---|-------------|------|--------|---------|
|
||||
| **D1** | OpenAPI 3.0 Specification | spec/63_oms_wms_erp_api_openapi.yaml | ✅ | 30 REST endpoints (OMS/WMS/ERP), 5 roles RBAC, audit trails, reversal-based model |
|
||||
| **D2** | Architecture Decision (ADR-001) | spec/65_adr_001_monolithic_spa_architecture.md | ✅ | Monolithic SPA decision, 7-layer arch, 4-layer components, Phase 1-4 roadmap |
|
||||
| **D3** | Database Schema v1 (PostgreSQL) | spec/64_oms_wms_erp_database_schema.sql | ✅ | 11 entity tables, audit_logs, 3NF normalization, seed data, role-based access |
|
||||
| **D4** | Component Taxonomy | spec/66_component_taxonomy.md | ✅ | 65 components (4 layers), 451 Storybook stories, folder structure, test strategy |
|
||||
| **D5** | CLAUDE.md Integration | CLAUDE.md (this file) | ✅ | Phase 0 results, Phase 1-4 dev commands, component dev guide, validation checklist |
|
||||
|
||||
**Go/No-Go Decision**: ✅ **GO** → Phase 1 (Dev Env & CI/CD) begins 2026-08-02
|
||||
|
||||
**Phase 0 Validation Checklist** (All ✅):
|
||||
- ✅ All stakeholders reviewed and approved specifications
|
||||
- ✅ OpenAPI spec validated by backend team
|
||||
- ✅ Database schema approved by DBA
|
||||
- ✅ Component taxonomy approved by UX/design
|
||||
- ✅ 30 Strategic Principles mapped to execution
|
||||
- ✅ Risk register completed (15+ risks with mitigation)
|
||||
- ✅ Team structure confirmed (13 FTE)
|
||||
- ✅ Budget approved ($371K USD)
|
||||
|
||||
### Strategic Vision
|
||||
|
||||
**Objective**: Enterprise-grade Order Management (OMS) + Warehouse Management (WMS) + Enterprise Resource Planning (ERP) platform commercialization with:
|
||||
- 4-layer input components (Primitive/Typed Field/Domain Field/Business Composite)
|
||||
- 11 standard CRUD templates (fully normalized data model)
|
||||
- Vue 3 + TypeScript modern stack
|
||||
- SOLID principles, data consistency, process simplification
|
||||
- 100% test-driven, zero hallucination, full traceability
|
||||
|
||||
**Duration**: 18 weeks (4.5 months, 12 phases)
|
||||
**Team**: 13 FTE (PM, PL, 4 FE devs, 2 BE, 1 UX, 2 QA, 1 DevOps, 0.5 security, 0.5 docs)
|
||||
**Budget**: $371K USD (infrastructure, tooling, salaries)
|
||||
**Target Launch**: Q4 2026
|
||||
|
||||
### 30 Strategic Principles (With Execution Framework)
|
||||
|
||||
**Complete framework**: 📄 [`spec/61_strategic_execution_framework.yaml`](../spec/61_strategic_execution_framework.yaml) (759 lines — previously miscited elsewhere as "7,000+ lines")
|
||||
|
||||
**30 Principles Applied**:
|
||||
|
||||
| # | Principle | PDF Source | Success Metric |
|
||||
|---|-----------|-----------|-----------------|
|
||||
| 1 | SOLID (SRP, OCP, LSP, ISP, DIP) | Architecture spec | No circular imports, domain independent |
|
||||
| 2 | Code Refactoring (Continuous) | "bloated monoliths" warning | Component <300 lines, dependencies <5 |
|
||||
| 3 | Data Consistency (SSOT) | "화면과 서버 데이터 해석 다르지 않게" | API DTO ≠ Screen Model ≠ Domain Model |
|
||||
| 4 | Parsimony (No Gold-Plating) | Template spec precise | Feature = PDF requirement + P0/P1 tag |
|
||||
| 5 | Normalization (3NF minimum) | Schema design | No repeating groups, full normalization |
|
||||
| 6 | Denormalization (Justified) | Performance-only | <100ms proof required, TTL strategy |
|
||||
| 7 | Process Simplification | Validate before automate | Workflow reviewed by domain experts |
|
||||
| 8 | Patterns & Design | Reusable business transactions | 3+ usage → abstract into pattern |
|
||||
| 9 | Standardization (Conventions) | Consistent naming, API contracts | ESLint rules, OpenAPI validation |
|
||||
| 10 | Structuring (Layered) | 7-layer architecture spec | No higher → lower layer imports |
|
||||
| 11 | Vibes Coding (Cognitive Load) | Clear naming, minimal overhead | Readable without docs, PR comment pass |
|
||||
| 12 | Hallucination Prevention | Test-driven, ground truth | Every feature sourced, not assumed |
|
||||
| 13 | Ground Truth & Reproducibility | Deterministic inputs, traceable | Seed data versioned, audit log exported |
|
||||
| 14 | Traceability (Audit) | Complete change history | All CRUD → audit_log row, compliance 100% |
|
||||
| 15 | Reliability (Fault Tolerance) | Graceful degradation | Retry logic, clear errors, atomicity |
|
||||
| 16 | Technical Debt (Zero New) | Audit existing, prevent new | No shortcuts, debt spreadsheet tracked |
|
||||
| 17 | Componentization (Smart/Dumb) | 4-layer hierarchy | Dumb (props→events), Smart (state+API) |
|
||||
| 18 | Professional Approach | Code review, pair prog, security | 24h PR SLA, no `any` types, OWASP |
|
||||
| 19 | Type Safety (TypeScript) | Strict mode enabled | `tsc --noEmit` 0 errors |
|
||||
| 20 | Accessibility (WCAG 2.1) | Label+ARIA+keyboard+color | axe-core 95+ score, AA contrast |
|
||||
| 21 | Internationalization (i18n) | Korean, English, Japanese | Externalized strings, locale-aware format |
|
||||
| 22 | Performance | Response P95 <250ms | Load test, bundle <500KB, Lighthouse |
|
||||
| 23 | Security (OWASP) | Input validation, XSS, CSRF | Server-side + client-side redundant |
|
||||
| 24 | Error Handling (User-Centric) | Clear business language | "Quantity exceeds stock" not "constraint violation" |
|
||||
| 25 | API Consistency (REST) | GET/POST/PUT/PATCH/DELETE | 200/400/401/403/404/500 standard codes |
|
||||
| 26 | Testing Pyramid (50/30/20) | Unit/Integration/E2E | 70%+ coverage, critical path 100% |
|
||||
| 27 | Deployment Pipeline (CI/CD) | Automated lint→test→deploy | Blue-green, rollback <5min, monitoring |
|
||||
| 28 | Documentation (Durable) | ADRs, OpenAPI, Storybook, Wiki | Auto-generated, never stale, version-controlled |
|
||||
| 29 | Team Discipline (Enforcement) | Code review, commit standards | ESLint checklist, squash merge, ownership |
|
||||
| 30 | Continuous Improvement (Iteration) | Weekly retrospectives, quarterly audit | Metrics tracked, debt reviewed, learning documented |
|
||||
|
||||
**All principles integrated into phased execution**, with specific phase gates and verification checkpoints.
|
||||
|
||||
### Phase Breakdown (12 Phases)
|
||||
|
||||
| Phase | Goal | Effort | Key Deliverables | Exit Criteria |
|
||||
|-------|------|--------|------------------|---------------|
|
||||
| **0** | Requirements & Baseline | 2wks | ✅ FRD, OpenAPI, wireframes, risk register | ✅ Stakeholder sign-off |
|
||||
| **1** | Dev Environment & CI/CD | 2wks | Vite project, Storybook, GitHub Actions, DB migrations | All devs local setup ✓ |
|
||||
| **2** | Primitive & Composite Layers | 2wks | 30 components, Storybook docs, 70%+ test coverage | WCAG 2.1 AA audit ✓ |
|
||||
| **3** | Smart Components & State | 2wks | 12 domain components, Pinia stores, API client | Integration tests ✓ |
|
||||
| **4** | CRUD Templates & E2E | 2wks | 11 full CRUD screens, 116 E2E tests, responsive design | All screens tested ✓ |
|
||||
| **5** | Design System & npm | 1wk | npm package @quantengine/ui, Storybook deployment | npm install works ✓ |
|
||||
| **6** | Authorization & Security | 1wk | RBAC (5 roles, 50 perms), audit trails, OWASP validation | Zero critical vulns ✓ |
|
||||
| **7** | Performance Optimization | 1wk | Lighthouse 90+, bundle <500KB, P95 <250ms | Performance budgets met ✓ |
|
||||
| **8** | UAT & Load Testing | 1wk | 20 users × 2wks UAT, load test 100 concurrent users | UAT sign-off, no P1 bugs ✓ |
|
||||
| **9** | Production Deployment | 1wk | Blue-green deployment, monitoring (Sentry), health checks | 99.9% uptime, rollback <5min ✓ |
|
||||
| **10** | Stabilization & Hotfixes | 2wks | Bug triage, performance tuning, user feedback | Error rate <0.5%, NPS >70 ✓ |
|
||||
| **11** | Documentation & Handover | 1wk | Wiki, training materials, ops runbooks, knowledge transfer | All docs reviewed ✓ |
|
||||
|
||||
---
|
||||
|
||||
## OMS·WMS·ERP Development (Phase 1-4)
|
||||
|
||||
### Phase 1: Dev Environment & CI/CD Setup (Week 1-2)
|
||||
|
||||
**Deliverables**: Vite SPA scaffold, Storybook 7.0, ESLint + Prettier, GitHub Actions CI
|
||||
|
||||
#### Step 1: Project Initialization
|
||||
```powershell
|
||||
# Create Vite + Vue 3 + TypeScript project
|
||||
npm create vite@latest oms-wms-erp -- --template vue-ts
|
||||
cd oms-wms-erp
|
||||
|
||||
# Install dependencies
|
||||
npm install
|
||||
|
||||
# Install dev dependencies
|
||||
npm install -D @storybook/vue3 @storybook/addon-essentials \
|
||||
@storybook/addon-a11y @storybook/addon-viewport \
|
||||
vite storybook @vitejs/plugin-vue typescript
|
||||
|
||||
# Install UI framework & tools
|
||||
npm install tailwindcss postcss autoprefixer axios pinia vue-router \
|
||||
@vueuse/core zod vitest @testing-library/vue @testing-library/user-event
|
||||
|
||||
# Install ESLint & Prettier
|
||||
npm install -D eslint prettier eslint-config-prettier \
|
||||
@typescript-eslint/eslint-plugin @typescript-eslint/parser \
|
||||
eslint-plugin-vue
|
||||
```
|
||||
|
||||
#### Step 2: Storybook Setup
|
||||
```powershell
|
||||
# Initialize Storybook
|
||||
npx sb init --type vue3 --package-manager npm
|
||||
|
||||
# Configure Storybook for Tabler UI theme
|
||||
# File: .storybook/preview.ts
|
||||
# Add Tabler CSS: https://cdn.jsdelivr.net/npm/@tabler/core@latest/dist/css/tabler.min.css
|
||||
```
|
||||
|
||||
#### Step 3: Folder Structure
|
||||
```powershell
|
||||
# Create component directory structure
|
||||
mkdir -p src/components/primitives
|
||||
mkdir -p src/components/fields/typed
|
||||
mkdir -p src/components/fields/domain
|
||||
mkdir -p src/components/composites
|
||||
mkdir -p src/stores/modules
|
||||
mkdir -p src/services/api
|
||||
mkdir -p src/types
|
||||
mkdir -p tests/unit
|
||||
mkdir -p tests/e2e
|
||||
```
|
||||
|
||||
#### Step 4: ESLint Configuration
|
||||
```powershell
|
||||
# File: .eslintrc.cjs
|
||||
# Extends: @typescript-eslint/recommended, plugin:vue/vue3-recommended
|
||||
# Rules: no-console (dev only), no-any, no-implicit-any
|
||||
```
|
||||
|
||||
**Exit Criteria**:
|
||||
- ✅ `npm install` succeeds (no peer dependency warnings)
|
||||
- ✅ `npm run dev` starts Vite dev server on localhost:5173
|
||||
- ✅ `npm run storybook` starts Storybook on localhost:6006
|
||||
- ✅ `npm run lint` passes with 0 errors
|
||||
- ✅ All 4 devs can build locally
|
||||
|
||||
---
|
||||
|
||||
### Phase 2: Primitive Components (Week 3-4)
|
||||
|
||||
**Deliverables**: 30 Primitive components, 180 Storybook stories, unit tests 70%+, WCAG 2.1 AA audit
|
||||
|
||||
#### Step 1: Component Development (Iterative)
|
||||
```powershell
|
||||
# Create ButtonBase component
|
||||
# File: src/components/primitives/Button/ButtonBase.vue
|
||||
cat > src/components/primitives/Button/ButtonBase.vue << 'EOF'
|
||||
<template>
|
||||
<button
|
||||
:class="['btn', `btn-${variant}`, `btn-${size}`, { disabled }]"
|
||||
:disabled="disabled || loading"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<span v-if="loading" class="spinner-border spinner-border-sm me-2"></span>
|
||||
<slot />
|
||||
</button>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
interface Props {
|
||||
variant?: 'primary' | 'secondary' | 'danger';
|
||||
size?: 'sm' | 'md' | 'lg';
|
||||
disabled?: boolean;
|
||||
loading?: boolean;
|
||||
}
|
||||
|
||||
withDefaults(defineProps<Props>(), {
|
||||
variant: 'primary',
|
||||
size: 'md',
|
||||
disabled: false,
|
||||
loading: false,
|
||||
});
|
||||
|
||||
defineEmits<{
|
||||
click: [];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.btn {
|
||||
border-radius: 6px;
|
||||
font-weight: 500;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
.btn:focus {
|
||||
outline: 2px solid #0d6efd;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
</style>
|
||||
EOF
|
||||
|
||||
# Create Storybook stories
|
||||
# File: src/components/primitives/Button/ButtonBase.stories.ts
|
||||
# Export: Default, Primary, Secondary, Loading, Disabled, etc.
|
||||
|
||||
# Create unit tests
|
||||
# File: src/components/primitives/Button/ButtonBase.spec.ts
|
||||
# Tests: Click event, disabled state, loading spinner, keyboard focus
|
||||
npm run test:unit
|
||||
```
|
||||
|
||||
#### Step 2: Accessibility Audit
|
||||
```powershell
|
||||
# Install axe-core addon (already in setup)
|
||||
# Run Storybook: npm run storybook
|
||||
# Open Accessibility tab in Storybook
|
||||
# Target: 95+ axe score, 0 violations
|
||||
```
|
||||
|
||||
#### Step 3: Design System Documentation
|
||||
```powershell
|
||||
# Create design tokens
|
||||
# File: src/styles/tokens.scss
|
||||
# Includes: Colors (Tabler palette), Typography, Spacing (8px grid), Shadows
|
||||
|
||||
# Publish Storybook
|
||||
npm run build-storybook
|
||||
# Deploy to GitHub Pages or Chromatic
|
||||
```
|
||||
|
||||
**Exit Criteria**:
|
||||
- ✅ All 30 Primitives built (Button, Input, Select, Table, Card, Badge, etc.)
|
||||
- ✅ 180 Storybook stories published
|
||||
- ✅ 70%+ unit test coverage (vitest)
|
||||
- ✅ axe-core 95+ (WCAG 2.1 AA)
|
||||
- ✅ All PRs include design tokens + Storybook links
|
||||
|
||||
---
|
||||
|
||||
### Phase 3: Typed Fields & Pinia State (Week 5-6)
|
||||
|
||||
**Deliverables**: 12 Typed Fields, 12 Domain Fields, Pinia stores, API client, 150 integration tests
|
||||
|
||||
#### Step 1: Typed Field Components
|
||||
```powershell
|
||||
# Example: TextField
|
||||
# File: src/components/fields/typed/TextField/TextField.vue
|
||||
cat > src/components/fields/typed/TextField/TextField.vue << 'EOF'
|
||||
<template>
|
||||
<div class="form-group">
|
||||
<label v-if="label" :for="`field-${id}`" class="form-label">
|
||||
{{ label }}
|
||||
<span v-if="required" class="text-danger">*</span>
|
||||
</label>
|
||||
<input
|
||||
:id="`field-${id}`"
|
||||
:value="modelValue"
|
||||
:type="type"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:class="['form-control', { 'is-invalid': errorMessage }]"
|
||||
:aria-describedby="errorMessage ? `error-${id}` : helpText ? `help-${id}` : undefined"
|
||||
@input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
|
||||
@blur="$emit('blur')"
|
||||
/>
|
||||
<small v-if="helpText" :id="`help-${id}`" class="form-text text-muted">
|
||||
{{ helpText }}
|
||||
</small>
|
||||
<div v-if="errorMessage" :id="`error-${id}`" class="invalid-feedback d-block">
|
||||
{{ errorMessage }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref } from 'vue';
|
||||
|
||||
interface Props {
|
||||
modelValue: string;
|
||||
label?: string;
|
||||
type?: 'text' | 'email' | 'password' | 'url' | 'number';
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
required?: boolean;
|
||||
helpText?: string;
|
||||
errorMessage?: string;
|
||||
validation?: (value: string) => string | null;
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
type: 'text',
|
||||
});
|
||||
|
||||
const id = ref(`field-${Math.random().toString(36).slice(2, 11)}`);
|
||||
|
||||
defineEmits<{
|
||||
'update:modelValue': [value: string];
|
||||
blur: [];
|
||||
}>();
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.form-group {
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
.form-label {
|
||||
font-weight: 500;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
</style>
|
||||
EOF
|
||||
|
||||
# Repeat for 11 more: DateField, CurrencyField, QuantityField, etc.
|
||||
```
|
||||
|
||||
#### Step 2: Pinia Store Setup
|
||||
```powershell
|
||||
# File: src/stores/modules/orders.ts
|
||||
cat > src/stores/modules/orders.ts << 'EOF'
|
||||
import { defineStore } from 'pinia';
|
||||
import { ref, computed } from 'vue';
|
||||
import type { Order, OrderLine } from '@/types/models';
|
||||
import { orderApi } from '@/services/api/orderApi';
|
||||
|
||||
export const useOrderStore = defineStore('orders', () => {
|
||||
// State
|
||||
const orders = ref<Order[]>([]);
|
||||
const selectedOrder = ref<Order | null>(null);
|
||||
const loading = ref(false);
|
||||
const error = ref<string | null>(null);
|
||||
|
||||
// Computed
|
||||
const orderCount = computed(() => orders.value.length);
|
||||
const totalAmount = computed(() =>
|
||||
orders.value.reduce((sum, o) => sum + o.totalAmount, 0)
|
||||
);
|
||||
|
||||
// Actions
|
||||
const fetchOrders = async () => {
|
||||
loading.value = true;
|
||||
error.value = null;
|
||||
try {
|
||||
orders.value = await orderApi.listOrders({ limit: 100 });
|
||||
} catch (err) {
|
||||
error.value = (err as Error).message;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
const createOrder = async (payload: Partial<Order>) => {
|
||||
loading.value = true;
|
||||
try {
|
||||
const newOrder = await orderApi.createOrder(payload);
|
||||
orders.value.push(newOrder);
|
||||
selectedOrder.value = newOrder;
|
||||
return newOrder;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
orders,
|
||||
selectedOrder,
|
||||
loading,
|
||||
error,
|
||||
orderCount,
|
||||
totalAmount,
|
||||
fetchOrders,
|
||||
createOrder,
|
||||
};
|
||||
});
|
||||
EOF
|
||||
|
||||
# Repeat for 9 more stores: inventory, products, customers, suppliers, etc.
|
||||
```
|
||||
|
||||
#### Step 3: OpenAPI Client Generation
|
||||
```powershell
|
||||
# Install OpenAPI generator
|
||||
npm install -D @openapi-generator/cli
|
||||
|
||||
# Generate TypeScript client from spec/63_oms_wms_erp_api_openapi.yaml
|
||||
npx @openapi-generator/cli generate \
|
||||
-i spec/63_oms_wms_erp_api_openapi.yaml \
|
||||
-g typescript-axios \
|
||||
-o src/services/api/generated
|
||||
|
||||
# Update service files
|
||||
# File: src/services/api/orderApi.ts
|
||||
# Re-export and wrap generated client
|
||||
```
|
||||
|
||||
**Exit Criteria**:
|
||||
- ✅ 12 Typed Fields built (TextField, DateField, CurrencyField, etc.)
|
||||
- ✅ 12 Domain Fields built (OrderLineField, ProductField, etc.)
|
||||
- ✅ 10 Pinia stores created (orders, inventory, products, etc.)
|
||||
- ✅ API client auto-generated from OpenAPI spec
|
||||
- ✅ 150 integration tests passing (vitest + MSW mocks)
|
||||
|
||||
---
|
||||
|
||||
### Phase 4: CRUD Templates & E2E Tests (Week 7-8)
|
||||
|
||||
**Deliverables**: 11 full CRUD components, 116 E2E tests, responsive design, Lighthouse 90+
|
||||
|
||||
#### Step 1: OrderForm CRUD
|
||||
```powershell
|
||||
# File: src/components/composites/Order/OrderForm.vue
|
||||
# Handles: Create (empty) / Edit (load from API) / Delete (soft delete)
|
||||
# Features:
|
||||
# - Customer lookup (SearchField)
|
||||
# - Line editor (add/edit/remove OrderLineField)
|
||||
# - Auto-calculate totals
|
||||
# - Validation (min 1 line, customer required)
|
||||
# - Approval workflow (if > 1M KRW)
|
||||
|
||||
# File: src/views/Order/OrderCreatePage.vue
|
||||
# Routes to: /admin/orders/new (pre-filled form)
|
||||
|
||||
# File: src/views/Order/OrderListPage.vue
|
||||
# Features: Table, pagination, search, filters (status, date), bulk actions
|
||||
```
|
||||
|
||||
#### Step 2: E2E Tests (Playwright)
|
||||
```powershell
|
||||
# Install Playwright
|
||||
npm install -D @playwright/test
|
||||
|
||||
# File: tests/e2e/order-crud.spec.ts
|
||||
cat > tests/e2e/order-crud.spec.ts << 'EOF'
|
||||
import { test, expect } from '@playwright/test';
|
||||
|
||||
test.describe('Order CRUD', () => {
|
||||
test('Create → Read → Edit → Delete', async ({ page }) => {
|
||||
// 1. Login
|
||||
await page.goto('/');
|
||||
await page.fill('[name="email"]', 'user@example.com');
|
||||
await page.fill('[name="password"]', 'password123!');
|
||||
await page.click('button[type="submit"]');
|
||||
await expect(page).toHaveURL('/admin/dashboard');
|
||||
|
||||
// 2. Create order
|
||||
await page.click('a[href="/admin/orders"]');
|
||||
await page.click('button:text("Create Order")');
|
||||
await page.selectOption('[name="customerId"]', 'CUST-001');
|
||||
await page.fill('[name="quantity"]', '100');
|
||||
await page.click('button:text("Submit")');
|
||||
|
||||
// 3. Verify created
|
||||
const orderNo = await page.locator('h1').textContent();
|
||||
expect(orderNo).toMatch(/ORD-\d+/);
|
||||
|
||||
// 4. Edit
|
||||
await page.click('button:text("Edit")');
|
||||
await page.fill('[name="quantity"]', '150');
|
||||
await page.click('button:text("Save")');
|
||||
|
||||
// 5. Delete
|
||||
await page.click('button:text("Delete")');
|
||||
await page.click('button:text("Confirm")');
|
||||
await expect(page).toHaveURL('/admin/orders');
|
||||
});
|
||||
});
|
||||
EOF
|
||||
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
#### Step 3: Performance Optimization
|
||||
```powershell
|
||||
# Measure Lighthouse score
|
||||
npm run build # Build for production
|
||||
npx lighthouse http://localhost:5173/admin/orders \
|
||||
--view --output-path=lighthouse-report.html
|
||||
|
||||
# Target: 90+ score
|
||||
# Actions:
|
||||
# - Code split at route level
|
||||
# - Lazy-load Tabler components
|
||||
# - Tree-shake unused code
|
||||
# - Gzip + Brotli compression
|
||||
```
|
||||
|
||||
**Exit Criteria**:
|
||||
- ✅ 11 full CRUD components built (Order, Inventory, Product, Customer, etc.)
|
||||
- ✅ 116 E2E tests passing (11 entities × 10-15 scenarios each)
|
||||
- ✅ Responsive design verified (mobile, tablet, desktop)
|
||||
- ✅ Lighthouse 90+ (all pages)
|
||||
- ✅ Bundle <500KB (gzip, main chunk)
|
||||
- ✅ Ready for Phase 5 (Design System & npm package)
|
||||
|
||||
---
|
||||
|
||||
### Component Development Guide
|
||||
|
||||
#### Rules (Principle 1-30 Applied)
|
||||
|
||||
1. **Single Responsibility**: Each component does one thing well
|
||||
- Primitives: UI only, no logic
|
||||
- Typed Fields: Validation + formatting
|
||||
- Domain Fields: Business rules + lookups
|
||||
- Composites: Workflows + state
|
||||
|
||||
2. **Props & Events** (Principle 11: Vibes Coding)
|
||||
```typescript
|
||||
interface Props {
|
||||
modelValue: T;
|
||||
label?: string;
|
||||
disabled?: boolean;
|
||||
errorMessage?: string;
|
||||
}
|
||||
|
||||
defineEmits<{
|
||||
'update:modelValue': [value: T];
|
||||
blur: [];
|
||||
}>();
|
||||
```
|
||||
|
||||
3. **Type Safety** (Principle 19)
|
||||
- No `any` types
|
||||
- `tsc --noEmit` must pass
|
||||
- TypeScript strict mode: ON
|
||||
|
||||
4. **Accessibility** (Principle 20)
|
||||
- All inputs: `<label>`, `aria-describedby`
|
||||
- Buttons: `aria-label` (if icon-only)
|
||||
- Tables: `scope`, `aria-sort`
|
||||
- Test with axe-core
|
||||
|
||||
5. **Testing** (Principle 26)
|
||||
```powershell
|
||||
# Unit: Test props, events, validation
|
||||
npm run test:unit
|
||||
|
||||
# Integration: Test field chains, API mocks
|
||||
npm run test:integration
|
||||
|
||||
# E2E: Test workflows end-to-end
|
||||
npm run test:e2e
|
||||
```
|
||||
|
||||
6. **Documentation**
|
||||
- Storybook stories: 5+ per component
|
||||
- Docstrings: Brief, explain WHY (not WHAT)
|
||||
- PR template: Links to Storybook + test coverage
|
||||
|
||||
#### Folder Template
|
||||
```
|
||||
src/components/primitives/Button/
|
||||
├── ButtonBase.vue # Component
|
||||
├── ButtonBase.stories.ts # 12+ stories
|
||||
├── ButtonBase.spec.ts # Unit tests
|
||||
├── types.ts # Props/Emits types
|
||||
└── README.md # Optional doc
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Phase 1 Go/No-Go Validation Checklist
|
||||
|
||||
**Before Phase 1 starts (2026-08-02)**:
|
||||
|
||||
- [ ] Vite scaffold created with TypeScript strict mode
|
||||
- [ ] Storybook 7.0 configured with Tabler theme
|
||||
- [ ] ESLint + Prettier config committed
|
||||
- [ ] GitHub Actions CI/CD pipeline setup (lint → test → build)
|
||||
- [ ] Initial 5 Primitive components created (Button, Input, Select, Table, Card)
|
||||
- [ ] Pinia store structure planned (orders, inventory, products, etc.)
|
||||
- [ ] OpenAPI spec reviewed by backend team
|
||||
- [ ] Database schema approved by DBA
|
||||
- [ ] All 13 team members have local dev environment working
|
||||
- [ ] Design system Figma library approved by UX
|
||||
- [ ] First Storybook deployment successful
|
||||
- [ ] CI/CD pipeline can build + deploy Storybook
|
||||
- [ ] Stakeholders agree on Phase 1-4 timeline (8 weeks)
|
||||
|
||||
**Decision**:
|
||||
- ✅ **GO**: All checklist items green → Start Phase 1
|
||||
- ❌ **NO-GO**: Any blocker → Address and re-check
|
||||
|
||||
### Quantified Success Metrics
|
||||
|
||||
**Quality Indicators**:
|
||||
- ✅ Test Coverage: 70%+ (Vitest)
|
||||
- ✅ TypeScript Strict: 100% (no `any`, no implicit `unknown`)
|
||||
- ✅ Accessibility: WCAG 2.1 AA minimum
|
||||
- ✅ Bundle Size: <500KB (gzip, main chunk)
|
||||
- ✅ Lighthouse Score: 90+ (desktop & mobile)
|
||||
- ✅ Uptime: 99.9% (SLA)
|
||||
- ✅ Response Time: P95 <250ms
|
||||
- ✅ Error Rate: <0.5%
|
||||
|
||||
**Process Indicators**:
|
||||
- ✅ Story Point Completion: 90%+ per sprint
|
||||
- ✅ Code Review Approval: 100%
|
||||
- ✅ Automated Tests: 50 E2E scenarios
|
||||
- ✅ Deployment Time: <30min (zero-downtime)
|
||||
- ✅ Documentation: 100% coverage
|
||||
|
||||
**Business Outcomes**:
|
||||
- ✅ Developer Productivity: +30% (vs baseline)
|
||||
- ✅ Ops Cost: -40% (automation & monitoring)
|
||||
- ✅ Defects: -80% (test automation)
|
||||
- ✅ User Satisfaction (NPS): 70+
|
||||
- ✅ ROI: 1:3 payback (within 4 months)
|
||||
|
||||
### Risk Matrix (Top 3)
|
||||
|
||||
| Risk | Probability | Impact | Mitigation |
|
||||
|------|------------|--------|-----------|
|
||||
| Requirement Creep | HIGH (80%) | HIGH | Fix scope per phase, Phase 12+ backlog |
|
||||
| Production Outage | LOW (5%) | CRITICAL | Blue-green, auto-rollback, RTO <5min |
|
||||
| Data Loss | VERY LOW (1%) | CRITICAL | Automated backup/restore testing |
|
||||
|
||||
### Team & Budget
|
||||
|
||||
**Composition**:
|
||||
- PM (Product Manager): 1 FTE
|
||||
- PL (Technical Lead/Architect): 1 FTE
|
||||
- Frontend Developers: 4 FTE (1 lead + 3 junior)
|
||||
- Backend Developers: 2 FTE (.NET dedicated)
|
||||
- UX/UI Designer: 1 FTE
|
||||
- QA Engineers: 2 FTE (1 automation + 1 manual)
|
||||
- DevOps/SRE: 1 FTE
|
||||
- Security Specialist: 0.5 FTE (consultant)
|
||||
- Technical Writer: 0.5 FTE
|
||||
|
||||
**Estimated Costs** (8 months):
|
||||
- Payroll: $360K (avg $2.7K/person/month × 13 × 8)
|
||||
- Infrastructure: $4K (AWS, PostgreSQL, CDN)
|
||||
- Tools & Licenses: $4K (Sentry, DataDog, BrowserStack, Chromatic)
|
||||
- **Total Budget**: $371K
|
||||
|
||||
**Expected ROI**:
|
||||
- 30% productivity improvement (component reuse, automation)
|
||||
- 40% ops cost reduction (monitoring, incident auto-response)
|
||||
- 80% defect reduction (test coverage)
|
||||
- **Payback Period**: 4 months
|
||||
|
||||
### Immediate Actions (Week 1-2, Phase 0)
|
||||
|
||||
**Tasks**:
|
||||
1. T0.1: Stakeholder requirements (3 days) → FRD
|
||||
2. T0.2: Architecture decision (4 days) → Monolithic SPA confirmed
|
||||
3. T0.3: 4-Layer component design (5 days) → Figma library
|
||||
4. T0.4: 11 CRUD template inventory (4 days) → Template matrix
|
||||
5. T0.5: API OpenAPI 3.0 (5 days) → 30 endpoints spec
|
||||
6. T0.6: UI/UX wireframes (5 days) → High-fidelity mockups
|
||||
7. T0.7: Risk register (2 days) → 15+ risks with mitigations
|
||||
|
||||
### Detailed WBS Document
|
||||
|
||||
**Complete work breakdown with all tasks, effort estimates, deliverables, and acceptance criteria:**
|
||||
|
||||
📄 **[spec/60_oms_wms_erp_wbs.yaml](../spec/60_oms_wms_erp_wbs.yaml)** (1,600 lines)
|
||||
|
||||
**Contents**:
|
||||
- 12 phases with detailed task breakdowns
|
||||
- Effort estimates (person-days per task)
|
||||
- Deliverables checklist
|
||||
- QA checkpoints and acceptance criteria
|
||||
- Risk mitigation strategies
|
||||
- Weekly retrospectives process
|
||||
- Post-project knowledge transfer plan
|
||||
|
||||
### Phase 0 Exit Checklist (GO/NO-GO Decision)
|
||||
|
||||
- [ ] FRD (Functional Requirements Document) signed by all stakeholders
|
||||
- [ ] OpenAPI 3.0 specification: 30 endpoints documented
|
||||
- [ ] Figma wireframes: 80%+ completion
|
||||
- [ ] 4-layer component architecture: Layer 1-4 defined
|
||||
- [ ] 11 CRUD templates: Business rules documented
|
||||
- [ ] Risk register: 15+ identified with mitigation plans
|
||||
- [ ] Architecture decision documented (ADR-001)
|
||||
- [ ] **Decision**: GO/NO-GO for Phase 1
|
||||
|
||||
### Alignment with QuantEngine Phases
|
||||
|
||||
This OMS·WMS·ERP WBS represents **Phase 12 of QuantEngine commercialization**:
|
||||
|
||||
- ✅ Phase 1 (Web UI Migration): Complete ✓ 2026-07-11
|
||||
- ✅ Phase 2 (KIS Data Collection): 95% complete ✓ 2026-07-24
|
||||
- ✅ Phase 4 (CI/CD Pipeline): 80% complete ✓ 2026-07-24
|
||||
- ✅ Phase 5 (Admin UI & Deployment): Complete ✓ 2026-07-11
|
||||
- 🆕 **Phase 12 (OMS·WMS·ERP Commercialization): START 2026-08-01**
|
||||
|
||||
**Constraint**: OMS·WMS·ERP development is **gated by QuantEngine Phase 2 completion** (KIS API integration). Phase 12 can begin only after Phase 2 validation in production.
|
||||
@@ -0,0 +1,54 @@
|
||||
# QuantEngine UI Design Guidelines
|
||||
|
||||
Full UI design principles, extracted from CLAUDE.md (2026-07-30) to keep the main file within
|
||||
the character budget. CLAUDE.md keeps a condensed summary; this file has the complete rules
|
||||
and the component mapping table.
|
||||
|
||||
## Framework & Design System (2026-07-11)
|
||||
|
||||
- **Primary Framework**: ASP.NET Core Razor Pages + Bootstrap 5 + Tabler UI
|
||||
- **Design System**: Tabler (Bootstrap 5 기반), 밀집 레이아웃 + 전통 서버 렌더링
|
||||
- **Render Mode**: **Server-side Razor Pages** — 모든 Admin UI는 서버에서 렌더링, Cookie 기반 인증 (API-First WASM 폐기)
|
||||
- **Authentication**: Cookie Authentication (HttpOnly) + BCrypt password hashing + IP lockout (3 strikes, 15-min)
|
||||
- **Deprecation**: **Blazor Interactive WebAssembly 폐기**, **MudBlazor 컴포넌트 폐기** (2026-07-11), **SmartAdmin 폐기**. `QuantEngine.Web.Client` 폴더는 저장소에 실재하지 않는다 — `.sln`에서 제외된 것이 아니라 완전히 삭제됨 (2026-07-30 확인)
|
||||
|
||||
## Component Development Rules
|
||||
|
||||
1. **All Admin UI Development** (New + Refactored):
|
||||
- Use **Razor Pages** (.cshtml + .cshtml.cs PageModel) exclusively for admin
|
||||
- UI는 Repository/Service를 생성자 DI로 직접 호출 (API 홉 없음)
|
||||
- Bootstrap 5 + Tabler UI CSS classes for styling
|
||||
- **Form Validation**: DataAnnotations DTO + FluentValidation IValidator<T> 이중 검증
|
||||
- HTML `<form>` + tag helpers (`asp-for`, `asp-action`, `asp-page`)
|
||||
|
||||
2. **Authentication & Authorization**:
|
||||
- Cookie name: `QuantEngine.Admin.Auth` (HttpOnly, SameSite=Lax)
|
||||
- Session duration: 12 hours (sliding expiration)
|
||||
- Folder-level `[Authorize]` via `AuthorizeFolder("/Admin")` convention (per-page 반복 금지)
|
||||
- Login: `/Account/Login` (Razor Page, NO WASM)
|
||||
- Password: BCrypt-hashed (auto-migrates existing SHA-256 hashes on first login)
|
||||
- IP Lockout: 3 failed attempts → 15-minute lockout
|
||||
|
||||
3. **Data & Form Patterns**:
|
||||
- PageModel constructor: `public IndexModel(IWorkspaceRepository repo, ILogger<IndexModel> logger)`
|
||||
- Form submission: `OnPostAsync()` / `OnPostDeleteAsync()` (multi-handler pattern)
|
||||
- Validation failures: return `Page()` (re-render with ModelState errors)
|
||||
- Pagination: `PaginationModel` record (Page, TotalPages, Func<int,string> BuildPageUrl)
|
||||
- Empty states: `<PartialView name="_EmptyState" model="message" />`
|
||||
|
||||
4. **Component Mapping** (Bootstrap 5 + Tabler):
|
||||
|
||||
| UI Element | Component | Notes |
|
||||
|-----------|-----------|-------|
|
||||
| Button | `<button class="btn btn-primary">` | — |
|
||||
| Input field | `<input asp-for="Property" class="form-control">` | tag helper |
|
||||
| Dropdown | HTML `<select asp-for="Property">` | tag helper |
|
||||
| Data grid | HTML `<table class="table">` | plain, no virtualization |
|
||||
| Card | `<div class="card">` | Bootstrap card |
|
||||
| Badge/Status | `<span class="badge bg-success">Active</span>` | Bootstrap badge |
|
||||
| Layout container | `<div class="container-xl">` / `<div class="row">` | Bootstrap grid |
|
||||
| Navigation | HTML navbar in `_AdminLayout.cshtml` | sidebar + topbar |
|
||||
| Loading | N/A (server-rendered) | no loading states needed |
|
||||
| Icons | Bootstrap Icons (`<i class="bi bi-*"></i>`) | CDN |
|
||||
| Modal/Dialog | Bootstrap modal or inline `confirm()` | avoid unnecessary modals |
|
||||
| Validation msg | `<span asp-validation-for="Property" class="d-block alert alert-danger mt-2">` | tag helper |
|
||||
@@ -1,3 +1,7 @@
|
||||
> **ARCHIVED (2026-07-30)**: 2026-07-11 시점 build.yml/wbs_9_3_*.yml/merge-to-main.yml 등
|
||||
> 이후 삭제된 워크플로우를 전제로 쓰였습니다. 현재 CI 구조는
|
||||
> [`../CICD_PIPELINE.md`](../CICD_PIPELINE.md)를 참고하세요.
|
||||
|
||||
# QuantEngine CI/CD 파이프라인 — 근본적 개선 분석 및 로드맵
|
||||
|
||||
**작성일**: 2026-07-11
|
||||
@@ -1,3 +1,7 @@
|
||||
> **ARCHIVED (2026-07-30)**: 2026-07-11 시점 파이프라인 구조 기준입니다. 현재 모니터링
|
||||
> 방법은 [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)의 "Deployment Monitoring" /
|
||||
> "API Monitoring (CLI)" 절을 참고하세요.
|
||||
|
||||
# CI/CD Pipeline 모니터링 가이드
|
||||
|
||||
**작성일**: 2026-07-11
|
||||
@@ -1,3 +1,7 @@
|
||||
> **ARCHIVED (2026-07-30)**: "로컬 Green-Blue 배포(SSH 제거)" 방식은 이후
|
||||
> SSH 기반 release-artifact 배포(prepare-release.yml → deploy-prod.yml)로 대체되었습니다.
|
||||
> 현재 배포 방식은 [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)를 참고하세요.
|
||||
|
||||
# QuantEngine CI/CD 파이프라인 구현 완료 보고서
|
||||
|
||||
**작성일**: 2026-07-11
|
||||
@@ -1,3 +1,8 @@
|
||||
> **ARCHIVED (2026-07-30)**: 이미 삭제된 `merge-to-main.yml`, 옛 `deploy_gb.sh` Green-Blue
|
||||
> 스크립트를 전제로 쓰였습니다. 현재 트러블슈팅 가이드는
|
||||
> [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)의 "Troubleshooting Deployment
|
||||
> Failures" 절을 참고하세요.
|
||||
|
||||
# CI/CD 배포 트러블슈팅 가이드
|
||||
|
||||
**작성일**: 2026-07-11
|
||||
@@ -1,3 +1,8 @@
|
||||
> **ARCHIVED (2026-07-30)**: 테스트 섹션은 bUnit + MudBlazor 컴포넌트(`Dashboard.razor`,
|
||||
> `mud-card-kpi` 등) 기준으로, MudBlazor/Blazor WASM이 Razor Pages로 대체된 2026-07-11
|
||||
> Phase 1 이후 더 이상 유효하지 않습니다. 배포 섹션은
|
||||
> [`../DEPLOYMENT_RUNBOOK.md`](../DEPLOYMENT_RUNBOOK.md)로 대체되었습니다.
|
||||
|
||||
# QuantEngine - Testing & Deployment Guide
|
||||
|
||||
**Status**: Phase 6 (Testing) & Phase 8 (Deployment) - Configuration & Documentation
|
||||
+249
-2
@@ -1,8 +1,14 @@
|
||||
// =============================================================================
|
||||
// QuantEngine Database Schema (DBML)
|
||||
// DbUp 마이그레이션(V1~V5)과 1:1 동기화 — 마이그레이션 추가 시 이 파일도 반드시 갱신
|
||||
// DbUp 마이그레이션(V1~V8, V003, V004)과 1:1 동기화 — 마이그레이션 추가 시 이 파일도 반드시 갱신
|
||||
// (CLAUDE.md 규칙: schema 변경 → DBML + 문서 동기화)
|
||||
//
|
||||
// 마이그레이션 파일명 규칙 2종 혼재 (2026-07-30 발견, 미해결):
|
||||
// V1__Name.sql .. V8__Name.sql (더블언더스코어, zero-pad 없음)
|
||||
// V003_name.sql, V004_name.sql (싱글언더스코어, zero-pad)
|
||||
// DbUp는 파일명 알파벳순으로 실행하므로 "V003" < "V1" 순서로 적용됨 — 신규 마이그레이션은
|
||||
// 반드시 하나의 규칙(권장: V{n}__Name.sql)만 사용할 것.
|
||||
//
|
||||
// 참고: Hangfire 스키마는 Hangfire.PostgreSql 라이브러리가 자동 생성
|
||||
// (DbUp 마이그레이션으로 관리하지 않음, 여기서도 제외)
|
||||
// =============================================================================
|
||||
@@ -382,7 +388,7 @@ Table engine_history.market_vs_engine_gap_history {
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Schema: engine_history (V5 normalized learning history)
|
||||
// V6: Market Time Series (quantengine schema)
|
||||
// =============================================================================
|
||||
|
||||
Table quantengine.price_history_daily {
|
||||
@@ -415,6 +421,10 @@ Table quantengine.macro_history_daily {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// V5: Normalized Learning History (engine_history schema, event-sourcing style)
|
||||
// =============================================================================
|
||||
|
||||
Table engine_history.source_observation {
|
||||
observation_id UUID [pk]
|
||||
observed_at TIMESTAMPTZ [not null]
|
||||
@@ -493,6 +503,243 @@ Table engine_history.outcome_evaluation {
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// V003: Audit Trail Tables (quantengine schema, 2026-07-24)
|
||||
//
|
||||
// 2026-07-30 확정 (실제 프로덕션 DB 조회로 검증): 이 마이그레이션은 CREATE TABLE 안에
|
||||
// MySQL 전용 인라인 "INDEX name (cols)" 구문을 사용해 PostgreSQL에서 문법 오류로 실패했다.
|
||||
// quantengine.schemaversions(DbUp 저널)에 V003이 아예 기록되어 있지 않고, 아래 3개 테이블도
|
||||
// 프로덕션에 실제로 존재하지 않음을 직접 확인했다. V003_add_audit_trail_tables.sql의 인라인
|
||||
// INDEX 구문은 이미 별도 CREATE INDEX 문으로 수정됐으므로, 다음 배포 시 DbUp가 이 마이그레이션을
|
||||
// 최초로 실행해 아래 3개 테이블을 생성할 것이다.
|
||||
// =============================================================================
|
||||
|
||||
Table quantengine.kis_collection_runs_audit {
|
||||
id BIGSERIAL [pk]
|
||||
run_id "UUID" [not null]
|
||||
action "VARCHAR(10)" [not null, note: "INSERT/UPDATE/DELETE"]
|
||||
changed_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
changed_by "VARCHAR(256)" [default: "CURRENT_USER"]
|
||||
change_reason TEXT
|
||||
old_values JSONB
|
||||
new_values JSONB
|
||||
created_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
|
||||
Note: "kis_collection_runs 변경 이력 (트리거 자동 기록) — ⚠️ 마이그레이션 문법 오류로 실제 생성 여부 미확인"
|
||||
}
|
||||
|
||||
Table quantengine.kis_collection_snapshots_audit {
|
||||
id BIGSERIAL [pk]
|
||||
snapshot_id "UUID" [not null]
|
||||
action "VARCHAR(10)" [not null, note: "INSERT/UPDATE/DELETE"]
|
||||
changed_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
changed_by "VARCHAR(256)" [default: "CURRENT_USER"]
|
||||
change_reason TEXT
|
||||
old_values JSONB
|
||||
new_values JSONB
|
||||
created_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
|
||||
Note: "kis_collection_snapshots 변경 이력 — ⚠️ 마이그레이션 문법 오류로 실제 생성 여부 미확인"
|
||||
}
|
||||
|
||||
Table quantengine.kis_collection_errors_audit {
|
||||
id BIGSERIAL [pk]
|
||||
error_id "UUID" [not null]
|
||||
action "VARCHAR(10)" [not null, note: "INSERT/UPDATE/DELETE"]
|
||||
changed_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
changed_by "VARCHAR(256)" [default: "CURRENT_USER"]
|
||||
change_reason TEXT
|
||||
old_values JSONB
|
||||
new_values JSONB
|
||||
created_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
|
||||
Note: "kis_collection_errors 변경 이력 — ⚠️ 마이그레이션 문법 오류로 실제 생성 여부 미확인"
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// V004: 3NF Normalization / Star Schema (quantengine schema, Adapter 패턴으로
|
||||
// 기존 kis_collection_snapshots와 병행 운영 — 마이그레이션 자체 주석에 명시됨)
|
||||
// =============================================================================
|
||||
|
||||
Table quantengine.stocks {
|
||||
id SERIAL [pk]
|
||||
ticker "VARCHAR(10)" [unique, not null]
|
||||
name "VARCHAR(255)"
|
||||
sector "VARCHAR(50)"
|
||||
market "VARCHAR(20)" [note: "KOSPI/KOSDAQ 등"]
|
||||
created_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
updated_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
|
||||
Note: "종목 차원 테이블 (Star Schema dimension)"
|
||||
}
|
||||
|
||||
Table quantengine.sources {
|
||||
id SERIAL [pk]
|
||||
name "VARCHAR(50)" [unique, not null]
|
||||
priority INT [not null, note: "1=주 소스, 2 이상=폴백"]
|
||||
fallback_to_id INT [ref: > quantengine.sources.id]
|
||||
created_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
|
||||
Note: "데이터 소스 차원 테이블 (KIS→Naver→Yahoo→OpenDART 폴백 체인)"
|
||||
}
|
||||
|
||||
Table quantengine.market_data {
|
||||
id BIGSERIAL [pk]
|
||||
stock_id INT [not null, ref: > quantengine.stocks.id]
|
||||
source_id INT [not null, ref: > quantengine.sources.id]
|
||||
price DECIMAL [not null]
|
||||
bid DECIMAL
|
||||
ask DECIMAL
|
||||
volume BIGINT
|
||||
collected_at TIMESTAMPTZ [not null]
|
||||
created_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
collection_run_id "UUID" [note: "kis_collection_runs 추적용"]
|
||||
|
||||
Note: "정규화된 시장 데이터 팩트 테이블 (Star Schema fact)"
|
||||
}
|
||||
|
||||
Table quantengine.kis_collection_snapshots_v2 {
|
||||
id "UUID" [pk]
|
||||
run_id "UUID" [not null, ref: > quantengine.kis_collection_runs.run_id]
|
||||
stock_id INT [not null, ref: > quantengine.stocks.id]
|
||||
market_data_id BIGINT [ref: > quantengine.market_data.id, note: "조회 성능을 위한 의도적 역정규화"]
|
||||
created_at TIMESTAMPTZ [not null, default: "CURRENT_TIMESTAMP"]
|
||||
|
||||
Note: "정규화된 kis_collection_snapshots — 레거시 kis_collection_snapshots와 Adapter 패턴으로 병행 운영, 완전 전환 여부 미확인"
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// V8: PostgreSQL History-First Operating Model (quantengine schema)
|
||||
//
|
||||
// ⚠️ 스키마 충돌 주의: 아래 4개 테이블(market_raw_history, factor_version_history,
|
||||
// factor_output_history, decision_result_history)은 engine_history 스키마(V3, 위 참고)에
|
||||
// 이미 동일한 이름으로 존재한다. 컬럼 구조를 대조한 결과 같은 테이블의 재적용이 아니라
|
||||
// 서로 다른 두 가지 설계다:
|
||||
// - engine_history.*: EAV형 원본 관측 이력 (field_name/field_value 페어)
|
||||
// - quantengine.*(이 섹션): OHLCV 와이드 테이블 / 팩터ID-스코어 구조
|
||||
// 둘 다 실제 마이그레이션 파일에 존재하므로 DBML에는 두 스키마 버전을 모두 남긴다.
|
||||
// 어느 쪽이 정본인지, 혹은 통합이 필요한지는 별도 아키텍처 결정 필요 (이번 작업 범위 밖).
|
||||
// =============================================================================
|
||||
|
||||
Table quantengine.market_raw_history {
|
||||
id BIGSERIAL [pk]
|
||||
ticker "VARCHAR(32)" [not null]
|
||||
as_of_date "VARCHAR(10)" [not null]
|
||||
open_price "NUMERIC(18,4)"
|
||||
high_price "NUMERIC(18,4)"
|
||||
low_price "NUMERIC(18,4)"
|
||||
close_price "NUMERIC(18,4)" [not null]
|
||||
volume BIGINT
|
||||
nav_price "NUMERIC(18,4)"
|
||||
disparate_ratio "NUMERIC(10,6)"
|
||||
tracking_error "NUMERIC(10,6)"
|
||||
aum_krw "NUMERIC(20,2)"
|
||||
raw_payload JSONB [not null]
|
||||
provenance JSONB [not null]
|
||||
created_at TIMESTAMPTZ [default: "NOW()"]
|
||||
|
||||
indexes {
|
||||
(ticker, as_of_date) [unique, name: "uk_market_raw_ticker_date"]
|
||||
}
|
||||
|
||||
Note: "OHLCV 와이드 테이블 — engine_history.market_raw_history(EAV형)와는 별개 설계"
|
||||
}
|
||||
|
||||
Table quantengine.factor_version_history {
|
||||
factor_id "VARCHAR(64)" [pk]
|
||||
formula_name "VARCHAR(128)" [not null]
|
||||
version "VARCHAR(32)" [not null]
|
||||
category "VARCHAR(64)" [not null]
|
||||
calibration_state "VARCHAR(32)" [not null, default: "'UNTESTED'"]
|
||||
threshold_params JSONB [not null]
|
||||
description TEXT
|
||||
updated_at TIMESTAMPTZ [default: "NOW()"]
|
||||
|
||||
Note: "팩터 정의 — engine_history.factor_version_history와는 별개 설계 (PK가 factor_id 단독, 버전 이력 미보존)"
|
||||
}
|
||||
|
||||
Table quantengine.factor_output_history {
|
||||
id BIGSERIAL [pk]
|
||||
run_id "VARCHAR(64)" [not null]
|
||||
ticker "VARCHAR(32)" [not null]
|
||||
as_of_date "VARCHAR(10)" [not null]
|
||||
factor_id "VARCHAR(64)" [not null, ref: > quantengine.factor_version_history.factor_id]
|
||||
score "NUMERIC(10,4)"
|
||||
calculation_state "VARCHAR(32)" [not null]
|
||||
provenance JSONB [not null]
|
||||
created_at TIMESTAMPTZ [default: "NOW()"]
|
||||
|
||||
Note: "팩터 계산 결과 — engine_history.factor_output_history와는 별개 설계"
|
||||
}
|
||||
|
||||
Table quantengine.decision_result_history {
|
||||
id BIGSERIAL [pk]
|
||||
run_id "VARCHAR(64)" [unique, not null]
|
||||
as_of_date "VARCHAR(10)" [not null]
|
||||
market_regime "VARCHAR(32)" [not null]
|
||||
portfolio_health "VARCHAR(32)" [not null]
|
||||
rebalance_required BOOLEAN [not null, default: "false"]
|
||||
mid_check_required BOOLEAN [not null, default: "false"]
|
||||
total_asset_krw "NUMERIC(20,2)" [not null]
|
||||
d2_cash_krw "NUMERIC(20,2)" [not null]
|
||||
decision_packet_json JSONB [not null]
|
||||
created_at TIMESTAMPTZ [default: "NOW()"]
|
||||
|
||||
Note: "의사결정 패킷 이력 — engine_history.decision_result_history와는 별개 설계"
|
||||
}
|
||||
|
||||
Table quantengine.order_waterfall_execution_history {
|
||||
id BIGSERIAL [pk]
|
||||
run_id "VARCHAR(64)" [not null, ref: > quantengine.decision_result_history.run_id]
|
||||
ticker "VARCHAR(32)" [not null]
|
||||
sell_priority_rank INT [not null]
|
||||
waterfall_stage "VARCHAR(64)" [not null]
|
||||
action "VARCHAR(16)" [not null]
|
||||
target_qty INT [not null]
|
||||
executed_qty INT [default: "0"]
|
||||
target_price "NUMERIC(18,4)"
|
||||
executed_price "NUMERIC(18,4)"
|
||||
bid_ask_spread_bps "NUMERIC(10,2)"
|
||||
slippage_bps "NUMERIC(10,2)"
|
||||
status "VARCHAR(32)" [not null]
|
||||
rationale TEXT
|
||||
created_at TIMESTAMPTZ [default: "NOW()"]
|
||||
|
||||
Note: "매도 워터폴 실행 이력"
|
||||
}
|
||||
|
||||
Table quantengine.shadow_ledger_history {
|
||||
id BIGSERIAL [pk]
|
||||
run_id "VARCHAR(64)" [not null, ref: > quantengine.decision_result_history.run_id]
|
||||
ticker "VARCHAR(32)" [not null]
|
||||
blocked_gate "VARCHAR(64)" [not null]
|
||||
blocked_reason TEXT [not null]
|
||||
shadow_price "NUMERIC(18,4)" [not null]
|
||||
shadow_qty INT [not null]
|
||||
shadow_tp_price "NUMERIC(18,4)"
|
||||
shadow_sl_price "NUMERIC(18,4)"
|
||||
created_at TIMESTAMPTZ [default: "NOW()"]
|
||||
|
||||
Note: "게이트에 막힌 주문의 가상 체결 감사 기록 (Shadow Ledger)"
|
||||
}
|
||||
|
||||
Table quantengine.scheduler_state_history {
|
||||
id BIGSERIAL [pk]
|
||||
task_name "VARCHAR(64)" [not null]
|
||||
execution_id "VARCHAR(64)" [unique, not null]
|
||||
state "VARCHAR(32)" [not null]
|
||||
started_at TIMESTAMPTZ [not null, default: "NOW()"]
|
||||
finished_at TIMESTAMPTZ
|
||||
error_message TEXT
|
||||
lock_token "VARCHAR(64)"
|
||||
|
||||
indexes {
|
||||
(task_name, state) [name: "idx_scheduler_state_task"]
|
||||
}
|
||||
|
||||
Note: "스케줄러 작업 상태 머신 이력"
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// Relationships (Logical, not enforced as FKs in DDL)
|
||||
// =============================================================================
|
||||
|
||||
Reference in New Issue
Block a user