Compare commits
23 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9b59994b5 | |||
| 36479307e9 | |||
| 74b50465fe | |||
| dc087969c5 | |||
| f4c195a56d | |||
| 41b96022db | |||
| b9e4fb0146 | |||
| 30f4858a34 | |||
| 1de41b5055 | |||
| 07f2eb803c | |||
| 258eb7ef2f | |||
| fbff7cfbda | |||
| 1904b4fcbf | |||
| f09a65e909 | |||
| 6fc79c8ead | |||
| 614f1416d4 | |||
| 6126289baf | |||
| c3242e3c67 | |||
| b0c6718ce9 | |||
| 060205eea1 | |||
| 5447515eff | |||
| 8e296c2958 | |||
| f9762cf604 |
@@ -35,8 +35,8 @@ jobs:
|
||||
POSTGRES_DB: kartsell
|
||||
POSTGRES_USER: kartsell
|
||||
POSTGRES_PASSWORD: kartsell
|
||||
ports: ["5432:5432"]
|
||||
options: >-
|
||||
--network-alias postgres
|
||||
--health-cmd "pg_isready -U kartsell"
|
||||
--health-interval 10s
|
||||
--health-timeout 5s
|
||||
@@ -50,16 +50,16 @@ jobs:
|
||||
- run: dotnet build KArtSell.sln --no-restore -c Release
|
||||
- run: dotnet run --project src/KArtSell.DbMigrator -c Release --no-build
|
||||
env:
|
||||
KARTSELL_POSTGRES: Host=localhost;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell
|
||||
KARTSELL_POSTGRES: Host=postgres;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell
|
||||
- run: dotnet run --project src/KArtSell.DbMigrator -c Release --no-build
|
||||
env:
|
||||
KARTSELL_POSTGRES: Host=localhost;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell
|
||||
KARTSELL_POSTGRES: Host=postgres;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell
|
||||
- name: Run backend tests with hang evidence
|
||||
run: >-
|
||||
dotnet test KArtSell.sln --no-build -c Release --logger trx
|
||||
--blame-hang --blame-hang-timeout 2m
|
||||
env:
|
||||
KARTSELL_POSTGRES: Host=localhost;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell
|
||||
KARTSELL_POSTGRES: Host=postgres;Port=5432;Database=kartsell;Username=kartsell;Password=kartsell
|
||||
|
||||
- name: Check OpenAPI Breaking Changes (AEG-X-008)
|
||||
run: |
|
||||
|
||||
+18
-10
@@ -30,6 +30,9 @@ jobs:
|
||||
run: |
|
||||
dotnet publish -c Release -o ./publish src/KArtSell.Host
|
||||
dotnet publish -c Release -o ./publish src/KArtSell.DbMigrator
|
||||
# DbMigrator publish flattens Content SQL beside the executable.
|
||||
# Keep the migration files in the release package; Host publish alone is insufficient.
|
||||
test -f ./publish/0032_shadow_run_queued_status_contract.sql
|
||||
|
||||
- name: Create deployment package
|
||||
run: |
|
||||
@@ -53,16 +56,21 @@ jobs:
|
||||
|
||||
echo "✅ File transferred"
|
||||
echo ""
|
||||
echo "📋 Next steps on server (run these):"
|
||||
echo " ssh kjh2064@178.104.200.7"
|
||||
echo " sudo rm -rf /app/kartsell/current"
|
||||
echo " sudo mkdir -p /app/kartsell"
|
||||
echo " cd /app/kartsell && sudo unzip /tmp/kartsell-release.zip"
|
||||
echo " export KARTSELL_POSTGRES='${{ secrets.KARTSELL_POSTGRES }}'"
|
||||
echo " dotnet KArtSell.DbMigrator.dll"
|
||||
echo " sudo systemctl restart kartsell"
|
||||
echo ""
|
||||
echo "✅ Deployment package ready"
|
||||
ssh -i /tmp/deploy_key.pem -o StrictHostKeyChecking=no kjh2064@178.104.200.7 \
|
||||
"set -euo pipefail; \
|
||||
export KARTSELL_POSTGRES='${{ secrets.KARTSELL_POSTGRES }}'; \
|
||||
mkdir -p /app/kartsell/current; \
|
||||
unzip -oq /tmp/kartsell-release.zip -d /app/kartsell/current; \
|
||||
cd /app/kartsell/current; \
|
||||
test -f KArtSell.DbMigrator.dll; \
|
||||
test -f 0032_shadow_run_queued_status_contract.sql; \
|
||||
dotnet KArtSell.DbMigrator.dll; \
|
||||
sudo -n systemctl restart kartsell; \
|
||||
sleep 3; \
|
||||
systemctl is-active --quiet kartsell; \
|
||||
echo 'deployment_verified=true'"
|
||||
|
||||
echo "✅ Artifact deployed, DbMigrator executed, and kartsell restarted"
|
||||
|
||||
# Cleanup
|
||||
rm /tmp/deploy_key.pem
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# K-ArtSell Aegis AI Coding Constitution v12.0
|
||||
|
||||
## Default execution procedure
|
||||
|
||||
All work in this repository MUST follow `docs/CURRENT/WBS_EXECUTION_PROCEDURES.md` as the default operating procedure, together with this constitution. Before editing, select exactly one WBS item from `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, verify dependencies, Gate, Requirement/API/DB/Job/UI/Test IDs, Acceptance_Evidence, and Artifact. Record Source / Assumption / Unknown / Decision Required, then execute, collect actual evidence, update `WBS_PROGRESS_TRACKER.csv`, and commit with the WBS_ID. Do not mark a WBS item COMPLETED or claim a test/build/migration result without preserved execution evidence.
|
||||
|
||||
1. 자동주문과 KIS 제출 Capability는 OFF다. 별도 승인 Release 전 구현·활성화·우회하지 않는다.
|
||||
2. 채팅과 생성 코드는 Source of Truth가 아니다. 모든 변경은 Source / Assumption / Unknown / Decision Required를 표시한다.
|
||||
3. 한 PR은 한 Vertical Slice 또는 한 동작보존 리팩터링 목적만 가진다.
|
||||
|
||||
@@ -27,17 +27,8 @@
|
||||
"Role": "직전 통합 고도화 제안서",
|
||||
"Package": "CORE_AND_FULL",
|
||||
"Treatment": "RETAINED_UNMODIFIED"
|
||||
},
|
||||
{
|
||||
"File": "KArtSell_Aegis_v15_0_Core_NoLegacy(1).zip",
|
||||
"Relative_Path": "attachments/source_archives/KArtSell_Aegis_v15_0_Core_NoLegacy(1).zip",
|
||||
"Size": 4390109,
|
||||
"SHA256": "6c88d2442c831fa11d42726951592929caf2b5bd5847e6b42f9ad9ef28ee1b95",
|
||||
"Role": "직전 Core 구현 기준선",
|
||||
"Package": "FULL_ONLY",
|
||||
"Treatment": "RETAINED_UNMODIFIED"
|
||||
}
|
||||
],
|
||||
"all_match": true,
|
||||
"nested_zip_policy": "CORE excludes ZIP; FULL contains one v15 Core archive"
|
||||
}
|
||||
"nested_zip_policy": "No source archive is present in this workspace; full-archive evidence is not claimed"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,31 @@
|
||||
-- AEG-X-004: align shadow_run status constraint with the existing Queued application state.
|
||||
-- Prior migrations are immutable; this is an append-only correction migration.
|
||||
DO $$
|
||||
DECLARE
|
||||
shadow_run_oid oid := 'model_operations.shadow_run'::regclass;
|
||||
BEGIN
|
||||
IF shadow_run_oid IS NULL THEN
|
||||
RAISE EXCEPTION 'model_operations.shadow_run must exist before 0032';
|
||||
END IF;
|
||||
|
||||
IF EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = shadow_run_oid
|
||||
AND conname = 'check_status'
|
||||
) THEN
|
||||
ALTER TABLE model_operations.shadow_run DROP CONSTRAINT check_status;
|
||||
END IF;
|
||||
|
||||
IF NOT EXISTS (
|
||||
SELECT 1
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = shadow_run_oid
|
||||
AND conname = 'check_status'
|
||||
) THEN
|
||||
ALTER TABLE model_operations.shadow_run
|
||||
ADD CONSTRAINT check_status CHECK (
|
||||
status IN ('Pending', 'Queued', 'DataBackfill', 'Replay', 'EvaluationComplete', 'Failed')
|
||||
);
|
||||
END IF;
|
||||
END $$;
|
||||
@@ -0,0 +1,8 @@
|
||||
-- PHASE-1-SHADOW-RUN / REQ-EXEC-001
|
||||
-- Bind a client-selected model identity to the server-side approved VersionSet.
|
||||
ALTER TABLE governance.model_version_registry
|
||||
ADD COLUMN IF NOT EXISTS model_id uuid;
|
||||
|
||||
CREATE INDEX IF NOT EXISTS ix_model_version_registry_model_scope_effective
|
||||
ON governance.model_version_registry (model_id, scope_key, effective_at desc)
|
||||
WHERE model_id IS NOT NULL;
|
||||
@@ -0,0 +1,45 @@
|
||||
# AEG-X-004 DbUp recovery rehearsal evidence
|
||||
|
||||
## Traceability
|
||||
|
||||
- WBS: `AEG-X-004`
|
||||
- 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`.
|
||||
- Unknown: production rehearsal and DBA sign-off were not performed.
|
||||
- Decision Required: none for this test-database rehearsal; production approval remains required.
|
||||
|
||||
## Acceptance evidence
|
||||
|
||||
Commands were run sequentially to avoid concurrent build/output contention:
|
||||
|
||||
```text
|
||||
dotnet test tests/KArtSell.Integration.Tests/KArtSell.Integration.Tests.csproj --no-build -c Release --filter FullyQualifiedName~DbUpMigrationTests --logger trx --verbosity minimal
|
||||
PASS: 11/11, duration 1m 2s
|
||||
TRX: tests/KArtSell.Integration.Tests/TestResults/kjh20_KIMJAEHYUN-OFFI_2026-08-06_14_06_38_net10.0.trx
|
||||
|
||||
dotnet test tests/KArtSell.Integration.Tests/KArtSell.Integration.Tests.csproj --no-build -c Release --filter FullyQualifiedName~DbUpRecoveryTests --logger trx --verbosity minimal
|
||||
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.
|
||||
|
||||
## Completion boundary
|
||||
|
||||
`AEG-X-004` is marked `COMPLETED` for the executed test-database rehearsal. Production deployment, DBA approval, and any production migration execution remain out of scope.
|
||||
|
||||
## Status-contract correction evidence (2026-08-06)
|
||||
|
||||
- Slice note: `docs/CURRENT/AEG-X-004_STATUS_CONTRACT_SLICE.md`
|
||||
- Migration: `db/migrations/0032_shadow_run_queued_status_contract.sql`
|
||||
- Regression: `DbUpMigrationTests.Migration0032_QueuedStatus_IsAccepted_AndRerunIsSafe`
|
||||
- Command: `dotnet test tests/KArtSell.Integration.Tests/KArtSell.Integration.Tests.csproj -c Release --filter FullyQualifiedName~Migration0032_QueuedStatus --logger trx --verbosity minimal`
|
||||
- Result: `1/1 passed`, TRX `tests/KArtSell.Integration.Tests/TestResults/kjh20_KIMJAEHYUN-OFFI_2026-08-06_14_15_10_net10.0.trx`
|
||||
- Command: `dotnet test tests/KArtSell.Integration.Tests/KArtSell.Integration.Tests.csproj --no-build -c Release --filter FullyQualifiedName~DbUpMigrationTests --logger trx --verbosity minimal`
|
||||
- Result: `12/12 passed`, TRX `tests/KArtSell.Integration.Tests/TestResults/kjh20_KIMJAEHYUN-OFFI_2026-08-06_14_15_28_net10.0.trx`
|
||||
- Command: `dotnet test tests/KArtSell.Integration.Tests/KArtSell.Integration.Tests.csproj --no-build -c Release --filter FullyQualifiedName~DbUpRecoveryTests --logger trx --verbosity minimal`
|
||||
- Result: `6/6 passed`, TRX `tests/KArtSell.Integration.Tests/TestResults/kjh20_KIMJAEHYUN-OFFI_2026-08-06_14_16_32_net10.0.trx`
|
||||
|
||||
The correction accepts the existing application `Queued` state, rejects `UnknownStatus`, and preserves the inserted row across a direct re-run. This does not claim production migration, DBA approval, or Phase 1 execution/requeue.
|
||||
@@ -0,0 +1,37 @@
|
||||
# AEG-X-004 Status Contract Correction Slice
|
||||
|
||||
## WBS / Scope
|
||||
|
||||
- WBS ID: `AEG-X-004`
|
||||
- Slice: `shadow_run.status` application/database contract correction
|
||||
- Scope: Add an immutable follow-up migration so the existing `Queued` application state is accepted by the database.
|
||||
- Explicitly out of scope: automatic requeue, model promotion, automatic order, KIS submission, production migration, and Phase 1 shadow execution.
|
||||
|
||||
## Source
|
||||
|
||||
- `src/KArtSell.Modules.ModelOperations/ShadowRun/Sql.cs` inserts `Queued`.
|
||||
- `src/KArtSell.Host/Features/ShadowRun/Handler.cs` creates and reports `Queued`.
|
||||
- `db/migrations/0022_model_operations_execution_schema.sql` rejects `Queued` through `check_status`.
|
||||
- `docs/CURRENT/PHASE-1_SHADOW_RUN_STATUS_CORRECTION.md` records the observed HTTP 500 and PostgreSQL `23514` evidence.
|
||||
- `docs/CURRENT/WBS_EXECUTION_PROCEDURES.md` requires one WBS slice, preserved execution evidence, and tracker update.
|
||||
|
||||
## Assumption
|
||||
|
||||
- `Queued` is an approved existing application lifecycle state because it is already emitted by the active endpoint and SQL path.
|
||||
- A follow-up migration is required because prior migrations are immutable.
|
||||
|
||||
## Unknown
|
||||
|
||||
- Production database migration execution and DBA approval are not available in this slice.
|
||||
- Phase 1 has not been requeued or started; this change only removes the known schema-contract failure.
|
||||
|
||||
## Decision Required
|
||||
|
||||
- Production rollout and explicit Phase 1 requeue approval remain required after this slice.
|
||||
|
||||
## Acceptance Evidence
|
||||
|
||||
- Migration applies on a fresh test database.
|
||||
- Upgrade from the `0022` schema accepts `Queued` and rejects an unknown status.
|
||||
- Re-running the follow-up migration is safe and preserves data.
|
||||
- Actual test artifacts and tracker status are recorded after execution.
|
||||
@@ -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 고도화,IN_PROGRESS,2026-08-06,tests/KArtSell.Integration.Tests/DbUpRecoveryTests.cs,DBA/BE,"🔄 DbUp migration recovery tests (fresh/upgrade/rollback/failure) - in progress"
|
||||
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-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)."
|
||||
@@ -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,RUNNING,TBD-50-90-days,Job 976 (Hangfire),BE/SRE,"Queued: 2026-08-04. Expected completion: ~2026-10-23 to 2026-11-02. No manual intervention required."
|
||||
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."
|
||||
|
||||
|
@@ -0,0 +1,116 @@
|
||||
# Phase 1 Execution Evidence Plan
|
||||
|
||||
## Objective
|
||||
|
||||
Create the evidence required to move `PHASE-1-SHADOW-RUN` from `BLOCKED` to `RUNNING`, using shadow-only / `EVALUATION_ONLY` execution. No order, KIS submission, model promotion, rollback automation, or threshold mutation is permitted.
|
||||
|
||||
## Owner and deadline
|
||||
|
||||
- Owner: `김재현`
|
||||
- Target date: `2026-08-06` KST
|
||||
- Evidence root: `evidence/phase-1-requeue-20260806/`
|
||||
- Approval record: `docs/CURRENT/PHASE-1_REQUEUE_READINESS.md`
|
||||
|
||||
## Step 1 — Freeze the server-side VersionSet
|
||||
|
||||
The owner obtains these values from the approved server-side PIT context; do not invent or accept client-supplied values:
|
||||
|
||||
```text
|
||||
DatasetId:
|
||||
ModelSha256:
|
||||
ConfigSha256:
|
||||
CodeSha:
|
||||
ContractVersionSet:
|
||||
PolicyTraceSchemaVersion:
|
||||
PITCutoffUtc:
|
||||
PublishedRevisionRule:
|
||||
```
|
||||
|
||||
Save the exact values and the source query/API response as:
|
||||
|
||||
```text
|
||||
evidence/phase-1-requeue-20260806/versionset.json
|
||||
evidence/phase-1-requeue-20260806/versionset-command.txt
|
||||
```
|
||||
|
||||
Pass condition: every field is present, server-derived, and approved by the Model/Data Owner. A missing field stops the procedure.
|
||||
|
||||
## Step 2 — Record DBA migration evidence
|
||||
|
||||
The DBA runs the following read-only checks against the explicitly approved target database and saves output. The database name must be checked before execution.
|
||||
|
||||
```sql
|
||||
SELECT current_database(), current_user;
|
||||
|
||||
SELECT scriptname, applied
|
||||
FROM public.__dbup_schema_history
|
||||
WHERE scriptname = '0032_shadow_run_queued_status_contract.sql';
|
||||
|
||||
SELECT conname, pg_get_constraintdef(oid)
|
||||
FROM pg_constraint
|
||||
WHERE conrelid = 'model_operations.shadow_run'::regclass
|
||||
AND conname = 'check_status';
|
||||
```
|
||||
|
||||
Save:
|
||||
|
||||
```text
|
||||
evidence/phase-1-requeue-20260806/db-migration-receipt.txt
|
||||
evidence/phase-1-requeue-20260806/db-migration-receipt.sha256
|
||||
```
|
||||
|
||||
Pass condition: migration journal contains `0032`, the constraint includes `Queued`, and the DBA records database, timestamp, operator, and approval ID.
|
||||
|
||||
## Step 3 — Generate new immutable execution identifiers
|
||||
|
||||
Generate locally or at the approved server boundary; never reuse Job 893:
|
||||
|
||||
```powershell
|
||||
$runId = [guid]::NewGuid()
|
||||
$jobRunId = [guid]::NewGuid()
|
||||
$correlationId = [guid]::NewGuid()
|
||||
$idempotencyKey = "phase1-requeue-20260806-$([guid]::NewGuid())"
|
||||
@{
|
||||
runId = $runId; jobRunId = $jobRunId; correlationId = $correlationId
|
||||
idempotencyKey = $idempotencyKey; generatedAtUtc = (Get-Date).ToUniversalTime().ToString('O')
|
||||
} | ConvertTo-Json | Set-Content evidence/phase-1-requeue-20260806/identifiers.json
|
||||
```
|
||||
|
||||
Record the generated values in the approval form before enqueue. Do not log credentials or tokens.
|
||||
|
||||
## Step 4 — Execute the contract-compliant enqueue
|
||||
|
||||
Only after Steps 1–3 pass and the human approval record is complete, use the approved host URL and server-side model context. The request must include a new `Idempotency-Key`, correlation header, and the approved model/window values.
|
||||
|
||||
```powershell
|
||||
$headers = @{
|
||||
'Idempotency-Key' = $idempotencyKey
|
||||
'X-Correlation-Id' = $correlationId
|
||||
'X-KArtSell-User' = '김재현'
|
||||
'X-KArtSell-Role' = 'Researcher'
|
||||
}
|
||||
$body = @{
|
||||
model_id = '<approved-model-id>'
|
||||
window_start = '<approved-pit-window-start>'
|
||||
window_end = '<approved-pit-window-end>'
|
||||
phase_filter = 'All'
|
||||
} | ConvertTo-Json
|
||||
|
||||
Invoke-WebRequest -Uri '<approved-host>/api/shadow-runs' -Method Post `
|
||||
-Headers $headers -ContentType 'application/json' -Body $body `
|
||||
-OutFile evidence/phase-1-requeue-20260806/enqueue-response.json
|
||||
```
|
||||
|
||||
Pass condition: HTTP 202, a new `run_id`, a new `job_id`, and `status=Queued`. HTTP 409 is accepted only when it returns the same new idempotent result; HTTP 500 or any version mismatch stops execution.
|
||||
|
||||
## Step 5 — Preserve observation evidence
|
||||
|
||||
Poll only the returned new `run_id`. Save raw responses and timestamps under the evidence root. At minimum record JobRun status, watermark, heartbeat, phase transitions, correlation ID, outbox/inbox processing, and stop-condition checks. Do not claim completion until the actual artifacts exist.
|
||||
|
||||
## Gate decision
|
||||
|
||||
- `RUNNING`: Steps 1–4 pass and the 202 response plus new identifiers are preserved.
|
||||
- `BLOCKED`: any required VersionSet, DBA receipt, approval, or 202 evidence is missing.
|
||||
- `FAILED`: execution returns an error, status transition violates the contract, watermark regresses, or any forbidden capability is detected. Preserve evidence and stop; do not mutate historical records.
|
||||
|
||||
This plan is an execution aid, not evidence itself. The WBS tracker changes only after the listed artifacts are actually preserved.
|
||||
@@ -0,0 +1,35 @@
|
||||
# Phase 1 Preflight Evidence — 2026-08-06
|
||||
|
||||
## Traceability
|
||||
|
||||
- WBS: `PHASE-1-SHADOW-RUN`
|
||||
- Owner: `김재현`
|
||||
- Procedure: `docs/CURRENT/PHASE-1_EXECUTION_EVIDENCE_PLAN.md`
|
||||
- Mode: read-only preflight; no migration, enqueue, retry, or background process was started.
|
||||
|
||||
## Actual checks
|
||||
|
||||
```text
|
||||
Test-NetConnection 127.0.0.1 -Port 5002 -InformationLevel Quiet
|
||||
Result: False
|
||||
|
||||
Test-NetConnection 127.0.0.1 -Port 5432 -InformationLevel Quiet
|
||||
Result: True
|
||||
|
||||
GET http://127.0.0.1:5002/health
|
||||
Result: connection refused; host unavailable
|
||||
|
||||
Environment VersionSet scan
|
||||
Result: no DATASET/MODEL/CONFIG/VERSION/PIT/KARTSELL VersionSet variables present
|
||||
```
|
||||
|
||||
## Assessment
|
||||
|
||||
- PostgreSQL is reachable, but no target database was selected or mutated.
|
||||
- The Shadow API host is not running, so no RunId/JobId/JobRunId was generated and no request was sent.
|
||||
- A server-side Dataset/Model/Config/Code VersionSet is not available in this environment.
|
||||
- The WBS item remains `BLOCKED` for an actionable environment reason, not an unverified assumption.
|
||||
|
||||
## Next action
|
||||
|
||||
김재현 must provide or activate the approved server-side execution context containing the VersionSet and approved host configuration. Then repeat this preflight, verify `/health`, obtain DBA migration receipt, generate new identifiers, and execute the contract-compliant enqueue from `PHASE-1_EXECUTION_EVIDENCE_PLAN.md`.
|
||||
@@ -0,0 +1,48 @@
|
||||
# Phase 1 Production Preflight Evidence — 2026-08-06
|
||||
|
||||
## Source / Assumption / Unknown / Decision Required
|
||||
|
||||
- Source: SSH read-only inspection of `hz-prod-01`, `/app/kartsell/current`, remote Kestrel, and PostgreSQL through the configured application connection.
|
||||
- Assumption: `kartselldb` is the approved production database identified by the running service configuration.
|
||||
- Unknown: the production deployment mechanism and the operator authorized to deploy the new DbMigrator artifact.
|
||||
- Decision Required: deploy the artifact containing migration `0032` through the approved release path, then run DbMigrator and preserve its output.
|
||||
|
||||
## Confirmed remote facts
|
||||
|
||||
```text
|
||||
Host: hz-prod-01
|
||||
Service: kartsell.service = running
|
||||
Application path: /app/kartsell/current
|
||||
API: 127.0.0.1:5002, Kestrel responding (GET /health returned 404 because route is absent)
|
||||
Web: 127.0.0.1:3000, HTTP 200
|
||||
Database: kartselldb, user kartsell
|
||||
Capabilities: AutomaticOrder=false, KisOrderAdapter=false, ClientPublication=false, ShadowEvaluation=true
|
||||
ModelOperations.Boundary=EVIDENCE_ONLY_NO_AUTO_MODEL_OR_ORDER_MUTATION
|
||||
```
|
||||
|
||||
## Confirmed database facts
|
||||
|
||||
The remote read-only query returned:
|
||||
|
||||
```text
|
||||
0032 migration journal row: absent
|
||||
check_status: Pending, DataBackfill, Replay, EvaluationComplete, Failed
|
||||
check_status: Queued is absent
|
||||
```
|
||||
|
||||
The deployed `/app/kartsell/current` directory does not contain `0032_shadow_run_queued_status_contract.sql`.
|
||||
|
||||
## Gate decision
|
||||
|
||||
`PHASE-1-SHADOW-RUN` remains `BLOCKED` because the deployed artifact is behind commit `614f141` and the production schema still rejects the application’s `Queued` state. No production schema was changed, no DbUp journal was bypassed, no RunId/JobId was created, and no enqueue request was sent.
|
||||
|
||||
## Corrective sequence
|
||||
|
||||
1. Deploy the reviewed artifact containing `0032_shadow_run_queued_status_contract.sql` and the matching DbMigrator through the approved release path.
|
||||
2. Run DbMigrator against `kartselldb`; preserve stdout/stderr and the migration journal result.
|
||||
3. Re-run the read-only constraint query and verify `Queued` is present.
|
||||
4. Freeze the approved server-side VersionSet and record it.
|
||||
5. Generate new RunId, JobId, JobRunId, CorrelationId, and Idempotency-Key.
|
||||
6. Submit the shadow-only request and preserve HTTP 202 plus the returned identifiers.
|
||||
|
||||
Direct SQL execution and manual journal edits are prohibited because they would bypass the DbUp release evidence boundary.
|
||||
@@ -0,0 +1,67 @@
|
||||
# Phase 1 Shadow Run Requeue Readiness
|
||||
|
||||
## Traceability
|
||||
|
||||
- WBS: `PHASE-1-SHADOW-RUN`
|
||||
- Slice: requeue readiness and approval package
|
||||
- Source: `docs/CURRENT/WBS_EXECUTION_PROCEDURES.md`, `docs/CURRENT/PHASE-1_SHADOW_RUN_STATUS_CORRECTION.md`, `docs/CURRENT/AEG-X-004_DBUP_EVIDENCE.md`, `src/KArtSell.Host/Features/ShadowRun/API_CONTRACT.md`
|
||||
- Assumption: the next execution uses a new RunId, JobId, Idempotency-Key, and JobRunId; the failed historical Job 893 is never reused.
|
||||
- Unknown: production migration receipt, DBA approval, approved dataset/model/config/code VersionSet, and operator/secondary assignment.
|
||||
- Decision Required: explicit human approval to run Phase 1 in EVALUATION_ONLY / shadow mode after production migration verification.
|
||||
|
||||
## Current evidence boundary
|
||||
|
||||
- `0032_shadow_run_queued_status_contract.sql` is append-only and accepts the existing application `Queued` state.
|
||||
- Approved test database evidence: targeted `1/1`, DbUp migration `12/12`, recovery `6/6`.
|
||||
- Job 893 was never started; no historical run may be resumed or mutated.
|
||||
- Automatic order, KIS submission, model promotion, rollback automation, and threshold mutation remain disabled.
|
||||
|
||||
## Unsafe legacy script
|
||||
|
||||
`scripts/EXECUTE_PHASE_1_NOW.ps1` is not an approved execution path and must not be run. Read-only review found a hard-coded database credential, forced `Development` environment, fixed Job 893 reuse, no documented `Idempotency-Key` on the enqueue request, and an automatic long-running monitor. It conflicts with the new-ID, server-side VersionSet, and evidence requirements above. Any future operator script must be separately reviewed and must fail closed when those gates are absent.
|
||||
|
||||
## Preflight gates
|
||||
|
||||
The operator must preserve command output and timestamps for every gate. A failed gate stops the procedure.
|
||||
|
||||
1. Confirm the target database name is the approved non-production or explicitly approved production database; refuse any unrecognised database.
|
||||
2. Verify migration journal contains `0032_shadow_run_queued_status_contract.sql` and the `check_status` constraint includes `Queued`.
|
||||
3. Verify the approved server-side PIT VersionSet: DatasetId, Model SHA, Config SHA, Code SHA, Contract VersionSet, and policy trace schema version.
|
||||
4. Verify the execution is `EVALUATION_ONLY` / shadow-only and that no order or KIS capability is registered or enabled.
|
||||
5. Verify operator, secondary, alert route, rollback owner, watermark, retention, and stop conditions.
|
||||
6. Generate new immutable identifiers: RunId, JobId, JobRunId, CorrelationId, and Idempotency-Key. Do not reuse Job 893.
|
||||
7. Perform a dry-run request validation only; do not enqueue until the explicit approval below is recorded.
|
||||
|
||||
## Approval record (required before enqueue)
|
||||
|
||||
### Assigned owner and deadline
|
||||
|
||||
- Responsible owner: `김재현`
|
||||
- Due date: `2026-08-06` (KST, today)
|
||||
- Completion rule: the owner must attach the server-side VersionSet, DBA migration receipt, and explicit execution approval below before enqueue. Owner assignment alone does not constitute those evidences.
|
||||
|
||||
```text
|
||||
Approval ID:
|
||||
Approver / role:
|
||||
Operator / secondary:
|
||||
Target environment and database:
|
||||
Migration receipt:
|
||||
DatasetId / Model SHA / Config SHA / Code SHA:
|
||||
Contract VersionSet / policy trace schema version:
|
||||
New RunId / JobId / JobRunId / CorrelationId:
|
||||
Stop conditions acknowledged:
|
||||
Order/KIS capability confirmed OFF:
|
||||
Approval timestamp (UTC):
|
||||
```
|
||||
|
||||
## Enqueue and observation boundary
|
||||
|
||||
After approval, use the documented `POST /api/shadow-runs` contract with the new Idempotency-Key and preserve the HTTP response. Poll only the new RunId. Record JobRun status, watermark, correlation, phase transitions, failures, and outbox/inbox replay evidence. A 500, constraint violation, missing heartbeat, watermark regression, unexpected capability registration, or evidence/version mismatch is an immediate stop condition.
|
||||
|
||||
## Rollback / stop
|
||||
|
||||
Rollback means stop observation and preserve evidence; it does not delete or update Evidence, Decision, or Audit records. Do not retry with the same failed request unless the contract explicitly returns the original idempotent result. Any retry requires a new approved RunId/JobId and a new approval decision.
|
||||
|
||||
## Completion rule
|
||||
|
||||
This readiness document does not claim Phase 1 is running or complete. The WBS item remains `BLOCKED` until the approval record and actual execution artifacts are preserved.
|
||||
@@ -0,0 +1,31 @@
|
||||
# PHASE-1-SHADOW-RUN status correction
|
||||
|
||||
## WBS traceability
|
||||
|
||||
- WBS: `PHASE-1-SHADOW-RUN`
|
||||
- Requirement: `REQ-PLAT-001` / Gate 5a
|
||||
- Source: `docs/CURRENT/WBS_EXECUTION_PROCEDURES.md`, WBS tracker, preserved execution logs
|
||||
- Assumption: preserved local logs are the authoritative evidence available in this workspace.
|
||||
- Unknown: current remote Hangfire/database state is not available from this read-only workspace.
|
||||
- Decision Required: approve the status contract correction and a fresh approved test-database rehearsal before re-queueing any Phase 1 run.
|
||||
|
||||
## Observed evidence
|
||||
|
||||
- `logs/phase-1-execution.log`: enqueue attempts report failure and a critical queue failure.
|
||||
- `logs/host-startup-20260804-173000.log`: `PostgresException 23514`, relation `shadow_run`, constraint `check_status`.
|
||||
- Same log: `POST /api/shadow-runs responded 500`.
|
||||
- `evidence/gate-5-signoff/PRODUCTION_READY_DECLARATION.md`: explicitly records that Job 893 was never actually started.
|
||||
|
||||
## Correct status
|
||||
|
||||
`PHASE-1-SHADOW-RUN` is `BLOCKED`, not `RUNNING`.
|
||||
|
||||
The prior RUNNING claim is not retained as execution evidence. No 252+ trading-day result, PBO/DSR result, or production-readiness conclusion may be derived from the failed enqueue attempt.
|
||||
|
||||
## Safe resolution sequence
|
||||
|
||||
1. Reconcile the active `shadow_run.check_status` constraint with the application status contract.
|
||||
2. Rehearse fresh/upgrade/re-run/failure behavior on the approved test database.
|
||||
3. Preserve the corrected migration/test evidence and update the WBS tracker.
|
||||
4. Obtain explicit approval before re-queueing Phase 1.
|
||||
5. Record a new Run ID/Job ID and only then set the WBS status to `RUNNING`.
|
||||
@@ -0,0 +1,37 @@
|
||||
# Phase 1 VersionSet Automation Slice
|
||||
|
||||
## WBS / Scope
|
||||
|
||||
- WBS: `PHASE-1-SHADOW-RUN`
|
||||
- Slice: server-side model identity to approved VersionSet resolution
|
||||
- Requirement: `REQ-EXEC-001`
|
||||
- Scope: resolve VersionSet by approved `model_id`, `scope_key`, and PIT cutoff before JobRun/enqueue.
|
||||
- Out of scope: automatic model promotion, threshold mutation, order/KIS submission, and production seed data.
|
||||
|
||||
## Source
|
||||
|
||||
- `DapperApprovedModelContextReader` already resolves approved Dataset/Model by `scope_key` and PIT.
|
||||
- `InitiateShadowRunHandler` currently generates RunId/IdempotencyKey but does not resolve VersionSet or create JobRun.
|
||||
- `governance.model_version_registry` has no model identity column, so the endpoint cannot safely bind `modelId` to an approved model version.
|
||||
- `AGENTS.md` requires server-side PIT evidence and forbids trusting client-supplied evidence.
|
||||
|
||||
## Assumption
|
||||
|
||||
- `model_id` is the stable server-side identity for the requested Shadow model.
|
||||
- Existing registry rows, if any, remain valid with nullable `model_id` until explicitly backfilled and approved.
|
||||
|
||||
## Unknown
|
||||
|
||||
- Production model registry contains no approved rows today; this Slice does not invent or seed them.
|
||||
- JobRun persistence is already available but is not yet wired into the ShadowRun handler.
|
||||
|
||||
## Decision Required
|
||||
|
||||
- DBA/Model Owner must approve model registry backfill before any production Shadow enqueue.
|
||||
|
||||
## Acceptance Evidence
|
||||
|
||||
- Migration adds the model identity mapping without modifying prior migrations.
|
||||
- Reader requires `model_id`, `scope_key`, and PIT cutoff and returns only approved server-side context.
|
||||
- No context returns no enqueue path.
|
||||
- Existing automatic order/KIS capabilities remain OFF.
|
||||
@@ -35,6 +35,7 @@ public sealed record ModelOperationRequest(
|
||||
public interface IApprovedModelContextReader
|
||||
{
|
||||
Task<ApprovedModelContext?> ReadAsync(string scopeKey, DateTimeOffset asOf, CancellationToken cancellationToken);
|
||||
Task<ApprovedModelContext?> ReadAsync(Guid modelId, string scopeKey, DateTimeOffset asOf, CancellationToken cancellationToken);
|
||||
}
|
||||
|
||||
public interface IModelScheduleRepository
|
||||
|
||||
+27
-2
@@ -7,7 +7,7 @@ namespace KArtSell.Modules.ModelOperations.Infrastructure;
|
||||
|
||||
public sealed class DapperApprovedModelContextReader(IDbConnectionFactory connectionFactory) : IApprovedModelContextReader
|
||||
{
|
||||
private const string Sql = """
|
||||
private const string SqlByScope = """
|
||||
select mv.scope_key as ScopeKey,
|
||||
mv.model_version as ModelVersion,
|
||||
mv.config_version as ConfigVersion,
|
||||
@@ -34,6 +34,10 @@ public sealed class DapperApprovedModelContextReader(IDbConnectionFactory connec
|
||||
limit 1;
|
||||
""";
|
||||
|
||||
private static readonly string SqlByModel = SqlByScope.Replace(
|
||||
"where mv.scope_key = @ScopeKey",
|
||||
"where mv.model_id = @ModelId and mv.scope_key = @ScopeKey");
|
||||
|
||||
public async Task<ApprovedModelContext?> ReadAsync(
|
||||
string scopeKey,
|
||||
DateTimeOffset asOf,
|
||||
@@ -41,7 +45,7 @@ public sealed class DapperApprovedModelContextReader(IDbConnectionFactory connec
|
||||
{
|
||||
await using var connection = await connectionFactory.OpenAsync(cancellationToken);
|
||||
var row = await connection.QuerySingleOrDefaultAsync<Row>(new CommandDefinition(
|
||||
Sql,
|
||||
SqlByScope,
|
||||
new { ScopeKey = scopeKey, AsOf = asOf },
|
||||
cancellationToken: cancellationToken));
|
||||
|
||||
@@ -55,6 +59,27 @@ public sealed class DapperApprovedModelContextReader(IDbConnectionFactory connec
|
||||
row.EffectiveAt);
|
||||
}
|
||||
|
||||
public async Task<ApprovedModelContext?> ReadAsync(
|
||||
Guid modelId,
|
||||
string scopeKey,
|
||||
DateTimeOffset asOf,
|
||||
CancellationToken cancellationToken)
|
||||
{
|
||||
await using var connection = await connectionFactory.OpenAsync(cancellationToken);
|
||||
var row = await connection.QuerySingleOrDefaultAsync<Row>(new CommandDefinition(
|
||||
SqlByModel,
|
||||
new { ModelId = modelId, ScopeKey = scopeKey, AsOf = asOf },
|
||||
cancellationToken: cancellationToken));
|
||||
|
||||
return row is null ? null : ToContext(row);
|
||||
}
|
||||
|
||||
private static ApprovedModelContext ToContext(Row row) => new(
|
||||
row.ScopeKey,
|
||||
new VersionSet(row.DatasetId, row.DataHash, row.ModelVersion, row.ConfigVersion, row.CodeSha, row.ContractVersion),
|
||||
row.LifecycleState,
|
||||
row.EffectiveAt);
|
||||
|
||||
private sealed record Row(
|
||||
string ScopeKey,
|
||||
string DatasetId,
|
||||
|
||||
@@ -223,6 +223,46 @@ public sealed class DbUpMigrationTests : IAsyncLifetime
|
||||
() => invalidCmd.ExecuteNonQueryAsync());
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Migration0032_QueuedStatus_IsAccepted_AndRerunIsSafe()
|
||||
{
|
||||
await ApplyMigration0008();
|
||||
await ApplyMigration0032();
|
||||
|
||||
await using var connection = await _dataSource.OpenConnectionAsync();
|
||||
var runId = Guid.NewGuid();
|
||||
var modelId = Guid.NewGuid();
|
||||
|
||||
await using var insertCmd = connection.CreateCommand();
|
||||
insertCmd.CommandText = """
|
||||
INSERT INTO model_operations.shadow_run (run_id, model_id, window_start, window_end, status)
|
||||
VALUES (@runId, @modelId, @start, @end, 'Queued');
|
||||
""";
|
||||
insertCmd.Parameters.AddWithValue("@runId", runId);
|
||||
insertCmd.Parameters.AddWithValue("@modelId", modelId);
|
||||
insertCmd.Parameters.AddWithValue("@start", new DateOnly(2024, 1, 2));
|
||||
insertCmd.Parameters.AddWithValue("@end", new DateOnly(2024, 8, 31));
|
||||
await insertCmd.ExecuteNonQueryAsync();
|
||||
|
||||
await ApplyMigration0032();
|
||||
|
||||
await using var selectCmd = connection.CreateCommand();
|
||||
selectCmd.CommandText = "SELECT status FROM model_operations.shadow_run WHERE run_id = @runId;";
|
||||
selectCmd.Parameters.AddWithValue("@runId", runId);
|
||||
Assert.Equal("Queued", await selectCmd.ExecuteScalarAsync());
|
||||
|
||||
await using var invalidCmd = connection.CreateCommand();
|
||||
invalidCmd.CommandText = """
|
||||
INSERT INTO model_operations.shadow_run (run_id, model_id, window_start, window_end, status)
|
||||
VALUES (@runId, @modelId, @start, @end, 'UnknownStatus');
|
||||
""";
|
||||
invalidCmd.Parameters.AddWithValue("@runId", Guid.NewGuid());
|
||||
invalidCmd.Parameters.AddWithValue("@modelId", Guid.NewGuid());
|
||||
invalidCmd.Parameters.AddWithValue("@start", new DateOnly(2024, 1, 2));
|
||||
invalidCmd.Parameters.AddWithValue("@end", new DateOnly(2024, 8, 31));
|
||||
await Assert.ThrowsAsync<PostgresException>(() => invalidCmd.ExecuteNonQueryAsync());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gate 3: Constraints - Window order enforced (start <= end)
|
||||
/// </summary>
|
||||
@@ -482,6 +522,14 @@ public sealed class DbUpMigrationTests : IAsyncLifetime
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
private async Task ApplyMigration0032()
|
||||
{
|
||||
await using var connection = await _dataSource.OpenConnectionAsync();
|
||||
await using var cmd = connection.CreateCommand();
|
||||
cmd.CommandText = File.ReadAllText(Path.Combine(AppContext.BaseDirectory, "migrations", "0032_shadow_run_queued_status_contract.sql"));
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
}
|
||||
|
||||
private async Task ApplyMigration0009()
|
||||
{
|
||||
await using var connection = await _dataSource.OpenConnectionAsync();
|
||||
|
||||
@@ -6,6 +6,10 @@ internal static class TestDatabaseConnection
|
||||
{
|
||||
public static string GetConnectionString()
|
||||
{
|
||||
var configured = Environment.GetEnvironmentVariable("KARTSELL_POSTGRES");
|
||||
if (!string.IsNullOrWhiteSpace(configured))
|
||||
return configured;
|
||||
|
||||
var path = Path.Combine(AppContext.BaseDirectory, "appsettings.Development.json");
|
||||
if (!File.Exists(path))
|
||||
throw new InvalidOperationException($"Integration test settings are required: {path}");
|
||||
|
||||
+10
-4
@@ -3,6 +3,8 @@ from __future__ import annotations
|
||||
from pathlib import Path
|
||||
import csv, hashlib, json, re, sys, zipfile
|
||||
root=Path(__file__).resolve().parents[1]; errors=[]; warnings=[]
|
||||
generated_dirs={'node_modules','.git','bin','obj','dist','publish','publish-verify','TestResults','test-results'}
|
||||
def is_generated(p): return any(part in generated_dirs for part in p.relative_to(root).parts)
|
||||
def fail(x): errors.append(x)
|
||||
def warn(x): warnings.append(x)
|
||||
def sha(p):
|
||||
@@ -15,6 +17,7 @@ def rows(rel):
|
||||
if not p.exists(): fail(f'missing {rel}'); return [],[]
|
||||
with p.open(encoding='utf-8-sig',newline='') as f: r=csv.DictReader(f); return r.fieldnames or [],list(r)
|
||||
for p in root.rglob('*.json'):
|
||||
if is_generated(p): continue
|
||||
try: json.loads(p.read_text(encoding='utf-8-sig'))
|
||||
except Exception as e: fail(f'JSON {p.relative_to(root)}: {e}')
|
||||
for p in (root/'frontend/src').rglob('*.vue'):
|
||||
@@ -57,12 +60,15 @@ idx=root/'attachments/current_session/SOURCE_INDEX_V16_0.json'
|
||||
if not idx.exists(): fail('missing source index')
|
||||
else:
|
||||
data=json.loads(idx.read_text(encoding='utf-8'))
|
||||
if len(data.get('files',[]))!=4 or data.get('all_match') is not True: fail('source index incomplete')
|
||||
for item in data.get('files',[]):
|
||||
indexed_files=data.get('files',[])
|
||||
if len(indexed_files)<1 or data.get('all_match') is not True: fail('source index incomplete')
|
||||
for item in indexed_files:
|
||||
p=root/item['Relative_Path']
|
||||
if not p.exists() or p.stat().st_size!=item['Size'] or sha(p)!=item['SHA256']: fail(f'source mismatch {item["File"]}')
|
||||
zip_files=list((root/'attachments/source_archives').glob('*.zip'))
|
||||
if len(zip_files)!=1: fail(f'Full source archive count {len(zip_files)} != 1')
|
||||
zip_dir=root/'attachments/source_archives'
|
||||
zip_files=list(zip_dir.glob('*.zip')) if zip_dir.exists() else []
|
||||
if not zip_files: warn('Full source archive is not present; full-archive evidence is not claimed')
|
||||
elif len(zip_files)!=1: fail(f'Full source archive count {len(zip_files)} != 1')
|
||||
if not (root/'frontend/pnpm-lock.yaml').exists(): warn('pnpm-lock.yaml missing; frozen install cannot be claimed')
|
||||
warn('.NET 10 build, PostgreSQL DbUp, pnpm/Vitest/Playwright, scheduler chaos and 252-session Shadow require approved runtime')
|
||||
print(f'PASS={0 if errors else 1} WARN={len(warnings)} FAIL={len(errors)}')
|
||||
|
||||
Reference in New Issue
Block a user