@e-volv/flags-kernel
v0.1.2
Published
e-volv Launch — the one definition of flag evaluation. Ported, never reimplemented: every Launch SDK runs this package's fixture and must agree with it byte for byte.
Readme
@e-volv/flags-kernel
The one definition of flag evaluation for e-volv Launch. Every Launch SDK
ports this package into its own language and proves the port against
fixture.json. No SDK computes a value its own way, even when it agrees:
two clients that disagree about the same user is a bug with no good failure
mode and no good place to notice it.
What it does
import { evaluate } from '@e-volv/flags-kernel';
const result = evaluate(ruleset, 'checkout.new', false, {
targetingKey: 'u_1',
email: '[email protected]',
country: 'AU',
});
// { value: true, variant: 'on', reason: 'RULE:0' }evaluate(ruleset, flagKey, fallback, context) resolves one flag for one
subject. evaluateAll(ruleset, context) resolves every flag — that is what a
client key receives instead of the rules, so a browser or a mobile binary
never holds targeting logic.
Precedence
First match wins:
- Flag absent from the ruleset → the caller's fallback, reason
FLAG_NOT_FOUND killed→ the environment default, reasonKILLED- An unmet prerequisite → the environment default, reason
PREREQUISITE_FAILED - Ordered rules; the first whose clauses and segments all match
- That rule's rollout if it has one (
ROLLOUT), else its variant (RULE:<index>) - The environment's
defaultVariant, reasonDEFAULT - The caller's fallback if the default variant does not exist, reason
ERROR
Two properties are load-bearing and a port must preserve both:
- A kill beats everything below it. During an incident the operator must not have to reason about whether some allowlist rule outranks them.
evaluatenever throws and never performs I/O. It sits on the customer's request path. A malformed ruleset returns the fallback with reasonERROR; it does not raise.
Bucketing
bucket(flagKey, subject, salt) → 0–9999, sha1(flagKey[:salt]:subject),
first 8 hex digits, modulo 10 000.
The flag key is inside the hash deliberately. Without it every flag at 5% selects the same 5% of subjects, so the same unlucky users receive every rollout and the sample is worthless. sha1 because every language a Launch SDK targets has it in its standard library, which is the property that matters for a number several implementations must agree on.
A rollout with no targetingKey cannot be deterministic. It serves the
environment default with reason DEFAULT rather than guessing — and it does
not fall through to a later rule, because the rule did match.
Validation
validateTargeting, validateFlagDefinition and validateRuleset run at
write time only, in the control plane. The evaluator cannot complain about
a malformed rule, so nothing malformed is allowed to be stored: a rule may
only serve a variant the flag declares, rollout weights must sum to 10 000,
prerequisites may not form a cycle.
Running the fixture
npx jest --config packages/flags-kernel/jest.config.ts --rootDir packages/flags-kernelAdding an SDK
See runner-protocol.md. A port costs a runner of about a hundred lines; it
does not cost a second evaluator.
