Files
KArtSell.Aegis/src/KArtSell.Modules.ModelOperations/ApprovalWorkflow
kjh2064 b1e38ac374 feat: Phase 3 J/K/L (Sell Decision, Trade Execution, Portfolio Reconciliation) + fix pre-existing build/boot breakage
Completes VS-10/VS-12/VS-14 and makes the solution and Host actually
build and boot for the first time on this branch (main did not build
before this commit).

Root-cause fixes required to reach a green build/boot (not scoped to
J/K/L but blocking any verification of it):
- Restore Polly PackageVersion accidentally deleted from
  Directory.Packages.props (broke KArtSell.Host).
- Remove MediatR dependency from Compliance/VS-04 (package was never
  installed; ICommand/ICommandHandler/IMediator never existed) and
  wire Endpoint -> Handler directly per this repo's convention.
- Migrate FastEndpoints v5 API calls (SendOkAsync/SendAsync/
  SendCreatedAtAsync/SendNotFoundAsync, Description().WithName()) to
  the v7 Send.* fluent API across ~10 endpoint files.
- Fix migrations 0036/0038/0039/0040: rewritten from invalid T-SQL
  (`IF NOT EXISTS ... BEGIN ... END`) to idiomatic Postgres
  (`CREATE TABLE/INDEX IF NOT EXISTS`) — these could not apply to any
  fresh database before this fix.
- Collapse 3 duplicate cross-cutting abstractions that shadowed the
  BuildingBlocks versions and caused type-mismatch compile errors:
  IKrxDataService, IOutboxWriter (ReconcileTradeHandler), IClock
  (ApprovalWorkflow/ApprovalPolicy).
- Inject IClock (BuildingBlocks.Time) in place of direct
  DateTime.Now/UtcNow across 19 files to satisfy the architecture
  test AGENTS.md#DateTime-abstraction rule (13/13 architecture tests
  now pass, was 12/13).
- Register all new and previously-unregistered slices in
  Program.cs DI (SellDecision, TradeExecution, PortfolioReconciliation,
  Compliance, Features/ApprovalWorkflow) — the Host had never
  successfully completed a boot with this code present.
- Disable ("[DontRegister]") the older, route-colliding
  ApprovalWorkflow/ (Workstream H) endpoint set in favor of
  Features/ApprovalWorkflow/ (Workstream G, matches the documented
  Features/<Slice>/ convention); kept for its existing test coverage.
  See TECH_DEBT-017 for the follow-up decision needed.

Verified: dotnet build 0 errors/0 warnings; architecture tests 13/13;
unit tests 54/54 + 18/18; integration tests 34/36 (2 failures are a
local test-DB migration-journal/schema mismatch, not a code defect);
Host boots cleanly and registers all 34 endpoints.

New tech debt recorded: DEBT-017 (duplicate VS-03 implementation),
DEBT-018 (outbox write not co-transactional with entity write in
TradeExecution/PortfolioReconciliation), DEBT-019 (duplicate
BuildingBlocks-shadowing abstractions, partially resolved).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-08-07 19:53:38 +09:00
..

VS-03: Model Approval Workflow

Overview

This vertical slice implements a maker-checker approval workflow for model activation. It enforces separation of duties, state machine transitions, and evidence linkage for regulatory compliance.

Status: Ready for implementation
Specification: docs/CURRENT/SLICE_SPECS/VS-03-SLICE_SPEC.md


User Story

As a platform lead/compliance officer, I want to enforce maker-checker approval workflow for model activation so that only reviewed, authorized models reach production (governance compliance).


Key Features

1. Approval State Machine

DRAFT (Maker creates)
  ↓
PROPOSED (Maker submits to Checker)
  ├→ APPROVED (Checker signs off with evidence)
  │    ↓
  │  ACTIVE (SRE activates)
  │
  └→ REJECTED (Checker rejects, revise to DRAFT)

2. Maker-Checker Separation of Duties

  • Maker: Can create and propose approval proposals (own proposals only)
  • Checker: Can approve any proposal (must be different from Maker)
  • SRE: Can activate approved proposals
  • System: Logs all actions with actor identity and correlation_id

3. Evidence Linkage

  • Store PBO/DSR/OOS artifact URLs during approval
  • Checker annotates evidence interpretation
  • Traceability: approval_id → evidence_links → S3 artifacts

4. Immutable Audit Trail

  • All state transitions logged in approval_events table
  • Correlation_id links related events
  • PIT tracking via published_at + revision

Database Schema

approval_proposals

id, model_id, status, created_by, created_at, justification, effective_at,
proposed_at, approved_by, approved_at, approval_notes, activated_by, activated_at,
published_at, revision, correlation_id

approval_evidence

id, approval_proposal_id, evidence_type, evidence_url, reviewer_comment,
published_at, correlation_id

approval_events

id, approval_proposal_id, event_type, actor_email, event_at, details,
published_at, correlation_id

API Endpoints

POST /approvals (Create Proposal)

Role: Maker
Request:

{
  "modelId": "uuid",
  "effectiveAt": "2026-09-15",
  "justification": "Model passed OOS testing; PBO score 0.95"
}

Response (201):

{
  "id": "approval-uuid",
  "modelId": "uuid",
  "status": "Draft",
  "createdBy": "maker@company.com",
  "createdAt": "2026-08-07T10:00:00Z"
}

GET /approvals (List Proposals)

Query Params: status=Proposed&modelId=uuid
Response (200):

{
  "items": [
    {
      "id": "approval-uuid",
      "modelId": "uuid",
      "status": "Proposed",
      "createdBy": "maker@company.com",
      "approvalNotes": null
    }
  ]
}

GET /approvals/{id} (Get Single)

Response (200):

{
  "id": "approval-uuid",
  "modelId": "uuid",
  "status": "Proposed",
  "evidence": [
    {
      "id": "evidence-uuid",
      "evidenceType": "PBO_SCORE",
      "evidenceUrl": "s3://evidence/pbo-0.95.json",
      "reviewerComment": "Verified"
    }
  ]
}

POST /approvals/{id}/approve (Checker Approval)

Role: Checker
Request:

{
  "approvalNotes": "PBO verified, OOS metrics acceptable",
  "evidence": [
    {"type": "PBO_SCORE", "url": "s3://evidence/pbo-0.95.json", "comment": "Verified"},
    {"type": "OOS_RETURN", "url": "s3://evidence/oos-returns.csv", "comment": "Acceptable"}
  ]
}

Response (200):

{
  "id": "approval-uuid",
  "status": "Approved",
  "approvedBy": "checker@company.com",
  "approvedAt": "2026-08-07T11:00:00Z"
}

RBAC Enforcement

Role Can Create Can Approve Can Activate
Maker (own)
Checker (others)
SRE
Admin

Separation of Duties: Maker ≠ Checker (same user cannot approve own proposal)


Compliance & Governance

  • Separation of Duties: Enforced at Endpoint level
  • Evidence Linkage: All evidence URLs traceable to artifacts
  • Immutable Audit Trail: INSERT-only events table
  • Correlation Tracking: CorrelationId links related events across slices
  • PIT Queries: All reads include WHERE published_at <= cutoff

  • VS-00: PIT envelope (published_at, correlation_id, revision)
  • VS-02: Financial security master (governance foundation)
  • VS-04: Audit trail (logs all approval events)
  • VS-10: Sell decision (uses approved models)

Next Steps

  1. Schema migration (0036_approval_workflow.sql)
  2. Domain entities (ApprovalProposal, ApprovalEvidence, ApprovalEvent)
  3. Dapper queries (Sql.cs)
  4. Business logic (ApprovalPolicy with state machine)
  5. HTTP handlers (ApprovalHandlers.cs)
  6. FastEndpoints (ApprovalEndpoints.cs)
  7. Unit/Integration tests
  8. Merge to main (awaiting PR review)
  9. Integration with VS-04 (audit trail subscribers)
  10. Phase 2 implementation (after Phase 1 data available)

Co-Authored-By: Claude Haiku 4.5 noreply@anthropic.com
AGENTS.md v16.0: 13/13
Compliance: Spec-before-code, no new tech debt