31 KiB
Implementation Plan: Global Context Shell Contract
Branch: 199-global-context-shell-contract | Date: 2026-04-18 | Spec: specs/199-global-context-shell-contract/spec.md
Input: Feature specification from specs/199-global-context-shell-contract/spec.md
Note: This plan keeps the work inside the existing Filament v5 / Livewire v4 admin and tenant panels, reuses WorkspaceContext as the session-backed storage owner, promotes one request-scoped shell resolution contract over the current split logic, and explicitly avoids new persistence, panel proliferation, or a generic context framework.
Summary
Cut one explicit workspace-first global shell contract for /admin and /admin/t/{external_id} by keeping workspace session ownership in WorkspaceContext, consolidating active workspace and tenant resolution behind one request-scoped shell contract consumed by OperateHubShell, aligning switch/select/clear controllers and EnsureFilamentTenantSelected to the same precedence and fallback rules, and reducing the shared context-bar partial to a pure consumer and dispatcher of resolved context. Preserve tenant-safe global search, deny-as-not-found isolation, intended-URL handling, and existing Filament panel topology while replacing scattered partial, controller, middleware, and panel-state heuristics with one documented source-of-truth hierarchy and one explicit invalid-context recovery model.
Contract Ownership
- Source inventory owner:
specs/199-global-context-shell-contract/data-model.mdcontains the canonicalContext Source Inventoryfor every in-scope workspace and tenant context source. - Documented recovery destinations:
- missing or unrecoverable workspace truth falls back to
/admin/choose-workspace - generic referrer-free or sentinel cleanup falls back to
admin.operations.index - tenant-scoped evidence cleanup falls back to
admin.evidence.overview - tenant-bound cleanup with a valid workspace falls back to
admin.workspace.managed-tenants.index - tenant-bound cleanup without recoverable workspace falls back to
admin.home - tenantless-capable workspace routes and canonical workspace record viewers remain on their current route when entitlement remains valid
- missing or unrecoverable workspace truth falls back to
- Workspace switch destination set:
- safe intended
/admin...URL when present and still valid admin.workspace.managed-tenants.indexwhen the resolved workspace has zero selectable tenants/admin/choose-tenantwhen the resolved workspace has multiple selectable tenants- tenant dashboard route under
/admin/t/{external_id}when the resolved workspace has exactly one selectable tenant
- safe intended
- Explicit page-category exceptions:
/admin/choose-workspaceis theworkspace_chooser_exceptionroute/admin/evidence/...except/admin/evidence/overviewis treated astenant_scoped_evidencefor recovery behavior
Technical Context
Language/Version: PHP 8.4.15
Primary Dependencies: Laravel 12, Filament v5, Livewire v4, Pest v4, Tailwind CSS v4, existing WorkspaceContext, OperateHubShell, EnsureFilamentTenantSelected, WorkspaceRedirectResolver, WorkspaceIntendedUrl, TenantPageCategory, and ResolvesPanelTenantContext
Storage: PostgreSQL unchanged plus existing Laravel session keys current_workspace_id, workspace_intended_url, and workspace_last_tenant_ids; no schema change planned
Testing: Pest unit and feature tests, existing Filament or Livewire page tests, and manual shell smoke validation through Laravel Sail; browser automation remains optional and not the proving default
Validation Lanes: fast-feedback, confidence
Target Platform: Laravel monolith web application under apps/platform, running via Sail locally, with shared operator shells on /admin and /admin/t/{external_id} and isolated /system remaining out of scope
Project Type: web application
Performance Goals: Keep shell context resolution request-scoped, DB-and-session-only at render time, avoid any outbound HTTP or queued work during shell hydration, avoid additional N+1 tenant lookups in the topbar, and keep context-bar rendering within existing operator-page latency expectations
Constraints: No new persisted truth, no generic context engine, no cross-plane auth redesign, no hidden page-state ownership inside the shell contract, no global navigation rewrite, no dependency changes, and no new asset pipeline requirements
Scale/Scope: 2 shared operator panels, 1 shared context-bar partial, 3 context mutation endpoints, 5 existing core support classes or middleware seams, targeted updates across existing workspace, monitoring, RBAC, and tenant-RBAC feature seams, and 2 to 4 new narrow regression files for shell resolution and recovery
Constitution Check
GATE: Passed before Phase 0 research. Re-checked after Phase 1 design and still passing.
| Principle | Pre-Research | Post-Design | Notes |
|---|---|---|---|
| Inventory-first / snapshots-second | PASS | PASS | The feature changes shell context truth only. It does not alter inventory, snapshot, or backup product truth. |
| Read/write separation | PASS | PASS | The work changes session-backed scope selection and recovery behavior only. No new Microsoft tenant write, queued work, or operational mutation is introduced. |
| Graph contract path | N/A | N/A | No Microsoft Graph call path is added or modified. |
| Deterministic capabilities | PASS | PASS | Existing workspace and tenant entitlement checks remain authoritative. No new capability strings or auth planes are introduced. |
| Workspace + tenant isolation | PASS | PASS | The design strengthens workspace-first isolation by preventing tenant truth from surviving outside a valid workspace and by standardizing 404 versus tenantless fallback rules. |
| RBAC-UX authorization semantics | PASS | PASS | Non-members remain 404, members without capability remain 403 only after scope is established, and shell context cleanup must not surface inaccessible tenant or workspace truth. |
| Run observability / Ops-UX | PASS | PASS | No OperationRun is introduced or changed. Shell resolution and display remain synchronous request work. |
| Data minimization | PASS | PASS | No new persistence, cache mirror, or derived artifact is added; remembered values remain support state only. |
| Proportionality / anti-bloat | PASS WITH JUSTIFIED SOURCE CONTRACT | PASS WITH JUSTIFIED SOURCE CONTRACT | The feature introduces one bounded source-of-truth hierarchy and one bounded runtime state vocabulary because multiple existing classes already resolve the same context with competing rules. It explicitly avoids a generic engine or persisted context model. |
| UI semantics / few layers | PASS | PASS | The plan keeps one thin request-scoped contract and removes partial-owned context logic instead of adding a presenter or UI framework layer. |
| Filament-native UI | PASS | PASS | Existing Filament panels, routes, middleware, and shared partials remain the implementation path. No hand-built alternate shell system is introduced. |
| Filament v5 / Livewire v4 compliance | PASS | PASS | All touched surfaces remain on Filament v5 and Livewire v4 semantics only. |
| Provider registration location | PASS | PASS | No provider registration change is required. Laravel 11+ provider registration remains in bootstrap/providers.php. |
| Global search hard rule | PASS | PASS | No new globally searchable resource is introduced. Existing searchable resources keep their View or Edit pages, and the contract explicitly preserves tenant-safe global search behavior under resolved shell scope. |
| Destructive action safety | PASS | PASS | No new destructive Filament action is added. Context reset via clear-tenant-context remains a scope action, not a record-destruction action. |
| Asset strategy | PASS | PASS | No new assets or build steps are planned. Existing cd apps/platform && php artisan filament:assets deployment handling remains sufficient. |
Filament-Specific Compliance Notes
- Livewire v4.0+ compliance: The implementation stays on Filament v5 and Livewire v4 pages, middleware, support classes, and shared Blade partials. No legacy Livewire or Filament APIs are introduced.
- Provider registration location: No panel or provider registration changes are planned. Provider registration remains in
bootstrap/providers.php. - Global search: No new searchable resources are added. Existing searchable resources remain
TenantResourceandPolicyResource, both of which already have View pages, and the shell contract must preserve existing tenant-safe and workspace-safe global search semantics. - Destructive actions: The feature introduces no destructive record actions. Existing shell actions
Switch workspace,Select tenant, andClear tenant contextremain scope-setting flows only. Any existing destructive actions elsewhere remain unchanged and continue to require confirmation and authorization under current resource contracts. - Asset strategy: No new JS or CSS assets are planned. Existing deployment handling of
cd apps/platform && php artisan filament:assetsremains unchanged. - Testing plan: Extend or reuse
WorkspaceContextRememberedTenantTest,WorkspaceContextTopbarAndTenantSelectionTest,WorkspaceContextRecoveryDisplayTest,SelectTenantControllerTest,ChooseTenantPageTest,ChooseWorkspacePageTest,ChooseWorkspaceRedirectsToChooseTenantTest,WorkspaceRedirectResolverTest,WorkspaceSwitchUserMenuTest,SwitchWorkspaceRedirectsToTenantRegistrationWhenNoTenantsTest,SwitchWorkspaceControllerTest,GlobalContextShellContractTest,EnsureWorkspaceSelectedMiddlewareTest,WorkspacesResourceIsTenantlessTest,OperationsIndexHeaderTest,AdminGlobalSearchContextSafetyTest,TenantSwitcherScopeTest,TenantActionSurfaceConsistencyTest,OperationsDbOnlyRenderTest, andOperationsActionsEnqueueRunTestso the contract is proven without introducing a new browser or heavy-governance family.
Test Governance Check
- Test purpose / classification by changed surface: Unit for runtime context resolution and invalidation rules in support classes; Feature for controller flows, middleware behavior, panel entry routes, and shared shell rendering.
- Affected validation lanes: fast-feedback, confidence.
- Why this lane mix is the narrowest sufficient proof: The feature changes request-time scope resolution, redirects, session mutation, and rendered shell truth. These are best proven with unit tests around the support layer plus feature tests over routes and rendered pages. A browser lane is not required unless later implementation introduces client-only shell behavior that feature tests cannot observe.
- Narrowest proving command(s):
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Unit/Support/OperateHub/OperateHubShellResolutionTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/WorkspaceContextTopbarAndTenantSelectionTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces/SelectTenantControllerTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces/ChooseTenantPageTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces/ChooseWorkspacePageTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Workspaces/WorkspaceRedirectResolverTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Spec085/OperationsIndexHeaderTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Monitoring/OperationsDbOnlyRenderTest.phpcd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Monitoring/OperationsActionsEnqueueRunTest.php
- Fixture / helper / factory / seed / context cost risks: Moderate but bounded. Tests need workspace membership, tenant membership, workspace session state, remembered tenant session state, and selective Filament tenant seeding. No new provider bootstraps, seeds, queues, or browser fixtures are expected.
- Expensive defaults or shared helper growth introduced?: No. Existing helper seams such as
createUserWithTenant()and targeted session seeding stay opt-in. The plan should avoid introducing a global full-shell fixture as the new default. - Heavy-family additions, promotions, or visibility changes: none.
- Non-functional shell-render proof: Reuse
OperationsDbOnlyRenderTestandOperationsActionsEnqueueRunTestso the shell-anchor workspace surfaces stay DB-only and non-enqueuing during render. - Closing validation and reviewer handoff: Reviewers should confirm that new tests stay in unit and feature lanes, that invalid-context recovery is proven without browser escalation, that shell display and actual resolved context match, and that remembered-tenant invalidation remains explicit in test names and assertions.
- Budget / baseline / trend follow-up: none beyond a small increase in focused unit and feature runtime.
- Review-stop questions: Is the new proof actually feature-level? Did any helper make full workspace or tenant context implicit by default? Did the implementation create a second source of truth in tests or UI? Did any browser-only assertion sneak in without necessity?
- Escalation path: document-in-feature.
- Why no dedicated follow-up spec is needed: Test cost remains feature-local as long as the work stays in the existing support layer, controllers, middleware, and shared shell partials without introducing a new test harness or a new browser family.
Phase 0 Research
Research outcomes are captured in specs/199-global-context-shell-contract/research.md.
Key decisions:
- Keep
WorkspaceContextas the session-backed owner of workspace selection and remembered tenant storage, but stop treating it andOperateHubShellas parallel visible truths. - Use one request-scoped resolved shell contract to unify route tenant, Filament tenant, remembered tenant, and tenantless fallback semantics instead of repeating those rules in controllers, partials, and middleware.
- Preserve the existing effective precedence for active tenant on admin surfaces: valid route tenant first, then explicit tenant selection, then validated query-backed tenant hints only on explicitly allowed workspace-scoped routes, then validated Filament tenant, then remembered tenant only on workspace-scoped pages, with tenant-bound pages rejecting query-hint and remembered fallback.
- Reduce
context-bar.blade.phpto a pure consumer and dispatcher of resolved context instead of letting it re-discover state on its own. - Make invalid-context recovery explicit and page-category-aware so missing workspace, missing tenant, incompatible tenant, and inaccessible tenant produce deterministic fallback rather than mixed 404, silent clear, or stale shell display behavior.
- Extend existing unit and feature seams instead of introducing a browser-first shell test family.
Phase 1 Design
Design artifacts are created under specs/199-global-context-shell-contract/:
research.md: shell source-of-truth decisions, risks, and rejected alternativesdata-model.md: runtime shell-context entities, validation rules, and state transitionscontracts/global-context-shell.logical.openapi.yaml: internal logical HTTP contract for shell context entry and mutation flowsquickstart.md: implementation and verification workflow for Spec 199
Design highlights:
- Keep the contract derived and request-scoped. No new table or persisted shell artifact is introduced.
- Preserve
WorkspaceContextas the storage owner and existing route controllers as explicit mutation entry points, but make them consume one resolved shell contract instead of each re-defining fallback behavior. - Treat
OperateHubShellas the canonical shared shell resolver for admin-facing context, with tenant-panel-native semantics remaining route-bound and panel-native where appropriate. - Keep the workspace chooser flow as the explicit current-release
workspace_chooser_exceptioninstead of letting missing-workspace handling stay implicit. - Encode invalid-context recovery as explicit outcome types tied to route requirements and
TenantPageCategory, instead of leaving recovery scattered acrosscontext-bar.blade.php,ClearTenantContextController, and middleware heuristics. - Keep page-local filters, tabs, inspect state, and other page-state concerns out of the shell contract so tenant-prefilter behavior remains explicit and opt-in.
- Keep
EnsureFilamentTenantSelectedandResolvesPanelTenantContextas consumers of the contract so shared panel behavior does not drift.
Phase 1 - Agent Context Update
Executed command:
.specify/scripts/bash/update-agent-context.sh copilot
This feature does not add a new language or framework, but the agent-context refresh still runs after design artifacts are complete so the current feature context is recorded in the agent guidance files.
Project Structure
Documentation (this feature)
specs/199-global-context-shell-contract/
├── spec.md
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── global-context-shell.logical.openapi.yaml
├── checklists/
│ └── requirements.md
└── tasks.md
Source Code (repository root)
apps/platform/
├── app/
│ ├── Filament/
│ │ └── Concerns/
│ │ ├── ResolvesPanelTenantContext.php # MODIFY
│ │ └── ScopesGlobalSearchToTenant.php # REUSE / possible small adjust
│ ├── Http/
│ │ └── Controllers/
│ │ ├── SwitchWorkspaceController.php # MODIFY
│ │ ├── SelectTenantController.php # MODIFY
│ │ └── ClearTenantContextController.php # MODIFY
│ ├── Providers/
│ │ └── Filament/
│ │ ├── AdminPanelProvider.php # REUSE / possible small adjust
│ │ └── TenantPanelProvider.php # REUSE / possible small adjust
│ └── Support/
│ ├── Middleware/
│ │ └── EnsureFilamentTenantSelected.php # MODIFY
│ ├── OperateHub/
│ │ └── OperateHubShell.php # MODIFY
│ ├── Tenants/
│ │ └── TenantPageCategory.php # MODIFY
│ └── Workspaces/
│ ├── WorkspaceContext.php # MODIFY
│ ├── WorkspaceIntendedUrl.php # REUSE / possible small adjust
│ └── WorkspaceRedirectResolver.php # MODIFY
├── resources/
│ └── views/
│ └── filament/
│ └── partials/
│ └── context-bar.blade.php # MODIFY
└── tests/
├── Unit/
│ └── Support/
│ ├── OperateHub/
│ │ └── OperateHubShellResolutionTest.php # NEW
│ └── Workspaces/
│ └── WorkspaceContextRememberedTenantTest.php # MODIFY
└── Feature/
├── Filament/
│ ├── WorkspaceContextTopbarAndTenantSelectionTest.php # MODIFY
│ └── WorkspaceContextRecoveryDisplayTest.php # NEW
├── Monitoring/
│ ├── OperationsActionsEnqueueRunTest.php # MODIFY / VERIFY
│ └── OperationsDbOnlyRenderTest.php # MODIFY / VERIFY
├── Rbac/
│ ├── AdminGlobalSearchContextSafetyTest.php # MODIFY
│ └── TenantActionSurfaceConsistencyTest.php # MODIFY
├── Spec085/
│ └── OperationsIndexHeaderTest.php # MODIFY
├── TenantRBAC/
│ └── TenantSwitcherScopeTest.php # MODIFY
└── Workspaces/
├── ChooseTenantPageTest.php # MODIFY
├── ChooseWorkspacePageTest.php # MODIFY
├── ChooseWorkspaceRedirectsToChooseTenantTest.php # MODIFY
├── EnsureWorkspaceSelectedMiddlewareTest.php # MODIFY
├── GlobalContextShellContractTest.php # NEW
├── SelectTenantControllerTest.php # MODIFY
├── SwitchWorkspaceControllerTest.php # NEW
├── WorkspaceRedirectResolverTest.php # MODIFY
├── SwitchWorkspaceRedirectsToTenantRegistrationWhenNoTenantsTest.php # MODIFY
├── WorkspaceSwitchUserMenuTest.php # MODIFY
└── WorkspacesResourceIsTenantlessTest.php # MODIFY
Structure Decision: Keep the work entirely inside the existing Laravel and Filament monolith under apps/platform. Reuse current support classes, controllers, middleware, panel providers, and the shared context-bar partial. Add only narrow new tests and, if the implementation proves it necessary, a tiny request-scoped result structure inside the existing support layer rather than a new framework directory.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| Bounded new source-of-truth contract and runtime shell-context taxonomy | Multiple existing classes and the shared shell partial already resolve active workspace and tenant context with competing precedence and recovery rules. The feature needs one explicit request-scoped contract to stop drift now. | Pure controller or partial cleanup would keep resolution logic split across WorkspaceContext, OperateHubShell, middleware, panel state, and the Blade partial, which would preserve the exact ambiguity Spec 199 exists to remove. |
Proportionality Review
- Current operator problem: Operators and reviewers cannot reliably trust the shell to answer where they are operating, which tenant is active, or what a switch or clear action will do.
- Existing structure is insufficient because: The current rules live across
WorkspaceContext,OperateHubShell, controllers, middleware, panel tenancy, andcontext-bar.blade.php, so local cleanup in one place cannot produce one shared source of truth. - Narrowest correct implementation: Keep workspace and remembered-tenant storage in the current session-backed support layer, introduce one explicit resolved shell contract for the request, and make existing shell consumers use it. Do not add persistence or a generic engine.
- Ownership cost created: Reviewers must maintain one shared shell source hierarchy, one invalid-context taxonomy, and focused unit and feature regression coverage for switch, select, clear, restore, and recovery.
- Alternative intentionally rejected: A generic multi-panel context framework was rejected as overproduction, and a partial-only cleanup was rejected as insufficient.
- Release truth: current-release operator trust and scope clarity
Implementation Strategy
Phase A - Canonicalize one resolved shell context
Goal: Replace competing runtime context truths with one request-scoped resolved contract while keeping existing session-backed storage ownership intact.
| Step | File | Change |
|---|---|---|
| A.0 | specs/199-global-context-shell-contract/data-model.md |
Maintain the canonical Context Source Inventory so every in-scope source has one declared role, one owner, and one validation note. |
| A.1 | apps/platform/app/Support/OperateHub/OperateHubShell.php |
Expand the shell support seam so it can resolve one canonical shell context for the current request, including workspace, tenant, page category, source precedence, tenantless validity, and invalid-context recovery metadata. |
| A.2 | apps/platform/app/Support/Workspaces/WorkspaceContext.php |
Keep workspace and remembered-tenant session ownership here, but align helper methods to the canonical contract by making remembered values restore-only and by making invalidation rules explicit and reusable. |
| A.3 | apps/platform/app/Filament/Concerns/ResolvesPanelTenantContext.php |
Make admin-panel tenant context consumption route through the canonical resolved shell contract instead of ad hoc tenant lookup. |
| A.4 | apps/platform/tests/Unit/Support/OperateHub/OperateHubShellResolutionTest.php and apps/platform/tests/Unit/Support/Workspaces/WorkspaceContextRememberedTenantTest.php |
Add or extend unit coverage for route-first, Filament-tenant, remembered-tenant, tenantless, and invalid remembered-context branches. |
Phase B - Align explicit scope mutation flows
Goal: Make switch, select, clear, restore, and intended-return behavior follow the same source hierarchy and fallback matrix.
| Step | File | Change |
|---|---|---|
| B.1 | apps/platform/app/Http/Controllers/SwitchWorkspaceController.php and apps/platform/app/Support/Workspaces/WorkspaceRedirectResolver.php |
Ensure workspace switching re-evaluates tenant compatibility deterministically, clears incompatible tenant state, and uses one redirect strategy after intended-URL consumption. |
| B.2 | apps/platform/app/Http/Controllers/SelectTenantController.php |
Keep explicit tenant selection as the only user-driven tenant activation flow on workspace-level pages, but align it with the canonical shell contract, selector-operability rules, and recovery semantics. |
| B.3 | apps/platform/app/Http/Controllers/ClearTenantContextController.php and apps/platform/app/Support/Tenants/TenantPageCategory.php |
Standardize tenant-clear recovery and route compatibility rules so tenant-required pages, workspace pages, evidence paths, and canonical record viewers resolve either to same-route tenantless workspace state or to the documented destinations admin.workspace.managed-tenants.index, admin.evidence.overview, admin.operations.index, admin.operations.view, or admin.home as appropriate. |
| B.4 | apps/platform/app/Support/Workspaces/WorkspaceIntendedUrl.php |
Reuse or slightly refine intended-URL handling so switch and recovery flows return to safe shell-compatible destinations only. |
| B.5 | apps/platform/tests/Feature/Workspaces/SwitchWorkspaceControllerTest.php, SwitchWorkspaceRedirectsToTenantRegistrationWhenNoTenantsTest.php, WorkspaceRedirectResolverTest.php, SelectTenantControllerTest.php, and ChooseWorkspacePageTest.php |
Cover workspace switch redirects, explicit tenant selection, workspace-independent chooser exceptions, tenant compatibility, and invalid or inaccessible context requests. |
Phase C - Make the shell surfaces consume the contract
Goal: Reduce the shared shell UI to one truthful display and action entry point.
| Step | File | Change |
|---|---|---|
| C.1 | apps/platform/resources/views/filament/partials/context-bar.blade.php |
Remove partial-owned source precedence and make the topbar display and controls derive only from the canonical resolved shell context and explicit available actions. |
| C.2 | apps/platform/app/Providers/Filament/AdminPanelProvider.php and TenantPanelProvider.php |
Keep the shared render-hook strategy, but adjust only if needed so both panels consume the same shared shell contract without panel-specific truth drift. |
| C.3 | apps/platform/tests/Feature/Filament/WorkspaceContextTopbarAndTenantSelectionTest.php, OperationsIndexHeaderTest.php, and WorkspaceContextRecoveryDisplayTest.php |
Cover active tenant display, explicit tenantless display, stale or inaccessible remembered context clearing, and panel-consistent shell output. |
Phase D - Harden page-category, middleware, and scope-safety behavior
Goal: Ensure that route type and access boundaries determine whether tenantless fallback, redirect, or 404 is correct.
| Step | File | Change |
|---|---|---|
| D.1 | apps/platform/app/Support/Middleware/EnsureFilamentTenantSelected.php |
Replace ad hoc tenant-selection and navigation heuristics with canonical shell-context checks while preserving tenant-bound route enforcement and workspace isolation. |
| D.2 | apps/platform/app/Support/Tenants/TenantPageCategory.php |
Tighten route categorization only where current path-pattern rules are too implicit for the new invalid-context matrix, including the explicit workspace_chooser_exception and tenant_scoped_evidence cases. |
| D.3 | apps/platform/tests/Feature/Workspaces/GlobalContextShellContractTest.php |
Add focused feature coverage for missing workspace, missing tenant, invalid tenant, inaccessible tenant, tenant-bound route fallback, and workspace-scoped tenantless behavior. |
Phase E - Close with regression protection and operator verification
Goal: Leave the repo with one documented shell contract, narrow regression coverage, and a clear manual validation path.
| Step | File | Change |
|---|---|---|
| E.1 | Existing unit and feature suites listed above | Extend current tests instead of creating a browser-heavy new family. Keep resolution, redirect, and display assertions explicit in names and expectations. |
| E.2 | apps/platform/tests/Feature/Monitoring/OperationsDbOnlyRenderTest.php and apps/platform/tests/Feature/Monitoring/OperationsActionsEnqueueRunTest.php |
Keep shell-anchor workspace rendering DB-only and non-enqueuing so the contract does not widen runtime behavior accidentally. |
| E.3 | specs/199-global-context-shell-contract/quickstart.md |
Record implementation order, manual smoke steps, documented fallback targets, and exact verification commands for workspace switch, tenant select, tenant clear, invalid recovery, panel parity, and non-functional render proof. |
| E.4 | specs/199-global-context-shell-contract/tasks.md |
Break work into dependency-ordered tasks after this plan is accepted. |
Key Design Decisions
D-001 - WorkspaceContext remains the storage owner, not the visible shell owner
The session-backed workspace and remembered-tenant state already live in WorkspaceContext. The right move is to keep it as the storage owner and validation seam, not replace it with persistence or a new framework.
D-002 - One request-scoped shell contract must replace partial-owned precedence
The shell currently re-discovers scope inside Blade, middleware, and controllers. The plan centralizes that into one request-scoped resolved contract consumed everywhere else.
D-003 - Route-bound tenant remains strongest on tenant-required surfaces
The existing effective precedence already treats route tenant as strongest, then validated panel tenant, then remembered tenant only on workspace-scoped pages. The plan keeps that precedence but makes it explicit and testable.
D-004 - Tenant clear and invalid-context recovery need the same route-compatibility matrix
The product currently mixes previous-URL redirecting, silent remembered-context clearing, and 404 behavior. The plan aligns tenant clear and invalid recovery under one page-category-aware outcome matrix.
D-005 - The context bar becomes a consumer and dispatcher only
The shared shell partial should show the resolved contract and expose explicit switch, select, and clear actions, but it should not be allowed to own a second context truth.