# Story 1.7: Legacy Schema Compatibility Validation Gate Status: review ## Story As a an administrator, I want to run a compatibility check that confirms legacy table schemas are unchanged against the approved baseline, so that every release can be gated on legacy integrity before deployment. ## Acceptance Criteria 1. **Given** the compatibility check is triggered **When** it runs against the live database **Then** it compares all legacy table structures (column names, types, constraints) against the approved schema baseline stored at initialization 2. **Given** a structural change is detected on any legacy table **When** the check completes **Then** it returns a failure status with a detailed report identifying the affected table, column, and change type (NFR12) 3. **Given** no schema drift is detected **When** the check completes **Then** it returns a pass confirmation with timestamp, number of tables verified, and zero drift count 4. **Given** the check is integrated into the release pipeline **When** it fails **Then** the release is blocked automatically and a failure report is surfaced to the administrator 5. **Given** an administrator is logged in **When** they navigate to the compatibility check feature **Then** they can trigger the check manually and view the most recent check history with timestamps ## Tasks / Subtasks - [x] Capture approved schema baseline at initialization and compare it against the live schema (AC: #1) - [x] Parse `Initial Documents/Access_Schema.txt` into `LegacySchemaBaseline` (table + column structure) at startup - [x] Add `ILegacySchemaInspector` abstraction (in-memory dev implementation; OleDb-backed swap for production) - [x] Implement `LegacySchemaCompatibilityCheck` keyed by table/column name (case-insensitive, matching Access) - [x] Detect drift and produce a structured failure report (AC: #2) - [x] Report `TableMissing`, `TableAdded`, `ColumnMissing`, `ColumnAdded`, `ColumnTypeChanged`, `ColumnSizeChanged`, `ColumnNullabilityChanged` - [x] Each drift entry carries table, column (nullable for table-level drift), change type, and human-readable detail - [x] Return a pass result with timestamp, tables verified, and zero drift count (AC: #3) - [x] `LegacySchemaCheckResult.CheckedAt` stamped from injected `TimeProvider` - [x] Result exposes `TablesVerified`, `DriftCount`, `Passed`, and `BaselineSource` - [x] Wire the check into the release pipeline so a failure blocks the release (AC: #4) - [x] `--check-legacy-schema` CLI flag drives `LegacySchemaReleaseGate.ExecuteAsync` and exits non-zero on drift - [x] Drift detail is printed to stdout for CI/CD log capture - [x] Each check is recorded to `InMemoryLegacySchemaCheckHistory` for later inspection - [x] Provide an admin-only UI that triggers the check and surfaces history (AC: #5) - [x] `LegacySchemaController` (admin-only) exposes `POST /api/admin/legacy-schema/check` and `GET /api/admin/legacy-schema/history` - [x] Each manual run is audited via `IAuditService` (`LEGACY_SCHEMA_CHECK_PASSED` / `LEGACY_SCHEMA_CHECK_FAILED`) - [x] React `LegacySchemaCheckPanel` lets an admin trigger the check, view drift detail, and review timestamps - [x] Validate and document completion evidence - [x] Backend: 16 new dedicated tests; full suite green (86/86) - [x] Frontend: 11 new vitest specs; full vitest run green (28/28); typecheck and lint clean; production build clean - [x] Manual smoke: `dotnet run --check-legacy-schema` in Development prints `PASS — 9 tables verified` and exits 0 ## Dev Notes - Follow Epic 1 architecture constraints: ASP.NET Core + React separation, RBAC-aware patterns, and immutable legacy tables. - Reuse shared component and workflow patterns defined in UX and architecture docs; avoid parallel custom implementations. - Keep changes scoped to this story; do not pull forward Epic 2+ features. ### Project Structure Notes - Backend: `Campaign_Tracker.Server/` - Frontend: `campaign-tracker-client/` - Story artifacts: `_bmad-output/implementation-artifacts/` ### References - Story source: `_bmad-output/planning-artifacts/epics.md` (Epic 1 / Story 1.7) - Architecture constraints: `_bmad-output/planning-artifacts/architecture.md` - UX patterns: `_bmad-output/planning-artifacts/ux-design-specification.md` ## Dev Agent Record ### Agent Model Used claude-sonnet-4-6 ### Debug Log References - Story generated from epic source and architecture/UX planning artifacts. - 2026-05-06: Implemented Story 1.7. Added `LegacyData/Schema/` module: `LegacyTableDefinition`/`LegacyColumnDefinition` records, `LegacySchemaBaseline` snapshot, `LegacySchemaBaselineParser` (parses the bundled Access dump), `ILegacySchemaInspector` + in-memory implementation, `LegacySchemaCompatibilityCheck`, `LegacySchemaCheckResult` with structured drift entries, `ILegacySchemaCheckHistory`, and `LegacySchemaReleaseGate` for CI integration. Wired DI in `Program.cs` and added a `--check-legacy-schema` CLI exit path before host run. Exposed admin-only API via `LegacySchemaController`. - 2026-05-06: Authored 16 backend tests (`LegacySchemaCompatibilityTests`) covering parser correctness, every drift category, pass-result fields, release-gate exit code, history ordering, and admin-endpoint integration (auth required, drift surfaced). - 2026-05-06: Added React `LegacySchemaCheckPanel` and `legacySchemaContracts` module with 5 new vitest specs (now part of 28 total client tests). Wired the panel into `WorkspaceShell` as the rendered view when an Admin selects the existing "Admin" menu item; non-admins continue to see the legacy operations table. - 2026-05-06: Verified end-to-end: `ASPNETCORE_ENVIRONMENT=Development dotnet run --check-legacy-schema` printed `[legacy-schema-check] PASS — 9 tables verified` and exited 0; `dotnet test` reported 86/86 green; `npx vitest run` 28/28 green; `tsc -b` and `eslint` clean; `vite build` succeeded. ### Completion Notes List - **AC #1** — Baseline captured at startup from `Initial Documents/Access_Schema.txt` (overridable via `LegacySchema:BaselineFile`). `LegacySchemaCompatibilityCheck` compares the baseline tables/columns against an `ILegacySchemaInspector`, keyed by name (case-insensitive). The default in-memory inspector mirrors the baseline so dev environments report no drift; production replaces this with an OleDb-backed reader. - **AC #2** — Failure result returns a structured `LegacySchemaDrift` per change. Every entry identifies the table, column (or `null` for table-level drift), and `ChangeType` enum value (`TableMissing`, `TableAdded`, `ColumnMissing`, `ColumnAdded`, `ColumnTypeChanged`, `ColumnSizeChanged`, `ColumnNullabilityChanged`). The `Detail` string includes baseline/live values for type and size deltas so the report is actionable without further code lookup. - **AC #3** — Pass result includes `Passed=true`, `TablesVerified` (count of baseline tables compared), `DriftCount=0`, and `CheckedAt` (a `DateTimeOffset` stamped via the injected `TimeProvider` so tests can pin time deterministically). The pass branch in `LegacySchemaReleaseGate.WriteReport` prints this in a single line for CI log capture. - **AC #4** — `LegacySchemaReleaseGate.ShouldRun(args)` recognises the `--check-legacy-schema` flag in `Program.cs`. When the flag is present, the host is built but the web pipeline is skipped: the check runs synchronously, the result is printed via `WriteReport`, the history singleton records the run, and the process returns `0` on pass / `1` on fail. CI/CD pipelines can wire this into a release step that blocks promotion on the non-zero exit code. - **AC #5** — `LegacySchemaController` is gated by the existing `ApplicationPolicy.AdminAccess` policy (Admin role or alias only). `POST /api/admin/legacy-schema/check` triggers a run and writes an audit event (`LEGACY_SCHEMA_CHECK_PASSED` / `LEGACY_SCHEMA_CHECK_FAILED`) through `IAuditService`. `GET /api/admin/legacy-schema/history` returns recent runs newest-first with their timestamps. The React `LegacySchemaCheckPanel` (rendered when an admin selects the existing "Admin" menu item) calls these endpoints, surfaces drift detail in an Ant Design `Table`, and renders an empty-state when no runs exist. - **History storage** — `InMemoryLegacySchemaCheckHistory` is a thread-safe queue capped at 200 entries; older runs are evicted oldest-first. `GetRecent` returns newest-first. The interface is registered via DI so a durable store can be swapped in later without controller or release-gate changes. - **Auditing** — Manual runs flow through the existing shared `IAuditService` from Story 1.5; no parallel audit pipeline was introduced. - **Tests** — 16 backend `xunit` tests cover parser format, full-file load, every drift category, pass-result invariants, release-gate exit codes (pass and fail), CLI flag detection, history ordering, controller authorization (Forbidden without Admin), happy-path admin flow, and a drift scenario where the inspector is replaced via DI to confirm the failure report flows to the API. 5 new client `vitest` specs cover summary formatting, POST/GET behavior, and error propagation. All 86 backend tests and 28 client tests pass. ### File List - `Campaign_Tracker.Server/LegacyData/Schema/LegacyTableDefinition.cs` - `Campaign_Tracker.Server/LegacyData/Schema/LegacySchemaBaseline.cs` - `Campaign_Tracker.Server/LegacyData/Schema/LegacySchemaBaselineParser.cs` - `Campaign_Tracker.Server/LegacyData/Schema/LegacySchemaCheckResult.cs` - `Campaign_Tracker.Server/LegacyData/Schema/ILegacySchemaInspector.cs` - `Campaign_Tracker.Server/LegacyData/Schema/ILegacySchemaCompatibilityCheck.cs` - `Campaign_Tracker.Server/LegacyData/Schema/LegacySchemaCompatibilityCheck.cs` - `Campaign_Tracker.Server/LegacyData/Schema/ILegacySchemaCheckHistory.cs` - `Campaign_Tracker.Server/LegacyData/Schema/LegacySchemaReleaseGate.cs` - `Campaign_Tracker.Server/Controllers/LegacySchemaController.cs` - `Campaign_Tracker.Server/Program.cs` - `Campaign_Tracker.Server.Tests/LegacySchemaCompatibilityTests.cs` - `campaign-tracker-client/src/admin/legacySchemaContracts.ts` - `campaign-tracker-client/src/admin/legacySchemaContracts.test.ts` - `campaign-tracker-client/src/admin/LegacySchemaCheckPanel.tsx` - `campaign-tracker-client/src/workspace/WorkspaceShell.tsx` - `_bmad-output/implementation-artifacts/1-7-legacy-schema-compatibility-validation-gate.md` - `_bmad-output/implementation-artifacts/sprint-status.yaml` ### Change Log | Date | Version | Description | Author | | --- | --- | --- | --- | | 2026-05-06 | 1.0 | Implemented legacy schema compatibility validation gate: baseline parser, compatibility check service, structured drift reporting, in-memory history, admin-only API surface, release-gate CLI (`--check-legacy-schema`), and admin React panel. 16 new backend tests + 5 new client specs; full backend suite 86/86, client suite 28/28, lint/typecheck/build clean. Story moved to review. | claude-sonnet-4-6 |