146 lines
11 KiB
Markdown
146 lines
11 KiB
Markdown
# Implementation Report: Spec 415 - Generic Content-Backed Capture
|
|
|
|
## Preflight
|
|
|
|
- Branch: `415-generic-content-backed-capture`
|
|
- Baseline HEAD: `dfda397e feat: migrate tcm first coverage core cutover (#481)`
|
|
- Initial dirty state: `specs/415-generic-content-backed-capture/` untracked as the active spec artifact set.
|
|
- Spec 414 dependency: treated as completed/validated context only; no files under `specs/414-tcm-first-coverage-core-cutover/` were modified.
|
|
- Existing equivalent check: no existing `tenant_configuration_resources` or `tenant_configuration_resource_evidence` tables/models were present before implementation.
|
|
|
|
## Implementation Scope
|
|
|
|
- Added `tenant_configuration_resources` and `tenant_configuration_resource_evidence` persistence with `workspace_id`, `managed_environment_id`, `provider_connection_id`, and `resource_type_id`; no `tenant_id` fields.
|
|
- Added PostgreSQL composite scope constraints for managed environment/workspace, provider connection/workspace/environment, evidence/resource/provider/type, evidence/latest-resource/provider/type, and evidence/operation-run/workspace/environment integrity while keeping SQLite-compatible migrations for tests.
|
|
- Added a database-level `provider_connections(managed_environment_id, workspace_id)` binding so provider connections cannot be attached to a managed environment from another workspace.
|
|
- Added a scoped `latest_evidence_id` foreign key so a resource can only point at evidence for the same resource, workspace, managed environment, provider connection, and resource type.
|
|
- Added models and factories for concrete resources and append-only evidence.
|
|
- Added bounded capture outcomes only: `captured`, `capture_blocked_missing_contract`, `capture_blocked_permission`, `capture_blocked_beta`, `capture_blocked_unsupported`, `capture_failed`.
|
|
- Added contract resolution through `GraphContractRegistry` with explicit mappings only:
|
|
- `deviceAndAppManagementAssignmentFilter` -> `assignmentFilter` -> captured when Graph succeeds.
|
|
- `notificationMessageTemplate` -> `notificationMessageTemplate` -> captured when Graph succeeds.
|
|
- `roleScopeTag` -> beta-backed and blocked by default.
|
|
- Other initial TCM types remain `capture_blocked_missing_contract` until explicit contracts are introduced.
|
|
- Added deterministic normalization, configured volatile-field stripping, SHA-256 payload hashes, and recursive secret/token/header/cookie redaction for metadata/context.
|
|
- Added `tenant_configuration.capture` operation type/catalog label and a queued capture job.
|
|
- Added defense-in-depth scope validation before remote provider work: the job resolves provider connections only inside the OperationRun workspace/environment, and the capture service validates tenant/provider/run/context scope before calling the provider gateway.
|
|
|
|
## Capture Eligibility Matrix
|
|
|
|
| Canonical type | Source class | Source contract key | Default outcome |
|
|
| --- | --- | --- | --- |
|
|
| `deviceAndAppManagementAssignmentFilter` | `tcm` | `assignmentFilter` | `captured` when Graph succeeds |
|
|
| `deviceEnrollmentLimitRestriction` | `tcm` | none | `capture_blocked_missing_contract` |
|
|
| `deviceEnrollmentPlatformRestriction` | `tcm` | none | `capture_blocked_missing_contract` |
|
|
| `deviceEnrollmentStatusPageWindows10` | `tcm` | none | `capture_blocked_missing_contract` |
|
|
| `appProtectionPolicyAndroid` | `tcm` | none | `capture_blocked_missing_contract` |
|
|
| `appProtectionPolicyiOS` | `tcm` | none | `capture_blocked_missing_contract` |
|
|
| `notificationMessageTemplate` | `graph_v1_fallback` | `notificationMessageTemplate` | `captured` when Graph succeeds |
|
|
| `roleScopeTag` | `graph_beta_experimental` | `roleScopeTag` | `capture_blocked_beta` unless beta capture is explicitly enabled |
|
|
|
|
## Operation And RBAC
|
|
|
|
- Start capability: existing `Capabilities::EVIDENCE_MANAGE`.
|
|
- Authorization behavior:
|
|
- non-member and excluded explicit environment entitlement -> 404.
|
|
- workspace member without capability / readonly -> 403.
|
|
- same workspace/environment/provider connection -> allowed.
|
|
- OperationRun behavior:
|
|
- creates/reuses `tenant_configuration.capture` queued runs from idempotent provider/resource-type inputs.
|
|
- remote/provider work is queued through the central OperationRun lifecycle.
|
|
- the central queued execution gate validates `provider_connection_id` against both the OperationRun workspace and managed environment before provider work is allowed.
|
|
- the capture job and capture service also fail closed before Graph access when the provider connection, managed environment, OperationRun columns, or OperationRun target scope do not match.
|
|
- summary keys are existing canonical keys only: `total`, `processed`, `succeeded`, `skipped`, `failed`, `errors_recorded`.
|
|
- raw payloads and Graph credential options are not written into OperationRun context or audit metadata.
|
|
- exception messages are reduced to bounded `RunFailureSanitizer` reason codes for resource outcomes; dispatch/job failure audit messages are sanitized before persistence.
|
|
- Audit action IDs used:
|
|
- `tenant_configuration.capture.started`
|
|
- `tenant_configuration.capture.completed`
|
|
- `tenant_configuration.capture.failed`
|
|
|
|
## Product Surface
|
|
|
|
- No rendered UI surface changed.
|
|
- No Filament resources, pages, widgets, relation managers, routes, panel providers, navigation entries, views, assets, review/report/evidence pages, or restore readiness surfaces were added or modified.
|
|
- Browser proof: `N/A - no rendered UI surface changed`.
|
|
- Human Product Sanity: `N/A - no product surface changed`.
|
|
- Visible complexity outcome: unchanged.
|
|
- Product Surface exceptions: none.
|
|
- No completed historical spec was rewritten or stripped of validation/task/review history.
|
|
|
|
## Filament V5 Contract
|
|
|
|
- Livewire v4.0+ compliance: no Livewire/Filament runtime UI code changed; application package baseline remains Livewire v4.
|
|
- Provider registration location: unchanged; Laravel 11+/12 panel provider registration remains `bootstrap/providers.php`.
|
|
- Global search: no globally searchable resource was added; `N/A`.
|
|
- Destructive/high-impact actions: no UI action added; queued capture start is backend service only and RBAC-gated.
|
|
- Asset strategy: no assets registered; no `filament:assets` requirement from this spec.
|
|
- Testing plan: Unit/Feature/PostgreSQL lanes only; no Livewire page/widget/relation-manager/action tests required.
|
|
|
|
## Files Changed
|
|
|
|
- Persistence:
|
|
- `apps/platform/database/migrations/2026_06_25_000415_create_tenant_configuration_capture_tables.php`
|
|
- `apps/platform/app/Models/TenantConfigurationResource.php`
|
|
- `apps/platform/app/Models/TenantConfigurationResourceEvidence.php`
|
|
- `apps/platform/database/factories/TenantConfigurationResourceFactory.php`
|
|
- `apps/platform/database/factories/TenantConfigurationResourceEvidenceFactory.php`
|
|
- Capture support/services:
|
|
- `apps/platform/app/Support/TenantConfiguration/CaptureOutcome.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/CoverageSourceContractResolver.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/CoverageSourceContractDecision.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/GenericPayloadNormalizer.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/CoveragePayloadRedactor.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/CoverageCaptureOutcomeSummarizer.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/CoverageResourceUpserter.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/CoverageEvidenceWriter.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/GenericContentEvidenceCaptureService.php`
|
|
- `apps/platform/app/Services/TenantConfiguration/StartTenantConfigurationCapture.php`
|
|
- `apps/platform/app/Jobs/TenantConfiguration/CaptureTenantConfigurationEvidenceJob.php`
|
|
- Operation/audit registry:
|
|
- `apps/platform/app/Services/Operations/QueuedExecutionLegitimacyGate.php`
|
|
- `apps/platform/app/Support/OperationRunType.php`
|
|
- `apps/platform/app/Support/OperationCatalog.php`
|
|
- `apps/platform/app/Support/Audit/AuditActionId.php`
|
|
- Tests:
|
|
- `apps/platform/tests/Unit/Support/TenantConfiguration/Spec415CoverageSourceContractResolverTest.php`
|
|
- `apps/platform/tests/Unit/Support/TenantConfiguration/Spec415GenericPayloadNormalizerTest.php`
|
|
- `apps/platform/tests/Unit/Support/TenantConfiguration/Spec415CoverageRedactionTest.php`
|
|
- `apps/platform/tests/Unit/Support/TenantConfiguration/Spec415CoverageCaptureOutcomeTest.php`
|
|
- `apps/platform/tests/Unit/Support/TenantConfiguration/Spec415CoverageCaptureOperationRunSummaryTest.php`
|
|
- `apps/platform/tests/Feature/TenantConfiguration/Spec415CoverageEvidencePersistenceTest.php`
|
|
- `apps/platform/tests/Feature/TenantConfiguration/Spec415ProviderConnectionScopeTest.php`
|
|
- `apps/platform/tests/Feature/TenantConfiguration/Spec415CoverageCaptureAuthorizationTest.php`
|
|
- `apps/platform/tests/Feature/TenantConfiguration/Spec415CoverageCaptureOperationRunTest.php`
|
|
- `apps/platform/tests/Feature/TenantConfiguration/Spec415GenericContentBackedCaptureTest.php`
|
|
- `apps/platform/tests/Feature/TenantConfiguration/Spec415NoLegacyNoUiActivationTest.php`
|
|
- `apps/platform/tests/Unit/Operations/QueuedExecutionLegitimacyGateTest.php`
|
|
|
|
## Validation
|
|
|
|
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent` -> pass.
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration` -> 22 passed, 69 assertions.
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Operations/QueuedExecutionLegitimacyGateTest.php` -> 12 passed, 106 assertions.
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration/Spec415GenericContentBackedCaptureTest.php` -> 4 passed, 50 assertions.
|
|
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration` -> 24 passed, 7 skipped, 129 assertions.
|
|
- `cd apps/platform && ./vendor/bin/sail php vendor/bin/pest -c phpunit.pgsql.xml tests/Feature/TenantConfiguration` -> 31 passed, 141 assertions.
|
|
- PostgreSQL migration smoke on `tenantatlas_testing`: `migrate:fresh --force` -> pass; targeted rollback of `2026_06_25_000415_create_tenant_configuration_capture_tables.php` -> pass; final `migrate:fresh --force` -> pass.
|
|
- PostgreSQL latest-evidence lifecycle coverage: scoped pointer mismatch is rejected, same-scope pointer is accepted, and resource deletion cascades linked evidence without constraint failure.
|
|
- `git diff --check` -> pass.
|
|
- Additional untracked-file whitespace scan -> pass.
|
|
|
|
## Deployment Impact
|
|
|
|
- Migrations: yes, adds two new tenant configuration capture tables with PostgreSQL JSONB/check constraints and composite scope foreign keys; also adds a provider-connection environment/workspace binding constraint on the existing `provider_connections` table.
|
|
- Queue worker: yes, new queued capture job.
|
|
- Environment variables: no new variables.
|
|
- Scheduler: no change.
|
|
- Storage/volumes: no change.
|
|
- Assets: no change; `filament:assets` not required by this spec.
|
|
- Staging/production: run migrations before enabling any future caller; ensure queue workers are running; validate staging data has no existing provider-connection workspace/environment mismatches before production promotion.
|
|
|
|
## Deferred Work
|
|
|
|
- Add explicit source contracts for the remaining TCM-backed resource types before claiming captured evidence for them.
|
|
- Add any UI start surface only through a future UI-affecting spec with Product Surface review.
|