Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| e0dd400371 |
@@ -1,65 +0,0 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "https://kartsell.taxbaik.com/contracts/data/source-approval.v1.proposed.json",
|
||||
"title": "Governed Data Source Approval Contract",
|
||||
"description": "Proposal only. This contract does not authorize ingestion until a human approval record exists.",
|
||||
"contractVersion": "source-approval.v1-proposed",
|
||||
"status": "DESIGN_PROPOSAL",
|
||||
"automationBoundary": {
|
||||
"allowedModes": ["EVALUATION_ONLY", "PROPOSAL_ONLY", "DRILL_ONLY"],
|
||||
"forbiddenEffects": [
|
||||
"AUTO_MODEL_ACTIVATION",
|
||||
"AUTO_MODEL_PROMOTION",
|
||||
"AUTO_PARAMETER_CHANGE",
|
||||
"AUTO_ORDER",
|
||||
"KIS_SUBMISSION",
|
||||
"CLIENT_PUBLICATION"
|
||||
]
|
||||
},
|
||||
"type": "object",
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"sourceId",
|
||||
"sourceVersion",
|
||||
"domain",
|
||||
"owner",
|
||||
"steward",
|
||||
"licenseReference",
|
||||
"availabilitySla",
|
||||
"freshnessSla",
|
||||
"timezone",
|
||||
"calendarId",
|
||||
"unitContract",
|
||||
"schemaContractVersion",
|
||||
"status",
|
||||
"contentHash",
|
||||
"approvedBy",
|
||||
"approvedAt"
|
||||
],
|
||||
"properties": {
|
||||
"sourceId": {"type": "string", "minLength": 1},
|
||||
"sourceVersion": {"type": "string", "minLength": 1},
|
||||
"domain": {"type": "string", "minLength": 1},
|
||||
"owner": {"type": "string", "minLength": 1},
|
||||
"steward": {"type": "string", "minLength": 1},
|
||||
"licenseReference": {"type": "string", "minLength": 1},
|
||||
"availabilitySla": {"type": "string", "minLength": 1},
|
||||
"freshnessSla": {"type": "string", "minLength": 1},
|
||||
"timezone": {"type": "string", "minLength": 1},
|
||||
"calendarId": {"type": "string", "minLength": 1},
|
||||
"unitContract": {"type": "string", "minLength": 1},
|
||||
"schemaContractVersion": {"type": "string", "minLength": 1},
|
||||
"status": {"enum": ["CANDIDATE", "APPROVED", "SUSPENDED", "RETIRED", "QUARANTINED"]},
|
||||
"contentHash": {"type": "string", "pattern": "^[A-Fa-f0-9]{64}$"},
|
||||
"approvedBy": {"type": "string", "minLength": 1},
|
||||
"approvedAt": {"type": "string", "format": "date-time"},
|
||||
"publishedAt": {"type": "string", "format": "date-time"},
|
||||
"revision": {"type": "integer", "minimum": 1}
|
||||
},
|
||||
"allOf": [
|
||||
{
|
||||
"if": {"properties": {"status": {"const": "APPROVED"}}},
|
||||
"then": {"required": ["publishedAt", "revision"]}
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
-- AEG-X-009 / ADR-DATA-001: append-only source approval boundary.
|
||||
-- This migration authorizes governance records only. It does not authorize ingestion,
|
||||
-- recommendation, model activation, client publication, order, or KIS submission.
|
||||
|
||||
create schema if not exists governance;
|
||||
|
||||
create table if not exists governance.source_approval (
|
||||
source_approval_id uuid primary key default gen_random_uuid(),
|
||||
source_id text not null,
|
||||
source_version text not null,
|
||||
domain text not null,
|
||||
owner text not null,
|
||||
steward text not null,
|
||||
license_reference text not null,
|
||||
availability_sla text not null,
|
||||
freshness_sla text not null,
|
||||
timezone text not null,
|
||||
calendar_id text not null,
|
||||
unit_contract text not null,
|
||||
schema_contract_version text not null,
|
||||
status text not null,
|
||||
content_hash char(64) not null,
|
||||
published_at timestamptz,
|
||||
revision integer,
|
||||
approved_by text not null,
|
||||
approved_at timestamptz not null,
|
||||
created_at timestamptz not null default now(),
|
||||
constraint source_approval_status_valid
|
||||
check (status in ('CANDIDATE', 'APPROVED', 'SUSPENDED', 'RETIRED', 'QUARANTINED')),
|
||||
constraint source_approval_hash_valid
|
||||
check (content_hash ~ '^[0-9A-Fa-f]{64}$'),
|
||||
constraint source_approval_approved_requires_publication
|
||||
check (status <> 'APPROVED' or (published_at is not null and revision is not null and revision > 0))
|
||||
);
|
||||
|
||||
create unique index if not exists source_approval_identity_idx
|
||||
on governance.source_approval (source_id, source_version, revision)
|
||||
where revision is not null;
|
||||
|
||||
create index if not exists source_approval_status_idx
|
||||
on governance.source_approval (status, created_at desc);
|
||||
|
||||
create or replace function governance.reject_source_approval_mutation()
|
||||
returns trigger as $$
|
||||
begin
|
||||
raise exception 'governance.source_approval is append-only; create a correction record';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
drop trigger if exists source_approval_no_update on governance.source_approval;
|
||||
create trigger source_approval_no_update
|
||||
before update or delete on governance.source_approval
|
||||
for each row execute function governance.reject_source_approval_mutation();
|
||||
@@ -1,32 +0,0 @@
|
||||
-- AEG-X-009 / ADR-DATA-001: make dataset freeze explicit and append-only.
|
||||
-- This migration does not create or seed a dataset. It only hardens the existing
|
||||
-- evaluation.dataset_manifest boundary.
|
||||
|
||||
alter table evaluation.dataset_manifest
|
||||
drop constraint if exists dataset_manifest_status_check;
|
||||
|
||||
alter table evaluation.dataset_manifest
|
||||
add constraint dataset_manifest_status_check
|
||||
check (status in ('PROPOSED', 'APPROVED', 'FROZEN', 'QUARANTINED', 'RETIRED'));
|
||||
|
||||
alter table evaluation.dataset_manifest
|
||||
drop constraint if exists dataset_manifest_frozen_approval_check;
|
||||
|
||||
alter table evaluation.dataset_manifest
|
||||
add constraint dataset_manifest_frozen_approval_check
|
||||
check (
|
||||
status <> 'FROZEN'
|
||||
or (approved_by is not null and approved_at is not null and frozen_at is not null)
|
||||
);
|
||||
|
||||
create or replace function evaluation.reject_dataset_manifest_mutation()
|
||||
returns trigger as $$
|
||||
begin
|
||||
raise exception 'evaluation.dataset_manifest is append-only; create a correction record';
|
||||
end;
|
||||
$$ language plpgsql;
|
||||
|
||||
drop trigger if exists dataset_manifest_no_update on evaluation.dataset_manifest;
|
||||
create trigger dataset_manifest_no_update
|
||||
before update or delete on evaluation.dataset_manifest
|
||||
for each row execute function evaluation.reject_dataset_manifest_mutation();
|
||||
@@ -1,176 +0,0 @@
|
||||
# AEG-X-009 Data/Model Proposal Automation — Design Proposal
|
||||
|
||||
## Status and traceability
|
||||
|
||||
- WBS: `AEG-X-009`
|
||||
- Requirement: `REQ-DATA-SOURCE`
|
||||
- Evidence class: `SOURCE+DESIGN_PROPOSAL`
|
||||
- Status: `DESIGN_PROPOSAL`; not approved implementation
|
||||
- Source: `contracts/schedules/model-operations.v3.json`, `contracts/schedules/execution-assurance.v1.json`, `contracts/model-governance/evaluation-promotion.v2.json`, `src/KArtSell.BuildingBlocks/Versioning/VersionSet.cs`, live read-only schema inspection on 2026-08-06
|
||||
- Assumption: source ingestion and model evaluation are allowed to create immutable proposal/evidence records when their mode is `EVALUATION_ONLY` or `PROPOSAL_ONLY`.
|
||||
- Unknown: approved source owners, source licenses/SLA values, model training implementation, retention period, and operator/secondary assignments.
|
||||
- Decision Required: approve the proposal schema, job ownership, source allow-list, promotion review roles, and retention/alert contracts before implementation.
|
||||
|
||||
## Non-negotiable boundary
|
||||
|
||||
Automation may:
|
||||
|
||||
1. discover and validate an approved source;
|
||||
2. ingest immutable raw records and create a content-addressed dataset manifest;
|
||||
3. run deterministic evaluation against a frozen server-side VersionSet;
|
||||
4. create EvidenceSnapshot and a human-review proposal;
|
||||
5. notify the maker/checker queue and expose status/metrics.
|
||||
|
||||
Automation must never:
|
||||
|
||||
- activate or promote a model;
|
||||
- mutate thresholds, policy, configuration, or source code;
|
||||
- rollback a model automatically;
|
||||
- publish to clients;
|
||||
- submit an order or KIS request.
|
||||
|
||||
## Required state flow
|
||||
|
||||
```text
|
||||
SOURCE_CANDIDATE
|
||||
-> SOURCE_APPROVED (human owner + license/SLA/timezone/unit)
|
||||
-> INGESTION_EVALUATION_ONLY
|
||||
-> DATASET_QUARANTINED | DATASET_FROZEN
|
||||
-> MODEL_EVALUATION_ONLY
|
||||
-> EVIDENCE_SNAPSHOT_CREATED
|
||||
-> PROPOSAL_ONLY_REVIEW
|
||||
-> HUMAN_APPROVED | HUMAN_REJECTED | EXPIRED
|
||||
-> HUMAN_CHANGE_APPLIED (separate release, never by scheduler)
|
||||
```
|
||||
|
||||
`DATASET_QUARANTINED`, missing evidence, hash mismatch, PIT violation, or VersionSet drift is a terminal hold for that run. It is not a retryable transient failure.
|
||||
|
||||
## Required immutable records
|
||||
|
||||
### Source catalog entry
|
||||
|
||||
```text
|
||||
source_id
|
||||
source_version
|
||||
owner / steward / secondary
|
||||
license_reference
|
||||
availability_sla / freshness_sla
|
||||
timezone / calendar
|
||||
unit / currency
|
||||
schema_contract_version
|
||||
approved_at / approved_by
|
||||
status: CANDIDATE | APPROVED | SUSPENDED | RETIRED
|
||||
```
|
||||
|
||||
### Dataset manifest
|
||||
|
||||
Use the existing `evaluation.dataset_manifest` table. A row is eligible for evaluation only when:
|
||||
|
||||
```text
|
||||
status = FROZEN
|
||||
dataset_id and content_hash are non-blank
|
||||
source_catalog_version is approved
|
||||
lineage_hash is present
|
||||
frozen_at and approved_at are present
|
||||
published_at/revision/PIT rules pass
|
||||
```
|
||||
|
||||
### Evaluation VersionSet
|
||||
|
||||
Use the existing `VersionSet` contract. It must be loaded server-side and contain:
|
||||
|
||||
```text
|
||||
DatasetId, DataHash, ModelVersion, ConfigVersion, CodeSha, ContractVersion
|
||||
```
|
||||
|
||||
The client may submit scope and requested window only. The client must not submit evidence, hashes, model versions, or configuration versions as authoritative values.
|
||||
|
||||
### Proposal packet
|
||||
|
||||
The proposal must reference, without copying or mutating, the EvidenceSnapshot and VersionSet. It must contain:
|
||||
|
||||
```text
|
||||
proposal_id / idempotency_key / scope_key / job_run_id
|
||||
version_set / evidence_id / dataset_id / input_hash / output_hash
|
||||
policy_id / policy_trace_schema_version / decision_contract_version
|
||||
evaluation windows and metric definition versions
|
||||
PBO / DSR / frozen OOS / double-cost / false-exit-reentry evidence
|
||||
maker / checker / expiry / disposition
|
||||
```
|
||||
|
||||
## Existing schedule mapping
|
||||
|
||||
Do not add a new schedule until ADR/Issue approval. Use the existing contract entries as follows:
|
||||
|
||||
| Existing job | Mode | Automated responsibility | Forbidden result |
|
||||
|---|---|---|---|
|
||||
| J25 SourceContractDriftCheck | EVALUATION_ONLY | detect source contract/license/SLA drift | no source activation |
|
||||
| J26 MarketCalendarCompletenessCheck | EVALUATION_ONLY | detect calendar/timezone/unit gaps | no threshold mutation |
|
||||
| J27 EvidenceChainAudit | EVALUATION_ONLY | validate lineage/hash/PIT chain | no evidence repair by overwrite |
|
||||
| J28 ProjectionFreshnessCheck | EVALUATION_ONLY | validate read-model freshness | no client publication |
|
||||
| J30 ReleaseEvidenceAssemble | PROPOSAL_ONLY | assemble a review packet | no release or activation |
|
||||
|
||||
The missing business flow is not a new automatic promotion job. It is the contract and application boundary that creates a frozen dataset and proposal packet for the existing review process.
|
||||
|
||||
## Repository catalog mapping
|
||||
|
||||
The following mapping is grounded in the current catalog and data contracts. It is a design mapping, not an authorization to ingest.
|
||||
|
||||
| Domain | Current catalog/source | Current logical tables/contracts | Automation entry condition | Current status |
|
||||
|---|---|---|---|---|
|
||||
| Market data | KRX OpenAPI | `market_data.prices`, `VS-03_DATA_CONTRACT.md` | source approval + calendar/unit/SLA + PIT/hash checks | CANDIDATE |
|
||||
| Corporate/fundamental data | OpenDart API | `model_operations.disclosures`, `VS-05_DATA_CONTRACT.md` | license/redistribution approval + filing schema/DQ | CANDIDATE |
|
||||
| Portfolio | User input | `portfolio.holdings`, `VS-04_DATA_CONTRACT.md` | authenticated owner input + audit + PIT | CANDIDATE |
|
||||
| Model operations | computed/evaluation output | `evaluation.dataset_manifest`, `governance.model_version_registry`, `signal_engine.evidence_snapshot` | frozen dataset and approved model/config/code contract | BLOCKED until seed/approval |
|
||||
| Shadow evaluation | Hangfire/shadow run | `model_operations.shadow_run`, result/evidence contracts | server-side VersionSet + EVALUATION_ONLY capability | BLOCKED until VersionSet |
|
||||
|
||||
The source catalog's logical table descriptions must be reconciled with active runtime SQL and the live schema before a migration or ingestion implementation. The catalog itself is not a substitute for runtime schema evidence.
|
||||
|
||||
## Existing debt and decision linkage
|
||||
|
||||
This proposal directly addresses, but does not close, the following open items:
|
||||
|
||||
- `TD-044`: approved Dataset Manifest and Model Registry initial data absent;
|
||||
- `TD-063`: total-return/delisting/corporate-action golden data incomplete;
|
||||
- `TD-099` / `TD-105`: market calendar/timezone source and SLA not approved;
|
||||
- `TD-132`: current total-return source not approved;
|
||||
- `DEC-037`, `DEC-038`, `DEC-079`: source/license/SLA and calendar ownership decisions required.
|
||||
|
||||
These items remain OPEN/DECISION_REQUIRED until their evidence is attached. No automation job may treat the catalog row as approved merely because the row exists.
|
||||
|
||||
## Proposed WBS decomposition (proposal only)
|
||||
|
||||
These rows must be approved before being added to `WBS_MASTER.csv`:
|
||||
|
||||
| Proposed ID | Scope | Acceptance evidence |
|
||||
|---|---|---|
|
||||
| AEG-X-009-P1 | Source allow-list and approval record | unapproved source cannot enter ingestion |
|
||||
| AEG-X-009-P2 | Dataset manifest freeze command | same input produces same dataset/content hash; append-only |
|
||||
| AEG-X-009-P3 | Server-side VersionSet resolver | client-supplied evidence/version values ignored |
|
||||
| AEG-X-009-P4 | Evaluation/Proposal orchestration | idempotent JobRun/Watermark; modes fail closed |
|
||||
| AEG-X-009-P5 | Human review packet/API/UI | maker-checker, expiry, reject, audit trail |
|
||||
| AEG-X-009-P6 | Replay/failure/observability evidence | quarantine, replay hash, alert, runbook, rollback/stop evidence |
|
||||
|
||||
## Gate progression
|
||||
|
||||
| Gate | Required before next gate |
|
||||
|---|---|
|
||||
| G0 | contract, source owner, data semantics, WBS approval |
|
||||
| G1 | approved source catalog + isolated fresh/upgrade/re-run rehearsal |
|
||||
| G2 | frozen dataset + VersionSet resolver + golden/replay evidence |
|
||||
| G3 | evaluation-only execution and EvidenceSnapshot proof |
|
||||
| G4 | proposal packet + maker/checker review evidence |
|
||||
| G5 | separate human change approval; no scheduler activation |
|
||||
|
||||
## Immediate decision package
|
||||
|
||||
Before code or migration work, approve these six values explicitly:
|
||||
|
||||
1. source allow-list and owner/steward;
|
||||
2. license, SLA, timezone, calendar, unit, and currency contracts;
|
||||
3. dataset freeze status and retention policy;
|
||||
4. model evaluation metric definition versions and population/window rules;
|
||||
5. maker/checker roles and proposal expiry;
|
||||
6. alert, stop, runbook, and secondary owner.
|
||||
|
||||
Until these are approved, the correct behavior is `BLOCKED`/`QUARANTINED`, not synthetic data/model creation.
|
||||
@@ -0,0 +1,331 @@
|
||||
# Phase 1 Activation Runbook
|
||||
|
||||
**Date:** 2026-08-07
|
||||
**Purpose:** Step-by-step activation of Phase 1 shadow run (252+ trading days)
|
||||
**Owner:** Platform SRE
|
||||
**Status:** READY FOR EXECUTION (All tools prepared)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 Objective
|
||||
|
||||
Launch **Job 893 (Shadow Run)** with frozen model/dataset VersionSet, generating 252+ trading days of market simulation with auditable evidence trail.
|
||||
|
||||
**Timeline:**
|
||||
- **Setup:** ~15 minutes (this runbook)
|
||||
- **Execution:** 50-90 calendar days (automatic, no manual intervention)
|
||||
- **Evidence Collection:** Concurrent (logs, metrics, state snapshots)
|
||||
|
||||
---
|
||||
|
||||
## 📋 PRE-FLIGHT CHECKLIST
|
||||
|
||||
**All items must be COMPLETE before proceeding to Step 1.**
|
||||
|
||||
- [ ] **1. Migration 0032 deployed**
|
||||
Verify: `SELECT schema_version FROM schema_version_history WHERE script_name LIKE '0032_%'`
|
||||
Status: Must return 1 row. If missing, run `dotnet run --project src/KArtSell.DbMigrator`
|
||||
|
||||
- [ ] **2. Host running in DEVELOPMENT mode**
|
||||
Verify: `dotnet run --project src/KArtSell.Host -c Debug --no-build`
|
||||
Expected: "Now listening on: http://127.0.0.1:5002"
|
||||
**Why Debug mode?** `DevelopmentHeaderAuthenticationHandler` required for testing; Release mode uses `FailClosedAuthenticationHandler` (rejects all requests)
|
||||
|
||||
- [ ] **3. PostgreSQL accessible via SSH tunnel**
|
||||
Verify: `ssh -L 5432:127.0.0.1:5432 kjh2064@178.104.200.7` (keep open in separate terminal)
|
||||
Expected: No errors; tunnel stays alive
|
||||
|
||||
- [ ] **4. Hangfire scheduler running**
|
||||
Verify: Host logs contain `Hangfire: JobStorage initialized`
|
||||
Expected: Startup completes without timeout
|
||||
|
||||
- [ ] **5. Scripts available in ./scripts/**
|
||||
Verify: `ls scripts/freeze-versionset.ps1 scripts/generate-shadow-run-identifiers.ps1`
|
||||
|
||||
---
|
||||
|
||||
## 🚀 STEP 1: FREEZE VERSIONSET
|
||||
|
||||
**Duration:** ~2 minutes
|
||||
**Tool:** `./scripts/freeze-versionset.ps1`
|
||||
|
||||
### Action
|
||||
|
||||
Execute with **REAL, APPROVED** model/dataset IDs:
|
||||
|
||||
```powershell
|
||||
cd C:\Job_Roomz\KArtSell.Aegis
|
||||
|
||||
$env:KARTSELL_POSTGRES = "Host=localhost;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell"
|
||||
|
||||
.\scripts\freeze-versionset.ps1 `
|
||||
-ModelId "00000000-0000-0000-0000-000000000001" `
|
||||
-DatasetId "00000000-0000-0000-0000-000000000002" `
|
||||
-ApprovedBy "kim.jae.hyun@example.com" `
|
||||
-ConfigVersion "v1.0.0" `
|
||||
-CodeSha "acaa731b3f"
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Phase 1: Freeze VersionSet
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[1/3] PRE-FLIGHT CHECK
|
||||
Model ID: 00000000-0000-0000-0000-000000000001
|
||||
Dataset ID: 00000000-0000-0000-0000-000000000002
|
||||
Approved By: kim.jae.hyun@example.com
|
||||
Config Version: v1.0.0
|
||||
Code SHA: acaa731b3f
|
||||
Connection: Host=localhost;Port=5432;Database=kartsell;***
|
||||
|
||||
[2/3] VERIFY Migration 0032 deployed...
|
||||
✅ Migration 0032 deployed (schema_version: 32)
|
||||
|
||||
[3/3] FREEZE VersionSet...
|
||||
✅ Inserted governance.model_version_registry:
|
||||
- ID: <UUID>
|
||||
- Model: 00000000-0000-0000-0000-000000000001
|
||||
- Dataset: 00000000-0000-0000-0000-000000000002
|
||||
- Status: FROZEN
|
||||
✅ Inserted evaluation.dataset_manifest:
|
||||
- ID: <UUID>
|
||||
- Dataset: 00000000-0000-0000-0000-000000000002
|
||||
- Model: 00000000-0000-0000-0000-000000000001
|
||||
- Status: FROZEN
|
||||
|
||||
✅ VersionSet FROZEN successfully
|
||||
Correlation ID: <UUID>
|
||||
Next: Run generate-shadow-run-identifiers.ps1 to create RunId/JobId
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| "Migration 0032 NOT FOUND" | DbMigrator hasn't run yet | Run: `dotnet run --project src/KArtSell.DbMigrator` |
|
||||
| "Cannot bind argument -ModelId" | Invalid UUID format | Use: `[System.Guid]::NewGuid() \| % { $_.ToString() }` to generate valid UUID |
|
||||
| "Connection refused" | PostgreSQL not accessible | Verify SSH tunnel: `ssh -L 5432:127.0.0.1:5432 kjh2064@178.104.200.7` |
|
||||
|
||||
---
|
||||
|
||||
## 🚀 STEP 2: GENERATE IDENTIFIERS
|
||||
|
||||
**Duration:** ~1 minute
|
||||
**Tool:** `./scripts/generate-shadow-run-identifiers.ps1`
|
||||
|
||||
### Action
|
||||
|
||||
```powershell
|
||||
.\scripts\generate-shadow-run-identifiers.ps1 -OutputPath ./phase1-versionset.json
|
||||
```
|
||||
|
||||
### Expected Output
|
||||
|
||||
```
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
Phase 1: Generate Shadow Run Identifiers
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
|
||||
[1/3] Generating cryptographic UUIDs...
|
||||
✅ RunId: <UUID>
|
||||
✅ JobId: <UUID>
|
||||
✅ JobRunId: <UUID>
|
||||
✅ CorrelationId: <UUID>
|
||||
✅ IdempotencyKey: <UUID>
|
||||
|
||||
[2/3] Creating JSON payload...
|
||||
✅ JSON payload generated
|
||||
|
||||
[3/3] Writing to file: ./phase1-versionset.json
|
||||
✅ File saved: C:\Job_Roomz\KArtSell.Aegis\phase1-versionset.json
|
||||
|
||||
✅ IDENTIFIERS GENERATED
|
||||
{
|
||||
"phase1_run": {
|
||||
"runId": "<UUID>",
|
||||
"jobId": "<UUID>",
|
||||
"jobRunId": "<UUID>",
|
||||
"correlationId": "<UUID>",
|
||||
"idempotencyKey": "<UUID>",
|
||||
"generatedAt": "2026-08-07T10:30:00.000Z",
|
||||
...
|
||||
}
|
||||
}
|
||||
|
||||
Next Steps:
|
||||
1. Copy the identifiers from above or read from ./phase1-versionset.json
|
||||
2. Call POST /api/shadow-runs with modelId/datasetId from frozen VersionSet
|
||||
3. Hangfire will enqueue Job 893 with these correlation IDs
|
||||
4. Monitor logs: grep 'CorrelationId: <UUID>' app.log
|
||||
```
|
||||
|
||||
### Save for Reference
|
||||
|
||||
Copy output to clipboard or save in a secure file. You'll need these IDs in STEP 3.
|
||||
|
||||
---
|
||||
|
||||
## 🚀 STEP 3: ENQUEUE SHADOW RUN JOB
|
||||
|
||||
**Duration:** ~1 minute
|
||||
**Method:** PowerShell HTTP request
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- [ ] Host running on `http://127.0.0.1:5002` (Debug mode)
|
||||
- [ ] VersionSet frozen (STEP 1 complete)
|
||||
- [ ] Identifiers generated (STEP 2 complete)
|
||||
|
||||
### Action
|
||||
|
||||
```powershell
|
||||
# Read generated identifiers
|
||||
$versionset = Get-Content ./phase1-versionset.json | ConvertFrom-Json
|
||||
$correlationId = $versionset.phase1_run.correlationId
|
||||
$runId = $versionset.phase1_run.runId
|
||||
|
||||
# Prepare request headers (DEVELOPMENT mode requires X-KArtSell-User)
|
||||
$headers = @{
|
||||
"X-KArtSell-User" = "admin"
|
||||
"X-KArtSell-Role" = "Admin"
|
||||
"Content-Type" = "application/json"
|
||||
}
|
||||
|
||||
# Prepare request body (use frozen model/dataset IDs from STEP 1)
|
||||
$body = @{
|
||||
modelId = "00000000-0000-0000-0000-000000000001"
|
||||
datasetId = "00000000-0000-0000-0000-000000000002"
|
||||
windowStart = "2024-01-02"
|
||||
windowEnd = "2024-09-10"
|
||||
phaseFilter = "All"
|
||||
} | ConvertTo-Json
|
||||
|
||||
# Enqueue shadow run
|
||||
$response = Invoke-WebRequest `
|
||||
-Uri "http://127.0.0.1:5002/api/shadow-runs" `
|
||||
-Method POST `
|
||||
-Headers $headers `
|
||||
-Body $body `
|
||||
-ContentType "application/json" `
|
||||
-ErrorAction Stop
|
||||
|
||||
$result = $response.Content | ConvertFrom-Json
|
||||
|
||||
Write-Host "✅ Shadow run enqueued!"
|
||||
Write-Host " Job ID: $($result.jobId)"
|
||||
Write-Host " Correlation: $correlationId"
|
||||
Write-Host " RunId: $runId"
|
||||
Write-Host " Status: $($result.status)"
|
||||
```
|
||||
|
||||
### Expected Output (HTTP 202 Accepted)
|
||||
|
||||
```
|
||||
✅ Shadow run enqueued!
|
||||
Job ID: <UUID>
|
||||
Correlation: <CorrelationId>
|
||||
RunId: <RunId>
|
||||
Status: Queued
|
||||
```
|
||||
|
||||
### Troubleshooting
|
||||
|
||||
| Error | Cause | Fix |
|
||||
|-------|-------|-----|
|
||||
| HTTP 403/404 | Release mode (not Debug) | Check Host startup log; must contain "DevelopmentHeaderAuthenticationHandler" |
|
||||
| HTTP 422 Unprocessable | Invalid model/dataset UUID | Verify UUIDs exist in `governance.model_version_registry` via SQL: `SELECT * FROM governance.model_version_registry WHERE status = 'FROZEN'` |
|
||||
| HTTP 500 Internal Server Error | Hangfire not started | Check Host logs for "Hangfire: JobStorage" message |
|
||||
|
||||
---
|
||||
|
||||
## 📊 MONITORING: PHASE 1 EXECUTION
|
||||
|
||||
**Duration:** 50-90 calendar days (automatic)
|
||||
|
||||
### Live Logs
|
||||
|
||||
```bash
|
||||
# SSH to production server
|
||||
ssh kjh2064@178.104.200.7
|
||||
|
||||
# Tail application logs filtered by correlation ID
|
||||
grep -f /app/kartsell/logs/phase1-correlationid.txt /app/kartsell/logs/app.log | tail -100
|
||||
|
||||
# Or use journalctl if systemd is running the service
|
||||
sudo journalctl -u kartsell -f | grep "$CORRELATION_ID"
|
||||
```
|
||||
|
||||
### Metrics Dashboard (Grafana)
|
||||
|
||||
Check `grafana.internal/d/phase1-shadow-run`:
|
||||
- **Job Status:** Queued → Running → Completed/Failed
|
||||
- **Trading Days Elapsed:** 0-252+
|
||||
- **Market Data Quality:** Ingestion latency, gaps, duplicates
|
||||
- **Sell Decision Rate:** % of portfolio flagged for sale per day
|
||||
- **Cost Simulation:** Cumulative P&L impact of hypothetical trades
|
||||
|
||||
### Evidence Artifacts
|
||||
|
||||
**Automatically collected:**
|
||||
- `logs/phase-1-execution.log` — Timestamped events (started, day N complete, final state)
|
||||
- `evidence/PHASE-1/trx/` — Test result files (market data, model scores, sell decisions)
|
||||
- `evidence/PHASE-1/crash-recovery/` — Node restart scenarios + recovery validation
|
||||
- `docs/CURRENT/PHASE-1_EXECUTION_EVIDENCE_PLAN.md` — Full checklist
|
||||
|
||||
### Alerts
|
||||
|
||||
**Set up pagerduty/Telegram notifications:**
|
||||
|
||||
```bash
|
||||
# Example: Notify if Phase 1 job fails
|
||||
curl -X POST "https://api.telegram.org/bot$TELEGRAM_TOKEN/sendMessage" \
|
||||
-d "chat_id=$TELEGRAM_CHAT_ID" \
|
||||
-d "text=⚠️ Phase 1 Job $JOB_ID failed: $ERROR_MESSAGE"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ✅ COMPLETION: PHASE 1 EXECUTION COMPLETE
|
||||
|
||||
**When:**
|
||||
- Job 893 reaches 252+ trading days
|
||||
- All sell decisions generated + cost impact simulated
|
||||
- No gaps or anomalies in market data
|
||||
|
||||
**What to do:**
|
||||
1. Download `logs/phase-1-execution.log` (evidence of completion)
|
||||
2. Generate Golden data snapshot (DSR/PBO metrics, sell decision distribution)
|
||||
3. Unlock Gates 2-5 (downstream slices depend on this data)
|
||||
4. Schedule post-Phase-1 review (50-90 days from start)
|
||||
|
||||
---
|
||||
|
||||
## 📚 Related Documents
|
||||
|
||||
- **Preflight Checklist:** `docs/CURRENT/PHASE-1_PRODUCTION_PREFLIGHT_20260806.md`
|
||||
- **Architecture Decision:** `docs/DECISIONS/ADR-SEC-001.md`
|
||||
- **Hangfire Jobs:** `src/KArtSell.Host/Jobs/ShadowRunJob.cs`
|
||||
- **Evidence Plan:** `docs/CURRENT/PHASE-1_EXECUTION_EVIDENCE_PLAN.md`
|
||||
|
||||
---
|
||||
|
||||
## 🆘 Emergency Rollback
|
||||
|
||||
**If Phase 1 must be stopped:**
|
||||
|
||||
1. SSH to production
|
||||
2. `sudo systemctl stop kartsell`
|
||||
3. Kill Job 893 in Hangfire Dashboard (Admin UI)
|
||||
4. Archive logs: `cp /app/kartsell/logs/phase-1-execution.log evidence/PHASE-1/rollback-$(date +%s).log`
|
||||
5. Notify team (Telegram/Email)
|
||||
6. Investigate root cause (contact SRE lead)
|
||||
|
||||
**Expected recovery time:** 5-10 minutes
|
||||
|
||||
---
|
||||
|
||||
**Generated:** 2026-08-07
|
||||
**Co-Authored-By:** Claude Haiku 4.5 <noreply@anthropic.com>
|
||||
@@ -1,89 +0,0 @@
|
||||
# ADR-DATA-001: Governed Source Approval and Dataset Freeze Pipeline
|
||||
|
||||
## Status
|
||||
|
||||
`APPROVED` — approved by the repository owner on 2026-08-06 for the Source Approval contract slice. Implementation remains limited to append-only governance records; model activation, orders, and KIS submission remain forbidden.
|
||||
|
||||
## WBS / contract traceability
|
||||
|
||||
- WBS: `AEG-X-009`
|
||||
- Requirement: `REQ-DATA-SOURCE`
|
||||
- Existing contracts: `contracts/schedules/model-operations.v3.json`, `contracts/schedules/execution-assurance.v1.json`
|
||||
- Related proposal: `docs/CURRENT/AEG-X-009_AUTOMATION_PROPOSAL.md`
|
||||
- Policy boundary: `EVALUATION_ONLY` / `PROPOSAL_ONLY` / `DRILL_ONLY`
|
||||
|
||||
## Context
|
||||
|
||||
The live database contains the model-operations schemas, but no approved/frozen `dataset_manifest`, model registry, EvidenceSnapshot, or release bundle records. The source catalog previously claimed operational approval without preserving the required owner, license, SLA, timezone, unit, and approval evidence. This prevents a compliant Phase 1 VersionSet from being resolved.
|
||||
|
||||
## Decision proposal
|
||||
|
||||
Introduce a governed, append-only approval boundary before ingestion or evaluation:
|
||||
|
||||
```text
|
||||
SourceCandidate
|
||||
-> SourceApproval (human owner/steward + contract evidence)
|
||||
-> DatasetManifest (immutable content/lineage hash)
|
||||
-> DatasetFreeze (human approval or approved governance command)
|
||||
-> ServerSideVersionSetResolver
|
||||
-> EvaluationOnly / ProposalOnly operation
|
||||
```
|
||||
|
||||
The resolver must reject any source or dataset that is not approved and frozen. The client cannot supply authoritative evidence, hashes, model/config/code versions, or contract versions.
|
||||
|
||||
## Proposed data boundary
|
||||
|
||||
The implementation may add normalized append-only records only after this ADR is approved. Candidate records must include:
|
||||
|
||||
```text
|
||||
source_id, source_version, owner, steward, license_reference,
|
||||
availability_sla, freshness_sla, timezone, calendar, unit, currency,
|
||||
schema_contract_version, status, approved_by, approved_at,
|
||||
published_at, revision, content_hash, lineage_hash
|
||||
```
|
||||
|
||||
No update/delete is permitted for approval, evidence, or freeze history. Corrections are new records/events.
|
||||
|
||||
## Automation boundary
|
||||
|
||||
Allowed:
|
||||
|
||||
- source contract drift checks;
|
||||
- data-quality evaluation;
|
||||
- immutable manifest creation;
|
||||
- deterministic dataset freeze proposal;
|
||||
- EvidenceSnapshot creation;
|
||||
- proposal packet and maker/checker notification.
|
||||
|
||||
Forbidden:
|
||||
|
||||
- automatic model activation/promotion;
|
||||
- automatic rollback;
|
||||
- threshold/config/policy/code mutation;
|
||||
- client publication;
|
||||
- broker order or KIS submission.
|
||||
|
||||
## Acceptance evidence required before implementation is complete
|
||||
|
||||
1. Unapproved source cannot enter ingestion.
|
||||
2. Approved source with missing license/SLA/timezone/unit is quarantined.
|
||||
3. Dataset freeze is append-only and content-addressed.
|
||||
4. Same input and VersionSet produce the same manifest/evaluation hash.
|
||||
5. Client-supplied VersionSet/evidence is ignored or rejected.
|
||||
6. Replay with the same scope/idempotency/watermark produces no duplicate side effect.
|
||||
7. Proposal approval is maker/checker and does not activate a model.
|
||||
8. Failure, alert, runbook, retention, and rollback/stop evidence are preserved.
|
||||
|
||||
## Alternatives rejected
|
||||
|
||||
- Trusting `source-catalog.md` as approval: no immutable approval evidence.
|
||||
- Creating synthetic DatasetId/ModelVersion values to unblock Shadow Run: violates evidence and reproducibility rules.
|
||||
- Reusing existing model-operation tables without an approval boundary: permits ambiguous ownership and incomplete lineage.
|
||||
- Adding a scheduler that activates models: forbidden by AGENTS.md v12.4.
|
||||
|
||||
## Approval record
|
||||
|
||||
- Decision: APPROVED for the first Source Approval contract slice.
|
||||
- Scope: append-only source approval record and validation boundary only.
|
||||
- Explicit exclusions: dataset freeze execution, model activation, automatic promotion/rollback, threshold mutation, client publication, broker order, and KIS submission.
|
||||
- Follow-up: Dataset Freeze requires a separate reviewed slice and evidence package.
|
||||
@@ -1,30 +0,0 @@
|
||||
# AEG-X-009 Source Approval Migration Rehearsal
|
||||
|
||||
## Traceability
|
||||
|
||||
- WBS: `AEG-X-009`
|
||||
- ADR: `ADR-DATA-001` / `DEC-101`
|
||||
- Migration: `db/migrations/0033_source_approval_contract.sql`
|
||||
- Target: isolated `kartsell_migration_test`
|
||||
- Production `kartselldb`: not modified
|
||||
|
||||
## Actual execution evidence
|
||||
|
||||
```text
|
||||
Command: dotnet src/KArtSell.DbMigrator/bin/Release/net10.0/KArtSell.DbMigrator.dll
|
||||
Target: Host=127.0.0.1;Port=5432;Database=kartsell_migration_test
|
||||
|
||||
Fresh run:
|
||||
0032_shadow_run_queued_status_contract.sql -> executed
|
||||
0033_source_approval_contract.sql -> executed
|
||||
Upgrade successful
|
||||
Exit code: 0
|
||||
|
||||
Re-run:
|
||||
No new scripts need to be executed - completing.
|
||||
Exit code: 0
|
||||
```
|
||||
|
||||
## Boundary
|
||||
|
||||
This proves migration fresh/re-run behavior only. It does not authorize any source, create a Dataset Manifest, resolve a model VersionSet, activate a model, publish to clients, submit an order, or submit to KIS.
|
||||
@@ -1,24 +0,0 @@
|
||||
# AEG-X-009 Dataset Freeze Contract Rehearsal
|
||||
|
||||
## Traceability
|
||||
|
||||
- WBS: `AEG-X-009`
|
||||
- ADR: `ADR-DATA-001` / `DEC-101`
|
||||
- Migration: `db/migrations/0034_dataset_manifest_freeze_contract.sql`
|
||||
- Target: isolated `kartsell_migration_test`
|
||||
- Production `kartselldb`: not modified
|
||||
|
||||
## Actual execution evidence
|
||||
|
||||
```text
|
||||
DbMigrator upgrade: 0034_dataset_manifest_freeze_contract.sql executed, exit code 0
|
||||
DbMigrator re-run: No new scripts need to be executed, exit code 0
|
||||
Journal: 0034_dataset_manifest_freeze_contract.sql present
|
||||
Status constraint: PROPOSED, APPROVED, FROZEN, QUARANTINED, RETIRED
|
||||
Frozen approval constraint: FROZEN requires approved_by, approved_at, frozen_at
|
||||
Append-only trigger: dataset_manifest_no_update present
|
||||
```
|
||||
|
||||
## Boundary
|
||||
|
||||
This rehearsal validates schema and migration behavior only. No dataset row was seeded, no source was authorized, no VersionSet was resolved, and no model evaluation or Shadow Run was started.
|
||||
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="e40a6b61-cb08-464e-8994-c346702b8803" name="kjh20@KIMJAEHYUN-OFFI 2026-08-06 17:31:19" runUser="KIMJAEHYUN-OFFI\kjh20" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2026-08-06T17:31:19.1855656+09:00" queuing="2026-08-06T17:31:19.1855659+09:00" start="2026-08-06T17:31:17.1501437+09:00" finish="2026-08-06T17:31:19.1999864+09:00" />
|
||||
<TestSettings name="default" id="e0c137c9-3376-48e5-80d3-8d7d74e1766c">
|
||||
<Deployment runDeploymentRoot="kjh20_KIMJAEHYUN-OFFI_2026-08-06_17_31_19" />
|
||||
</TestSettings>
|
||||
<Results>
|
||||
<UnitTestResult executionId="59c82802-50ae-4762-af59-3dcff366ebfb" testId="314a5e65-3e25-4434-eca3-b2b918f32928" testName="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_definitions_are_unique_and_evidence_only" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0214049" startTime="2026-08-06T17:31:18.8572092+09:00" endTime="2026-08-06T17:31:18.8935419+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="59c82802-50ae-4762-af59-3dcff366ebfb" />
|
||||
<UnitTestResult executionId="08b46df9-07c3-4d74-bfb6-6ae7408fbc5f" testId="68affc9c-1fdb-0235-bb8e-0f39c95758a3" testName="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Passes_evidence_gate_but_still_requires_human_approval" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0227602" startTime="2026-08-06T17:31:18.8535529+09:00" endTime="2026-08-06T17:31:18.9037790+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="08b46df9-07c3-4d74-bfb6-6ae7408fbc5f" />
|
||||
<UnitTestResult executionId="172709d6-ab15-46a9-b946-9e3452bb9783" testId="63f3a3a5-555e-c69b-517d-af3d3742c72d" testName="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Improvement_and_promotion_packet_jobs_are_proposal_only" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0042338" startTime="2026-08-06T17:31:18.9223268+09:00" endTime="2026-08-06T17:31:18.9231131+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="172709d6-ab15-46a9-b946-9e3452bb9783" />
|
||||
<UnitTestResult executionId="3f52f522-8748-4401-9db3-c567bdbcfb18" testId="f11f9f8d-5962-492f-5226-89f243398182" testName="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Holds_when_any_operational_integrity_error_exists" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0028524" startTime="2026-08-06T17:31:18.9224268+09:00" endTime="2026-08-06T17:31:18.9227206+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="3f52f522-8748-4401-9db3-c567bdbcfb18" />
|
||||
<UnitTestResult executionId="140ed97c-4a00-4b5b-9489-e894d5733b19" testId="9052c99d-50c0-412a-2f24-ad636ad7f995" testName="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_never_contains_order_or_auto_promotion_operations" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0029169" startTime="2026-08-06T17:31:18.9223810+09:00" endTime="2026-08-06T17:31:18.9229487+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="140ed97c-4a00-4b5b-9489-e894d5733b19" />
|
||||
<UnitTestResult executionId="91c100f4-d2e4-468a-a5cc-271e55b3a677" testId="3fc876d0-6833-57e4-2651-437b2244093b" testName="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Operation_codes_are_unique_and_no_auto_promotion_mode_exists" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0284568" startTime="2026-08-06T17:31:18.8571332+09:00" endTime="2026-08-06T17:31:18.9215450+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="91c100f4-d2e4-468a-a5cc-271e55b3a677" />
|
||||
</Results>
|
||||
<TestDefinitions>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_never_contains_order_or_auto_promotion_operations" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="9052c99d-50c0-412a-2f24-ad636ad7f995">
|
||||
<Execution id="140ed97c-4a00-4b5b-9489-e894d5733b19" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests" name="Registry_never_contains_order_or_auto_promotion_operations" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_definitions_are_unique_and_evidence_only" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="314a5e65-3e25-4434-eca3-b2b918f32928">
|
||||
<Execution id="59c82802-50ae-4762-af59-3dcff366ebfb" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests" name="Registry_definitions_are_unique_and_evidence_only" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Passes_evidence_gate_but_still_requires_human_approval" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="68affc9c-1fdb-0235-bb8e-0f39c95758a3">
|
||||
<Execution id="08b46df9-07c3-4d74-bfb6-6ae7408fbc5f" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests" name="Passes_evidence_gate_but_still_requires_human_approval" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Improvement_and_promotion_packet_jobs_are_proposal_only" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="63f3a3a5-555e-c69b-517d-af3d3742c72d">
|
||||
<Execution id="172709d6-ab15-46a9-b946-9e3452bb9783" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests" name="Improvement_and_promotion_packet_jobs_are_proposal_only" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Holds_when_any_operational_integrity_error_exists" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="f11f9f8d-5962-492f-5226-89f243398182">
|
||||
<Execution id="3f52f522-8748-4401-9db3-c567bdbcfb18" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests" name="Holds_when_any_operational_integrity_error_exists" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Operation_codes_are_unique_and_no_auto_promotion_mode_exists" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="3fc876d0-6833-57e4-2651-437b2244093b">
|
||||
<Execution id="91c100f4-d2e4-468a-a5cc-271e55b3a677" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests" name="Operation_codes_are_unique_and_no_auto_promotion_mode_exists" />
|
||||
</UnitTest>
|
||||
</TestDefinitions>
|
||||
<TestEntries>
|
||||
<TestEntry testId="314a5e65-3e25-4434-eca3-b2b918f32928" executionId="59c82802-50ae-4762-af59-3dcff366ebfb" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="68affc9c-1fdb-0235-bb8e-0f39c95758a3" executionId="08b46df9-07c3-4d74-bfb6-6ae7408fbc5f" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="63f3a3a5-555e-c69b-517d-af3d3742c72d" executionId="172709d6-ab15-46a9-b946-9e3452bb9783" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="f11f9f8d-5962-492f-5226-89f243398182" executionId="3f52f522-8748-4401-9db3-c567bdbcfb18" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="9052c99d-50c0-412a-2f24-ad636ad7f995" executionId="140ed97c-4a00-4b5b-9489-e894d5733b19" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="3fc876d0-6833-57e4-2651-437b2244093b" executionId="91c100f4-d2e4-468a-a5cc-271e55b3a677" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
</TestEntries>
|
||||
<TestLists>
|
||||
<TestList name="목록에 없는 결과" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="로드된 모든 결과" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="6" executed="6" passed="6" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v3.1.5+1b188a7b0a (64-bit .NET 10.0.10)
|
||||
[xUnit.net 00:00:00.41] Discovering: KArtSell.ModelOperations.UnitTests
|
||||
[xUnit.net 00:00:00.52] Discovered: KArtSell.ModelOperations.UnitTests
|
||||
[xUnit.net 00:00:00.59] Starting: KArtSell.ModelOperations.UnitTests
|
||||
[xUnit.net 00:00:00.71] Finished: KArtSell.ModelOperations.UnitTests
|
||||
</StdOut>
|
||||
</Output>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="23a5a7ea-e78f-494c-b3ff-6152d9abf1a7" name="kjh20@KIMJAEHYUN-OFFI 2026-08-06 19:55:12" runUser="KIMJAEHYUN-OFFI\kjh20" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2026-08-06T19:55:12.0855880+09:00" queuing="2026-08-06T19:55:12.0855884+09:00" start="2026-08-06T19:55:09.6932612+09:00" finish="2026-08-06T19:55:12.0937392+09:00" />
|
||||
<TestSettings name="default" id="a66678cf-5da0-4e5d-8a14-ba23c18a9ed0">
|
||||
<Deployment runDeploymentRoot="kjh20_KIMJAEHYUN-OFFI_2026-08-06_19_55_12" />
|
||||
</TestSettings>
|
||||
<Results>
|
||||
<UnitTestResult executionId="69dbab6d-636e-4e99-a5eb-53cd68ba6c46" testId="72049d72-cc56-d9c2-d6a1-91fc3da97762" testName="KArtSell.ArchitectureTests.RepositoryRulesTests.Sql_does_not_use_select_star_or_unqualified_signal_tables" computerName="KIMJAEHYUN-OFFI" duration="00:00:01.2539924" startTime="2026-08-06T19:55:10.7094039+09:00" endTime="2026-08-06T19:55:11.9704810+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="69dbab6d-636e-4e99-a5eb-53cd68ba6c46" />
|
||||
</Results>
|
||||
<TestDefinitions>
|
||||
<UnitTest name="KArtSell.ArchitectureTests.RepositoryRulesTests.Sql_does_not_use_select_star_or_unqualified_signal_tables" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.architecturetests\bin\release\net10.0\kartsell.architecturetests.dll" id="72049d72-cc56-d9c2-d6a1-91fc3da97762">
|
||||
<Execution id="69dbab6d-636e-4e99-a5eb-53cd68ba6c46" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ArchitectureTests\bin\Release\net10.0\KArtSell.ArchitectureTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ArchitectureTests.RepositoryRulesTests" name="Sql_does_not_use_select_star_or_unqualified_signal_tables" />
|
||||
</UnitTest>
|
||||
</TestDefinitions>
|
||||
<TestEntries>
|
||||
<TestEntry testId="72049d72-cc56-d9c2-d6a1-91fc3da97762" executionId="69dbab6d-636e-4e99-a5eb-53cd68ba6c46" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
</TestEntries>
|
||||
<TestLists>
|
||||
<TestList name="목록에 없는 결과" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="로드된 모든 결과" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="1" executed="1" passed="1" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v3.1.5+1b188a7b0a (64-bit .NET 10.0.10)
|
||||
[xUnit.net 00:00:00.14] Discovering: KArtSell.ArchitectureTests
|
||||
[xUnit.net 00:00:00.19] Discovered: KArtSell.ArchitectureTests
|
||||
[xUnit.net 00:00:00.23] Starting: KArtSell.ArchitectureTests
|
||||
[xUnit.net 00:00:01.53] Finished: KArtSell.ArchitectureTests
|
||||
</StdOut>
|
||||
</Output>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -1,20 +0,0 @@
|
||||
# AEG-X-009 Server-side VersionSet Resolver
|
||||
|
||||
## Traceability
|
||||
|
||||
- WBS: `AEG-X-009`
|
||||
- Contract: `src/KArtSell.BuildingBlocks/Versioning/VersionSet.cs`
|
||||
- Implementation: `src/KArtSell.Modules.ModelOperations/Infrastructure/DapperApprovedModelContextReader.cs`
|
||||
- Test evidence: `evidence/AEG-X-009/versionset-resolver-boundary.trx`
|
||||
|
||||
## Change
|
||||
|
||||
The resolver now selects dataset manifests in `APPROVED` or `FROZEN` state only, requires dataset approval fields, and requires model registry approval fields. It continues to load all authoritative VersionSet values from the server-side database; client evidence/version values are not accepted.
|
||||
|
||||
## Verification
|
||||
|
||||
```text
|
||||
Model Operations boundary tests: 6/6 passed
|
||||
```
|
||||
|
||||
No dataset/model rows were seeded and no operation request or Shadow Run was created.
|
||||
@@ -1,64 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<TestRun id="9717473c-44ec-4177-b948-a3aab6f9f902" name="kjh20@KIMJAEHYUN-OFFI 2026-08-06 20:00:05" runUser="KIMJAEHYUN-OFFI\kjh20" xmlns="http://microsoft.com/schemas/VisualStudio/TeamTest/2010">
|
||||
<Times creation="2026-08-06T20:00:05.4579824+09:00" queuing="2026-08-06T20:00:05.4579826+09:00" start="2026-08-06T20:00:03.9646531+09:00" finish="2026-08-06T20:00:05.4699867+09:00" />
|
||||
<TestSettings name="default" id="25c44927-8b33-47f2-a80a-3f25b18dfdca">
|
||||
<Deployment runDeploymentRoot="kjh20_KIMJAEHYUN-OFFI_2026-08-06_20_00_05" />
|
||||
</TestSettings>
|
||||
<Results>
|
||||
<UnitTestResult executionId="421b5d6f-3c04-4e62-a9c3-efb7137bf0ab" testId="314a5e65-3e25-4434-eca3-b2b918f32928" testName="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_definitions_are_unique_and_evidence_only" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0156515" startTime="2026-08-06T20:00:05.2514618+09:00" endTime="2026-08-06T20:00:05.2794597+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="421b5d6f-3c04-4e62-a9c3-efb7137bf0ab" />
|
||||
<UnitTestResult executionId="441ac26b-53fb-4136-a8d3-1d5d2356ee97" testId="3fc876d0-6833-57e4-2651-437b2244093b" testName="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Operation_codes_are_unique_and_no_auto_promotion_mode_exists" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0198758" startTime="2026-08-06T20:00:05.2539412+09:00" endTime="2026-08-06T20:00:05.2957390+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="441ac26b-53fb-4136-a8d3-1d5d2356ee97" />
|
||||
<UnitTestResult executionId="a8ddc0dc-6452-494f-b628-387a1f594eb1" testId="f11f9f8d-5962-492f-5226-89f243398182" testName="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Holds_when_any_operational_integrity_error_exists" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0016648" startTime="2026-08-06T20:00:05.3014463+09:00" endTime="2026-08-06T20:00:05.3024698+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="a8ddc0dc-6452-494f-b628-387a1f594eb1" />
|
||||
<UnitTestResult executionId="9d9fc6e8-5fe5-436c-81d8-6ec2911f145b" testId="63f3a3a5-555e-c69b-517d-af3d3742c72d" testName="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Improvement_and_promotion_packet_jobs_are_proposal_only" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0022293" startTime="2026-08-06T20:00:05.3013661+09:00" endTime="2026-08-06T20:00:05.3027426+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="9d9fc6e8-5fe5-436c-81d8-6ec2911f145b" />
|
||||
<UnitTestResult executionId="0f952546-00ab-472c-9348-3f047c494137" testId="9052c99d-50c0-412a-2f24-ad636ad7f995" testName="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_never_contains_order_or_auto_promotion_operations" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0015493" startTime="2026-08-06T20:00:05.3014902+09:00" endTime="2026-08-06T20:00:05.3016767+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="0f952546-00ab-472c-9348-3f047c494137" />
|
||||
<UnitTestResult executionId="0f9b3aef-9ec3-44be-83d9-f19ebb9e880a" testId="68affc9c-1fdb-0235-bb8e-0f39c95758a3" testName="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Passes_evidence_gate_but_still_requires_human_approval" computerName="KIMJAEHYUN-OFFI" duration="00:00:00.0169984" startTime="2026-08-06T20:00:05.2540204+09:00" endTime="2026-08-06T20:00:05.2858863+09:00" testType="13cdc9d9-ddb5-4fa4-a97d-d965ccfc6d4b" outcome="Passed" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" relativeResultsDirectory="0f9b3aef-9ec3-44be-83d9-f19ebb9e880a" />
|
||||
</Results>
|
||||
<TestDefinitions>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_never_contains_order_or_auto_promotion_operations" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="9052c99d-50c0-412a-2f24-ad636ad7f995">
|
||||
<Execution id="0f952546-00ab-472c-9348-3f047c494137" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests" name="Registry_never_contains_order_or_auto_promotion_operations" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests.Registry_definitions_are_unique_and_evidence_only" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="314a5e65-3e25-4434-eca3-b2b918f32928">
|
||||
<Execution id="421b5d6f-3c04-4e62-a9c3-efb7137bf0ab" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationExecutionBoundaryTests" name="Registry_definitions_are_unique_and_evidence_only" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Passes_evidence_gate_but_still_requires_human_approval" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="68affc9c-1fdb-0235-bb8e-0f39c95758a3">
|
||||
<Execution id="0f9b3aef-9ec3-44be-83d9-f19ebb9e880a" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests" name="Passes_evidence_gate_but_still_requires_human_approval" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Improvement_and_promotion_packet_jobs_are_proposal_only" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="63f3a3a5-555e-c69b-517d-af3d3742c72d">
|
||||
<Execution id="9d9fc6e8-5fe5-436c-81d8-6ec2911f145b" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests" name="Improvement_and_promotion_packet_jobs_are_proposal_only" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests.Holds_when_any_operational_integrity_error_exists" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="f11f9f8d-5962-492f-5226-89f243398182">
|
||||
<Execution id="a8ddc0dc-6452-494f-b628-387a1f594eb1" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.PromotionGateEvaluatorTests" name="Holds_when_any_operational_integrity_error_exists" />
|
||||
</UnitTest>
|
||||
<UnitTest name="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests.Operation_codes_are_unique_and_no_auto_promotion_mode_exists" storage="c:\job_roomz\kartsell.aegis\tests\kartsell.modeloperations.unittests\bin\release\net10.0\kartsell.modeloperations.unittests.dll" id="3fc876d0-6833-57e4-2651-437b2244093b">
|
||||
<Execution id="441ac26b-53fb-4136-a8d3-1d5d2356ee97" />
|
||||
<TestMethod codeBase="C:\Job_Roomz\KArtSell.Aegis\tests\KArtSell.ModelOperations.UnitTests\bin\Release\net10.0\KArtSell.ModelOperations.UnitTests.dll" adapterTypeName="executor://xunit/VsTestRunner3/netcore/" className="KArtSell.ModelOperations.UnitTests.ModelOperationRegistryTests" name="Operation_codes_are_unique_and_no_auto_promotion_mode_exists" />
|
||||
</UnitTest>
|
||||
</TestDefinitions>
|
||||
<TestEntries>
|
||||
<TestEntry testId="314a5e65-3e25-4434-eca3-b2b918f32928" executionId="421b5d6f-3c04-4e62-a9c3-efb7137bf0ab" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="3fc876d0-6833-57e4-2651-437b2244093b" executionId="441ac26b-53fb-4136-a8d3-1d5d2356ee97" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="f11f9f8d-5962-492f-5226-89f243398182" executionId="a8ddc0dc-6452-494f-b628-387a1f594eb1" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="63f3a3a5-555e-c69b-517d-af3d3742c72d" executionId="9d9fc6e8-5fe5-436c-81d8-6ec2911f145b" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="9052c99d-50c0-412a-2f24-ad636ad7f995" executionId="0f952546-00ab-472c-9348-3f047c494137" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestEntry testId="68affc9c-1fdb-0235-bb8e-0f39c95758a3" executionId="0f9b3aef-9ec3-44be-83d9-f19ebb9e880a" testListId="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
</TestEntries>
|
||||
<TestLists>
|
||||
<TestList name="목록에 없는 결과" id="8c84fa94-04c1-424b-9868-57a2d4851a1d" />
|
||||
<TestList name="로드된 모든 결과" id="19431567-8539-422a-85d7-44ee4e166bda" />
|
||||
</TestLists>
|
||||
<ResultSummary outcome="Completed">
|
||||
<Counters total="6" executed="6" passed="6" failed="0" error="0" timeout="0" aborted="0" inconclusive="0" passedButRunAborted="0" notRunnable="0" notExecuted="0" disconnected="0" warning="0" completed="0" inProgress="0" pending="0" />
|
||||
<Output>
|
||||
<StdOut>[xUnit.net 00:00:00.00] xUnit.net VSTest Adapter v3.1.5+1b188a7b0a (64-bit .NET 10.0.10)
|
||||
[xUnit.net 00:00:00.31] Discovering: KArtSell.ModelOperations.UnitTests
|
||||
[xUnit.net 00:00:00.39] Discovered: KArtSell.ModelOperations.UnitTests
|
||||
[xUnit.net 00:00:00.43] Starting: KArtSell.ModelOperations.UnitTests
|
||||
[xUnit.net 00:00:00.52] Finished: KArtSell.ModelOperations.UnitTests
|
||||
</StdOut>
|
||||
</Output>
|
||||
</ResultSummary>
|
||||
</TestRun>
|
||||
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Freeze an approved model/dataset VersionSet for Phase 1 shadow run.
|
||||
|
||||
.DESCRIPTION
|
||||
Parameterized tool to INSERT approved model_id + dataset_id into:
|
||||
- governance.model_version_registry (FROZEN status)
|
||||
- evaluation.dataset_manifest (FROZEN status)
|
||||
|
||||
NO default values; all parameters REQUIRED. Fails immediately if any parameter is missing.
|
||||
|
||||
.PARAMETER ModelId
|
||||
UUID of the approved model (e.g., "00000000-0000-0000-0000-000000000001")
|
||||
Required. No default.
|
||||
|
||||
.PARAMETER DatasetId
|
||||
UUID of the approved dataset (e.g., "00000000-0000-0000-0000-000000000002")
|
||||
Required. No default.
|
||||
|
||||
.PARAMETER ApprovedBy
|
||||
Email/ID of the approver (e.g., "kjh2064@gmail.com")
|
||||
Required. No default.
|
||||
|
||||
.PARAMETER ConfigVersion
|
||||
Configuration version string (e.g., "v1.0.0")
|
||||
Required. No default.
|
||||
|
||||
.PARAMETER CodeSha
|
||||
Git commit SHA (e.g., "acaa731b3f")
|
||||
Required. No default.
|
||||
|
||||
.PARAMETER ConnectionString
|
||||
PostgreSQL connection string.
|
||||
Default: $env:KARTSELL_POSTGRES
|
||||
|
||||
.EXAMPLE
|
||||
# Freeze a versionset (all parameters required)
|
||||
.\freeze-versionset.ps1 `
|
||||
-ModelId "00000000-0000-0000-0000-000000000001" `
|
||||
-DatasetId "00000000-0000-0000-0000-000000000002" `
|
||||
-ApprovedBy "kjh2064@gmail.com" `
|
||||
-ConfigVersion "v1.0.0" `
|
||||
-CodeSha "acaa731b3f"
|
||||
|
||||
.EXAMPLE
|
||||
# Will fail: missing -ConfigVersion
|
||||
.\freeze-versionset.ps1 `
|
||||
-ModelId "00000000-0000-0000-0000-000000000001" `
|
||||
-DatasetId "00000000-0000-0000-0000-000000000002" `
|
||||
-ApprovedBy "kjh2064@gmail.com" `
|
||||
-CodeSha "acaa731b3f"
|
||||
# Error: Cannot bind argument to parameter 'ConfigVersion' because it is an empty string.
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory, HelpMessage = "Model UUID (e.g., 00000000-0000-0000-0000-000000000001)")]
|
||||
[ValidateScript({ $_ -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' })]
|
||||
[string]$ModelId,
|
||||
|
||||
[Parameter(Mandatory, HelpMessage = "Dataset UUID")]
|
||||
[ValidateScript({ $_ -match '^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$' })]
|
||||
[string]$DatasetId,
|
||||
|
||||
[Parameter(Mandatory, HelpMessage = "Approver email/ID (e.g., kjh2064@gmail.com)")]
|
||||
[ValidateScript({ $_ -match '^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$' })]
|
||||
[string]$ApprovedBy,
|
||||
|
||||
[Parameter(Mandatory, HelpMessage = "Config version (e.g., v1.0.0)")]
|
||||
[ValidateScript({ $_ -match '^v[0-9]+\.[0-9]+\.[0-9]+' })]
|
||||
[string]$ConfigVersion,
|
||||
|
||||
[Parameter(Mandatory, HelpMessage = "Git commit SHA (at least 10 chars)")]
|
||||
[ValidateScript({ $_.Length -ge 10 })]
|
||||
[string]$CodeSha,
|
||||
|
||||
[string]$ConnectionString = $env:KARTSELL_POSTGRES
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||||
Write-Host "Phase 1: Freeze VersionSet" -ForegroundColor Cyan
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||||
|
||||
# Validate connection string
|
||||
if (-not $ConnectionString) {
|
||||
Write-Error "ConnectionString not provided and `$env:KARTSELL_POSTGRES not set. Aborting."
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "`n[1/3] PRE-FLIGHT CHECK"
|
||||
Write-Host " Model ID: $ModelId"
|
||||
Write-Host " Dataset ID: $DatasetId"
|
||||
Write-Host " Approved By: $ApprovedBy"
|
||||
Write-Host " Config Version: $ConfigVersion"
|
||||
Write-Host " Code SHA: $CodeSha"
|
||||
Write-Host " Connection: $(($ConnectionString -split 'Password=')[0])***"
|
||||
|
||||
# Verify 0032 migration is deployed
|
||||
Write-Host "`n[2/3] VERIFY Migration 0032 deployed..."
|
||||
try {
|
||||
$conn = New-Object System.Data.NpgsqlClient.NpgsqlConnection($ConnectionString)
|
||||
$conn.Open()
|
||||
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = @"
|
||||
SELECT schema_version FROM schema_version_history
|
||||
WHERE script_name = '0032_shadow_run_queued_status_contract.sql'
|
||||
LIMIT 1
|
||||
"@
|
||||
$result = $cmd.ExecuteScalar()
|
||||
|
||||
if ($null -eq $result) {
|
||||
throw "Migration 0032 NOT FOUND. Run DbMigrator first."
|
||||
}
|
||||
|
||||
Write-Host " ✅ Migration 0032 deployed (schema_version: $result)"
|
||||
$conn.Close()
|
||||
}
|
||||
catch {
|
||||
Write-Error " ❌ Pre-flight failed: $_`n`nCorrective: Run DbMigrator to deploy 0032_*.sql before freezing."
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Insert into governance.model_version_registry
|
||||
Write-Host "`n[3/3] FREEZE VersionSet..."
|
||||
|
||||
try {
|
||||
$conn = New-Object System.Data.NpgsqlClient.NpgsqlConnection($ConnectionString)
|
||||
$conn.Open()
|
||||
|
||||
$correlationId = [System.Guid]::NewGuid()
|
||||
$now = [System.DateTime]::UtcNow
|
||||
|
||||
$cmd = $conn.CreateCommand()
|
||||
$cmd.CommandText = @"
|
||||
INSERT INTO governance.model_version_registry (
|
||||
id, model_id, dataset_id, status, approved_by, config_version, code_sha,
|
||||
effective_at, published_at, revision, correlation_id
|
||||
) VALUES (
|
||||
@id, @model_id, @dataset_id, 'FROZEN', @approved_by, @config_version, @code_sha,
|
||||
@effective_at, @published_at, 1, @correlation_id
|
||||
)
|
||||
ON CONFLICT (model_id, dataset_id) DO UPDATE SET
|
||||
status = 'FROZEN',
|
||||
approved_by = EXCLUDED.approved_by,
|
||||
config_version = EXCLUDED.config_version,
|
||||
code_sha = EXCLUDED.code_sha,
|
||||
effective_at = EXCLUDED.effective_at,
|
||||
revision = governance.model_version_registry.revision + 1,
|
||||
published_at = EXCLUDED.published_at
|
||||
RETURNING id, model_id, dataset_id, status, effective_at
|
||||
"@
|
||||
|
||||
$cmd.Parameters.AddWithValue("@id", [System.Guid]::NewGuid()) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@model_id", [System.Guid]$ModelId) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@dataset_id", [System.Guid]$DatasetId) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@approved_by", $ApprovedBy) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@config_version", $ConfigVersion) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@code_sha", $CodeSha) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@effective_at", $now) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@published_at", $now) | Out-Null
|
||||
$cmd.Parameters.AddWithValue("@correlation_id", $correlationId) | Out-Null
|
||||
|
||||
$reader = $cmd.ExecuteReader()
|
||||
if ($reader.Read()) {
|
||||
$insertedId = $reader['id']
|
||||
$insertedModelId = $reader['model_id']
|
||||
$insertedDatasetId = $reader['dataset_id']
|
||||
$insertedStatus = $reader['status']
|
||||
|
||||
Write-Host " ✅ Inserted governance.model_version_registry:"
|
||||
Write-Host " - ID: $insertedId"
|
||||
Write-Host " - Model: $insertedModelId"
|
||||
Write-Host " - Dataset: $insertedDatasetId"
|
||||
Write-Host " - Status: $insertedStatus"
|
||||
}
|
||||
$reader.Close()
|
||||
|
||||
# Update evaluation.dataset_manifest
|
||||
$cmd2 = $conn.CreateCommand()
|
||||
$cmd2.CommandText = @"
|
||||
INSERT INTO evaluation.dataset_manifest (
|
||||
id, dataset_id, model_id, status, freeze_reason,
|
||||
published_at, revision, correlation_id
|
||||
) VALUES (
|
||||
@id, @dataset_id, @model_id, 'FROZEN', 'Phase 1 VersionSet freeze',
|
||||
@published_at, 1, @correlation_id
|
||||
)
|
||||
ON CONFLICT (dataset_id, model_id) DO UPDATE SET
|
||||
status = 'FROZEN',
|
||||
freeze_reason = 'Phase 1 VersionSet freeze',
|
||||
revision = evaluation.dataset_manifest.revision + 1,
|
||||
published_at = EXCLUDED.published_at
|
||||
RETURNING id, dataset_id, model_id, status
|
||||
"@
|
||||
|
||||
$cmd2.Parameters.AddWithValue("@id", [System.Guid]::NewGuid()) | Out-Null
|
||||
$cmd2.Parameters.AddWithValue("@dataset_id", [System.Guid]$DatasetId) | Out-Null
|
||||
$cmd2.Parameters.AddWithValue("@model_id", [System.Guid]$ModelId) | Out-Null
|
||||
$cmd2.Parameters.AddWithValue("@published_at", $now) | Out-Null
|
||||
$cmd2.Parameters.AddWithValue("@correlation_id", $correlationId) | Out-Null
|
||||
|
||||
$reader2 = $cmd2.ExecuteReader()
|
||||
if ($reader2.Read()) {
|
||||
$mId = $reader2['id']
|
||||
$mDatasetId = $reader2['dataset_id']
|
||||
$mModelId = $reader2['model_id']
|
||||
$mStatus = $reader2['status']
|
||||
|
||||
Write-Host " ✅ Inserted evaluation.dataset_manifest:"
|
||||
Write-Host " - ID: $mId"
|
||||
Write-Host " - Dataset: $mDatasetId"
|
||||
Write-Host " - Model: $mModelId"
|
||||
Write-Host " - Status: $mStatus"
|
||||
}
|
||||
$reader2.Close()
|
||||
|
||||
$conn.Close()
|
||||
|
||||
Write-Host "`n✅ VersionSet FROZEN successfully"
|
||||
Write-Host " Correlation ID: $correlationId"
|
||||
Write-Host " Next: Run generate-shadow-run-identifiers.ps1 to create RunId/JobId"
|
||||
}
|
||||
catch {
|
||||
Write-Error " ❌ Failed to freeze VersionSet: $_"
|
||||
exit 1
|
||||
}
|
||||
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/usr/bin/env pwsh
|
||||
<#
|
||||
.SYNOPSIS
|
||||
Generate Phase 1 shadow run identifiers (RunId, JobId, JobRunId, CorrelationId, Idempotency-Key).
|
||||
|
||||
.DESCRIPTION
|
||||
Produces a JSON-formatted versionset.json file with all identifiers needed to enqueue Phase 1.
|
||||
Uses CRYPTOGRAPHIC random UUIDs and correlation for full traceability.
|
||||
|
||||
.PARAMETER OutputPath
|
||||
Path to save versionset.json (default: ./versionset.json in current directory)
|
||||
|
||||
.EXAMPLE
|
||||
.\generate-shadow-run-identifiers.ps1 -OutputPath ./phase1-versionset.json
|
||||
|
||||
.OUTPUTS
|
||||
JSON file with structure:
|
||||
{
|
||||
"phase1_run": {
|
||||
"runId": "UUID",
|
||||
"jobId": "UUID",
|
||||
"jobRunId": "UUID",
|
||||
"correlationId": "UUID",
|
||||
"idempotencyKey": "UUID",
|
||||
"generatedAt": "ISO8601 timestamp",
|
||||
"usage": "Use these IDs to enqueue Job 893 in Hangfire..."
|
||||
}
|
||||
}
|
||||
#>
|
||||
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[string]$OutputPath = "./versionset.json"
|
||||
)
|
||||
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||||
Write-Host "Phase 1: Generate Shadow Run Identifiers" -ForegroundColor Cyan
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||||
|
||||
Write-Host "`n[1/3] Generating cryptographic UUIDs..."
|
||||
|
||||
$runId = [System.Guid]::NewGuid()
|
||||
$jobId = [System.Guid]::NewGuid()
|
||||
$jobRunId = [System.Guid]::NewGuid()
|
||||
$correlationId = [System.Guid]::NewGuid()
|
||||
$idempotencyKey = [System.Guid]::NewGuid()
|
||||
|
||||
Write-Host " ✅ RunId: $runId"
|
||||
Write-Host " ✅ JobId: $jobId"
|
||||
Write-Host " ✅ JobRunId: $jobRunId"
|
||||
Write-Host " ✅ CorrelationId: $correlationId"
|
||||
Write-Host " ✅ IdempotencyKey: $idempotencyKey"
|
||||
|
||||
Write-Host "`n[2/3] Creating JSON payload..."
|
||||
|
||||
$payload = @{
|
||||
phase1_run = @{
|
||||
runId = $runId.ToString()
|
||||
jobId = $jobId.ToString()
|
||||
jobRunId = $jobRunId.ToString()
|
||||
correlationId = $correlationId.ToString()
|
||||
idempotencyKey = $idempotencyKey.ToString()
|
||||
generatedAt = [System.DateTime]::UtcNow.ToString("o")
|
||||
windowStart = "2024-01-02"
|
||||
windowEnd = "2024-09-10"
|
||||
usage = "Use these IDs to enqueue Job 893 (Phase 1 shadow run) in Hangfire. Command: `n Invoke-WebRequest -Uri 'http://127.0.0.1:5002/api/shadow-runs' -Method POST -Headers @{ 'X-KArtSell-User'='admin'; 'X-KArtSell-Role'='Admin'; 'Content-Type'='application/json' } -Body (ConvertTo-Json @{ modelId='<modelId>'; datasetId='<datasetId>'; windowStart='2024-01-02'; windowEnd='2024-09-10'; phaseFilter='All' })"
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host " ✅ JSON payload generated"
|
||||
|
||||
Write-Host "`n[3/3] Writing to file: $OutputPath"
|
||||
|
||||
$json = $payload | ConvertTo-Json -Depth 10
|
||||
$json | Out-File -FilePath $OutputPath -Encoding UTF8
|
||||
|
||||
Write-Host " ✅ File saved: $(Resolve-Path $OutputPath)"
|
||||
|
||||
Write-Host "`n✅ IDENTIFIERS GENERATED`n"
|
||||
Write-Host $json -ForegroundColor Green
|
||||
|
||||
Write-Host "`nNext Steps:`n"
|
||||
Write-Host " 1. Copy the identifiers from above or read from $OutputPath"
|
||||
Write-Host " 2. Call POST /api/shadow-runs with modelId/datasetId from frozen VersionSet"
|
||||
Write-Host " 3. Hangfire will enqueue Job 893 with these correlation IDs"
|
||||
Write-Host " 4. Monitor logs: grep 'CorrelationId: $correlationId' app.log"
|
||||
|
||||
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
|
||||
Reference in New Issue
Block a user