fix: resolve DEBT-017 duplicate ApprovalWorkflow implementation

Adopt Features/ApprovalWorkflow/ (wired into Program.cs, reachable over
HTTP) as the sole VS-26 (formerly VS-03) maker-checker approval slice.
Delete the dead, [DontRegister]'d duplicate under
ApprovalWorkflow/ (Workstream H) and its dedicated test file, which had
been misleadingly credited with "20/20 tests PASS" while being
unreachable at runtime.

- Sql.cs: fix the same Dapper DateOnly-parameter-binding bug that was
  already found and fixed in the now-deleted implementation
  (commit 2ccf74c) but had not been ported to this one; InsertProposalAsync
  would have failed 100% of the time against a real database.
- tests/.../ApprovalWorkflow/ApprovalWorkflowTests.cs: new Handler+Sql+
  real-Postgres integration coverage (create/approve/activate role
  gating, maker!=checker separation of duties, evidence attachment,
  DateOnly round-trip, list filtering) replacing the deleted dead-code
  suite at the same path.
- ApprovalWorkflowPolicyTests.cs: extended (5->10 cases) rather than
  replaced, since it already tested the kept implementation's Policy.
- Program.cs: drop the reference comment to the deleted namespace.
- TECH_DEBT_REGISTER.md: DEBT-017 marked Completed (DB verification
  pending); corrected stale DEBT-023 to point at this resolution;
  registered two residual gaps discovered (not introduced) by this
  cleanup as DEBT-025 (no GET /approvals/{id}, evidence unreachable via
  HTTP) and DEBT-026 (no wired Draft->Proposed transition, so the
  approve/activate path is currently unreachable end-to-end via HTTP).
- WBS_PROGRESS_TRACKER.csv / CURRENT_ROADMAP.md: AEG-VS-26-01 kept
  BLOCKED, not COMPLETED — no PostgreSQL was reachable in this session
  (127.0.0.1:5432 connection refused), so the 8 new integration tests
  are unverified; only the 10 pure-Policy tests were confirmed passing.

Cherry-picked cedc8d7/8c777df from docs/wbs-tracker-current-state onto
this worktree branch first, to bring in the VS-26 renumbering and
ADR-WBS-001 that this task's brief assumed already existed.

dotnet build -c Release: 0 errors/0 warnings.
dotnet test --filter "FullyQualifiedName~ApprovalWorkflow" -c Release:
10 passed (Policy, no DB), 15 failed (DB connection refused - includes
6 unrelated pre-existing tests matched by the filter substring).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-08-08 13:05:03 +09:00
parent df7d41df7d
commit 14e2cedc4f
14 changed files with 324 additions and 1284 deletions
@@ -17,7 +17,7 @@ AEG-VS-00-07,S0,VS-00,회귀·관제·Runbook·Rollback 증거,COMPLETED,2026-08
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; db/migrations/0033_market_data_import_logs.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. ✅ Workstream G (commit 136665c, 2026-08-07) also now COMPLETE: live KRX OpenAPI / OpenDart / KIS service integrations (P1-P3), daily scheduling + error classification + SLA tracking + LKG fallback (P4-P6), market_data schema with append-only import logs, correlation_id-based idempotent replay. ⚠️ Note: 0033 is used by two different, unrelated migrations across branches (source_approval_contract.sql vs market_data_import_logs.sql) — confirm actual applied migration number in the target DB's kartsell_schema_versions journal before assuming both landed as authored."
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-26-01,S2,VS-26,모델 승인 워크플로우 구현 (Maker-Checker Governance),BLOCKED,-,"docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/ (ApprovalSql.cs, ApprovalPolicy.cs, [DontRegister]'d); src/KArtSell.Modules.ModelOperations/Features/ApprovalWorkflow/ (Handlers.cs, Sql.cs, Policy.cs — the one actually wired in Program.cs); db/migrations/0036_approval_workflow.sql; tests/KArtSell.Integration.Tests/ApprovalWorkflow/*; TECH_DEBT_REGISTER.md DEBT-017; commit a2e742c (Workstream H, PR #23, merged to main)",PM/BE Lead,"🔴 CORRECTED 2026-08-08 — this row previously said 'Backend implementation + tests complete, 20/20 tests PASS' and that claim is misleading: TECH_DEBT_REGISTER.md DEBT-017 records that TWO independent ApprovalWorkflow implementations exist with colliding routes. The one this row's evidence links to and the one with 20/20 passing tests (`ApprovalWorkflow/`, Workstream H) has all 4 endpoints annotated `[DontRegister]` and is NOT reachable via HTTP. The implementation actually registered in `Program.cs` and reachable at runtime is `Features/ApprovalWorkflow/` (Workstream G) — grep found no dedicated test file exercising it. Net effect: the maker-checker approval gate that is actually live has not been shown to work, and the one that has been shown to work is dead code. Do not build frontend UI against either implementation, and do not re-mark this COMPLETED, until an architect decision (DEBT-017) picks the canonical implementation, deletes the other, and a test suite runs against whichever is kept. Renumbered from VS-03 to VS-26 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-03 in WBS_MASTER.csv ('IngestMarketDataPIT') is a separate, unrelated, still-unimplemented slice and keeps its original number unchanged. Also note: src/KArtSell.Host/Features/MarketData/VS03_*.cs is a THIRD, unrelated, already-implemented-and-tested body of work also labeled 'VS-03' (Market Data Ingestion Dashboard, commits 2bc2b1e/32b49a4) that is entirely absent from this tracker — see CURRENT_ROADMAP.md for a note to investigate and add it."
AEG-VS-26-01,S2,VS-26,모델 승인 워크플로우 구현 (Maker-Checker Governance),BLOCKED,-,"docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/Features/ApprovalWorkflow/ (Handlers.cs, Sql.cs, Policy.cs, Endpoints.cs — sole implementation, wired in Program.cs); db/migrations/0036_approval_workflow.sql; tests/KArtSell.Integration.Tests/ApprovalWorkflow/ApprovalWorkflowTests.cs (new, 8 cases); tests/KArtSell.Integration.Tests/ApprovalWorkflowPolicyTests.cs (extended, 10 cases); TECH_DEBT_REGISTER.md DEBT-017/DEBT-023; commit a2e742c (original duplication, superseded)",PM/BE Lead,"🟡 UPDATED 2026-08-08 — DEBT-017 architect decision made and executed: the duplicate `src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/` (dead code, `[DontRegister]`'d, previously credited with the misleading '20/20 tests PASS') was DELETED along with its dedicated test file. `Features/ApprovalWorkflow/` (the implementation actually reachable over HTTP) is now the sole implementation and gained new Handler+Sql+real-Postgres integration tests covering create/approve/activate role gating, maker≠checker separation of duties, evidence attachment, and an explicit `DateOnly EffectiveAt` round-trip. While porting: found `Features/ApprovalWorkflow/Sql.cs InsertProposalAsync` had the same Dapper `DateOnly`-binding bug already fixed in the deleted implementation (commit 2ccf74c) — fixed identically here (`::date` cast + string parameter). Two residual (pre-existing, not introduced by this cleanup) gaps documented in the slice README rather than fixed: no `GET /approvals/{id}` endpoint, and no wired Draft→Proposed transition anywhere in the running app (approve/activate are therefore currently unreachable end-to-end via HTTP with real data). **Still BLOCKED, not COMPLETED, because no PostgreSQL was reachable in this session (127.0.0.1:5432 connection refused, no SSH tunnel open): `dotnet build -c Release` is clean, but `dotnet test --filter FullyQualifiedName~ApprovalWorkflow -c Release` shows only the 10 pure-Policy (no-DB) tests passing — all 8 new DB-backed integration tests fail with a connection error, unverified either way against a live database.** Do not mark COMPLETED until that run happens against a reachable Postgres and actually passes. Renumbered from VS-03 to VS-26 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-03 in WBS_MASTER.csv ('IngestMarketDataPIT') remains a separate, unrelated, still-unimplemented slice. Also still open: `src/KArtSell.Host/Features/MarketData/VS03_*.cs` is a THIRD, unrelated, already-implemented-and-tested body of work also labeled 'VS-03' that is entirely absent from this tracker — see CURRENT_ROADMAP.md."
AEG-VS-27-01,S2,VS-27,불변 감사 추적 구현 (Audit Trail / GDPR),COMPLETED,2026-08-07,"docs/CURRENT/SLICE_SPECS/VS-27-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/Compliance/ (AuditSql.cs, GdprRetention.cs); tests/KArtSell.Integration.Tests/Compliance/AuditTrailTests.cs; commit 97444c9 (Workstream I, PR #24, merged to main)",PM/BE Lead,"✅ Backend implementation + tests complete: append-only audit_events table, GDPR retention tracking + redaction (actor_email→'<redacted>', customer_id→'<purged>'), PII fields (ip_address). 5/5 tests PASS run in isolation (2026-08-07). Also fixed same day (fix/dapper-underscore-mapping-and-build branch): ip_address (inet) and kis_response-style jsonb columns threw InvalidCastException when read through Dapper into a typed class; GdprRetention.RetentionEndsAt was declared DateTime against a DATE column, same failure mode; and a process-wide Dapper snake_case-mapping race condition (KArtSell.BuildingBlocks' [ModuleInitializer] only fires once that assembly loads — AuditSql doesn't reliably touch it) intermittently nulled out every column read from this table depending on unrelated test/host startup order. None of this had ever been exercised against a live database before. ⚠️ No frontend UI yet — in progress 2026-08-08. Renumbered from VS-04 to VS-27 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-04 in WBS_MASTER.csv ('ApplyCorporateActions') was an unrelated, still-unimplemented slice and keeps its original number unchanged."
AEG-VS-05-01,S3,VS-05,정책·범위·실패상태 계약 확정,PLANNED,-,-,PM/Architect,"Not started. No docs/CURRENT/SLICE_SPECS/VS-05-SLICE_SPEC.md and no src/ implementation exist yet (confirmed 2026-08-07). Previous note referenced 'Job 976 (~50-90 days)' as if Phase 1 were actively running and counting down — that was false; see PHASE-1-SHADOW-RUN row below. Correct statement: blocked behind Gate 1 (Phase 1 shadow run), which itself has not been queued yet."
AEG-X-011,S4,Cross,Golden vector 고도화,BLOCKED,TBD,"AGENTS.md: Algorithm changes require Golden data",Quant/QA,"Gate 2 prerequisite. Blocked by Phase 1 completion. Corrected 2026-08-07: Phase 1 (Job 893/976) has not been started, not 'in progress' — see PHASE-1-SHADOW-RUN row. No countdown is currently running."
1 WBS_ID Sprint Slice_ID Task Status Completion_Date Evidence_Link Owner Notes
17 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; db/migrations/0033_market_data_import_logs.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. ✅ Workstream G (commit 136665c, 2026-08-07) also now COMPLETE: live KRX OpenAPI / OpenDart / KIS service integrations (P1-P3), daily scheduling + error classification + SLA tracking + LKG fallback (P4-P6), market_data schema with append-only import logs, correlation_id-based idempotent replay. ⚠️ Note: 0033 is used by two different, unrelated migrations across branches (source_approval_contract.sql vs market_data_import_logs.sql) — confirm actual applied migration number in the target DB's kartsell_schema_versions journal before assuming both landed as authored.
18 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.
19 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.
20 AEG-VS-26-01 S2 VS-26 모델 승인 워크플로우 구현 (Maker-Checker Governance) BLOCKED - docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/ (ApprovalSql.cs, ApprovalPolicy.cs, [DontRegister]'d); src/KArtSell.Modules.ModelOperations/Features/ApprovalWorkflow/ (Handlers.cs, Sql.cs, Policy.cs — the one actually wired in Program.cs); db/migrations/0036_approval_workflow.sql; tests/KArtSell.Integration.Tests/ApprovalWorkflow/*; TECH_DEBT_REGISTER.md DEBT-017; commit a2e742c (Workstream H, PR #23, merged to main) docs/CURRENT/SLICE_SPECS/VS-26-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/Features/ApprovalWorkflow/ (Handlers.cs, Sql.cs, Policy.cs, Endpoints.cs — sole implementation, wired in Program.cs); db/migrations/0036_approval_workflow.sql; tests/KArtSell.Integration.Tests/ApprovalWorkflow/ApprovalWorkflowTests.cs (new, 8 cases); tests/KArtSell.Integration.Tests/ApprovalWorkflowPolicyTests.cs (extended, 10 cases); TECH_DEBT_REGISTER.md DEBT-017/DEBT-023; commit a2e742c (original duplication, superseded) PM/BE Lead 🔴 CORRECTED 2026-08-08 — this row previously said 'Backend implementation + tests complete, 20/20 tests PASS' and that claim is misleading: TECH_DEBT_REGISTER.md DEBT-017 records that TWO independent ApprovalWorkflow implementations exist with colliding routes. The one this row's evidence links to and the one with 20/20 passing tests (`ApprovalWorkflow/`, Workstream H) has all 4 endpoints annotated `[DontRegister]` and is NOT reachable via HTTP. The implementation actually registered in `Program.cs` and reachable at runtime is `Features/ApprovalWorkflow/` (Workstream G) — grep found no dedicated test file exercising it. Net effect: the maker-checker approval gate that is actually live has not been shown to work, and the one that has been shown to work is dead code. Do not build frontend UI against either implementation, and do not re-mark this COMPLETED, until an architect decision (DEBT-017) picks the canonical implementation, deletes the other, and a test suite runs against whichever is kept. Renumbered from VS-03 to VS-26 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-03 in WBS_MASTER.csv ('IngestMarketDataPIT') is a separate, unrelated, still-unimplemented slice and keeps its original number unchanged. Also note: src/KArtSell.Host/Features/MarketData/VS03_*.cs is a THIRD, unrelated, already-implemented-and-tested body of work also labeled 'VS-03' (Market Data Ingestion Dashboard, commits 2bc2b1e/32b49a4) that is entirely absent from this tracker — see CURRENT_ROADMAP.md for a note to investigate and add it. 🟡 UPDATED 2026-08-08 — DEBT-017 architect decision made and executed: the duplicate `src/KArtSell.Modules.ModelOperations/ApprovalWorkflow/` (dead code, `[DontRegister]`'d, previously credited with the misleading '20/20 tests PASS') was DELETED along with its dedicated test file. `Features/ApprovalWorkflow/` (the implementation actually reachable over HTTP) is now the sole implementation and gained new Handler+Sql+real-Postgres integration tests covering create/approve/activate role gating, maker≠checker separation of duties, evidence attachment, and an explicit `DateOnly EffectiveAt` round-trip. While porting: found `Features/ApprovalWorkflow/Sql.cs InsertProposalAsync` had the same Dapper `DateOnly`-binding bug already fixed in the deleted implementation (commit 2ccf74c) — fixed identically here (`::date` cast + string parameter). Two residual (pre-existing, not introduced by this cleanup) gaps documented in the slice README rather than fixed: no `GET /approvals/{id}` endpoint, and no wired Draft→Proposed transition anywhere in the running app (approve/activate are therefore currently unreachable end-to-end via HTTP with real data). **Still BLOCKED, not COMPLETED, because no PostgreSQL was reachable in this session (127.0.0.1:5432 connection refused, no SSH tunnel open): `dotnet build -c Release` is clean, but `dotnet test --filter FullyQualifiedName~ApprovalWorkflow -c Release` shows only the 10 pure-Policy (no-DB) tests passing — all 8 new DB-backed integration tests fail with a connection error, unverified either way against a live database.** Do not mark COMPLETED until that run happens against a reachable Postgres and actually passes. Renumbered from VS-03 to VS-26 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-03 in WBS_MASTER.csv ('IngestMarketDataPIT') remains a separate, unrelated, still-unimplemented slice. Also still open: `src/KArtSell.Host/Features/MarketData/VS03_*.cs` is a THIRD, unrelated, already-implemented-and-tested body of work also labeled 'VS-03' that is entirely absent from this tracker — see CURRENT_ROADMAP.md.
21 AEG-VS-27-01 S2 VS-27 불변 감사 추적 구현 (Audit Trail / GDPR) COMPLETED 2026-08-07 docs/CURRENT/SLICE_SPECS/VS-27-SLICE_SPEC.md; src/KArtSell.Modules.ModelOperations/Compliance/ (AuditSql.cs, GdprRetention.cs); tests/KArtSell.Integration.Tests/Compliance/AuditTrailTests.cs; commit 97444c9 (Workstream I, PR #24, merged to main) PM/BE Lead ✅ Backend implementation + tests complete: append-only audit_events table, GDPR retention tracking + redaction (actor_email→'<redacted>', customer_id→'<purged>'), PII fields (ip_address). 5/5 tests PASS run in isolation (2026-08-07). Also fixed same day (fix/dapper-underscore-mapping-and-build branch): ip_address (inet) and kis_response-style jsonb columns threw InvalidCastException when read through Dapper into a typed class; GdprRetention.RetentionEndsAt was declared DateTime against a DATE column, same failure mode; and a process-wide Dapper snake_case-mapping race condition (KArtSell.BuildingBlocks' [ModuleInitializer] only fires once that assembly loads — AuditSql doesn't reliably touch it) intermittently nulled out every column read from this table depending on unrelated test/host startup order. None of this had ever been exercised against a live database before. ⚠️ No frontend UI yet — in progress 2026-08-08. Renumbered from VS-04 to VS-27 on 2026-08-08 per docs/DECISIONS/ADR-WBS-001-slice-renumbering.md — VS-04 in WBS_MASTER.csv ('ApplyCorporateActions') was an unrelated, still-unimplemented slice and keeps its original number unchanged.
22 AEG-VS-05-01 S3 VS-05 정책·범위·실패상태 계약 확정 PLANNED - - PM/Architect Not started. No docs/CURRENT/SLICE_SPECS/VS-05-SLICE_SPEC.md and no src/ implementation exist yet (confirmed 2026-08-07). Previous note referenced 'Job 976 (~50-90 days)' as if Phase 1 were actively running and counting down — that was false; see PHASE-1-SHADOW-RUN row below. Correct statement: blocked behind Gate 1 (Phase 1 shadow run), which itself has not been queued yet.
23 AEG-X-011 S4 Cross Golden vector 고도화 BLOCKED TBD AGENTS.md: Algorithm changes require Golden data Quant/QA Gate 2 prerequisite. Blocked by Phase 1 completion. Corrected 2026-08-07: Phase 1 (Job 893/976) has not been started, not 'in progress' — see PHASE-1-SHADOW-RUN row. No countdown is currently running.