Context
Access gates the Enterprise values ofSupportSessionService by hand, because @Setting cannot say that a key, or one
value of it, belongs to a plan. A hand-written gate is missed on some read or write path.
Decision
- Declared on the field.
@Setting({ entitlement })names the entitlement that unlocks a key;@Setting({ entitlementValues: { unlimited: "access.enterprise_support" } })names the one that unlocks a value (matched by its text, so it fits enum, number and boolean settings). Gen carries both into the key metadata. - One port, text keys.
SettingEntitlementSource.granted(entitlements, target)answers the granted keys. Settings depends on no entitlements package; a deployment binds a driver overEntitlementSource(the plan snapshot in SaaS, the licence on-prem). With no source bound every gate stays shut. - Every path. On read (
SettingsReader#getandread,SettingsResolver#getandresolve) a value the plan does not unlock reads as the default, with provenanceentitlement. Every write (write,applyChange, an approved or scheduled revision) refuses it with the coreentitlement_required, one field error per path. The CLI runner knows no plan, so it refuses gated keys.
Consequences
- A downgrade takes effect on the next read without rewriting stored values; an upgrade brings them back.
- The admin shows an upgrade path from the
entitlementprovenance and the refusal.
Enforcement
planned —packages/state/settings/src/core/services/__tests__/settings-entitlements.service.test.ts.
Supersedes and sources
Supersedes: none. Sources: OSservices/access/SPEC.md gap 3; STA-816.