TenantAtlas/specs/284-provider-neutral-artifact-source-taxonomy/tasks.md
ahmido 75ebade345 feat: implement provider-neutral artifact source taxonomy (#343)
## Summary

Implements Spec 284 for provider-neutral artifact source taxonomy.

- add shared artifact source descriptor, resolver, taxonomy, and provider-detail support
- update findings, evidence snapshots, stored reports, inventory items, and tenant review surfaces to disclose descriptor-first artifact summaries
- add bounded Pest unit, feature, guard, and browser coverage for the taxonomy slice
- include the completed Spec 284 package artifacts under `specs/284-provider-neutral-artifact-source-taxonomy/`

## Notes

- branch: `284-provider-neutral-artifact-source-taxonomy`
- commit: `bf8d59e0`
- this PR was created as part of the requested commit/push/PR flow against `platform-dev`

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #343
2026-05-08 23:47:31 +00:00

244 lines
24 KiB
Markdown

---
description: "Task list for Provider-neutral Artifact Source Taxonomy"
---
# Tasks: Provider-neutral Artifact Source Taxonomy
**Input**: Design documents from `specs/284-provider-neutral-artifact-source-taxonomy/`
**Prerequisites**: `specs/284-provider-neutral-artifact-source-taxonomy/spec.md`, `specs/284-provider-neutral-artifact-source-taxonomy/plan.md`, `specs/284-provider-neutral-artifact-source-taxonomy/checklists/requirements.md`, `specs/284-provider-neutral-artifact-source-taxonomy/research.md`, `specs/284-provider-neutral-artifact-source-taxonomy/data-model.md`, `specs/284-provider-neutral-artifact-source-taxonomy/quickstart.md`, and `specs/284-provider-neutral-artifact-source-taxonomy/contracts/provider-neutral-artifact-source-taxonomy.logical.openapi.yaml`
**Implementation Posture**: Runtime implementation explicitly accepted on 2026-05-09. Targeted test execution, browser smoke, and dirty-file formatting validation remain required before merge readiness.
**Tests**: REQUIRED (Pest). Keep proof bounded to `apps/platform/tests/Unit/Artifacts/ArtifactSourceTaxonomyCatalogTest.php`, `apps/platform/tests/Unit/Inventory/InventoryCanonicalTypeDescriptorTest.php`, `apps/platform/tests/Feature/Artifacts/FindingArtifactSourceTaxonomyTest.php`, `apps/platform/tests/Feature/Artifacts/EvidenceSnapshotSourceTaxonomyTest.php`, `apps/platform/tests/Feature/Artifacts/StoredReportSourceTaxonomyTest.php`, `apps/platform/tests/Feature/Artifacts/InventoryArtifactTypeTaxonomyTest.php`, `apps/platform/tests/Feature/Filament/Artifacts/ArtifactSourceTaxonomySurfaceTest.php`, `apps/platform/tests/Feature/Guards/ArtifactSourceProviderTruthGuardTest.php`, and `apps/platform/tests/Browser/Spec284ArtifactSourceTaxonomySmokeTest.php`.
**Operations**: No new `OperationRun` family. Reuse existing read-only operation lineage where `OperationsSummarySource`, `OperationRunLinks`, or adjacent presenters already surface operation context. `284` does not change start, completion, or link UX for operation execution.
**RBAC**: Workspace membership remains the first `404` boundary, managed-environment entitlement remains the second `404` boundary, and current findings, evidence, reports, inventory, and review capability denials remain `403`. Provider-neutral artifact typing must not bypass existing policies or capabilities.
**Shared Pattern Reuse**: Reuse `PlatformVocabularyGlossary`, `GovernanceSubjectTaxonomyRegistry`, `CanonicalControlResolutionRequest`, `CanonicalControlResolver`, `EvidenceSnapshotService`, `InventoryPolicyTypeMeta`, `ArtifactTruthPresenter`, `TenantReviewSectionFactory`, and touched support or AI `source_family` consumers. Do not introduce a provider framework, a detector catalog, a full control-catalog expansion, a package runtime, a new artifact table, historical backfill, or adjacent Spec `285` through `287` scope.
**Filament / Panel Guardrails**: Filament remains v5 on Livewire v4. Provider registration remains in `apps/platform/bootstrap/providers.php`. `FindingResource` and `InventoryItemResource` keep valid `View` pages for any current or future search posture. `EvidenceSnapshotResource`, `StoredReportResource`, and `TenantReviewResource` remain non-globally-searchable while keeping `View` pages. Any touched destructive action must continue to use `->action(...)`, `->requiresConfirmation()`, and current server authorization. Asset strategy stays unchanged.
**Compatibility Posture**: Reject historical backfill, dual-write compatibility paths, a new artifact table, a detector catalog, package runtime, a provider framework, RBAC redesign, route-shell work, copy neutralization, and no-legacy enforcement work.
**External Prerequisite**: Specs `281`, `282`, and `283` must already be merged or otherwise present on the implementation branch before any runtime or test task starts, and SCOPE-001 ownership compliance for touched tenant-owned artifact tables must be satisfied or explicitly excepted before runtime implementation begins.
**Organization**: Tasks are grouped by user story so descriptor derivation, inventory type splitting, operator-surface disclosure, and downstream source-family alignment remain independently testable.
**Review Outcome**: `implementation-ready`
**Workflow Outcome**: `keep`
**Test-governance Outcome**: `keep`
**Prerequisite Resolution**: SCOPE-001 is no longer a blocking prerequisite for this implementation loop. Specs `281`, `282`, and `283` are present on the branch, Spec `279` records the approved managed-environment core exception, and current touched artifact tables carry the established workspace plus managed-environment ownership boundary. `284` still must not add a new table, descriptor columns, historical backfill, detector catalog, package runtime, provider framework, or adjacent Specs `285` through `287` scope.
## Test Governance Checklist
- [x] Lane assignment stays `fast-feedback`, `confidence`, and one narrow `browser` lane.
- [x] New or changed tests stay in the named unit, feature, guard, and browser files only.
- [x] Workspace, managed-environment, finding, evidence, stored-report, review, and inventory fixtures remain explicit and opt-in; no hidden shared defaults or backfill helpers are planned.
- [x] Planned validation commands match `spec.md`, `plan.md`, and `quickstart.md` exactly.
- [x] `standard-native-filament` and `shared-detail-family` expectations stay explicit for touched surfaces.
- [x] Any attempt to absorb Specs `285` through `287` resolves as `split` or `reject-or-split`, not hidden follow-up inside `284`.
## Pinned Initial Descriptor Inventories
- `source_family`:
- `finding`
- `stored_report`
- `evidence_snapshot`
- `inventory`
- `operation_run`
- `source_kind`:
- `model_summary`
- `stored_report`
- `operation_rollup`
- `inventory_projection`
- `source_target_kind`:
- `managed_environment`
- `governed_subject`
- `provider_connection`
- `operation_run`
This pinned inventory is authoritative for Spec `284` tasks and must remain identical across the package.
## Phase 0: External Gate
**Purpose**: Confirm the inherited provider-boundary and artifact-surface prerequisites are available before implementation begins.
- [x] T000 Confirm Specs `281`, `282`, and `283` are already merged or otherwise present on the implementation branch before any runtime or test task begins.
---
## Phase 1: Setup (Shared Context)
**Purpose**: Confirm the bounded artifact-source inventory, proof files, and deferred-scope posture before runtime edits begin.
- [x] T001 Review `specs/284-provider-neutral-artifact-source-taxonomy/spec.md`, `plan.md`, `checklists/requirements.md`, `research.md`, `data-model.md`, `quickstart.md`, and `contracts/provider-neutral-artifact-source-taxonomy.logical.openapi.yaml` together so implementation stays on Spec `284` only, and stop for a prerequisite decision if SCOPE-001 ownership compliance is still unresolved for touched tenant-owned artifact tables.
- [x] T002 [P] Confirm the current persisted-truth seams in `apps/platform/app/Models/Finding.php`, `apps/platform/app/Models/EvidenceSnapshotItem.php`, `apps/platform/app/Models/StoredReport.php`, `apps/platform/app/Models/InventoryItem.php`, and the related migrations before changing descriptor fields.
- [x] T003 [P] Confirm the current evidence-source and control-resolution seams in `apps/platform/app/Services/Evidence/Contracts/EvidenceSourceProvider.php`, `apps/platform/app/Services/Evidence/EvidenceSnapshotService.php`, `apps/platform/app/Services/Evidence/Sources/FindingsSummarySource.php`, `apps/platform/app/Services/Evidence/Sources/PermissionPostureSource.php`, `apps/platform/app/Services/Evidence/Sources/EntraAdminRolesSource.php`, `apps/platform/app/Services/Evidence/Sources/BaselineDriftPostureSource.php`, `apps/platform/app/Services/Evidence/Sources/OperationsSummarySource.php`, `apps/platform/app/Support/Governance/Controls/CanonicalControlResolutionRequest.php`, and `apps/platform/app/Support/Governance/Controls/CanonicalControlResolver.php`.
- [x] T004 [P] Confirm the current inventory type and metadata seams in `apps/platform/app/Support/Inventory/InventoryPolicyTypeMeta.php`, `apps/platform/app/Models/InventoryItem.php`, and `apps/platform/app/Filament/Resources/InventoryItemResource.php` before changing `policy_type` semantics.
- [x] T005 [P] Confirm the current operator-surface and review-summary seams in `apps/platform/app/Filament/Resources/FindingResource.php`, `EvidenceSnapshotResource.php`, `StoredReportResource.php`, `TenantReviewResource.php`, `apps/platform/app/Services/TenantReviews/TenantReviewSectionFactory.php`, and `apps/platform/app/Support/Ui/GovernanceArtifactTruth/ArtifactTruthPresenter.php`.
- [x] T006 [P] Confirm current `source_family` naming precedent and deferred boundaries in `apps/platform/app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php`, `apps/platform/app/Support/Ai/AiUseCaseCatalog.php`, and `specs/284-provider-neutral-artifact-source-taxonomy/checklists/requirements.md` so Specs `285` through `287` remain explicitly out of scope.
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Establish the proving suite and the canonical descriptor inventory that every story depends on.
**Critical**: No user-story work should begin until this phase is complete.
- [x] T007 [P] Add failing coverage in `apps/platform/tests/Unit/Artifacts/ArtifactSourceTaxonomyCatalogTest.php` for the exact `source_family`, `source_kind`, and `source_target_kind` inventories plus the no-detector-catalog rule.
- [x] T008 [P] Add failing coverage in `apps/platform/tests/Unit/Inventory/InventoryCanonicalTypeDescriptorTest.php` for the `canonical_type`, `provider_object_type`, `provider_display_type`, and `legacy_policy_type` split.
- [x] T009 [P] Add failing coverage in `apps/platform/tests/Feature/Artifacts/FindingArtifactSourceTaxonomyTest.php`, `apps/platform/tests/Feature/Artifacts/EvidenceSnapshotSourceTaxonomyTest.php`, and `apps/platform/tests/Feature/Artifacts/StoredReportSourceTaxonomyTest.php` for shared descriptor derivation over findings, evidence, and stored reports, including `workspace_id`, `tenant_id`, `provider_connection_id`, and `source_target_identifier` semantics when present.
- [x] T010 [P] Add failing guard coverage in `apps/platform/tests/Feature/Guards/ArtifactSourceProviderTruthGuardTest.php` for `finding_type`, `report_type`, or `policy_type` reappearing as top-level summary truth and for `package_run_id` being treated as active runtime truth.
- [x] T011 [P] Add the narrow browser smoke in `apps/platform/tests/Browser/Spec284ArtifactSourceTaxonomySmokeTest.php` for one finding, one evidence snapshot, one stored report, one inventory item, and one tenant-review section under the live Filament shell.
- [x] T012 Introduce the bounded shared descriptor support seam in the smallest viable namespace under `apps/platform/app/Support/Artifacts/` or existing helpers, derive the descriptor from existing persisted truth plus the established workspace and tenant scope, carry `provider_connection_id` and `source_target_identifier` when current truth exposes them, and pin the exact inventories across consumers without adding a new table, detector catalog, descriptor columns, or backfill flow.
**Checkpoint**: The proving files exist, the descriptor inventory is explicit, and later stories can consume one canonical artifact-source contract.
---
## Phase 3: User Story 1 - Interpret findings, evidence, and stored reports with one descriptor (Priority: P1)
**Goal**: Findings, evidence summaries, and stored reports expose the same canonical artifact-source descriptor and control summary before provider-native detail.
**Independent Test**: Load one finding, one evidence snapshot item, and one stored report for the same managed environment and confirm each surface or reader exposes the same descriptor-first summary contract.
### Tests for User Story 1
- [x] T013 [P] [US1] Extend `apps/platform/tests/Feature/Artifacts/FindingArtifactSourceTaxonomyTest.php`, `apps/platform/tests/Feature/Artifacts/EvidenceSnapshotSourceTaxonomyTest.php`, and `apps/platform/tests/Feature/Artifacts/StoredReportSourceTaxonomyTest.php` after T012 to prove the same descriptor and `control_key` semantics survive across finding, evidence, and stored-report readers.
### Implementation for User Story 1
- [x] T014 [US1] Update `apps/platform/app/Models/Finding.php`, `apps/platform/app/Services/Evidence/Sources/FindingsSummarySource.php`, and the smallest related control-binding seam so finding summaries derive the canonical descriptor without page-local Microsoft-only checks.
- [x] T015 [US1] Update `apps/platform/app/Services/Evidence/Contracts/EvidenceSourceProvider.php`, `apps/platform/app/Services/Evidence/EvidenceSnapshotService.php`, `apps/platform/app/Services/Evidence/Sources/PermissionPostureSource.php`, `apps/platform/app/Services/Evidence/Sources/EntraAdminRolesSource.php`, `apps/platform/app/Services/Evidence/Sources/BaselineDriftPostureSource.php`, `apps/platform/app/Services/Evidence/Sources/OperationsSummarySource.php`, and `apps/platform/app/Models/EvidenceSnapshotItem.php` so evidence summaries derive the descriptor consistently from existing persisted truth.
- [x] T016 [US1] Update `apps/platform/app/Models/StoredReport.php`, `apps/platform/app/Services/EntraAdminRoles/EntraAdminRolesReportService.php`, and any directly touched stored-report readers so report summaries align to source-family semantics while keeping raw `report_type` nested. Note: no `EntraAdminRolesReportService` edit was required because the shared `StoredReport` resolver derives descriptor and provider detail from existing report payloads without producer changes.
**Checkpoint**: Findings, evidence summaries, and stored reports now expose one shared descriptor-first lineage contract.
---
## Phase 4: User Story 2 - Read inventory items with canonical and provider type separation (Priority: P1)
**Goal**: Inventory items distinguish platform-owned canonical type from provider-owned object type and display type while still participating in the shared artifact-source descriptor contract.
**Independent Test**: Open one inventory item and confirm the page shows `canonical_type`, `provider_object_type`, and `provider_display_type` as separate concepts and still exposes the shared source descriptor.
### Tests for User Story 2
- [x] T017 [P] [US2] Extend `apps/platform/tests/Unit/Inventory/InventoryCanonicalTypeDescriptorTest.php` and `apps/platform/tests/Feature/Artifacts/InventoryArtifactTypeTaxonomyTest.php` after T012 to prove the inventory type split, legacy `policy_type` nesting, and shared source descriptor on inventory read models.
### Implementation for User Story 2
- [x] T018 [US2] Update `apps/platform/app/Support/Inventory/InventoryPolicyTypeMeta.php` and the smallest adjacent inventory read-model seam so inventory emits `canonical_type`, `provider_object_type`, `provider_display_type`, and optional `legacy_policy_type` without promoting raw `policy_type` back to top-level truth.
- [x] T019 [US2] Update `apps/platform/app/Models/InventoryItem.php` and `apps/platform/app/Filament/Resources/InventoryItemResource.php` so list and detail surfaces disclose the type split, carry the shared source descriptor, and keep raw metadata secondary.
**Checkpoint**: Inventory surfaces now separate platform type from provider object type cleanly.
---
## Phase 5: User Story 3 - Keep operator-facing artifact surfaces descriptor-first (Priority: P2)
**Goal**: Findings, evidence, stored reports, and tenant-review sections show the canonical descriptor and control summary before provider-native detail.
**Independent Test**: Open one finding, one evidence snapshot, one stored report, and one tenant review with supporting sections and confirm each touched summary is descriptor-first.
### Tests for User Story 3
- [x] T020 [P] [US3] Extend `apps/platform/tests/Feature/Filament/Artifacts/ArtifactSourceTaxonomySurfaceTest.php` after T012 to prove `FindingResource`, `EvidenceSnapshotResource`, `InventoryItemResource`, `StoredReportResource`, and `TenantReviewResource` surface the descriptor first, provider detail second, and preserve inherited `404` versus `403` behavior.
### Implementation for User Story 3
- [x] T021 [US3] Update `apps/platform/app/Filament/Resources/FindingResource.php`, `apps/platform/app/Filament/Resources/EvidenceSnapshotResource.php`, `apps/platform/app/Filament/Resources/StoredReportResource.php`, `apps/platform/app/Filament/Resources/TenantReviewResource.php`, `apps/platform/app/Services/TenantReviews/TenantReviewSectionFactory.php`, and `apps/platform/app/Support/Ui/GovernanceArtifactTruth/ArtifactTruthPresenter.php` so summary ordering converges on the shared descriptor and control summary. Note: no `ArtifactTruthPresenter` edit was required because descriptor-first disclosure lives in the resource and review-section summary layers while existing truth envelopes remain unchanged.
- [x] T022 [US3] Update touched review or evidence related-context seams only where required so navigation stays unchanged while summary ordering converges. Note: no related-context changes were required; existing links stayed unchanged.
**Checkpoint**: Operator-facing read paths now teach one artifact-source vocabulary and keep provider detail secondary.
---
## Phase 6: User Story 4 - Align downstream shared consumers without adding package runtime (Priority: P3)
**Goal**: Touched support or AI `source_family` consumers reuse the same pinned family names and keep `package_run_id` optional only.
**Independent Test**: Inspect touched support or AI source-family bundles and confirm they use the same source-family vocabulary as the artifact descriptor while leaving `package_run_id` null or absent.
### Tests for User Story 4
- [x] T023 [P] [US4] Extend `apps/platform/tests/Feature/Guards/ArtifactSourceProviderTruthGuardTest.php` after T012 to prove touched support or AI `source_family` consumers use the pinned family names and that `package_run_id` remains optional-only in current runtime.
### Implementation for User Story 4
- [x] T024 [US4] Update `apps/platform/app/Support/SupportDiagnostics/SupportDiagnosticBundleBuilder.php`, `apps/platform/app/Support/Ai/AiUseCaseCatalog.php`, and any directly touched shared descriptor helpers only where required so source-family semantics align without implying package runtime. Note: no runtime edit was required because existing support and AI families remain non-artifact source families and the guard test proves they do not collide with the pinned artifact inventory.
**Checkpoint**: Downstream shared consumers no longer risk inventing a second artifact-source vocabulary.
---
## Phase 7: Polish & Cross-Cutting Validation
**Purpose**: Run the exact bounded proof set, perform the final Filament and taxonomy review, and confirm the slice stayed inside Spec `284`.
- [x] T025 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Unit/Artifacts/ArtifactSourceTaxonomyCatalogTest.php tests/Unit/Inventory/InventoryCanonicalTypeDescriptorTest.php)`.
- [x] T026 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Feature/Artifacts/FindingArtifactSourceTaxonomyTest.php tests/Feature/Artifacts/EvidenceSnapshotSourceTaxonomyTest.php tests/Feature/Artifacts/StoredReportSourceTaxonomyTest.php tests/Feature/Artifacts/InventoryArtifactTypeTaxonomyTest.php tests/Feature/Filament/Artifacts/ArtifactSourceTaxonomySurfaceTest.php tests/Feature/Guards/ArtifactSourceProviderTruthGuardTest.php)`.
- [x] T027 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail artisan test --compact tests/Browser/Spec284ArtifactSourceTaxonomySmokeTest.php)`.
- [x] T028 [P] Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && REPO_ROOT="$(git rev-parse --show-toplevel)" && (cd "$REPO_ROOT/apps/platform" && ./vendor/bin/sail bin pint --dirty --format agent)`.
- [x] T029 [P] Review touched files to confirm Filament v5 and Livewire v4 compliance, provider registration staying in `apps/platform/bootstrap/providers.php`, truthful global-search posture, unchanged asset strategy, no new artifact table, no detector catalog, no package runtime, no historical backfill, and Specs `285` through `287` remaining deferred.
---
## Dependencies & Execution Order
### Phase Dependencies
- **Phase 0 (External Gate)**: no dependencies; complete before implementation starts.
- **Phase 1 (Setup)**: depends on Phase 0.
- **Phase 2 (Foundational)**: depends on Phase 1 and blocks all story work.
- **Phase 3 (US1)**: depends on Phase 2 and establishes the canonical descriptor for findings, evidence, and stored reports.
- **Phase 4 (US2)**: depends on Phase 2 and should land with or immediately after US1 so inventory type semantics align with the shared descriptor vocabulary.
- **Phase 5 (US3)**: depends on US1 and US2 because operator-facing surfaces should consume the final descriptor and inventory split.
- **Phase 6 (US4)**: depends on US1 through US3 so downstream shared consumers inherit the final vocabulary instead of an intermediate mapping.
- **Phase 7 (Polish)**: depends on all desired user stories being complete.
### User Story Dependencies
- **US1 (P1)**: independently testable after Phase 2 and is the first required implementation increment.
- **US2 (P1)**: independently testable after Phase 2, but should ship after or with US1 because inventory should not introduce parallel artifact-type language.
- **US3 (P2)**: independently testable after US1 and US2 once the shared descriptor is stable.
- **US4 (P3)**: independently testable after US1 through US3 and closes the remaining `source_family` drift in touched shared consumers.
### Within Each User Story
- Write or extend the listed Pest coverage first and make it fail for the intended gap.
- Apply the smallest shared-seam changes needed to satisfy the story without reopening Specs `285` through `287`.
- Re-run the narrowest relevant validation command for that story before moving to the next story.
## Parallel Execution Examples
- **Setup**: T002 through T006 can run in parallel once T000 and T001 set the bounded scope.
- **Foundational**: T007 through T011 can run in parallel before T012 converges the canonical descriptor seam.
- **US1**: T013 can run alongside implementation prep; T014 through T016 should merge serially around finding, evidence, and stored-report seams.
- **US2**: T017 can run alongside T018, then T019 follows once the inventory type split is stable.
- **US3**: T020 can run alongside implementation prep; T021 and T022 should merge serially around shared presenter files.
- **US4**: T023 can run alongside T024 because both touch bounded shared-consumer alignment only.
- **Polish**: T025 through T028 can run in parallel after implementation is complete; T029 closes the bounded-scope review last.
## Implementation Strategy
### Suggested MVP Scope
- MVP = **US1 + US2**. Land the shared descriptor and inventory type split first so later surface cleanup does not build on inconsistent underlying semantics.
### Incremental Delivery
1. Complete Phase 0, Phase 1, and Phase 2.
2. Deliver US1 so findings, evidence, and stored reports stop depending on Microsoft-only top-level summary nouns.
3. Deliver US2 so inventory type semantics stop teaching `policy_type` as universal truth.
4. Deliver US3 so operator-facing surfaces visibly converge on the final descriptor-first disclosure.
5. Deliver US4 only if touched support or AI consumers need explicit alignment.
6. Finish with the exact validation commands and the final bounded-scope review in Phase 7.
### Team Strategy
1. Parallelize the failing test work first.
2. Serialize merges around the shared descriptor seam, evidence providers, and Filament resource presenters to avoid contract-shape conflicts.
3. Reject any implementation branch that introduces a detector catalog, package runtime, backfill work, a provider framework, or adjacent copy or RBAC scope.
## Deferred Follow-Ups / Non-Goals
- Spec `285` workspace-first RBAC and environment-access scoping
- Spec `286` broader UI copy, IA, and localization neutralization
- Spec `287` cutover quality gates and no-legacy enforcement
- package runtime or package-output surfaces beyond the optional `package_run_id` contract slot
- detector catalog or broader control-catalog expansion