# Exception Governance Migration Guide > **Imposed rule:** All exceptions must be time-bound, tenant-scoped, and auditable; legacy perpetual suppressions are prohibited after cutover. This guide explains how to migrate from legacy suppressions/notifications to the unified Exception Governance model in Excititor and Console. ## 1. What changes - **Unified exception object:** replaces ad-hoc suppressions. Fields: `tenant`, `scope` (purl/image/component), `vuln` (CVE/alias), `justification`, `expiration`, `owner`, `evidence_refs`, `policy_binding`, `status` (draft/staged/active/expired). - **Two-phase activation:** `draft → staged → active` with policy simulator snapshot; rollbacks produce a compensating exception marked `supersedes`. - **Notifications:** move from broad email hooks to route-specific notifications (policy events, expiring exceptions) using Notify service templates. - **Auditability:** each lifecycle change emits Timeline + Evidence Locker entries; exports include DSSE attestation of the exception set. ## 2. Migration phases 1. **Inventory legacy suppressions** - Export current suppressions and notification rules (per tenant) to NDJSON. - Classify by scope: package, image, repo, tenant-wide. 2. **Normalize and enrich** - Map each suppression to the unified schema; add `expiration` (default 30/90 days), `owner`, `justification` (use VEX schema categories when available). - Attach evidence references (ticket URL, VEX claim ID, scan report digest) where missing. 3. **Create staged exceptions** - Import NDJSON via Console or `stella exceptions import --stage` (CLI guide: `docs/modules/cli/guides/exceptions.md`). - Run policy simulator; resolve conflicts flagged by Aggregation-Only Contract (AOC) enforcement. 4. **Activate with guardrails** - Promote staged → active in batches; each promotion emits Timeline events and optional Rekor-backed attestation bundle (if Attestor is enabled). - Configure Notify templates for expiring exceptions (T‑14/T‑3 days) and denied promotions. 5. **Decommission legacy paths** - Disable legacy suppression writes; keep read-only for 30 days with banner noting deprecation. - Remove legacy notification hooks after confirming staged/active parity. ## 3. Data shapes - **Import NDJSON record (minimal):** `{ tenant, vuln, scope:{type:'purl'|'image'|'component', value}, justification, expiration, owner } - **Export manifest:** `{ generated_at, tenant, count, sha256, aoc_enforced, source:'migration-legacy-suppressions' }` - **Attestation (optional):** DSSE over exception set digest; stored alongside manifest in Evidence Locker. ## 4. Rollback plan - Keep legacy suppressions read-only for 30 days. - If a promotion batch causes regressions, mark affected exceptions `expired` and re-enable corresponding legacy suppressions for that tenant only. - Emit `rollback_notice` Timeline events and Notify operators. ## 5. Air-gap considerations - Imports/exports are file-based (NDJSON + manifest); no external calls required. - Verification uses bundled attestations; Rekor proofs are optional offline. - Console shows AOC badge when Aggregation-Only Contract limits apply; exports record `aoc=true`. ## 6. Checklists - [ ] All legacy suppressions exported to NDJSON per tenant. - [ ] Every exception has justification, owner, expiration. - [ ] Policy simulator run and results attached to exception batch. - [ ] Notify templates enabled for expiring/denied promotions. - [ ] Legacy write paths disabled; read-only banner present. - [ ] Attestation bundle stored (if Attestor available) and Evidence Locker entry created. ## 7. References - `docs/modules/excititor/architecture.md` - `docs/modules/excititor/implementation_plan.md` - `docs/modules/cli/guides/exceptions.md` - `docs/security/export-hardening.md` - `docs/policy/ui-integration.md`