mirror of
https://github.com/azaion/admin.git
synced 2026-06-21 20:01:08 +00:00
a77b3f8a59
Refreshes _docs/02_document/ to reflect the cycle-2 auth-modernization
+ CMMC hardening landings (AZ-531..AZ-538). Authoritative source for
the ripple set is ripple_log_cycle2.md.
Covered:
- architecture.md (section 1 rewritten, ADRs 6-9 added)
- data_model.md (sessions, audit_events, user columns, migrations)
- system-flows.md (F1 rewritten; F11-F17 added; F2/F7/F9 minor)
- module-layout.md (cycle-2 sub-component table)
- diagrams/flows/flow_login.md (dual-token + MFA)
- components/{01_data_layer,03_auth_and_security,05_admin_api}
- modules/ (12 new, 8 modified — full Argon2id/ES256/MFA/refresh
/mission/session/audit/jwks rollup)
- tests/{blackbox,security,traceability-matrix}
Step 13 (Update Docs) output for cycle 2.
Co-authored-by: Cursor <cursoragent@cursor.com>
236 lines
18 KiB
Markdown
236 lines
18 KiB
Markdown
# Traceability Matrix
|
|
|
|
## Acceptance Criteria Coverage
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AC-1 | Valid login returns JWT | FT-P-01, NFT-PERF-01, NFT-RES-01, NFT-RES-LIM-04 | Covered |
|
|
| AC-2 | Unknown email returns code 10 | FT-N-01 | Covered |
|
|
| AC-3 | Wrong password returns code 30 | FT-N-02 | Covered |
|
|
| AC-4 | JWT lifetime 4 hours | FT-P-03, NFT-SEC-04 | Covered |
|
|
| AC-5 | Email min 8 chars | FT-P-02, FT-N-03 | Covered |
|
|
| AC-6 | Email format validation | FT-P-02, FT-N-04 | Covered |
|
|
| AC-7 | Password min 8 chars | FT-P-02, FT-N-08 | Covered |
|
|
| AC-8 | Duplicate email returns code 20 | FT-N-07 | Covered |
|
|
| AC-9 | Only ApiAdmin can manage users | FT-P-06, FT-P-07, FT-P-11, FT-P-12, FT-P-13, NFT-SEC-02, NFT-SEC-06 | Covered |
|
|
| AC-10 | First hardware check stores | FT-P-04, NFT-RES-03 | Covered |
|
|
| AC-11 | Subsequent hardware check validates | FT-P-05, NFT-RES-03 | Covered |
|
|
| AC-12 | Hardware mismatch returns code 40 | FT-N-06 | Covered |
|
|
| AC-13 | Max upload 200 MB | FT-P-08, NFT-RES-LIM-01, NFT-RES-LIM-02, NFT-PERF-03 | Covered |
|
|
| AC-14 | AES-256-CBC encryption | FT-P-09, FT-P-10, NFT-PERF-02, NFT-PERF-03, NFT-RES-LIM-03 | Covered |
|
|
| AC-15 | Encrypt-decrypt round-trip | FT-P-10 | Covered |
|
|
| AC-16 | Empty file upload returns code 70 | FT-N-05 | Covered |
|
|
| AC-17 | SHA-384 password hashing | NFT-SEC-03 | Covered |
|
|
| AC-18 | All non-login endpoints require auth | FT-P-09, NFT-SEC-01, NFT-RES-02, NFT-RES-LIM-04 | Covered |
|
|
| AC-19 | Encryption key derived from email+password+hw | FT-P-10, NFT-SEC-05 | Covered |
|
|
|
|
## Restrictions Coverage
|
|
|
|
| Restriction ID | Restriction | Test IDs | Coverage |
|
|
|---------------|-------------|----------|----------|
|
|
| RESTRICT-SW-01 | .NET 10.0 runtime | All tests (implicit — Docker build uses .NET 10.0) | Covered |
|
|
| RESTRICT-SW-02 | PostgreSQL database | All DB tests (implicit — docker-compose uses PostgreSQL) | Covered |
|
|
| RESTRICT-SW-03 | Max request body 200 MB | NFT-RES-LIM-01, NFT-RES-LIM-02 | Covered |
|
|
| RESTRICT-SW-04 | JWT HMAC-SHA256 signing | FT-P-03, NFT-SEC-04 | Covered |
|
|
| RESTRICT-HW-01 | ARM64 target architecture | — | NOT COVERED — CI builds ARM64; tests run on dev x64 host |
|
|
| RESTRICT-ENV-01 | Secrets via env vars | All tests (implicit — docker-compose passes env vars) | Covered |
|
|
| RESTRICT-ENV-02 | CORS admin.azaion.com | — | NOT COVERED — CORS is browser-enforced, not testable at API level |
|
|
| RESTRICT-OP-01 | Serilog logging | — | NOT COVERED — log output verification not in scope |
|
|
|
|
## Coverage Summary
|
|
|
|
| Category | Total Items | Covered | Not Covered | Coverage % |
|
|
|----------|-----------|---------|-------------|-----------|
|
|
| Acceptance Criteria (baseline) | 19 | 19 | 0 | 100% |
|
|
| Acceptance Criteria (cycle 1) | 24 | 24 | 0 | 100% |
|
|
| Acceptance Criteria (cycle 2) | 6 | 6 | 0 | 100% |
|
|
| Restrictions | 8 | 5 | 3 | 63% |
|
|
| **Total** | **57** | **54** | **3** | **95%** |
|
|
|
|
## Uncovered Items Analysis
|
|
|
|
| Item | Reason Not Covered | Risk | Mitigation |
|
|
|------|-------------------|------|-----------|
|
|
| RESTRICT-HW-01 (ARM64) | Tests run on x64 dev/CI host; cross-architecture testing requires ARM hardware | Low — .NET runtime handles arch differences; no arch-specific code in application | CI builds ARM64 image; manual smoke test on target device |
|
|
| RESTRICT-ENV-02 (CORS) | CORS is enforced by browsers, not by server-to-server HTTP calls | Low — CORS policy is declarative in Program.cs | Visual inspection of CORS configuration in code |
|
|
| RESTRICT-OP-01 (Logging) | Log output format/content verification adds complexity without proportional value | Low — Serilog configuration is declarative | Code review of Serilog setup |
|
|
|
|
## Cycle 1 Additions (2026-05-13) — AZ-513, AZ-196, AZ-183, AZ-197
|
|
|
|
Appended during the existing-code cycle 1 Test-Spec Sync (autodev Step 12). Cycle 1 ACs are namespaced by their tracker ID to avoid colliding with the baseline AC-1..AC-19 numbering above.
|
|
|
|
### AZ-513 — Detection Classes CRUD
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-513 AC-1 | POST /classes creates a class | FT-P-14 | Covered |
|
|
| AZ-513 AC-2 | POST /classes requires ApiAdmin authorization | FT-N-09 | Covered |
|
|
| AZ-513 AC-3 | PATCH /classes/{id} updates an existing class (full body) | FT-P-15 | Covered |
|
|
| AZ-513 AC-4 | PATCH /classes/{id} accepts partial body (partial-merge) | FT-P-16 | Covered |
|
|
| AZ-513 AC-5 | PATCH /classes/{id} returns 404 for unknown id | FT-N-10 | Covered |
|
|
| AZ-513 AC-6 | PATCH /classes/{id} requires ApiAdmin authorization | FT-N-11 | Covered |
|
|
| AZ-513 AC-7 | DELETE /classes/{id} removes a class | FT-P-17 | Covered |
|
|
| AZ-513 AC-8 | DELETE /classes/{id} returns 404 for unknown id | FT-N-12 | Covered |
|
|
| AZ-513 AC-9 | DELETE /classes/{id} requires ApiAdmin authorization | FT-N-13 | Covered |
|
|
| AZ-513 AC-10 | UI add/delete/edit affordances work end-to-end | — | Cross-workspace (ui/ e2e harness) — out of scope for this workspace |
|
|
|
|
### AZ-196 — Device Auto-Registration
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-196 AC-1 | First device gets serial azj-0000 (shape: serial / email / 32-hex password) | FT-P-18 | Covered |
|
|
| AZ-196 AC-2 | Sequential numbering on subsequent calls | FT-P-19 | Covered |
|
|
| AZ-196 AC-3 | Persisted user has Role=CompanionPC, IsEnabled=true | FT-P-20 | Covered (verified via successful login → role-gated behaviour) |
|
|
| AZ-196 AC-4 | Returned plaintext password is hashed (SHA-384) in DB, not stored plaintext | FT-P-20 | Covered (verified via successful login round-trip) |
|
|
| AZ-196 AC-5 | Requires ApiAdmin authorization | FT-N-14 | Covered |
|
|
|
|
### AZ-183 — Resources OTA Update Check (REVERTED post-cycle-1)
|
|
|
|
The OTA Update Check & Publish feature shipped in cycle 1 was reverted later the same day after the security audit (finding F-1: `/get-update` disclosed plaintext per-resource encryption keys to any authenticated caller; the OTA delivery model itself was deemed obsolete in the target architecture). The endpoints, service, entity, table, request DTOs, response DTO, cache key, master-key config field, and the e2e test class `ResourceUpdateTests` were all removed.
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Status |
|
|
|-------|---------------------|----------|--------|
|
|
| AZ-183 AC-1 | Resources table created with required columns | — | **Reverted** — table dropped from migration set (`env/db/05_resources.sql` deleted) |
|
|
| AZ-183 AC-2 | POST /get-update returns newer resources | ~~FT-P-21~~ | **Reverted** — endpoint and test deleted |
|
|
| AZ-183 AC-3 | POST /get-update returns empty when device already current | ~~FT-P-22~~ | **Reverted** — endpoint and test deleted |
|
|
| AZ-183 AC-4 | Memory cache avoids DB pressure under 2000-device polling | — | **Reverted** — cache key removed |
|
|
| AZ-183 AC-5 | Cache invalidated on CI/CD publish | ~~FT-P-23~~ | **Reverted** — endpoint and test deleted |
|
|
|
|
### AZ-197 — Hardware-Binding Removal
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-197 AC-1 | Resource download works without `Hardware` field | FT-P-09 / FT-P-10 (legacy bodies retained; wire shape now omits the field) | Covered (e2e `ResourceTests` updated by AZ-197 batch 6) |
|
|
| AZ-197 AC-2 | `PUT /users/hardware/set` and `POST /resources/check` return 404 | FT-N-15 | Covered |
|
|
| AZ-197 AC-3 | `Security.GetApiEncryptionKey` signature simplified to (email, password) | — | Internal signature — covered by `Azaion.Test/SecurityTest` unit tests, not blackbox |
|
|
| AZ-197 AC-4 | `HardwareBindingTests` removed; no remaining test asserts code 40 / hardware-hash binding | — | Build/CI invariant — verified by test-suite enumeration |
|
|
| AZ-197 AC-5 | Resource calls in remaining tests do not send `Hardware` | — | Build/CI invariant — verified by source review during AZ-197 batch 6 |
|
|
| AZ-197 AC-6 | `ExceptionEnum` no longer carries `HardwareIdMismatch` / `BadHardware` | — | Build/CI invariant — verified by enum read |
|
|
| AZ-197 AC-7 | `dotnet build` is clean (no new warnings) | — | Build invariant |
|
|
| AZ-197 AC-8 | Test suite passes (excluding deleted `HardwareBindingTests`) | All e2e tests + `Azaion.Test` | Covered by Step 11 Run Tests (48/48 e2e + 2/2 unit, 2026-05-13) |
|
|
|
|
### Obsoleted Baseline Entries (superseded by AZ-197)
|
|
|
|
The matrix rows below are kept for ID stability but no longer reflect production behaviour. They are superseded by the AZ-197 entries above and by FT-N-15 in `blackbox-tests.md`. Do NOT regenerate or delete these in cycle-update mode — wait for a full `/test-spec` rerun.
|
|
|
|
| Legacy Matrix Row | Status |
|
|
|-------------------|--------|
|
|
| AC-10 (First hardware check stores) | Obsoleted by AZ-197 — endpoint removed |
|
|
| AC-11 (Subsequent hardware check validates) | Obsoleted by AZ-197 — endpoint removed |
|
|
| AC-12 (Hardware mismatch returns code 40) | Obsoleted by AZ-197 — `ExceptionEnum` value removed |
|
|
| AC-19 (Encryption key derived from email+password+hw) | Partially obsoleted — derivation is now `email + password` only |
|
|
|
|
## Cycle 2 Cleanup (2026-05-14) — Obsolete Resource Endpoints Removed
|
|
|
|
The encrypted-download and installer-download endpoints were removed as obsolete. Affected matrix rows below are kept for ID stability but the underlying behaviour is gone; they are superseded by FT-N-16 in `blackbox-tests.md`.
|
|
|
|
| Removed surface | Endpoint(s) | Affected legacy entries | Status |
|
|
|-----------------|-------------|-------------------------|--------|
|
|
| Per-user encrypted resource download | `POST /resources/get/{dataFolder?}` | AC-14 (AES-256-CBC encryption), AC-15 (round-trip), AC-19 (key derivation), FT-P-09, FT-P-10 | **Reverted** — endpoint deleted; `Security.GetApiEncryptionKey` / `EncryptTo` / `DecryptTo` and `ResourcesService.GetEncryptedResource` deleted; `GetResourceRequest` DTO deleted; e2e tests `Encrypted_download_returns_octet_stream_and_non_empty_body` and `Encryption_round_trip_decrypt_matches_original_bytes` deleted from `ResourceTests.cs`; e2e test `Per_user_encryption_produces_distinct_ciphertext_for_same_file` deleted from `SecurityTests.cs`; `Azaion.Test/SecurityTest.cs` deleted (and the now-empty `Azaion.Test` project removed from the solution). |
|
|
| Installer download (production + staging) | `GET /resources/get-installer`, `GET /resources/get-installer/stage` | AC-23 (latest installer), `ResourcesConfig.SuiteInstallerFolder` / `SuiteStageInstallerFolder` references | **Reverted** — endpoints deleted; `ResourcesService.GetInstaller` deleted; both config properties removed from `appsettings.json`, `.env.example`, `secrets/staging.public.env`, `secrets/production.public.env`, and `docker-compose.test.yml`. No e2e tests had been written for these endpoints, so no tests required removal. |
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| Cycle-2 AC-1 | `POST /resources/get/{dataFolder?}` returns 404 | FT-N-16 | Covered |
|
|
| Cycle-2 AC-2 | `GET /resources/get-installer` returns 404 | FT-N-16 | Covered |
|
|
| Cycle-2 AC-3 | `GET /resources/get-installer/stage` returns 404 | FT-N-16 | Covered |
|
|
| Cycle-2 AC-4 | `ExceptionEnum` no longer carries `WrongResourceName` (50); the gap is preserved | — | Build/CI invariant — verified by enum read |
|
|
| Cycle-2 AC-5 | `Azaion.Test` project no longer in solution; build is clean | — | Build invariant — `dotnet build Azaion.AdminApi.sln` clean post-cleanup |
|
|
| Cycle-2 AC-6 | E2E suite passes after the test deletions above | All e2e tests | Covered by Step 11 Run Tests post-cleanup (2026-05-14) |
|
|
|
|
## Cycle 2 Additions (2026-05-14) — Auth Modernization (AZ-529 + AZ-530)
|
|
|
|
Appended during the existing-code cycle 2 Test-Spec Sync (autodev Step 12) for the eight tasks delivered by the auth-modernization + CMMC-hardening epics. Rows below are namespaced by tracker ID; functional scenarios live in `blackbox-tests.md`, security-only invariants in `security-tests.md`. Existing AC/test IDs from earlier cycles are preserved unchanged.
|
|
|
|
### AZ-536 — Argon2id Password Hashing (epic AZ-530, 5 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-536 AC-1 | New users get Argon2id hashes (PHC, m ≥ 64 MiB, t ≥ 3, p ≥ 1) | NFT-SEC-07 | Covered |
|
|
| AZ-536 AC-2 | Legacy SHA-384 hashes still validate | FT-P-24 | Covered |
|
|
| AZ-536 AC-3 | Successful legacy login transparently re-hashes to Argon2id | FT-P-25 | Covered |
|
|
| AZ-536 AC-4 | Wrong password fails for both formats with the same error code | FT-N-17 | Covered |
|
|
| AZ-536 AC-5 | Verify is constant-time (no remotely observable timing leak) | NFT-SEC-08 | Covered (with known suite-concurrency flake — see cycle-2 carry-forward F6) |
|
|
|
|
### AZ-537 — /login Rate Limit + Account Lockout (epic AZ-530, 6 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-537 AC-1 | Per-IP rate limit triggers HTTP 429 with `Retry-After` | NFT-SEC-09 | Covered (legitimate environment-mismatch skip in shared-IP container env) |
|
|
| AZ-537 AC-2 | Per-account rate limit triggers HTTP 429 across IPs | NFT-SEC-10 | Covered |
|
|
| AZ-537 AC-3 | Account lockout after 10 failures returns 423 even on correct password | NFT-SEC-11 | Covered |
|
|
| AZ-537 AC-4 | Successful login resets `failed_login_count` and clears `lockout_until` | FT-P-26 | Covered |
|
|
| AZ-537 AC-5 | Lockout auto-expires after configured duration | FT-P-27 | Covered |
|
|
| AZ-537 AC-6 | Audit-log entry written on each lockout event | NFT-SEC-12 | Covered |
|
|
|
|
### AZ-538 — CORS HTTPS-Only + HSTS (epic AZ-530, 5 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-538 AC-1 | HTTP origin gets no `Access-Control-Allow-Origin` header | NFT-SEC-13 | Covered |
|
|
| AZ-538 AC-2 | HTTPS origin preflight echoes credentials flag | FT-P-28 | Covered |
|
|
| AZ-538 AC-3 | HSTS header present in production responses | NFT-SEC-14 | Covered (legitimate Production-only environment-mismatch skip in dev test harness — verified by code inspection of `Program.cs UseHsts`) |
|
|
| AZ-538 AC-4 | HTTP request returns 307 to HTTPS in production | NFT-SEC-15 | Covered (legitimate Production-only environment-mismatch skip in dev test harness — verified by code inspection of `Program.cs UseHttpsRedirection`) |
|
|
| AZ-538 AC-5 | Development env unchanged (no redirect, no HSTS) | FT-P-29 | Covered |
|
|
|
|
### AZ-531 — Refresh-Token Flow (epic AZ-529, 5 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-531 AC-1 | `/login` returns dual tokens, session row persisted | FT-P-30 | Covered |
|
|
| AZ-531 AC-2 | `/token/refresh` rotates refresh + chains via `parent_session_id` | FT-P-31 | Covered |
|
|
| AZ-531 AC-3 | Reuse-detection kills the entire session family | NFT-SEC-16 | Covered |
|
|
| AZ-531 AC-4 | Sliding window + 12 h absolute family expiry | FT-P-32 | Covered |
|
|
| AZ-531 AC-5 | Refresh tokens are opaque, hashed at rest, never logged in raw form | NFT-SEC-17 | Covered |
|
|
|
|
### AZ-532 — Asymmetric Signing + JWKS (epic AZ-529, 5 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-532 AC-1 | Access tokens carry `alg=ES256` + `kid` | NFT-SEC-18 | Covered |
|
|
| AZ-532 AC-2 | `GET /.well-known/jwks.json` serves the active public key with cache headers | FT-P-33 | Covered |
|
|
| AZ-532 AC-3 | Two-key overlap during rotation (both JWKS entries valid) | FT-P-34 | Covered |
|
|
| AZ-532 AC-4 | JWKS never exposes private material | NFT-SEC-19 | Covered |
|
|
| AZ-532 AC-5 | alg-confusion forgery (HS256 with public key as secret) is rejected | NFT-SEC-20 | Covered |
|
|
|
|
### AZ-533 — Mission-Token Issuance for UAV (epic AZ-529, 6 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-533 AC-1 | Mission token issued with correct lifetime (`planned_duration_h + 1h`) | FT-P-35 | Covered |
|
|
| AZ-533 AC-2 | Hard cap of 12 h enforced (HTTP 400 with cap message) | FT-N-19 | Covered |
|
|
| AZ-533 AC-3 | Mission token carries `mission_id`, `aircraft_id`, `aud`, `permissions`, `sid`, `jti` | FT-P-36 | Covered |
|
|
| AZ-533 AC-4 | Mission session auto-revoked when aircraft user reconnects | FT-P-37 | Covered |
|
|
| AZ-533 AC-5 | Endpoint requires authenticated session | FT-N-18 | Covered |
|
|
| AZ-533 AC-6 | MFA step-up required (`amr` must include `mfa`) | NFT-SEC-21 | **Spec only** — pending wire-up post-AZ-534 (cycle-2 carry-forward F1) |
|
|
|
|
### AZ-534 — TOTP-Based 2FA at Login (epic AZ-529, 6 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-534 AC-1 | Enrollment returns secret + QR + 10 recovery codes | FT-P-38 | Covered |
|
|
| AZ-534 AC-2 | Confirm with valid TOTP completes enrollment | FT-P-39 | Covered |
|
|
| AZ-534 AC-3 | Two-step `/login` → `/login/mfa` flow; access-token `amr=["pwd","mfa"]` | FT-P-40 | Covered |
|
|
| AZ-534 AC-4 | Recovery code substitutes for TOTP and is single-use | FT-P-41 | Covered |
|
|
| AZ-534 AC-5 | Disable requires password + valid TOTP | FT-P-42 | Covered |
|
|
| AZ-534 AC-6 | TOTP secret encrypted at rest in `users.mfa_secret` | NFT-SEC-22 | Covered |
|
|
|
|
### AZ-535 — Logout + Revocation Surface (epic AZ-529, 5 ACs)
|
|
|
|
| AC ID | Acceptance Criterion | Test IDs | Coverage |
|
|
|-------|---------------------|----------|----------|
|
|
| AZ-535 AC-1 | `POST /logout` revokes the current session and kills refresh | FT-P-43 | Covered |
|
|
| AZ-535 AC-2 | `POST /logout/all` revokes every session for the user | FT-P-44 | Covered |
|
|
| AZ-535 AC-3 | Admin can revoke any session by id; row records actor | FT-P-45 | Covered |
|
|
| AZ-535 AC-4 | `GET /sessions/revoked?since=…` returns recent, non-expired entries | FT-P-46 | Covered |
|
|
| AZ-535 AC-5 | `POST /logout` is idempotent (no second DB write) | FT-P-47 | Covered |
|
|
|
|
## Cycle 2 Coverage Update
|
|
|
|
| Category | Total Items | Covered | Not Yet Wired | Coverage % |
|
|
|----------|-----------|---------|---------------|-----------|
|
|
| Acceptance Criteria (cycle 2 — auth modernization) | 43 | 42 | 1 (AZ-533 AC-6 — pending wire-up F1) | 98% |
|
|
| Acceptance Criteria — combined total (baseline + cycle 1 + cycle 2 cleanup + cycle 2 auth) | 100 | 96 | 1 (F1) + 3 baseline restrictions still uncovered | 96% |
|
|
|
|
The single uncovered cycle-2 AC (AZ-533 AC-6) is documented in the cycle-2 implementation report as carry-forward item F1 — the `/sessions/mission` `amr=mfa` enforcement was deferred during AZ-533, became implementable once AZ-534 shipped, and is filed as a follow-up ticket to be picked up in a later cycle.
|