Live Versions, Approvals and Loading Rules

A rule in the Rule Repository has any number of versions, and at most one live version: the one your application evaluates. A source object likewise has at most one live version, the one the repository treats as current. Nothing becomes live by accident: a person makes a rule version live in the repository, and your application then loads the live versions when it evaluates. Together, immutable versions, approvals, Go Live and Take Offline make up the repository's decision workflow: the governed path every change takes to production and back, with each step allowed only to the users you give its permission.

You need this page to decide who may put rules into production, how approval works, what the repository checks before a version goes live, what it tells your application when that happens, and how your application loads the live rules for the Rule Engine.

The Model

  • A version is immutable. Once saved, a rule version's logic never changes; a change is a new version. Only its name and description can be edited.
  • The live version is named by its rule. Making a version live moves the rule's pointer to it, in one step: there is never a moment with two live versions, and two people who try at once cannot both succeed.
  • No live version is a real state. A rule nobody has put into production has none, and neither does a rule whose live version was taken offline with nothing to replace it. Your application finds no live version of such a rule.
  • A version that has ever been live cannot be deleted, except together with its whole source object. Its history, tests and recorded evaluations stay explainable.
  • Only people make rule versions live. AI agents and services never do: every save they make requests approval, whatever their permissions say.

Who May Go Live

What a user may do is decided by the permission set your application gives each request (see Permissions). These permissions govern production:

Permission Lets the user
RuleVersionGoLive (42) Make a rule version live: Go Live in its dialog, or Save & Go Live when saving it
RuleVersionTakeOffline (43) Take a rule's live version offline, with or without a replacement
RuleVersionRequestApproval (44) Ask for approval of a version they cannot make live themselves
RuleVersionApproveAndDecline (45) Approve, approve and go live, decline, or revoke an approval. Requires RuleVersionGoLive
SourceVersionGoLive (21) Make a source version live
SourceVersionTakeOffline (22) Take a source object's live version offline

A typical setup has three roles:

Role Permissions related to production
Author RuleVersionRequestApproval
Publisher RuleVersionGoLive, RuleVersionTakeOffline, RuleVersionApproveAndDecline
Administrator Everything, including the source version permissions
using CodeEffects.Rule.Common.Models;
using CodeEffects.Rule.Repository;

// An author: may write rules and ask for approval, may not publish
var author = new Configuration
{
	AccessMode = ActionPermissionMode.AllowAllExcept,
	ActionList = new HashSet<ActionPermissionType>
	{
		ActionPermissionType.RuleVersionGoLive,
		ActionPermissionType.RuleVersionTakeOffline,
		ActionPermissionType.SourceVersionGoLive,
		ActionPermissionType.SourceVersionTakeOffline
	}
};

Under AllowAllExcept, withholding RuleVersionGoLive also withholds RuleVersionApproveAndDecline, which depends on it. The user interface turns off what a user may not do, and the server refuses it anyway.

Approval

Approval is a step of its own, with four states for each rule version: None, Requested, Approved and Declined.

  1. Requesting. A user who cannot make a version live is asked, when they save it, whether to request approval. They can also request it later from the version's dialog. Every save by an AI agent or a service requests it.
  2. Approving. A user with RuleVersionApproveAndDecline approves the version (it may then go live) or approves it and makes it live in one step (Approve & Go Live). Nobody may approve a request they made themselves for their own direct save.
  3. Declining. The approver may decline with a reason. The reason is also written as a note on the version, so it stays with it.
  4. Going live. Making a version live meets its request, however it is done. A live version never requires approval.

What the states allow:

  • Only a Requested version is held back: a publisher cannot make it live until it is approved or declined.
  • A Declined version can still be made live by a publisher, who overrules the decline.
  • An Approved version can have its approval revoked, or be declined.
  • A version taken offline can be made live again without a new approval.

Going live, taking offline, approving, approving and going live, declining and revoking approval each ask the user to confirm by typing a word, with an optional note, so production never changes by a stray click.

What Is Checked Before a Version Goes Live

Before a rule version becomes live, the repository checks everything that could break, using its member usage index:

Check Result
A reusable rule the version calls is not available: not reusable, not an evaluation rule, or has no live version Blocks
A call would lead back into the rule itself (a cycle) Blocks
A rule the version calls uses members the version's source version does not declare Blocks
A called rule is used where its source object is not the one evaluated (for example, a rule written on a collection's items used outside a condition on that collection) Blocks
When the rule can be called by others: a rule whose live version calls it would reach members the new version's source does not declare Blocks; a warning when it cannot be checked
A call cannot be checked A warning the user must acknowledge

Taking a version offline is refused while other rules' live versions call it: they would stop working the moment it went. The user is shown each calling rule and must take those offline first. Taking a version offline with a replacement makes the replacement live and takes the old one offline in one step.

The checks run again inside the write, with the called rules locked, so two people acting at the same moment cannot slip a broken combination past each other.

Source versions have no such checks: a source version's live state does not change what any rule evaluates.

Telling People: the Notify Event

The repository raises an event when something happens that people may need to hear about: approval requested, a request approved or declined, a version made live or taken offline. Your application decides who hears about it and how, because only it knows its users and how to reach them. See Notifications for the full list and a complete example of emailing approvers.

Loading the Live Rules for Evaluation

Your application evaluates rules with the Rule Engine. It gets the live versions from the repository in one of two ways:

Your application has Load rules with Evaluations are
The Repository library only Context.GetRules(dbProvider, query) Not recorded
The Audit library AuditRecorder.GetRules(query) Recorded, and shown in the repository's Live Evaluations

Both take the same RuleQuery and return a ruleset with every rule asked for and every rule those call, each at its live version, in one document the engine reads as it is. The engine needs no rule resolver for the called rules.

using CodeEffects.Rule.Common.Models;
using CodeEffects.Rule.Engine;
using CodeEffects.Rule.Repository;

// Every rule of the Order source object that has a live version
LoadedRuleset ruleset = Context.GetRules(dbProvider, RuleQuery.BySource("Acme.Sales.Order"));

var evaluator = new Evaluator<Order>(ruleset.Xml);

LoadedRule discount = ruleset.Rule("Holiday discount");
bool applies = evaluator.Evaluate(order, discount.Id);

Choosing the Rules

Query Loads
RuleQuery.ByNames("Holiday discount", "Credit check") The rules with these names
RuleQuery.ByIds(id1, id2) The rules with these IDs (the IDs the Rule Editor gives rules)
RuleQuery.BySource("Acme.Sales.Order") Every rule of a source object, by its alias, that has a live version
RuleQuery.BySource(sourceId) The same, by the source object's ID
.InSource("Acme.Sales.Order") Narrows a query by names when names repeat across source objects
.OfType(true) / .OfType(false) Only evaluation rules, or only execution rules
// Two execution rules of the Order source object
var query = RuleQuery.ByNames("Apply discounts", "Route for review")
	.InSource("Acme.Sales.Order")
	.OfType(false);

Each LoadedRule says which rule it is (Id, Name, IsEvaluationType), which live version the ruleset holds (RuleVersionId, RuleVersionName), which source version it was written against (SourceVersionId, SourceAlias), and whether it is there only because another rule calls it (IsCalled).

When Loading Fails

GetRules(..) throws a RuleException instead of returning a partial ruleset:

Control number Cause
CE10256 No rule with a live version matches the query
CE10257 A rule calls rules that have no live version
CE10258 A live version holds no rule, or its definition cannot be read
CE10259 A rule asked for by ID or name does not exist, or has no live version
CE10260 The query is null
CE19004 The database is behind the version the library needs; the message names the scripts to run

Caching and Refreshing

GetRules(..) reads the database every time it is called. Build the evaluator once and keep it while the ruleset is current, rather than loading for every evaluation. Ways to decide when to reload:

  • On a timer. Reload every few minutes. Simple, and works in a web farm.
  • On the Notify event. Reload when RuleVersionMadeLive or RuleVersionTakenOffline is raised. The event is raised only in the process that handled the repository's request, so in a web farm, pass it on to the other servers through whatever your application already uses (a message bus, a cache key).
  • On deployment. For applications where rules change only with releases.
public sealed class OrderRules
{
	private readonly IDbProvider dbProvider;
	private readonly object gate = new object();
	private Evaluator<Order> evaluator;
	private LoadedRuleset ruleset;

	public OrderRules(IDbProvider dbProvider) { this.dbProvider = dbProvider; }

	/// <summary>Call from the Notify event, a timer, or a deployment step.</summary>
	public void Reload()
	{
		var loaded = Context.GetRules(this.dbProvider, RuleQuery.BySource("Acme.Sales.Order"));
		var built = new Evaluator<Order>(loaded.Xml);

		lock(this.gate)
		{
			this.ruleset = loaded;
			this.evaluator = built;
		}
	}

	public bool Evaluate(Order order, string ruleName)
	{
		Evaluator<Order> current;
		LoadedRuleset rules;

		lock(this.gate)
		{
			if(this.evaluator == null) this.Reload();
			current = this.evaluator;
			rules = this.ruleset;
		}

		var rule = rules.Rule(ruleName) ?? throw new InvalidOperationException("No live rule named " + ruleName);
		return current.Evaluate(order, rule.Id);
	}
}

Source Versions Going Live

A source object's live version is the one new rules and tests are built on. The first version of every source object is live as soon as it is synced. After that, SourceSettings.NewVersionsGoLive decides: left false, a new version waits for a person with SourceVersionGoLive to make it live in the repository; set true, the sync makes it live at once. See Source Synchronization and Reindexing.

Ask on Stack Overflow - Yes, it's still active
Ask in GitHub Discussions

p102

l097 --

l102

p101

×