TenantAtlas/specs/043-cross-tenant-compare-and-promotion/plan.md
ahmido ff3392892b
Some checks failed
Main Confidence / confidence (push) Failing after 56s
Heavy Governance Lane / heavy-governance (push) Has been skipped
Browser Lane / browser (push) Has been skipped
Merge 248-private-ai-policy-foundation into dev (#288)
Automated PR: merge branch 248-private-ai-policy-foundation into dev (created by Copilot)

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #288
2026-04-27 21:18:37 +00:00

15 KiB

Implementation Plan: Cross-Tenant Compare Preview and Promotion Preflight

Branch: 043-cross-tenant-compare-and-promotion | Date: 2026-04-27 | Spec: spec.md Input: Feature specification from spec.md

Summary

Refresh Spec 043 into a narrow, implementation-ready workflow that adds one canonical workspace-context compare page under /admin, one reusable compare preview builder, and one read-only promotion preflight action. The slice reuses existing baseline compare subject identity, portfolio-triage context continuity, capability resolvers, and workspace audit logging. It deliberately stops before actual promotion execution, queueing, or persisted promotion drafts.

Filament remains on Livewire v4, no panel-provider registration changes are required (bootstrap/providers.php remains the authoritative provider registration location), no globally searchable compare resource is added, and no new panel asset bundle is expected.

Technical Context

Language/Version: PHP 8.4, Laravel 12
Primary Dependencies: Filament v5, Livewire v4, Pest v4, existing baseline compare services, portfolio-triage seams, audit services, and capability resolvers
Storage: PostgreSQL via existing inventory, policy-version, and audit tables; no new compare or promotion table
Testing: Pest v4 Unit and Feature coverage only
Validation Lanes: fast-feedback, confidence
Target Platform: Laravel monolith in apps/platform, admin panel only (/admin)
Project Type: Web application (Laravel monolith with Filament pages)
Performance Goals: compare preview and promotion preflight stay synchronous and derived from existing persisted truth; no background execution path in v1
Constraints: no target mutation, no OperationRun, no queue, no new persisted draft, no cross-workspace compare, no raw payload view by default
Scale/Scope: 2 tenant selectors, 1 canonical compare page, 1 preflight action, 1 launch/return continuity path, focused reuse of existing compare builders

UI / Surface Guardrail Plan

  • Guardrail scope: one new canonical compare page plus one launch action from existing tenant-registry/portfolio context
  • Native vs custom classification summary: native Filament page with shared compare/audit/navigation primitives
  • Shared-family relevance: canonical admin pages, compare drill-down patterns, launch actions, audit-backed modal/action copy
  • State layers in scope: page, query state
  • Audience modes in scope: operator-MSP only
  • Decision/diagnostic/raw hierarchy plan: decision-first compare summary, diagnostics second, raw evidence stays on existing tenant/baseline surfaces
  • Raw/support gating plan: no new raw/support surface; keep payload proof behind existing pages
  • One-primary-action / duplicate-truth control: the compare page keeps one dominant next action, Generate promotion preflight; drill-down and return actions stay secondary
  • Launch default: the tenant-registry launch action prefills the launched tenant as target tenant; the operator chooses the source tenant explicitly
  • Handling modes by drift class or surface: review-mandatory; any actual promotion execution or queue path is exception-required and out of scope
  • Repository-signal treatment: review-mandatory
  • Special surface test profiles: standard-native-filament
  • Required tests or manual smoke: functional-core, state-contract
  • Exception path and spread control: none
  • Active feature PR close-out entry: Guardrail

Shared Pattern & System Fit

  • Cross-cutting feature marker: yes
  • Systems touched:
    • App\Filament\Pages\BaselineCompareLanding
    • App\Filament\Pages\BaselineCompareMatrix
    • App\Filament\Resources\TenantResource
    • App\Filament\Resources\TenantResource\Pages\ListTenants
    • App\Services\Baselines\BaselineCompareService
    • App\Support\Baselines\BaselineCompareMatrixBuilder
    • App\Support\Baselines\Compare\CompareStrategyRegistry
    • App\Services\PortfolioTriage\TenantTriageReviewService
    • App\Services\Audit\WorkspaceAuditLogger
    • App\Support\Audit\AuditActionId
    • App\Support\Navigation\CanonicalNavigationContext
  • Shared abstractions reused: capability resolvers, baseline compare strategy selection, canonical navigation context, existing audit recorder/logger path, and tenant-registry return-state conventions
  • New abstraction introduced? why?: one narrow compare preview builder and one narrow promotion preflight service, because no existing service accepts source+target tenant scope and computes promotion readiness without execution
  • Why the existing abstraction was sufficient or insufficient: tenant-level baseline compare is sufficient for subject identity, evidence posture, and drill-down semantics, but insufficient for dual-tenant scope and promotion-readiness reasoning
  • Bounded deviation / spread control: no local compare sidecars on tenant pages; future callers must route through the canonical compare page and its services

OperationRun UX Impact

  • Touches OperationRun start/completion/link UX?: no
  • Central contract reused: N/A
  • Delegated UX behaviors: N/A
  • Surface-owned behavior kept local: compare preview and preflight remain synchronous and read-only
  • Queued DB-notification policy: N/A
  • Terminal notification path: N/A
  • Exception path: none

Provider Boundary & Portability Fit

  • Shared provider/platform boundary touched?: yes
  • Provider-owned seams: Microsoft-first inventory subject identity and policy-type mapping remain inside existing baseline compare strategy selection and inventory data
  • Platform-core seams: source/target tenant scope, compare preview contract, promotion preflight contract, operator-facing readiness vocabulary
  • Neutral platform terms / contracts preserved: source tenant, target tenant, governed subject, compare preview, promotion preflight, and blocked reason
  • Retained provider-specific semantics and why: existing policy-type and inventory semantics remain Microsoft-first because this repo still has one real provider domain; the compare page should not invent fake provider-neutral mapping logic above that seam
  • Bounded extraction or follow-up path: follow-up-spec only if later provider domains become current-release truth

Constitution Check

GATE: Must pass before implementation preparation continues.

  • Inventory-first: PASS. Compare preview and preflight derive from existing inventory and policy-version truth rather than a new compare snapshot.
  • Read/write separation: PASS. This slice stays read-only; no write execution is introduced.
  • Graph contract path: PASS. No new Graph endpoint or direct provider call is added.
  • Deterministic capabilities: PASS. Reuse existing capability registries such as Capabilities::TENANT_VIEW, Capabilities::WORKSPACE_BASELINES_VIEW, Capabilities::WORKSPACE_BASELINES_MANAGE, and existing tenant sync/manage seams.
  • Workspace and tenant isolation: PASS. The compare page must resolve workspace membership first and source/target entitlement second, with 404 for inaccessible tenants.
  • RBAC-UX plane separation: PASS. This slice lives only in /admin; no /system or cross-plane route is introduced.
  • Destructive action discipline: PASS by non-use. The slice contains no destructive action.
  • Global search: PASS. No new Resource or Global Search result is introduced.
  • OperationRun / Ops-UX: PASS by non-use. Actual promotion execution is deferred.
  • Data minimization: PASS. The compare page summarizes derived readiness and blocks; raw payloads stay on existing tenant/baseline pages.
  • Test governance: PASS. Proof stays in Unit plus Feature; no browser or heavy-governance expansion is planned.
  • Proportionality / no premature abstraction: PASS. One preview builder and one preflight service are justified by the dual-tenant workflow; no new persistence or framework layer is added.
  • Persisted truth: PASS. No new compare or promotion table.
  • Behavioral state: PASS. Readiness and blocked reasons remain derived, not persisted.
  • Shared pattern first / UI semantics / Filament-native UI: PASS. Existing compare, navigation, and audit paths are extended rather than replaced.
  • Provider boundary: PASS. Microsoft-shaped subject matching stays in existing strategy seams; the page contract stays platform-neutral.
  • Filament/Laravel panel safety: PASS. Filament v5 remains on Livewire v4, no provider registration change beyond bootstrap/providers.php, and no new assets are planned.

Gate evaluation: PASS.

Test Governance Check

  • Test purpose / classification by changed surface: Feature for the compare page, launch context, auth, and audit; Unit for compare preview matching and promotion-preflight classification
  • Affected validation lanes: fast-feedback, confidence
  • Why this lane mix is the narrowest sufficient proof: feature tests prove the Filament page and launch path while unit tests keep preview/preflight rules cheap and isolated. Browser or heavy-governance coverage is not required for the first read-only slice.
  • Narrowest proving command(s):
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/PortfolioCompare/CrossTenantComparePreviewBuilderTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/PortfolioCompare/CrossTenantPromotionPreflightTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PortfolioCompare/CrossTenantComparePageTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PortfolioCompare/CrossTenantCompareAuthorizationTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PortfolioCompare/CrossTenantPromotionPreflightAuditTest.php
    • export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/PortfolioCompare/CrossTenantCompareLaunchContextTest.php
  • Fixture / helper / factory / seed / context cost risks: reuse existing inventory, baseline compare, tenant registry, and portfolio-triage fixtures; avoid browser setup, queue fixtures, or seeded promotion history
  • Expensive defaults or shared helper growth introduced?: no
  • Heavy-family additions, promotions, or visibility changes: none
  • Surface-class relief / special coverage rule: standard-native-filament
  • Closing validation and reviewer handoff: rerun the six focused commands above and confirm the slice remains read-only, deny-as-not-found-safe, and grounded on existing compare + portfolio seams
  • Budget / baseline / trend follow-up: none expected
  • Review-stop questions: lane fit, hidden fixture growth, accidental write execution, accidental queue/runtime scope
  • Escalation path: document-in-feature for contained lane drift, reject-or-split for any attempt to add execution scope
  • Active feature PR close-out entry: Guardrail
  • Why no dedicated follow-up spec is needed: test upkeep remains feature-local; only actual promotion execution or multi-provider compare would warrant a separate follow-up spec

Project Structure

Documentation (this feature)

specs/043-cross-tenant-compare-and-promotion/
├── checklists/
│   └── requirements.md
├── spec.md
├── plan.md
└── tasks.md

This refresh intentionally limits itself to the core preparation package plus checklists/requirements.md. No additional research/data-model/contracts artifact is required to make the narrowed slice implementation-ready.

Source Code (repository root)

apps/platform/
├── app/
│   ├── Filament/Pages/
│   │   ├── BaselineCompareLanding.php
│   │   ├── BaselineCompareMatrix.php
│   │   └── [new canonical compare page]
│   ├── Filament/Resources/TenantResource.php
│   ├── Filament/Resources/TenantResource/Pages/ListTenants.php
│   ├── Models/
│   │   ├── InventoryItem.php
│   │   └── PolicyVersion.php
│   ├── Services/Audit/
│   │   └── WorkspaceAuditLogger.php
│   ├── Services/Baselines/
│   │   └── BaselineCompareService.php
│   ├── Services/PortfolioTriage/
│   │   └── TenantTriageReviewService.php
│   ├── Support/Audit/AuditActionId.php
│   ├── Support/Baselines/
│   │   ├── BaselineCompareMatrixBuilder.php
│   │   └── Compare/CompareStrategyRegistry.php
│   └── Support/PortfolioCompare/ or Services/PortfolioCompare/
└── tests/
		├── Feature/PortfolioCompare/
		└── Unit/Support/PortfolioCompare/

Structure Decision: keep implementation inside apps/platform, reuse existing compare and portfolio seams, and introduce at most one small PortfolioCompare support/service namespace for the new dual-tenant preview/preflight logic.

Complexity Tracking

Violation Why Needed Simpler Alternative Rejected Because
New compare preview builder dual-tenant compare needs one place to translate existing inventory/baseline truth into a canonical preview contract page-local mapping would duplicate compare logic and drift from existing baseline compare seams
New promotion preflight service readiness reasoning must stay read-only and auditable before any execution path exists bolting readiness rules into the page would make later reuse and testing brittle

Proportionality Review

  • Current operator problem: portfolio operators still lack one bounded surface that answers whether a target tenant can follow a source tenant.
  • Existing structure is insufficient because: existing baseline compare is tenant-vs-reference, not tenant-vs-tenant, and portfolio triage does not compute promotion readiness.
  • Narrowest correct implementation: one canonical page plus one preview builder and one preflight service, no new table, no execution path.
  • Ownership cost created: maintain a small preview/preflight contract and a focused test family.
  • Alternative intentionally rejected: actual promotion execution, persisted promotion drafts, and local compare sidecars were rejected as premature.
  • Release truth: current-release gap, not speculative platform work.

Implementation Strategy

Suggested MVP Scope

MVP = US1 + US2 together. A compare page without a promotion preflight leaves the core decision incomplete, and a preflight without a canonical compare page has no trustworthy operator context.

Incremental Delivery

  1. Reuse current compare, navigation, capability, and audit seams.
  2. Deliver the canonical compare preview.
  3. Add the read-only promotion preflight on top of the same page and services.
  4. Add launch/return continuity from portfolio-triage and tenant-registry context.
  5. Finish with narrow validation and formatting.

Team Strategy

  1. Settle the preview/preflight contracts first.
  2. Parallelize unit tests for preview/preflight rules and feature tests for page/auth behavior.
  3. Serialize merges around the canonical compare page and the shared PortfolioCompare service namespace so the page contract does not drift.