Skip to content

feat: policy engine#450

Merged
stefan-gorules merged 3 commits into
masterfrom
feat/policy-engine
Jun 23, 2026
Merged

feat: policy engine#450
stefan-gorules merged 3 commits into
masterfrom
feat/policy-engine

Conversation

@stefan-gorules

@stefan-gorules stefan-gorules commented Jun 23, 2026

Copy link
Copy Markdown
Contributor

Policy engine

This PR adds the Policy engine, a second way to author logic in ZEN that runs next to the existing JDM decision graphs.

The difference is in how execution order is decided. In a graph you place nodes and connect them yourself, so the wiring is the order. A policy is declarative: you write a set of blocks, each one reads some properties and writes others, and the engine works out the dependencies between them, sorts them, and runs them. Nothing is wired by hand.

Policy definition
Policy definition
Inspecting evaluation
Inspecting evaluation (answering "Why?")

What a policy looks like

A policy document is a list of blocks plus a list of imports, so one policy can build on another. There are five block types:

  • Data model blocks describe the schema: entities, their fields, relationships and references, and whether something is entity-scoped or global.
  • Expression blocks compute one property from an expression.
  • Decision tables map conditions to outputs.
  • Match blocks handle case logic and have to be exhaustive.
  • Assertion blocks check that a condition holds.

Block types the engine doesn't recognise are kept as-is through a load/save cycle, so a frontend can attach layout or other metadata without the engine touching it.

Authoring layer

Most of the code here is the authoring support, exposed through PolicyWorkspace. It works like a language server for policies:

  • Diagnostics that combine type checking, validation, and lint hints. Each one carries a code, a severity, and the source span it points at. Type checking is strict, so array indexing is nullable and an any left in a resolved type is an error.
  • Hover and autocomplete at a cursor position.
  • Rename across the whole workspace. Renaming a field updates every reference to it, including in policies that import it.
  • The resolved scope at any point: entities, globals, inputs, outputs, and a generated sample input.
  • Multi-policy analysis, such as which policies form one connected unit and where two of them write the same property.

Evaluation

evaluate runs a policy against an input. It is demand-driven: pass a set of goals and it only runs the blocks needed to produce those outputs. Turn on trace to record how each value was derived, and enhanceTrace adds the operand values behind each expression for a trace viewer. If a block fails, evaluation returns the partial trace alongside the error instead of throwing, so you can see how far it got and why it stopped.

Bindings and loaders

The Node.js binding exposes the full PolicyWorkspace API. The Python, UniFFI and C bindings will be updated later to include a new loader config (static map, filesystem path, or zip) that pre-loads and pre-compiles decisions up front.

@stefan-gorules stefan-gorules merged commit 6c0ca51 into master Jun 23, 2026
55 checks passed
@stefan-gorules stefan-gorules deleted the feat/policy-engine branch June 23, 2026 16:51
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants