TenantAtlas/specs/097-settings-foundation/research.md

3.6 KiB
Raw Blame History

Research — Settings Foundation (Workspace + Optional Tenant Override) (097)

Decisions

Decision 1 — Canonical capability registry + role mapping

  • Chosen: Add new workspace capabilities as constants in App\Support\Auth\Capabilities, and map them in App\Services\Auth\WorkspaceRoleCapabilityMap.
    • Capabilities::WORKSPACE_SETTINGS_VIEW
    • Capabilities::WORKSPACE_SETTINGS_MANAGE
  • Rationale: The repo already enforces “no raw strings” and uses Capabilities::isKnown() checks in WorkspaceCapabilityResolver.
  • Alternatives considered:
    • Using raw strings (rejected: violates RBAC-UX-006).

Decision 2 — Workspace RBAC-UX enforcement for Filament actions

  • Chosen: Use App\Support\Rbac\WorkspaceUiEnforcement for Filament page actions.
  • Rationale: This helper already implements the exact semantics required by the constitution:
    • non-member → abort(404) (deny-as-not-found)
    • member missing capability → abort(403)
    • defense-in-depth server-side guard via before().
  • Alternatives considered:
    • Ad-hoc abort() checks in each action (rejected: inconsistent and easier to regress).

Decision 3 — Audit logging sink + stable action IDs

  • Chosen:
    • Workspace-scoped audit entries: App\Services\Audit\WorkspaceAuditLogger (writes audit_logs with workspace_id, tenant_id = null).
    • Stable action identifiers: extend App\Support\Audit\AuditActionId enum with two new cases:
      • WorkspaceSettingUpdated = 'workspace_setting.updated'
      • WorkspaceSettingReset = 'workspace_setting.reset'
  • Rationale: The repo already has a stable-action-id convention and tests around audit redaction and scoping; using the existing audit logger preserves sanitizer behavior (no secrets).
  • Alternatives considered:
    • Introducing a new audit sink (rejected: violates “use existing audit sinks” precedent and increases inconsistency risk).
    • Using un-enumed string action IDs (possible, but rejected in favor of stronger standardization).

Decision 4 — Storage model for workspace defaults + tenant overrides

  • Chosen: Use two tables (scope-pure) rather than a single polymorphic table:
    • workspace_settings (workspace-owned; includes workspace_id; no tenant_id)
    • tenant_settings (tenant-owned; includes workspace_id and tenant_id NOT NULL)
  • Rationale: Aligns with the constitutions scope/ownership rule:
    • “Workspace-owned tables MUST include workspace_id and MUST NOT include tenant_id.”
    • “Tenant-owned tables MUST include workspace_id and tenant_id as NOT NULL.”
  • Alternatives considered:
    • Single workspace_settings table with nullable tenant_id (rejected: risks violating the constitution and blurs ownership).

Decision 5 — Resolver precedence + caching

  • Chosen:
    • Precedence: tenant override → workspace override → system default.
    • Caching: request-local only, implemented in-memory inside the resolver (no cross-request cache store in v1).
  • Rationale: Matches the clarified spec requirements and mirrors existing request-local caching patterns (e.g., capability resolver).
  • Alternatives considered:
    • Cross-request cache with TTL (rejected for v1: adds invalidation complexity and isnt required).

Notes on existing repo patterns (evidence)

  • Canonical capability registry exists: App\Support\Auth\Capabilities.
  • Workspace capability checks and request-local caching exist: App\Services\Auth\WorkspaceCapabilityResolver.
  • Workspace RBAC-UX enforcement helper exists: App\Support\Rbac\WorkspaceUiEnforcement.
  • Workspace audit logger exists (with sanitizer): App\Services\Audit\WorkspaceAuditLogger.