Automated commit and PR created by Copilot per user request. Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #287
27 KiB
Implementation Plan: Plans, Entitlements & Billing Readiness
Branch: 247-plans-entitlements-billing-readiness | Date: 2026-04-27 | Spec: /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/247-plans-entitlements-billing-readiness/spec.md
Input: Feature specification from /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/247-plans-entitlements-billing-readiness/spec.md
Note: This template is filled in by the /speckit.plan command. See .specify/scripts/ for helper scripts.
Summary
- Extend the existing workspace settings foundation with one bounded workspace entitlement slice: one code-owned plan-profile catalog, two first-slice entitlement keys, explicit workspace override values plus rationale, and one derived decision path that surfaces effective value, source, rationale, and usage truth.
- Reuse
WorkspaceSetting,SettingsResolver,SettingsWriter,WorkspaceSettings, the capability registries,ManagedTenantOnboardingWizard,ReviewPackService, the current review-pack Filament entry points, and the system directory workspace detail page rather than introducing a billing/account domain. - Hard enforcement remains narrow: block onboarding activation before tenant lifecycle mutation and block review-pack generation before
ReviewPackorOperationRuncreation, while preserving existing queued-start UX when entitlement allows execution.
Technical Context
Language/Version: PHP 8.4 (Laravel 12)
Primary Dependencies: Filament v5 + Livewire v4 + Laravel 12, existing workspace settings stack, ReviewPackService, capability registries
Storage: PostgreSQL via existing workspace_settings persistence; no new table or billing/account model
Testing: Pest feature and unit tests via Laravel Sail
Validation Lanes: fast-feedback, confidence
Target Platform: Monorepo Laravel web application in apps/platform, using Filament admin and system panels
Project Type: web
Performance Goals: Resolve entitlement truth from existing settings plus one scoped usage aggregate; perform no new external calls during page render; preserve existing review-pack dedupe and queued-start behavior when allowed
Constraints: Keep scope to one workspace plan profile, two entitlement keys, explicit workspace overrides with rationale, read-only system visibility, 404 for non-members/wrong plane, 403 for members missing capability, truthful business-state block for otherwise authorized actors, and no checkout/invoice/provider/subscription lifecycle work
Scale/Scope: One new bounded entitlement resolver, one entitlement section on the existing workspace settings page, one onboarding completion gate, one review-pack action family gate, one read-only system summary, and focused Sail/Pest coverage
Filament v5 / Panel Notes
- Livewire v4.0+ compliance: The slice stays inside existing Filament v5 pages, widgets, resources, and Livewire-backed actions. No Livewire v3 assumptions or compatibility work are introduced.
- Provider registration location: No panel/provider registration changes are planned. Existing Laravel 12 + Filament provider registration remains in
bootstrap/providers.php. - Global search: No new globally searchable resource is introduced. Touched existing resources already have dedicated view pages where applicable, and current global-search behavior remains unchanged.
- Destructive and high-impact actions: Existing onboarding draft cancellation/deletion remain
->requiresConfirmation()plus capability enforcement. Any new override-reset action must also require confirmation because it can change runtime access. Entitlement denials themselves are non-destructive business-state blocks, not hidden RBAC failures. - Asset strategy: No new panel or shared assets are planned. Deployment remains unchanged, including
cd apps/platform && php artisan filament:assetswhen registered Filament assets are deployed.
UI / Surface Guardrail Plan
- Guardrail scope: changed surfaces
- Native vs custom classification summary: mixed
- Shared-family relevance: workspace settings, action gating/helper text, review-pack queued-start UX, read-only system diagnostics
- State layers in scope: page, detail
- Audience modes in scope: operator-MSP, support-platform
- Decision/diagnostic/raw hierarchy plan: decision-first on workspace settings, onboarding completion, and review-pack entry points; diagnostics-second on the read-only system page; no raw payload disclosure in the first slice
- Raw/support gating plan: capability-gated system-plane diagnostics only; no new raw/support payload section on admin surfaces
- One-primary-action / duplicate-truth control: workspace settings remains the only mutation surface for commercial posture; onboarding and review-pack surfaces show only the decision truth needed for the current action; the system directory mirrors resolved truth read-only
- Handling modes by drift class or surface: review-mandatory for shared action-family gating and cross-plane wording consistency
- Repository-signal treatment: review-mandatory because the slice spans admin and system planes plus an OperationRun-starting workflow family
- Special surface test profiles: standard-native-filament, shared-detail-family, monitoring-state-page
- Required tests or manual smoke: functional-core, state-contract
- Exception path and spread control: none planned; all first-slice surfaces must consume the same resolved decision object or a thin projection from it
- Active feature PR close-out entry: Guardrail
Shared Pattern & System Fit
- Cross-cutting feature marker: yes
- Systems touched:
WorkspaceSetting,SettingsRegistry,SettingsResolver,SettingsWriter, workspace settings audit logging,ManagedTenantOnboardingWizard,ReviewPackService, current review-pack action surfaces,OperationUxPresenter,OperationRunLinks,Capabilities,PlatformCapabilities, and the system directory workspace detail page - Shared abstractions reused:
SettingsResolver,SettingsWriter,App\Services\Audit\WorkspaceAuditLogger,App\Support\Auth\Capabilities,App\Support\Auth\PlatformCapabilities,App\Support\Rbac\UiEnforcement,ReviewPackService,OperationUxPresenter,OperationRunLinks - New abstraction introduced? why?: one bounded
WorkspaceEntitlementResolveris justified because existing settings helpers resolve individual keys but do not provide plan-profile defaults, override rationale, usage context, or action-ready allow/block truth across multiple surfaces - Why the existing abstraction was sufficient or insufficient: existing settings infrastructure is sufficient for persistence, validation, and audit; it is insufficient for multi-surface commercial decision truth because the same effective result must drive settings readback, onboarding activation gating, review-pack gating, and system diagnostics
- Bounded deviation / spread control: hard enforcement for review packs belongs in
ReviewPackServiceand onboarding activation belongs in the existing wizard action; UI surfaces may project the same decision but must not create page-local entitlement rules
OperationRun UX Impact
- Touches OperationRun start/completion/link UX?: yes
- Central contract reused: existing shared review-pack OperationRun start UX through
ReviewPackService,OperationUxPresenter, andOperationRunLinks - Delegated UX behaviors: queued toast, run link, run-enqueued browser event, dedupe messaging, blocked-before-start behavior, tenant/workspace-safe URL resolution, and existing terminal notifications remain on the current shared path when entitlement allows generation
- Surface-owned behavior kept local: workspace settings helper text, onboarding completion blocked explanation, and review-pack helper text/disabled state remain local projections of the resolved decision
- Queued DB-notification policy: unchanged explicit opt-in only; blocked attempts create no run and therefore no queued notification
- Terminal notification path: unchanged central lifecycle mechanism for existing review-pack runs only
- Exception path: none
Provider Boundary & Portability Fit
- Shared provider/platform boundary touched?: no
- Provider-owned seams: N/A
- Platform-core seams: workspace commercial vocabulary, plan profile labels, entitlement source labels, override rationale, read-only support visibility
- Neutral platform terms / contracts preserved:
workspace,plan profile,managed tenant limit,review pack generation,override reason,source - Retained provider-specific semantics and why: none; review-pack generation is provider-backed operationally, but the new entitlement vocabulary remains platform-core and provider-neutral
- Bounded extraction or follow-up path: none
Constitution Check
GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.
- Inventory-first: PASS - this slice adds workspace-owned product truth, not new inventory or snapshot semantics.
- Read/write separation: PASS - settings writes stay inside the existing audited settings path; onboarding and review-pack enforcement block before mutation or run creation; high-impact reset actions remain confirmation-protected.
- Graph contract path: PASS - no new Graph calls are introduced; entitlement evaluation is local to settings, workspace counts, and existing review-pack start logic.
- Deterministic capabilities: PASS - capability checks remain registry-backed through
CapabilitiesandPlatformCapabilities. - RBAC-UX: PASS -
/adminand/systemstay separated; wrong-plane and non-member access remain 404; member-without-capability remains 403; no raw capability strings are introduced. - Workspace isolation: PASS - workspace membership and workspace context remain required for admin-plane surfaces.
- RBAC-UX destructive confirmation: PASS - existing onboarding destructive actions already confirm, and any new override-reset action must also use
->requiresConfirmation(). - RBAC-UX global search: PASS - no new searchable resource or search scope is added.
- Tenant isolation: PASS - onboarding and review-pack surfaces remain tenant-safe; no cross-tenant leakage is introduced.
- Run observability: PASS - review-pack generation keeps the existing
OperationRunpath when allowed, and blocked attempts stop beforeOperationRuncreation. - OperationRun start UX: PASS - shared review-pack start UX is preserved; no local queued-toast composition is planned.
- Ops-UX 3-surface feedback: PASS - existing review-pack feedback stays toast + progress surfaces + terminal notification only when a run exists.
- Ops-UX lifecycle: PASS - no new
OperationRuntransitions are introduced. - Ops-UX summary counts: N/A - no summary-count shape change is planned.
- Ops-UX guards: N/A - no new OperationRun guard rule is required for the planning slice.
- Ops-UX system runs: N/A - no initiator-null behavior is touched.
- Automation: N/A - no new queued or scheduled workflow family is introduced.
- Data minimization: PASS - no secrets, billing payloads, or provider credentials are persisted for entitlements.
- Test governance (TEST-GOV-001): PASS - the plan stays in focused unit plus feature lanes with explicit commands and local fixtures only.
- Proportionality (PROP-001): PASS - persistence stays in existing workspace settings; only one bounded resolver is added for multi-surface truth.
- No premature abstraction (ABSTR-001): PASS - no new registry, interface, or framework is planned; the profile catalog remains plain code-owned data.
- Persisted truth (PERSIST-001): PASS - no new table or durable artifact is introduced; all new truth stays in existing
workspace_settingsrows. - Behavioral state (STATE-001): PASS - over-limit and blocked states remain derived behavior, not a new persisted lifecycle model.
- UI semantics (UI-SEM-001): PASS - the design prefers direct mapping from resolved decision truth to UI helper text or summary rows.
- Shared pattern first (XCUT-001): PASS - the design reuses existing settings, audit, review-pack, and capability paths first.
- Provider boundary (PROV-001): PASS - the entitlement vocabulary remains platform-core and provider-neutral.
- V1 explicitness / few layers (V1-EXP-001, LAYER-001): PASS - the narrow shape is explicit settings keys plus one resolver and thin UI projections.
- Spec discipline / bloat check (SPEC-DISC-001, BLOAT-001): PASS - the only added structural element is one resolver, justified below.
- Badge semantics (BADGE-001): PASS - if source or availability is badged later, implementation must reuse existing badge infrastructure or stay text-only; no page-local badge taxonomy is planned.
- Filament-native UI (UI-FIL-001): PASS - the slice extends existing Filament pages, widgets, resources, and the existing system detail view.
- Filament-native UI local Blade/Tailwind: PASS - the only custom view touch remains the current system directory Blade view, which must preserve existing Filament visual language.
- UI/UX surface taxonomy (UI-CONST-001 / UI-SURF-001): PASS - existing singleton settings, guided workflow, action family, and read-only detail surface types remain intact.
- Decision-first operating model (DECIDE-001): PASS - workspace settings remains primary, onboarding and review packs stay contextual decision points, and the system page remains tertiary diagnostics.
- Audience-aware disclosure (DECIDE-AUD-001 / OPSURF-001): PASS - settings and action surfaces stay operator-first, while the system page is support-platform and read-only.
- UI/UX inspect model (UI-HARD-001): PASS - no duplicate inspect affordances are added.
- UI/UX action hierarchy (UI-HARD-001 / UI-EX-001): PASS - no new parallel action hierarchy is introduced; current action families remain primary where already present.
- UI/UX scope, truth, and naming (UI-HARD-001 / UI-NAMING-001 / OPSURF-001): PASS - product-facing labels remain narrow and non-billing.
- UI/UX placeholder ban (UI-HARD-001): PASS - no placeholder action groups are planned.
- UI naming (UI-NAMING-001): PASS - labels remain
Plan profile,Managed tenant limit,Review pack generation, andOverride reason. - Operator surfaces (OPSURF-001): PASS - mutation scope is explicit and system-plane visibility remains read-only.
- Operator surface page contract: PASS - the spec already defines the required page and action contracts.
- Filament UI Action Surface Contract: PASS - touched surfaces already have action contracts or exemptions; the plan preserves them while adding entitlement truth.
- Filament UI UX-001 (Layout & IA): PASS - no new page shell or alternate layout is planned.
- Action-surface discipline (ACTSURF-001 / HDR-001): PASS - workspace settings remains the primary configuration surface; review-pack generation remains the primary reporting action where already present.
- UI review workflow: PASS - guardrail, shared-family, and exception posture remain explicit in this plan.
Test Governance Check
- Test purpose / classification by changed surface:
Unitfor the bounded resolver and profile defaults;Featurefor workspace settings, onboarding, review-pack entry surfaces, and the system directory page - Affected validation lanes:
fast-feedback,confidence - Why this lane mix is the narrowest sufficient proof: the business truth is a deterministic resolver plus existing Filament/Livewire action paths; browser and heavy-governance coverage would add cost without proving extra risk for this bounded 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/Entitlements/WorkspaceEntitlementResolverTest.php tests/Unit/Entitlements/WorkspacePlanProfileCatalogTest.phpexport PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament/Settings/WorkspaceEntitlementsSettingsPageTest.php tests/Feature/Onboarding/ManagedTenantOnboardingEntitlementTest.phpexport PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ReviewPacks/ReviewPackEntitlementEnforcementTest.php tests/Feature/System/Directory/ViewWorkspaceEntitlementsTest.php
- Fixture / helper / factory / seed / context cost risks: local workspace, workspace membership, active managed-tenant count, tenant review/review-pack context, and platform-user fixtures only
- Expensive defaults or shared helper growth introduced?: no - implementation should reuse existing factories and scope helpers with opt-in entitlement fixtures
- Heavy-family additions, promotions, or visibility changes: none
- Surface-class relief / special coverage rule: standard-native relief for workspace settings and onboarding; shared-detail-family coverage for review-pack entry points; one read-only system detail assertion for the system plane
- Closing validation and reviewer handoff: rerun the exact targeted Sail/Pest commands above and verify 404/403/business-state semantics separately, verify blocked review-pack attempts create no
OperationRun, and verify lowered limits do not mutate existing tenants - Budget / baseline / trend follow-up: none expected beyond normal feature-local growth
- Review-stop questions: lane fit, hidden fixture cost, service-level bypass risk on review-pack generation, and cross-plane wording drift
- Escalation path: none
- Active feature PR close-out entry: Guardrail
- Why no dedicated follow-up spec is needed: the testing cost stays local to one resolver and four existing surface families; no new heavy family or platform-wide harness is introduced
Project Structure
Documentation (this feature)
specs/247-plans-entitlements-billing-readiness/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ └── workspace-entitlements-foundation.logical.openapi.yaml
├── checklists/
│ └── requirements.md
└── tasks.md # Created later by /speckit.tasks, not by this plan step
Source Code (repository root)
apps/platform/
├── app/
│ ├── Filament/
│ │ ├── Pages/
│ │ │ ├── Reviews/ReviewRegister.php
│ │ │ ├── Settings/WorkspaceSettings.php
│ │ │ └── Workspaces/ManagedTenantOnboardingWizard.php
│ │ ├── Resources/
│ │ │ ├── ReviewPackResource.php
│ │ │ ├── ReviewPackResource/Pages/ListReviewPacks.php
│ │ │ ├── ReviewPackResource/Pages/ViewReviewPack.php
│ │ │ ├── TenantReviewResource.php
│ │ │ └── TenantReviewResource/Pages/ViewTenantReview.php
│ │ ├── System/Pages/Directory/ViewWorkspace.php
│ │ └── Widgets/Tenant/TenantReviewPackCard.php
│ ├── Models/WorkspaceSetting.php
│ ├── Services/
│ │ ├── ReviewPackService.php
│ │ ├── Settings/SettingsResolver.php
│ │ ├── Settings/SettingsWriter.php
│ │ └── Entitlements/WorkspaceEntitlementResolver.php # likely new bounded service
│ ├── Support/
│ │ ├── Auth/Capabilities.php
│ │ ├── Auth/PlatformCapabilities.php
│ │ └── Settings/SettingsRegistry.php
├── tests/
│ ├── Feature/
│ └── Unit/
└── resources/views/filament/system/pages/directory/view-workspace.blade.php
Structure Decision: Single Laravel/Filament application inside apps/platform, with one new bounded entitlement resolver and changes limited to existing settings, onboarding, review-pack, and system-directory surfaces plus focused Pest coverage.
Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| New bounded entitlement resolver | Multiple existing surfaces need the same effective plan-default versus override decision, source attribution, rationale, and usage truth | Surface-local checks in WorkspaceSettings, ManagedTenantOnboardingWizard, and the review-pack action family would drift immediately and duplicate business-state wording |
Proportionality Review
- Current operator problem: The product cannot currently answer, in one auditable path, whether a workspace may activate another managed tenant or generate a review pack, nor can it show why a manual override exists.
- Existing structure is insufficient because: raw settings rows and direct capability checks do not produce plan-profile defaults, source attribution, override rationale, usage context, or reusable allow/block truth for multiple surfaces.
- Narrowest correct implementation: persist only workspace-selected plan profile and explicit override values plus rationale through existing
WorkspaceSettingrows, keep plan defaults code-owned, and add one bounded resolver that derives effective decisions for the four affected surface families. - Ownership cost created: one small default catalog plus one resolver require focused unit tests and wording discipline across settings, onboarding, review-pack, and system visibility.
- Alternative intentionally rejected: a new
Plan,Subscription,CustomerAccount, or broad entitlement matrix domain was rejected because the spec only needs workspace-owned current-release truth for two entitlement keys. - Release truth: current-release truth
Phase 0 — Research (output: research.md)
See: /Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/247-plans-entitlements-billing-readiness/research.md
Goals:
- Confirm the narrowest reuse of the existing workspace settings stack for plan profile and override persistence.
- Confirm the exact service and page-level enforcement points that prevent onboarding activation or review-pack run creation before mutation.
- Confirm how to preserve existing review-pack OperationRun UX while inserting entitlement checks ahead of run creation.
- Confirm system/admin plane separation and read-only directory visibility requirements for support users.
Phase 1 — Design & Contracts (outputs: data-model.md, contracts/, quickstart.md)
See:
/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/247-plans-entitlements-billing-readiness/data-model.md/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/247-plans-entitlements-billing-readiness/contracts/workspace-entitlements-foundation.logical.openapi.yaml/Users/ahmeddarrazi/Documents/projects/wt-plattform/specs/247-plans-entitlements-billing-readiness/quickstart.md
Design focus:
- Represent workspace commercial truth with explicit
WorkspaceSettingkeys under anentitlementsdomain rather than a new model family. - Keep the plan-profile catalog code-owned and small, and keep the new logic surface to one bounded
WorkspaceEntitlementResolverrather than a registry or framework. - Extend
WorkspaceSettingswith one entitlement section that edits plan profile and override/rationale pairs using existing settings write and reset patterns. - Use the same decision path in
ManagedTenantOnboardingWizardcompletion state and in review-pack generation entry surfaces, with hard enforcement centralized incompleteOnboarding()andReviewPackServicebefore any mutation or run creation occurs. - Extend
App\Filament\System\Pages\Directory\ViewWorkspaceplus its Blade view with a read-only entitlement summary instead of adding a second mutation plane.
Phase 1 — Agent Context Update
After Phase 1 artifacts are generated, update Copilot context from the completed plan:
/Users/ahmeddarrazi/Documents/projects/wt-plattform/.specify/scripts/bash/update-agent-context.sh copilot
Phase 2 — Implementation Outline (tasks created later by /speckit.tasks)
- Add bounded entitlement settings definitions and the plan-profile default map without creating new persistence tables.
- Implement
WorkspaceEntitlementResolverto merge code-owned plan defaults, workspace overrides, override rationale, and current usage for the managed-tenant limit. - Extend
WorkspaceSettingswith a plan profile selector, two override controls, rationale inputs, resolved-source helper text, and confirmed reset actions. - Gate onboarding completion in
ManagedTenantOnboardingWizardusing the shared entitlement decision and preserve existing confirmation plus audit semantics. - Gate every current review-pack generate/regenerate/export entry point through the shared decision, with service-level enforcement in
ReviewPackServiceto prevent bypass and preserve existing OperationRun UX when allowed. - Add a read-only entitlement summary to the system directory workspace detail page and preserve system-plane-only visibility.
- Add focused Sail/Pest unit and feature coverage for resolver behavior, settings save/reset, onboarding blocking, review-pack no-run blocking, and system visibility.
Constitution Check (Post-Design)
Re-check result: PASS. The design keeps persistence inside existing workspace settings, adds only one bounded resolver, preserves Filament v5 + Livewire v4 surfaces, keeps panel provider registration unchanged in bootstrap/providers.php, leaves global search and asset strategy unchanged, enforces 404/403 semantics separately from business-state blocks, and preserves existing review-pack OperationRun UX by gating before run creation instead of replacing shared run infrastructure.
Guardrail Close-Out
- Outcome: keep
- Livewire v4.0+ compliance remained intact across the touched Filament v5 pages, widgets, resources, and Livewire-backed actions.
- Provider registration location remains unchanged in
bootstrap/providers.php; no panel registration changes were needed. - Global-search scope remains unchanged; no new searchable resources were introduced.
- Destructive actions remain confirmation-protected where applicable. The existing
regeneratereview-pack action keeps its confirmation requirement, while the new entitlement denials are non-destructive business-state blocks enforced beforeReviewPackorOperationRuncreation. - Asset strategy remains unchanged. No new Filament assets were added; deploy behavior still uses
cd apps/platform && php artisan filament:assetswhen registered assets are shipped. - Validation lanes completed:
- Targeted unit entitlement lane: completed earlier in the feature implementation loop for
WorkspaceEntitlementResolverandWorkspacePlanProfileCatalog. - Targeted settings and onboarding feature lane: completed earlier in the feature implementation loop for workspace settings and managed-tenant onboarding gating.
- Targeted review-pack and system-directory feature lane:
31 passed (133 assertions)on the final post-format run.
- Targeted unit entitlement lane: completed earlier in the feature implementation loop for
- Browser smoke note: attempted because changed surfaces were user-facing, but classified as environment-blocked. The integrated browser could reach
/adminwith a synthetic session cookie, but tenant-panel route resolution stayed on 404s and the system panel continued redirecting to/system/login, so no reliable PASS/FAIL smoke result could be established. - Document-in-feature note: shared review-pack blocked-state wording remains centralized in the resource/service helper path. Direct tooltip introspection on the wrapped recordless Filament header action was not stable proof in the test harness, so the final assertion strategy validates the shared tooltip helper text and disabled UI state instead of comparing the wrapped action tooltip object directly.