feat(license): add Lite SKU; remove user-facing free trial
Two coupled changes:
1. Lite tier
- New Tier.LITE in src/license/schema.py.
- FEATURES_BY_TIER[Tier.LITE] = {Deduplicator, Text Cleaner,
Format Standardizer}. The three universally-useful tools that
cover the most common bookkeeping / RevOps / Klaviyo prep
workflows. Other six tools require Core.
- i18n: license.tier_lite, license.feature_locked_title,
license.feature_locked_body, license.upgrade_link,
license.status_locked (en + es).
- Per-tool feature gate at every GUI tool page
(require_feature_or_render_upgrade) and every tool CLI
(guard(feature=...)). A locked tool renders an upgrade
prompt + Manage-license button (GUI) or exits with code 2
(CLI).
- Home grid: tool cards the user's tier doesn't unlock get a
red 🔒 Locked badge in place of green Ready.
2. Trial removed
- Activation form's "Start 1-year trial" button removed.
- license_cli's `trial` subcommand removed.
- activation.trial_button / activation.trial_help i18n keys
dropped (pack parity test stays green).
- Tier.TRIAL stays in the enum (back-compat with any field-
tested trial licenses); LicenseManager._mint stays internal
for tests and the seller's key generator.
- Decision logged in DECISIONS §9b: a 1-year all-features
trial undercuts paid Lite; paid-only keeps tier economics
clean.
Tests (+29 net): +17 Lite-tier unit/guard tests + 13 Lite-tier
GUI tests + 1 trial-absent assertion - 2 trial CLI tests - 1
trial GUI button test. Total: 1995 → 2024.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -165,8 +165,37 @@ explicitly use ``isolated_license_path`` /
|
||||
1. Add the enum value to ``Tier``.
|
||||
2. Add a row to ``FEATURES_BY_TIER`` listing the unlocked flags.
|
||||
3. Add ``license.tier_<name>`` translation keys to every i18n pack.
|
||||
4. The activation flow, sidebar status badge, and feature gate all
|
||||
pick up the new tier automatically.
|
||||
4. The activation flow, sidebar status badge, feature gate, and home
|
||||
grid lock badge all pick up the new tier automatically.
|
||||
|
||||
**Worked example — the Lite tier**:
|
||||
|
||||
```python
|
||||
# src/license/schema.py
|
||||
class Tier(str, Enum):
|
||||
LITE = "lite" # new
|
||||
CORE = "core"
|
||||
...
|
||||
|
||||
# src/license/features.py
|
||||
FEATURES_BY_TIER = {
|
||||
...
|
||||
Tier.LITE: frozenset({
|
||||
FeatureFlag.DEDUPLICATOR,
|
||||
FeatureFlag.TEXT_CLEANER,
|
||||
FeatureFlag.FORMAT_STANDARDIZER,
|
||||
}),
|
||||
Tier.CORE: _all(),
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
Then in en.json/es.json add ``license.tier_lite``. That's it — the
|
||||
existing ``require_feature_or_render_upgrade`` (GUI) and
|
||||
``guard(feature=...)`` (CLI) calls in every tool page/CLI route a
|
||||
Lite user into the upgrade prompt for any tool the tier doesn't
|
||||
unlock. The home grid's lock badge fires off the same feature
|
||||
lookup.
|
||||
|
||||
**Minting a license** (creator-only):
|
||||
|
||||
|
||||
Reference in New Issue
Block a user