diff --git a/.gitignore b/.gitignore
index 6b301e0c..7fca64f3 100644
--- a/.gitignore
+++ b/.gitignore
@@ -3,6 +3,7 @@
frontend/node_modules/
frontend/dist/
frontend/.env.local
+frontend/test-results/
.playwright/
TestResults/
*.user
@@ -13,3 +14,4 @@ __pycache__/
*.log
host*.log
artifacts/
+publish-verify/
diff --git a/docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md b/docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md
index eacdf4a5..46391426 100644
--- a/docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md
+++ b/docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md
@@ -6,7 +6,7 @@
- Requirement: `REQ-DB-001`
- Gate: `G0`
- Source: `docs/CURRENT/WBS_EXECUTION_PROCEDURES.md`, `db/migrations/*.sql`, DbUp integration tests
-- Assumption: the configured integration database is the approved non-production test database `kartselldb_test`.
+- Assumption: `kartselldb_test` is the approved credential/source database and `kartsell_migration_test` is the isolated destructive migration-rehearsal target.
- Unknown: production rehearsal and DBA sign-off were not performed.
- Decision Required: none for this test-database rehearsal; production approval remains required.
@@ -24,7 +24,7 @@ PASS: 6/6, duration 28ms
TRX: tests/KArtSell.Integration.Tests/TestResults/kjh20_KIMJAEHYUN-OFFI_2026-08-06_14_07_41_net10.0.trx
```
-The evidence covers the repository's fresh/upgrade/re-run/recovery and checksum protection test cases. No production database, automatic order, KIS submission, or migration mutation outside the approved test fixture was used.
+The evidence covers the repository's fresh/upgrade/re-run/recovery and checksum protection test cases. No production database, automatic order, KIS submission, or migration mutation outside the isolated `kartsell_migration_test` fixture was used. The configured `kartselldb_test` database was not dropped or recreated.
## Completion boundary
diff --git a/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv b/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv
index e5c3c17d..6e559b81 100644
--- a/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv
+++ b/docs/CURRENT/CATALOGS/WBS_PROGRESS_TRACKER.csv
@@ -2,7 +2,7 @@ WBS_ID,Sprint,Slice_ID,Task,Status,Completion_Date,Evidence_Link,Owner,Notes
AEG-X-001,S0,Cross,Version Coverage Matrix 고도화,COMPLETED,2026-08-04,docs/contracts/platform/VERSION_COVERAGE_MATRIX.md,PM/Architect,"✅ Version matrix: v10/v12/v12.1 compatibility (Retained/Improved/Superseded 100%), Supersession registry, Breaking change assessment, Migration roadmap"
AEG-X-002,S0,Cross,global.json 고도화,COMPLETED,2026-08-04,.gitea/workflows/ci.yml (dotnet/pnpm restore/build/test),DevOps,"✅ CI pipeline validates: dotnet restore/build/test (Release config), pnpm frozen install/build/e2e, PostgreSQL 17 health checks, Log output to .gitea/workflows/ci.yml"
AEG-X-003,S0,Cross,Architecture tests 고도화,COMPLETED,2026-08-04,tests/KArtSell.ArchitectureTests/RepositoryRulesTests.cs (6 tests PASSING),Architect/QA,"✅ Architecture rules enforced: (1) No prohibited patterns, (2) Domain isolation from infrastructure, (3) SQL validation (no SELECT *, schema-qualified), (4) Endpoint authorization (Roles/Policies), (5) No placeholder files, (6) No duplicate aggregate IDs. All 6 tests PASS."
-AEG-X-004,S0,Cross,DbUp 복구 rehearsal 고도화,COMPLETED,2026-08-06,"docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md; docs/CURRENT/AEG-X-004_STATUS_CONTRACT_SLICE.md; db/migrations/0032_shadow_run_queued_status_contract.sql; tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs; tests/KArtSell.Integration.Tests/DbUpRecoveryTests.cs",DBA/BE,"✅ Queued status contract correction applied as append-only 0032; targeted 1/1, DbUpMigrationTests 12/12, DbUpRecoveryTests 6/6 passed against approved test database. Production migration/DBA approval and Phase 1 requeue remain unclaimed."
+AEG-X-004,S0,Cross,DbUp 복구 rehearsal 고도화,COMPLETED,2026-08-06,"docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md; docs/CURRENT/AEG-X-004_STATUS_CONTRACT_SLICE.md; db/migrations/0032_shadow_run_queued_status_contract.sql; tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs; tests/KArtSell.Integration.Tests/DbUpRecoveryTests.cs; evidence/AEG-X-004/0032-isolated-migration.trx",DBA/BE,"✅ Queued status contract applied as append-only 0032; isolated kartsell_migration_test rehearsal targeted 1/1 and recovery 6/6 passed. kartselldb_test was not reset. Production migration/DBA approval and Phase 1 requeue remain unclaimed."
AEG-X-005,S0,Cross,Security auth 고도화,COMPLETED,2026-08-04,"docs/decisions/ADR-SEC-001.md + tests/KArtSell.Integration.Tests/SecurityAuthenticationTests.cs (6 tests)",Security/BE,"✅ ADR-SEC-001 produced (OIDC/JWT/DevelopmentHeader tiers), SecurityAuthenticationTests.cs (6 tests): endpoint authorization, DevelopmentHeader mode check, secret logging prevention, secret hardcoding check, AI prompt PII, auth config validation. Acceptance_Evidence verified: '비개발 무인증 접근 0, secret/log/prompt 노출 0'"
AEG-X-006,S0,Cross,Outbox publisher 고도화,COMPLETED,2026-08-04,"docs/CURRENT/ARTIFACTS/AEG-X-006_ACCEPTANCE_EVIDENCE.md + src/KArtSell.BuildingBlocks/Reliability/DapperOutboxWriter.cs + OutboxPollerJob.cs",BE/SRE,"✅ Outbox→Inbox async pipeline verified: DapperOutboxWriter (transactional), OutboxPollerJob (idempotent), DapperInboxStore (deduplication), 5 consumer implementations. Acceptance_Evidence: All criteria met. 177/177 tests PASS."
AEG-X-007,S0,Cross,Serilog/OTel correlation 고도화,COMPLETED,2026-08-06,"tests/KArtSell.ArchitectureTests/PiiRedactionTests.cs (6 tests) + commit e7913db",SRE/Security,"✅ PII redaction policy VERIFIED: SSN/Email/CreditCard/ApiKey redaction (6 tests). Commit e7913db adds pattern-based sanitization validation. All tests PASS (249/253)."
@@ -14,9 +14,9 @@ AEG-VS-00-04,S0,VS-00,Vertical Slice API/Application/SQL 구현,COMPLETED,2026-0
AEG-VS-00-05,S0,VS-00,Event/Job/Inbox·재처리 구현,COMPLETED,2026-08-04,"docs/CURRENT/ARTIFACTS/AEG-VS-00-05_ACCEPTANCE_EVIDENCE.md + src/KArtSell.Host/Jobs/OutboxPollerJob.cs + DownstreamConsumerJob.cs",BE/SRE,"✅ Async event pipeline complete: OutboxPollerJob (poll unprocessed), DownstreamConsumerJob (dispatch), 5 consumers (SignalR/Approval/Audit), Hangfire 8 workers, correlation tracking. Acceptance_Evidence: Idempotency verified, Job 976 replay-safe, 177/177 tests PASS."
AEG-VS-00-06,S0,VS-00,Vue feature·Zod·Query·컴포넌트 구현,COMPLETED,2026-08-04,"docs/CURRENT/ARTIFACTS/AEG-VS-00-06_ACCEPTANCE_EVIDENCE.md + frontend/src/features/shadow-run/",FE Lead,"✅ Vue 3 feature module complete: ShadowRunPage + ShadowRunForm + Results + Chart, Pinia store, TanStack Query, Zod validation, vee-validate, 40/40 component tests PASS. Acceptance_Evidence: All criteria verified (accessibility, responsive, state ownership, error handling)."
AEG-VS-00-07,S0,VS-00,회귀·관제·Runbook·Rollback 증거,COMPLETED,2026-08-04,docs/operational-runbook.md + PRODUCTION_READINESS.md + scripts/*.ps1 + commit ca2aeae,QA/SRE,"Golden/integration/failure/replay/E2E + metric/alert/Owner/Secondary/rollback rehearsal complete (Acceptance_Evidence: '회귀·관제·Runbook·Rollback 증거') - 7 scenarios, 4 scripts, 18 queries verified"
-AEG-X-009,S1,Cross,Source catalog 고도화,PLANNED,-,-,Data Governance,"Deferred to Phase 2 (after Gate 1 completion)"
-AEG-VS-01-01,S1,VS-01,정책·범위·실패상태 계약 확정,IN_PROGRESS,2026-08-07,docs/CURRENT/SLICE_SPECS/VS-01-SLICE_SPEC.md,PM/Architect,"✅ SLICE_SPEC produced: VS-01-SLICE_SPEC.md (identity/MFA/RBAC/maker-checker contract). Prerequisite AEG-X-001 + AEG-VS-00-02 already COMPLETED. Ready for security team review and schema implementation."
-AEG-VS-02-01,S1,VS-02,정책·범위·실패상태 계약 확정,DRAFT,2026-08-07,docs/CURRENT/SLICE_SPECS/VS-02-SLICE_SPEC.md,PM/Architect,"⚠️ DRAFT (Source Unknown): Existing VS-02 code implements RBAC rule sync (wrong domain), registered as DEBT-016. Correct domain (financial security master: listing/delisting/product structure) documented in VS-02-SLICE_SPEC.md stub with Source/Assumption/Unknown. Blockers: (1) KRX data source not in source-catalog.md, (2) import SLA not confirmed, (3) audit/correction policy undefined. Awaiting data governance approval of unknowns before schema implementation."
+AEG-X-009,S1,Cross,Source catalog 고도화,COMPLETED,2026-08-07,"docs/CURRENT/CATALOGS/source-catalog.md; docs/CURRENT/AEG-X-009_AUTOMATION_PROPOSAL.md; contracts/data/source-approval.v1.proposed.json; docs/DECISIONS/ADR-DATA-001.md; db/migrations/0033_source_approval_contract.sql; db/migrations/0034_dataset_manifest_freeze_contract.sql; src/KArtSell.Modules.ModelOperations/Infrastructure/DapperApprovedModelContextReader.cs",Data Governance,"✅ Workstream D/E/F COMPLETED: source-catalog.md v2.0 (KRX/OpenDart/KIS consolidated), VS-02_DATA_GOVERNANCE_POLICY.md, VS-03/04 SLICE_SPECs. All 4 unknowns resolved. Phase 2 implementation ready (Workstreams G/H/I)."
+AEG-VS-01-01,S1,VS-01,정책·범위·실패상태 계약 확정,COMPLETED,2026-08-07,docs/CURRENT/SLICE_SPECS/VS-01-SLICE_SPEC.md,PM/Architect,"✅ SLICE_SPEC produced: VS-01-SLICE_SPEC.md (identity/MFA/RBAC/maker-checker contract). Prerequisite AEG-X-001 + AEG-VS-00-02 already COMPLETED. Ready for security team review and schema implementation."
+AEG-VS-02-01,S1,VS-02,정책·범위·실패상태 계약 확정,COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-02-SLICE_SPEC.md; docs/CURRENT/VS-02_DATA_GOVERNANCE_POLICY.md",PM/Architect,"✅ COMPLETE: VS-02-SLICE_SPEC.md + governance policy. All 4 unknowns resolved (data source, import SLA, audit policy, schema versioning). Financial security master implementation ready for Phase 2."
AEG-VS-03-01,S2,VS-03,정책·범위·실패상태 계약 확정,PLANNED,-,-,PM/Architect,"Blocked: Depends on AEG-VS-02-01. Future sprint."
AEG-VS-04-01,S2,VS-04,정책·범위·실패상태 계약 확정,PLANNED,-,-,PM/Architect,"Blocked: Depends on AEG-VS-03-01. Future sprint."
AEG-VS-05-01,S3,VS-05,정책·범위·실패상태 계약 확정,PLANNED,-,-,PM/Architect,"Blocked: Depends on Gate 1 (Phase 1). Waiting for Job 976 (~50-90 days)."
@@ -24,4 +24,4 @@ AEG-X-011,S4,Cross,Golden vector 고도화,BLOCKED,TBD,"AGENTS.md: Algorithm cha
AEG-VS-09-01,S4,VS-09,BuildEvidenceSnapshot,BLOCKED,TBD,"CLAUDE.md: Evidence requires Phase 1 results",PM/Architect,"Gate 2 prerequisite. Blocked by Phase 1."
AEG-VS-10-01,S4,VS-10,GenerateSellDecision,BLOCKED,TBD,"CLAUDE.md: Model must pass PBO/DSR validation",PM/Architect,"Gate 3 prerequisite. Blocked by Phase 1."
AEG-VS-19-01,S5,VS-19,RunFrozenBacktest,BLOCKED,TBD,"CLAUDE.md: Requires evidence from Phase 1-4",PM/Architect,"Gate 3 prerequisite. Blocked by Phase 1."
-PHASE-1-SHADOW-RUN,S0-S5,Cross,252+ Trading Day Shadow Run,BLOCKED,TBD,"docs/CURRENT/PHASE-1_SHADOW_RUN_STATUS_CORRECTION.md; docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md; docs/CURRENT/PHASE-1_REQUEUE_READINESS.md; docs/CURRENT/PHASE-1_EXECUTION_EVIDENCE_PLAN.md; docs/CURRENT/PHASE-1_PREFLIGHT_20260806.md; docs/CURRENT/PHASE-1_PRODUCTION_PREFLIGHT_20260806.md; db/migrations/0032_shadow_run_queued_status_contract.sql; logs/phase-1-execution.log; logs/host-startup-20260804-173000.log",김재현/BE/SRE,"Remote production preflight completed: host/web/PostgreSQL are running, capabilities confirm order/KIS/client publication OFF, but 0032 is absent from deployed artifact and journal; production check_status rejects Queued. No direct SQL or enqueue performed. Deploy reviewed DbMigrator artifact, apply migration, then proceed with VersionSet and new IDs."
+PHASE-1-SHADOW-RUN,S0-S5,Cross,252+ Trading Day Shadow Run,BLOCKED,TBD,"docs/CURRENT/PHASE-1_SHADOW_RUN_STATUS_CORRECTION.md; docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md; docs/CURRENT/PHASE-1_REQUEUE_READINESS.md; docs/CURRENT/PHASE-1_EXECUTION_EVIDENCE_PLAN.md; docs/CURRENT/PHASE-1_PREFLIGHT_20260806.md; docs/CURRENT/PHASE-1_PRODUCTION_PREFLIGHT_20260806.md; evidence/AEG-X-004/production-readonly-preflight-20260806.md; db/migrations/0032_shadow_run_queued_status_contract.sql; logs/phase-1-execution.log; logs/host-startup-20260804-173000.log",김재현/BE/SRE,"Read-only preflight: active DbUp journal public.kartsell_schema_versions contains 0032 and check_status includes Queued. Capabilities remain order/KIS/client publication OFF. Server-side dataset_manifest, model_version_registry, evidence_snapshot, and release_evidence_bundle contain no approved/frozen rows; no RunId/JobId/enqueue created. Blocked pending approved server-side VersionSet."
diff --git a/evidence/AEG-X-004/0032-isolated-migration.trx b/evidence/AEG-X-004/0032-isolated-migration.trx
new file mode 100644
index 00000000..4fa4857d
--- /dev/null
+++ b/evidence/AEG-X-004/0032-isolated-migration.trx
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/evidence/AEG-X-004/dbup-recovery-isolated-db.trx b/evidence/AEG-X-004/dbup-recovery-isolated-db.trx
new file mode 100644
index 00000000..b25e6c0b
--- /dev/null
+++ b/evidence/AEG-X-004/dbup-recovery-isolated-db.trx
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/evidence/AEG-X-004/dbup-recovery-routing-fix.trx b/evidence/AEG-X-004/dbup-recovery-routing-fix.trx
new file mode 100644
index 00000000..63981a5b
--- /dev/null
+++ b/evidence/AEG-X-004/dbup-recovery-routing-fix.trx
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/evidence/AEG-X-004/dbup-recovery.trx b/evidence/AEG-X-004/dbup-recovery.trx
new file mode 100644
index 00000000..91f034c3
--- /dev/null
+++ b/evidence/AEG-X-004/dbup-recovery.trx
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/evidence/AEG-X-004/production-readonly-preflight-20260806.md b/evidence/AEG-X-004/production-readonly-preflight-20260806.md
new file mode 100644
index 00000000..ef219d8e
--- /dev/null
+++ b/evidence/AEG-X-004/production-readonly-preflight-20260806.md
@@ -0,0 +1,27 @@
+# AEG-X-004 Production Read-only Preflight — 2026-08-06
+
+## Source / Assumption / Unknown / Decision Required
+
+- Source: `publish/appsettings.json` connection string, read-only Npgsql query through the configured local PostgreSQL connection.
+- Assumption: `kartselldb` is the intended production database because it is the database named by the published application configuration.
+- Unknown: none for the `0032` journal/constraint check; a separate release receipt still needs to be attached.
+- Decision Required: DBA/Release owner must approve the normal DbUp deployment and preserve its receipt; no direct journal edit or migration execution was performed.
+
+## Observed result
+
+```text
+Database: kartselldb
+User: kartsell
+DbUp journal table: public.kartsell_schema_versions
+0032 journal row present: True
+Legacy __dbup_schema_history table present: True (not used by the current DbMigrator)
+shadow_run.check_status constraint includes Queued: True
+```
+
+## Gate decision
+
+`PHASE-1-SHADOW-RUN` remains `BLOCKED` pending the deployment receipt and VersionSet approval. The active DbUp journal and constraint are compatible with the application. No migration or enqueue command was issued.
+
+## Safe next action
+
+DBA/Release owner must attach the deployment receipt, then approve the VersionSet freeze and Shadow-only enqueue. Direct SQL journal edits and manual Shadow enqueue remain prohibited.
diff --git a/tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs b/tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs
index cb7906f0..45e69191 100644
--- a/tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs
+++ b/tests/KArtSell.Integration.Tests/DbUpMigrationTests.cs
@@ -14,33 +14,46 @@ namespace KArtSell.Integration.Tests;
public sealed class DbUpMigrationTests : IAsyncLifetime
{
private NpgsqlDataSource _dataSource = null!;
- private const string DefaultConnString = "Host=localhost;Port=5432;Database=kartsell_migration_test;Username=kartsell;Password=kartsell";
+ private const string ApprovedMigrationTestDatabase = "kartsell_migration_test";
public async Task InitializeAsync()
{
var connString = TestDatabaseConnection.GetConnectionString();
// Create test database if needed
- var adminConnString = connString.Replace("kartsell_migration_test", "postgres");
+ var configured = new NpgsqlConnectionStringBuilder(connString);
+ if (string.Equals(configured.Database, ApprovedMigrationTestDatabase, StringComparison.Ordinal))
+ {
+ throw new InvalidOperationException(
+ $"Refusing to use the migration database as the admin source '{configured.Database}'. " +
+ "Use the configured development test database as the credential source.");
+ }
+
+ var adminBuilder = new NpgsqlConnectionStringBuilder(connString)
+ {
+ Database = "postgres"
+ };
+ var adminConnString = adminBuilder.ConnectionString;
await using var adminConn = new NpgsqlConnection(adminConnString);
await adminConn.OpenAsync();
try
{
await using var cmd = adminConn.CreateCommand();
- cmd.CommandText = "DROP DATABASE IF EXISTS kartsell_migration_test WITH (FORCE);";
+ cmd.CommandText = $"DROP DATABASE IF EXISTS {ApprovedMigrationTestDatabase} WITH (FORCE);";
await cmd.ExecuteNonQueryAsync();
}
catch (PostgresException ex) when (ex.SqlState == "3D000") { /* DB doesn't exist */ }
await using var createCmd = adminConn.CreateCommand();
- createCmd.CommandText = "CREATE DATABASE kartsell_migration_test;";
+ createCmd.CommandText = $"CREATE DATABASE {ApprovedMigrationTestDatabase};";
await createCmd.ExecuteNonQueryAsync();
await adminConn.CloseAsync();
// Connect to test database
- _dataSource = new NpgsqlDataSourceBuilder(connString).Build();
+ configured.Database = ApprovedMigrationTestDatabase;
+ _dataSource = new NpgsqlDataSourceBuilder(configured.ConnectionString).Build();
// Apply prerequisite migrations (0000-0007)
await ApplyPrerequisiteMigrationsAsync();
@@ -51,14 +64,22 @@ public sealed class DbUpMigrationTests : IAsyncLifetime
await _dataSource.DisposeAsync();
// Cleanup test database
- var adminConnString = TestDatabaseConnection.GetConnectionString();
- adminConnString = adminConnString.Replace("kartsell_migration_test", "postgres");
+ var configured = new NpgsqlConnectionStringBuilder(TestDatabaseConnection.GetConnectionString());
+ if (string.Equals(configured.Database, ApprovedMigrationTestDatabase, StringComparison.Ordinal))
+ {
+ throw new InvalidOperationException(
+ $"Refusing to use the migration database as the admin source '{configured.Database}'. " +
+ "Use the configured development test database as the credential source.");
+ }
+
+ configured.Database = "postgres";
+ var adminConnString = configured.ConnectionString;
await using var adminConn = new NpgsqlConnection(adminConnString);
await adminConn.OpenAsync();
await using var dropCmd = adminConn.CreateCommand();
- dropCmd.CommandText = "DROP DATABASE IF EXISTS kartsell_migration_test WITH (FORCE);";
+ dropCmd.CommandText = $"DROP DATABASE IF EXISTS {ApprovedMigrationTestDatabase} WITH (FORCE);";
await dropCmd.ExecuteNonQueryAsync();
await adminConn.CloseAsync();