TenantAtlas/specs/417-canonical-identity-engine/implementation-report.md
ahmido 8cbf1f7fe3 feat: implement canonical identity engine (#484)
Automated PR provided by Codex via Gitea API.

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #484
2026-06-26 06:50:25 +00:00

12 KiB

Implementation Report: Spec 417 - Canonical Identity Engine

Preflight

  • Branch: 417-canonical-identity-engine
  • Starting HEAD: 332f6325 feat: add tenantpilot agent skill layer v1 (#483)
  • Starting dirty state: specs/417-canonical-identity-engine/ untracked as the active spec artifact set.
  • Dirty-state assessment: active Spec 417 preparation artifacts only; no runtime code was dirty before implementation.
  • Activated skills: spec-kit-implementation-loop, pest-testing, tenantpilot-spec-readiness-gate, tenantpilot-workspace-scope-safety, tenantpilot-evidence-anchor-contract, tenantpilot-tcm-cutover-guard.
  • Hard-gate stop conditions before implementation: none observed. Coverage v2 remains inactive; no UI/customer proof surface is in scope.

Dependency Context

  • Spec 414: completed/validated context only; not modified.
  • Spec 415: completed/validated context only; not modified.
  • Existing canonical identity storage: tenant_configuration_resources.canonical_resource_key remains the single persisted canonical key truth.
  • Browser proof decision: N/A - no rendered UI surface changed.

Implementation Summary

  • Added a bounded canonical identity engine for the initial eight Coverage v2 resource types.
  • Added CanonicalKeyKind values without display-name/name-only stable key kinds.
  • Added identity strategy, resolver, secondary-key, diagnostics, and same-scope conflict evaluation services under App\Services\TenantConfiguration.
  • Extended tenant_configuration_resources with additive identity metadata: canonical_key_kind, identity_strategy, source_identity, secondary_identity_keys, identity_diagnostics, and identity_evaluated_at.
  • Kept canonical_resource_key as the single persisted canonical key truth; no canonical_key column or duplicate key truth was added.
  • Updated CoverageResourceUpserter to consume resolver/evaluator output instead of hardcoding id/sourceId.
  • Updated CoverageEvidenceWriter to preserve resource identity/claim state instead of resetting to resource-type defaults.
  • Updated ClaimGuard to block identity_conflict, missing_external_id, and unsupported_identity, and to limit/block derived identity unless explicitly allowed.
  • Kept Coverage v2 inactive; no UI, route, navigation, report, review, restore, customer-output, or browser-visible activation was added.

Identity Strategy Matrix

Canonical type Strategy Stable ID posture Derived/experimental posture Default claim consequence
deviceAndAppManagementAssignmentFilter tcm.assignment_filter.v1 TCM/provider IDs stable source/derived composite allowed derived limited
deviceEnrollmentLimitRestriction tcm.device_enrollment_limit_restriction.v1 TCM/provider IDs stable source/derived composite allowed derived limited
deviceEnrollmentPlatformRestriction tcm.device_enrollment_platform_restriction.v1 TCM/provider IDs stable source/derived composite allowed derived limited
deviceEnrollmentStatusPageWindows10 tcm.device_enrollment_status_page_windows10.v1 TCM/provider IDs stable source/derived composite allowed derived limited
appProtectionPolicyAndroid tcm.app_protection_policy_android.v1 TCM/provider IDs stable source/derived composite allowed derived limited
appProtectionPolicyiOS tcm.app_protection_policy_ios.v1 TCM/provider IDs stable source/derived composite allowed derived limited
notificationMessageTemplate graph.notification_message_template.v1 Graph/template IDs stable source/derived composite allowed no certification by default
roleScopeTag graph_beta.role_scope_tag.v1 beta IDs are experimental, not stable proof experimental source key resolves as derived internal-only by default

Schema And Persistence

  • Migration added: apps/platform/database/migrations/2026_06_26_000417_extend_tenant_configuration_resource_identity.php.
  • Added PostgreSQL check constraint for canonical_key_kind.
  • Added PostgreSQL JSONB object constraints for source_identity, secondary_identity_keys, and identity_diagnostics.
  • Added targeted indexes for scope/type/identity-state and resource-type/key-kind lookup paths.
  • No speculative JSONB GIN index was added.
  • Tombstone behavior is deferred; no tombstone field or active delete/drift workflow is implemented in this slice.

Scope, Claim, And Evidence Safety

  • Scope tuple remains workspace_id, managed_environment_id, provider_connection_id, resource_type_id, and canonical_resource_key.
  • Provider connections are still validated against the same workspace and managed environment before upsert/capture.
  • Stable ID rename updates the same same-scope resource row.
  • Duplicate display names with different stable IDs remain separate rows.
  • Display-name-only payloads resolve to missing_external_id, not stable; repeated same-scope display-only observations are promoted to identity_conflict instead of merging by secondary/display fingerprint.
  • Unsupported identity observations are promoted to identity_conflict on same-scope fallback-key collision instead of merging by unsupported fallback key.
  • Same-scope unsafe derived identity collisions mark existing/incoming resources as identity_conflict and claim_blocked.
  • Cross-workspace, cross-managed-environment, and cross-provider resources do not merge because the existing scope tuple remains part of identity uniqueness.
  • Diagnostics and secondary keys are redacted and bounded; no raw provider payloads, tokens, secrets, cookies, authorization headers, private keys, certificates, or full provider response dumps are persisted in identity diagnostics.
  • No fallback-to-latest evidence path, v1-to-v2 adapter, dual-write path, old snapshot promotion, or old v1 gap taxonomy was introduced.

Product Surface Close-Out

  • UI Surface Impact: none.
  • Product Surface Impact: N/A - no rendered product surface changed.
  • Browser smoke result: N/A - no rendered UI surface changed.
  • Human Product Sanity: N/A - no product surface changed; visible complexity outcome is neutral.
  • Livewire v4 compliance: Livewire v4 baseline unchanged; no Livewire code changed.
  • Provider registration location: no panel provider change; Laravel 12 providers remain in apps/platform/bootstrap/providers.php.
  • Global search posture: no Filament resource/global search change.
  • Destructive/high-impact actions: none added.
  • Asset strategy: no assets registered; filament:assets is not required by this spec.
  • No completed historical spec was rewritten or stripped of validation, task, smoke, browser, or review history.

Validation

  • PASS: php -l syntax sweep for tracked and untracked changed PHP files.
  • PASS: cd apps/platform && ./vendor/bin/sail artisan test --compact --filter=Spec417 (24 passed, 1 PostgreSQL-only skipped, 162 assertions).
  • PASS: cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/TenantConfiguration (35 passed, 170 assertions).
  • PASS: cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/TenantConfiguration (35 passed, 8 PostgreSQL-only skipped, 190 assertions).
  • PASS: cd apps/platform && ./vendor/bin/sail php vendor/bin/pest -c phpunit.pgsql.xml tests/Feature/TenantConfiguration (43 passed, 203 assertions).
  • PASS: cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent.
  • PASS: git diff --check.
  • PASS: untracked-file trailing-whitespace scan.

Analysis And Fix Loop

  • Iteration 1 finding: secondary-key and diagnostics builders redacted nested values without preserving key context, so sensitive scalar fields were not redacted. Fixed by redacting with field-name context; regression tests pass.
  • Iteration 2 finding: PostgreSQL JSONB object constraints rejected empty PHP arrays encoded as []. Fixed by normalizing empty metadata/defaults to JSON objects; PostgreSQL lane passes.
  • Post-implementation scope finding: optional tombstone timestamp was unnecessary without tombstone behavior. Fixed by removing the unused field and documenting tombstone deferral.
  • Manual final-review finding: repeated same-scope display-name-only payloads and unsupported identity observations could merge by fallback candidate keys even though they were not marked stable. Fixed by treating repeated missing_external_id and unsupported_identity candidate collisions as identity_conflict, marking existing candidates claim_blocked, generating a separate conflict key for the new unsafe observation, normalizing empty source_metadata to JSON object form, and adding feature regressions for both paths.
  • Remaining confirmed in-scope findings: none.

Files Changed

  • apps/platform/app/Models/TenantConfigurationResource.php
  • apps/platform/app/Services/TenantConfiguration/CanonicalIdentityResolver.php
  • apps/platform/app/Services/TenantConfiguration/CanonicalIdentityResult.php
  • apps/platform/app/Services/TenantConfiguration/ClaimGuard.php
  • apps/platform/app/Services/TenantConfiguration/CoverageEvidenceWriter.php
  • apps/platform/app/Services/TenantConfiguration/CoverageIdentityStrategyRegistry.php
  • apps/platform/app/Services/TenantConfiguration/CoverageResourceIdentityEvaluator.php
  • apps/platform/app/Services/TenantConfiguration/CoverageResourceUpserter.php
  • apps/platform/app/Services/TenantConfiguration/CoverageSecondaryKeyBuilder.php
  • apps/platform/app/Services/TenantConfiguration/IdentityConflictDiagnosticsBuilder.php
  • apps/platform/app/Support/TenantConfiguration/CanonicalKeyKind.php
  • apps/platform/database/factories/TenantConfigurationResourceFactory.php
  • apps/platform/database/migrations/2026_06_26_000417_extend_tenant_configuration_resource_identity.php
  • apps/platform/tests/Feature/TenantConfiguration/Spec417CanonicalIdentityPersistenceTest.php
  • apps/platform/tests/Feature/TenantConfiguration/Spec417CoverageResourceIdentityUpsertTest.php
  • apps/platform/tests/Feature/TenantConfiguration/Spec417IdentityClaimGuardFeatureTest.php
  • apps/platform/tests/Feature/TenantConfiguration/Spec417IdentityConflictScopeTest.php
  • apps/platform/tests/Feature/TenantConfiguration/Spec417IdentityNoLegacyNoUiActivationTest.php
  • apps/platform/tests/Unit/Support/TenantConfiguration/ClaimGuardTest.php
  • apps/platform/tests/Unit/Support/TenantConfiguration/Spec417CanonicalIdentityResolverTest.php
  • apps/platform/tests/Unit/Support/TenantConfiguration/Spec417CoverageIdentityStrategyRegistryTest.php
  • apps/platform/tests/Unit/Support/TenantConfiguration/Spec417CoverageSecondaryKeyBuilderTest.php
  • apps/platform/tests/Unit/Support/TenantConfiguration/Spec417IdentityConflictDiagnosticsTest.php
  • specs/417-canonical-identity-engine/implementation-report.md
  • specs/417-canonical-identity-engine/tasks.md

Deployment Impact

  • Migrations: yes, additive identity metadata/check constraints/indexes on tenant_configuration_resources.
  • Environment variables: none.
  • Queues/workers: no new queue or OperationRun path.
  • Scheduler: no change.
  • Storage/volumes: no change.
  • Assets: no change.
  • Staging/production: run migrations before any later Coverage v2 activation; validate PostgreSQL migration in staging.

Deferred Work

  • Tombstone/delete workflow remains out of scope.
  • Customer/operator identity diagnostics UI remains out of scope.
  • Coverage v2 activation, legacy cutover/removal, compare/render/restore/report/customer claims remain future specs.

Final Gate Result

  • Spec Readiness Gate: PASS.
  • Implementation Scope Gate: PASS.
  • Test Gate: PASS.
  • Browser Smoke Test Gate: PASS as not applicable, N/A - no rendered UI surface changed.
  • Post-Implementation Analysis Gate: PASS.
  • Merge Readiness Gate: PASS; ready for manual review/merge.