Skip to content

feat(ios): Entitlement model + EntitlementService + tests (PR 4/5 — premium tier, paused)#51

Draft
DocNR wants to merge 1 commit into
mainfrom
feat/ios-entitlement-service
Draft

feat(ios): Entitlement model + EntitlementService + tests (PR 4/5 — premium tier, paused)#51
DocNR wants to merge 1 commit into
mainfrom
feat/ios-entitlement-service

Conversation

@DocNR
Copy link
Copy Markdown
Owner

@DocNR DocNR commented May 10, 2026

Status: paused, queued for Phase 2

Sibling to drafts #48 / #49 / #50. Same queued-phase-2 rationale — preserved for Lightning-native future, dispensable if we go Apple IAP. See those PRs for full context. Plan: ~/.claude/plans/i-d-like-to-create-eventual-moon.md.

Summary

iOS data layer for the server-side entitlement system in #48 + #49 + #50. Pure additive — no production behavior change. PR 5 (cap-check site updates + Settings tier row) is what would have wired this in; not built.

Files

Path Purpose
Shared/Entitlement.swift (new) Tier enum + Entitlement struct mirroring the /entitlement response shape. Forward-compat decoder (ignores unknown fields). effectiveTier() downgrades expired premium to free.
Shared/EntitlementService.swift (new) Cache + network refresh. NSE-safe sync reads via cachedTier(for:); main app uses refreshAll() over a TaskGroup with parallel signed GETs. 24h TTL in app-group UserDefaults. Falls back to last-known cache on network failure. Closure-based signer so the service doesn't touch the Keychain (testable + clean separation).
ClaveTests/EntitlementServiceTests.swift (new) 15 unit tests covering decoder, cache, effective-tier composition, refresh paths (success / http error / signer error / decode error / pubkey-mismatch rejection), atomic durability. Uses MockURLProtocol + isolated UserDefaults suites.
Shared/LogExporter.swift Added "entitlement" to allCategories.
ClaveTests/LogExporterFormattingTests.swift Matching expected-set update.
Clave.xcodeproj/project.pbxproj Added the two new Shared/ files to Clave + ClaveNSE targets (Shared/ is not a synchronized root group in this project — needed explicit references).

Tests

  • ✅ 15 new EntitlementServiceTests pass
  • ✅ 4 LogExporterFormattingTests pass (including regression guard for allCategories)
  • ✅ Full ClaveTests suite green on iOS 26.4 simulator — no regressions

Risk

Zero. Pure additive code, not wired into AppState or any cap-check site. PR 5 was where actual behavior would have changed, but that's not being built.

🤖 Generated with Claude Code

PR 4 of the premium-tier series. iOS data layer for the server-side
entitlement system from PRs #48 + #49 + #50. Pure additive — no
production behavior change. PR 5 (cap-check site updates + Settings
tier row) is what would have wired this in.

Files:
- Shared/Entitlement.swift: Tier enum + Entitlement struct mirroring
  /entitlement response. Forward-compat decoder (ignores unknown
  fields). effectiveTier() downgrades expired premium to free.
- Shared/EntitlementService.swift: cache + network refresh. NSE-safe
  sync reads via cachedTier(for:); main app uses refreshAll() over a
  TaskGroup. 24h TTL in app-group UserDefaults. Falls back to last-
  known cache on network failure. Closure-based signer so the service
  doesn't touch the Keychain (testable + clean separation).
- ClaveTests/EntitlementServiceTests.swift: 15 unit tests covering
  decoder (snake_case → camelCase, expiry semantics, forward-compat),
  cache (TTL, version invalidation, expired-premium downgrade,
  case-insensitive keying), effectiveTier composition (any-premium-
  elevates, all-free, empty, uncached-ignored), refresh paths
  (success, http error, signer error, decode error, pubkey-mismatch
  rejection), atomic write durability. Uses MockURLProtocol +
  isolated UserDefaults suites.
- Shared/LogExporter.swift: add "entitlement" to allCategories.
- ClaveTests/LogExporterFormattingTests: matching expected-set update.
- Clave.xcodeproj/project.pbxproj: added two new Shared/ files to
  Clave + ClaveNSE targets via xcodeproj gem (Shared/ is not a
  synchronized root group in this project).

Status: PAUSED. PR series #48#49#50#51 was determined to be
premature for current Phase 1 needs (developer + testers want elevated
caps; server-side enforcement is overkill). Phase 1 is being rebuilt
as iOS-only allowlist gating in a separate branch.

Tests: full ClaveTests suite green on iOS 26.4 simulator (no
regressions); 15 new EntitlementServiceTests + 4 LogExporter
formatting tests + entire pre-existing suite all passing.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@DocNR DocNR added the queued-phase-2 Premium-tier work paused; revisit when payment architecture is chosen label May 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

queued-phase-2 Premium-tier work paused; revisit when payment architecture is chosen

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant