TenantAtlas/specs/199-global-context-shell-contract/research.md
Ahmed Darrazi b515796839
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 53s
feat: finalize global shell context contract
2026-04-18 15:59:02 +02:00

8.8 KiB

Research: Global Context Shell Contract

Decision 1 - Keep WorkspaceContext as the session-backed storage owner, but not as a competing visible shell truth

  • Decision: WorkspaceContext remains the owner of current_workspace_id, workspace_intended_url, and workspace_last_tenant_ids, while one request-scoped shell contract becomes the only visible and consumable truth for the active workspace and tenant.
  • Rationale: The current repo already uses WorkspaceContext correctly as the place where session-backed workspace and remembered-tenant state live. The ambiguity comes from visible shell truth being recomputed separately in OperateHubShell, middleware, controllers, and context-bar.blade.php, not from the storage location itself.
  • Alternatives considered:
    • Replace WorkspaceContext with a new persistence model: rejected because the spec explicitly disallows new persisted truth.
    • Leave WorkspaceContext and OperateHubShell as parallel visible truths: rejected because that preserves the ambiguity Spec 199 exists to remove.

Decision 2 - Preserve the current effective tenant precedence, but make it explicit and shared

  • Decision: On admin-shell surfaces, active tenant resolution remains: valid route tenant first, then explicit tenant selection, then a validated query-backed tenant hint only on workspace-scoped routes that explicitly allow it, then validated Filament::getTenant(), then remembered tenant only on workspace-scoped pages, then explicit tenantless fallback. Tenant-bound pages do not accept query hints or remembered tenant fallback as normal active truth.
  • Rationale: This is already the effective behavior in OperateHubShell::resolveActiveTenant(), and it matches the repo's workspace-first intent while keeping route-bound tenant requirements strongest where the route semantics demand them.
  • Alternatives considered:
    • Make remembered tenant equal to route or panel tenant: rejected because remembered context must remain support-only.
    • Make Filament tenant always win even when route tenant is explicit: rejected because tenant-bound routes need explicit route authority.

Decision 3 - Convert the context bar into a pure consumer and dispatcher of the resolved contract

  • Decision: context-bar.blade.php should stop owning resolution rules and should render only the already resolved workspace and tenant state plus the explicit switch, select, and clear actions that mutate shell scope.
  • Rationale: The current partial queries WorkspaceContext, OperateHubShell, Filament::getTenant(), route name, query tenant, and TenantPageCategory in one Blade file. That makes the shell UI a second source of truth and hides business rules in a rendering surface.
  • Alternatives considered:
    • Keep the partial as-is and only tweak labels: rejected because the issue is structural, not cosmetic.
    • Move all logic into Alpine or client-side state: rejected because the authoritative context is server-side and request-scoped.

Decision 4 - Invalid-context recovery must be explicit and page-category-aware

  • Decision: Missing workspace, missing tenant, incompatible tenant, inaccessible tenant, and invalid remembered context should map to explicit recovery outcomes that depend on route type and page category rather than on ad hoc previous-URL heuristics.
  • Rationale: The current repo mixes silent remembered-context clearing, page-category redirect logic in ClearTenantContextController, and 404 behavior in middleware and route guards. That inconsistency is the operator-facing confusion Spec 199 is meant to remove.
  • Alternatives considered:
    • Always 404 on any invalid tenant state: rejected because workspace-scoped pages intentionally support a valid tenantless state.
    • Always redirect to /admin/operations: rejected because tenant-bound routes and canonical record viewers need different recovery behavior.

Decision 5 - Keep the solution in the current support layer instead of introducing a generic context engine

  • Decision: The implementation should stay inside the existing support layer around WorkspaceContext, OperateHubShell, middleware, and controllers. If a new runtime object is needed, it should be a narrow request-scoped result structure only.
  • Rationale: The repo already has the needed building blocks. The problem is coordination and precedence, not lack of extension points.
  • Alternatives considered:
    • New multi-panel context framework with registries, strategies, or factories: rejected under ABSTR-001 and PROP-001.
    • Panel-specific duplicated fixes: rejected because the same shell contract spans both admin and tenant panels.

Decision 6 - Reuse existing unit and feature seams as the primary proof strategy

  • Decision: The proving default for Spec 199 is unit coverage around runtime resolution and feature coverage around controllers, middleware, and rendered shell surfaces. Browser automation stays optional and secondary.
  • Rationale: The current repo already has targeted tests for remembered-tenant invalidation, context-bar display, choose-tenant behavior, redirect resolution, and clear-tenant fallbacks. Extending these seams is cheaper and more precise than introducing a new browser-heavy shell suite.
  • Alternatives considered:
    • Browser-first shell regression family: rejected because the contract is server-driven and the narrowest sufficient proof already exists in unit and feature seams.
    • Manual-only verification: rejected because Spec 199 changes request-time contract behavior that should be regression-protected.

Decision 7 - Preserve panel topology and cross-plane boundaries unchanged

  • Decision: /admin and /admin/t/{external_id} continue to share the web guard and shared shell contract, while /system remains out of scope and isolated under the platform guard.
  • Rationale: The spec is about global admin and tenant shell truth, not about re-cutting panel or guard boundaries.
  • Alternatives considered:
    • Fold tenant-panel behavior into /admin only: rejected because tenant-panel-native routing and tenancy already exist and remain valid.
    • Expand the feature to /system: rejected because cross-plane behavior is a different product boundary.

Decision 8 - Keep global search tenant-safe under the new shell contract without changing searchable resources

  • Decision: The shell contract must preserve existing tenant-safe and workspace-safe global search behavior, but it does not introduce or remove searchable resources.
  • Rationale: A context contract that widens tenant-owned global search results on workspace-scoped surfaces would violate the existing RBAC and tenant isolation guarantees.
  • Alternatives considered:
    • Ignore global search as unrelated: rejected because the resolved shell context influences whether tenant-owned search results are safe to return.
    • Expand searchable resource scope during shell cleanup: rejected because the spec is about context truth, not search surface growth.

Decision 9 - Keep the explicit source inventory in the feature data model artifact

  • Decision: The canonical source inventory for Spec 199 lives in data-model.md under Context Source Inventory, not in controller comments or scattered plan prose.
  • Rationale: The feature needs one maintained place that lists every in-scope source, its source role, the seam that owns it, and the validation boundary it must pass.
  • Alternatives considered:
    • Keep the source inventory implicit in the plan only: rejected because task generation and implementation reviews need a concrete artifact that can be updated without re-reading the entire plan narrative.
    • Split the inventory across multiple code comments: rejected because that would recreate the same drift problem inside the documentation layer.

Decision 10 - Document fallback destinations from the current route families instead of inventing abstract recovery targets

  • Decision: The contract documents the existing workspace-safe fallback routes used by the product today: admin.operations.index, admin.evidence.overview, admin.workspace.managed-tenants.index, admin.home, and route-stable workspace record viewers where they remain valid.
  • Rationale: The repo already has concrete fallback behavior in ClearTenantContextController, WorkspaceRedirectResolver, and route families around monitoring, evidence, and tenant selection. The contract should reflect that real product behavior instead of describing a generic fallback abstraction.
  • Alternatives considered:
    • Keep fallback wording abstract as “workspace-level fallback”: rejected because that leaves implementers and reviewers guessing about the actual destination.
    • Collapse all recovery into /admin/operations: rejected because tenant-bound and evidence-specific flows already use more precise workspace-safe landings.