HF-307: entitlement model, capability registry, gate B (PR 1/4) - #1728
Open
marcin-kordas-hoc wants to merge 1 commit into
Open
HF-307: entitlement model, capability registry, gate B (PR 1/4)#1728marcin-kordas-hoc wants to merge 1 commit into
marcin-kordas-hoc wants to merge 1 commit into
Conversation
Implements tasks 1.1-1.5 of the HF-307 spec (rev 2.4): the license entitlement model, the capability registry, and gate B in the interpreter's FUNCTION_CALL evaluation. Gate A (existing key-validity check) is untouched. New: - src/license/LicenseEntitlement.ts: FeatureId, LicenseExpiry, LicenseEntitlement, unrestrictedEntitlement() - src/license/capabilities.ts: CapabilityGrant, CAPABILITY_TABLE (placeholder: every built-in under one 'core' token, refreshed from the static function registry on every CapabilityRegistry construction rather than at module load, since src/index.ts registers built-in plugins only after Config/Interpreter have already been evaluated) - src/license/CapabilityRegistry.ts: resolve() (transitive, cycle-safe `implies` expansion), capabilityOf(), allowsFunction(), allowsFeature() Modified: - src/Config.ts: licenseCapabilities/isLicenseGateActive/ capabilityRegistry added to the existing privatePool WeakMap, never exposed through getConfig() - src/interpreter/Interpreter.ts: gate B added after gate A in the FUNCTION_CALL case, with a custom-function exemption predicate (capabilityOf() === undefined) and alias canonicalization so an alias gates identically to its canonical name - src/error-message.ts: ErrorMessage.LicenseCapability Decision deltas applied (Kuba, 2026-08-10): D1 (custom_functions token dropped this release, FeatureId.CustomFunctions kept as reserved vocabulary) and D3 (fail-closed + silent: an entitlement with no recognized token grants only core, without a message, warning, or diagnostics getter - unrestrictedEntitlement() no longer covers that case). D2 and D4 are out of scope for this PR. PR 1 ships without a real license-key payload adapter (PR 3), so Config always resolves an unrestricted entitlement for now - gate B is a correct, independently-testable no-op in production until PR 3 lands. Full PR description, verification notes, and drafted private-repo tests (no credentials available this session for hyperformula-tests): marcin-kb/handoffs/hf-307-pr1-tests/ Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GuUdq242TtFaepKRNdEkj9
Contributor
|
Task linked: HF-307 Implement feature packages and add-ons in HF |
13 tasks
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
hyperformula-docs | 0a966e3 | Commit Preview URL Branch Preview URL |
Aug 11 2026, 09:26 AM |
Performance comparison of head (0a966e3) vs base (61ead73) |
marcin-kordas-hoc
marked this pull request as ready for review
August 11, 2026 10:22
13 tasks
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## develop #1728 +/- ##
===========================================
+ Coverage 97.31% 97.32% +0.01%
===========================================
Files 195 198 +3
Lines 15719 15789 +70
Branches 3455 3469 +14
===========================================
+ Hits 15297 15367 +70
Misses 414 414
Partials 8 8
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
HF-307 (feature packages / entitlement gating). This is PR 1 of 4: the entitlement model, the capability registry, and gate B in the interpreter. It only touches
src/, has zero dependency on the (not-yet-shared) license-key package, and does not change behaviour for any key this library recognizes today (gpl-v3, legacy keys) — gate A (existing key-validity check) is untouched.New:
src/license/LicenseEntitlement.ts—FeatureId,LicenseExpiry,LicenseEntitlement,unrestrictedEntitlement()src/license/capabilities.ts—CapabilityGrant,CAPABILITY_TABLE(placeholder: every built-in under a singlecoretoken, refreshed from the static function registry on everyCapabilityRegistryconstruction — see the comment on why it can't be built eagerly at module load)src/license/CapabilityRegistry.ts—resolve()(transitive, cycle-safeimpliesexpansion),capabilityOf(),allowsFunction(),allowsFeature()Modified:
src/Config.ts— license-derived state added to the existing private pool, never exposed throughgetConfig()src/interpreter/Interpreter.ts— gate B added after gate A in theFUNCTION_CALLcase, with a custom-function exemption (a function not covered by the capability table is treated as instance-registered/custom, not gated) and alias canonicalization (an alias must gate identically to its canonical function name)src/error-message.ts—ErrorMessage.LicenseCapabilityThis PR ships without a real license-key payload adapter (that's a later PR), so
Configalways resolves an unrestricted entitlement today — gate B is a correct, independently-testable no-op in production until that adapter lands.FeatureId.CustomFunctionsis kept in the enum as reserved vocabulary (no grant maps to it this release) per team decision to drop function-registration gating from this pass; happy to remove it instead if reviewers prefer.How did you test your changes?
tsc --noEmitandtsc -p tsconfig.test.json: clean.eslinton all new/changed files: clean.test/smoke.spec.ts: passes.gpl-v3and missing/invalid-key behaviour unchanged (gate A untouched, exact same#LIC!messages);getConfig()does not expose the new license-derived state; a function outside a restricted entitlement's granted set returns#LIC!with the new message while a function inside it computes normally;VERSION/OFFSET(protected) bypass the gate regardless of restriction; a custom function (config.functionPlugins) is exempt from gate B; calling a gated built-in through an alias (e.g.VARvs. canonicalVAR.S) gates identically to the canonical name, not the alias string;CapabilityRegistry.resolve()handles transitiveimplies, cycles, and unknown tokens correctly.Update: private test suite pushed — hyperformula-tests#30 (
unit/license/{entitlement,capability-registry,config-wiring}.spec.ts+ a gate-B section appended tounit/licence.spec.ts, the 2 existing cases untouched). Against the paired branch: the new license specs pass, and the full existing suite (505 suites / 6204 tests, 0 failures) is unaffected. CI on this PR now runs the real suite (pushed directly here rather than from a fork, so the private-repo checkout has secrets access) —build/lint/unit-tests/browser-tests/Test performance/CodeQL/audit/Snyk all pass.Task 1.5 benchmark, from this PR's own CI run (
Test performancejob, base61ead730vs head0a966e35):Reporting this as-is rather than smoothing it over: Sheet A (the highest-resolution signal) is flat (+0.37%), but several rows show a consistent single-digit-percent slowdown on this one run, on shared GitHub Actions runners rather than the codespace the spike's own noise-floor numbers were calibrated against. I have not repeated the run to separate signal from noise — happy to re-run
Test performancea second time before merge if reviewers want more confidence than a single sample gives. The structural claim I can stand behind: the common path (isLicenseGateActive === false, checked before gate B does anything) is one boolean read replacing today's string-enum comparison, so there's no new work on the hot path forgpl-v3/legacy keys — that part is a code-inspection fact, not a benchmark claim.Codecov: patch coverage is 58.1% (
CapabilityRegistry.ts48.7%,Interpreter.ts41.7%,Config.ts71.4%) and project coverage -0.19%, both still red. This is checked againstdevelop, but the new private tests exercise the paired branch, not this diff directly through Codecov's own accounting — worth a look before merge, though the "full suite green" result above is the stronger correctness signal.Types of changes
Related issues:
Checklist:
Ready for review. The paragraph that used to sit here said this was opening as a draft pending the private suite, the benchmark and the
FeatureId.CustomFunctionsquestion — all three are resolved: the suite is pushed (hyperformula-tests#30), the benchmark ran, and Kuba settled the token with decision D1 (dropped from this release, kept as reserved vocabulary).Note
Medium Risk
Changes the per-function-call licensing path in the interpreter hot path, though production keys still skip gate B work via unrestricted resolution; incorrect wiring in later PRs could wrongly block formulas.
Overview
Introduces HF-307 PR 1: license entitlement types, a capability registry that expands tokens into allowed functions/features, and gate B on formula function calls—without changing behavior for keys the library already treats as fully valid today.
Confignow keeps license-derived state in a private pool (licenseCapabilities,isLicenseGateActive,capabilityRegistry) and resolvesunrestrictedEntitlement()until a real key payload adapter lands in a later PR. The interpreter replaces a single validity check withisLicenseGateActive, then gate A (invalid key), then gate B (per-function entitlement viaallowsFunction, with alias canonicalization and an exemption whencapabilityOfis undefined for custom/instance functions). A new#LIC!message covers functions not included in the license.The placeholder
corecapability table refreshes built-in function ids from the static registry on each registry construction so late plugin registration stays accurate.Reviewed by Cursor Bugbot for commit 0a966e3. Bugbot is set up for automated code reviews on this repo. Configure here.