Skip to content

Out of Scope (v1)

What Criterion will not include in v1.

This is intentional. Criterion is a micro-engine with strict boundaries.


1. Visual Rule Builder

Not included: Drag-and-drop UI for building decisions.

Why: UI is a layer on top of Criterion, not part of it. Build your own UI that generates Criterion decisions.


2. Database Integration

Not included: Built-in DB connections, ORM, or data fetching.

Why: Criterion is pure. Data fetching is the host system's job.

ts
// Host system fetches data
const user = await db.users.findById(userId);
const context = { userId, balance: user.balance };

// Criterion evaluates
engine.run(decision, context, { profile });

3. Workflow Orchestration

Not included: Multi-step workflows, state machines, or process orchestration.

Why: Criterion makes single decisions. Orchestration is a different concern.

Use Criterion inside your workflow engine, not as your workflow engine.


4. ML/AI Inference

Not included: Machine learning models, predictions, or AI-based decisions.

Why: Criterion is rule-based and deterministic. ML outputs are probabilistic and non-deterministic.

You can use ML to build context, then let Criterion decide:

ts
const riskScore = await mlModel.predict(features);
const context = { riskScore };
engine.run(decision, context, { profile });

5. Plugin System

Not included: Third-party plugins, extensions, or middleware.

Why: The core must remain small and stable. Plugins add complexity and maintenance burden.

If you need extensibility, compose decisions in your code.


6. Remote Profile Fetching

Not included: Fetching profiles from URLs, APIs, or remote storage.

Why: IO in the engine breaks purity.

Use ProfileRegistry and load profiles externally:

ts
const profiles = await fetchProfilesFromAPI();
const registry = createProfileRegistry();
profiles.forEach(p => registry.register(p.id, p));
engine.run(decision, context, { profile: "my-profile" }, registry);

7. Decision Chaining

Not included: Automatic chaining of decisions or dependency resolution.

Why: Chaining creates implicit dependencies and complexity.

Chain decisions explicitly in your code:

ts
const eligibilityResult = engine.run(eligibilityDecision, context, opts);
if (eligibilityResult.data?.eligible) {
  const pricingResult = engine.run(pricingDecision, context, opts);
}

8. Caching

Not included: Built-in result caching or memoization.

Why: Caching belongs to the infrastructure layer.

Implement caching in your host system if needed.


9. Logging / Telemetry

Not included: Built-in logging, metrics, or tracing integration.

Why: Observability is infrastructure, not engine logic.

Use the Result.meta for your own logging:

ts
const result = engine.run(decision, context, opts);
logger.info({
  decision: result.meta.decisionId,
  matched: result.meta.matchedRule,
  trace: result.meta.evaluatedRules,
});

10. Hot Reloading

Not included: Runtime decision updates without restart.

Why: Decisions should be versioned and deployed, not mutated at runtime.

Deploy new decision versions through your normal release process.


11. Access Control

Not included: Permission checks, role-based access, or authentication.

Why: Security is a host system concern.

Check permissions before calling Criterion, not inside it.


12. Internationalization

Not included: Multi-language explanations or localized outputs.

Why: I18n is a presentation layer concern.

Use explanation keys and translate in your UI:

ts
emit: () => ({ reason: "RISK_HIGH" }),
// UI translates "RISK_HIGH" → "Alto riesgo"

Summary

FeatureIn v1?Why Not
Visual builderBuild on top, not inside
DatabasePurity: no IO
WorkflowsDifferent concern
ML/AIDeterminism
PluginsKeep core small
Remote profilesNo IO in engine
Decision chainingExplicit > implicit
CachingInfrastructure layer
LoggingInfrastructure layer
Hot reloadVersion and deploy
Access controlHost system concern
I18nPresentation layer

Philosophy

Criterion is a knife, not a Swiss Army knife.

One thing, done well.

Released under the MIT License.