Rulealize.Plugin.Graph
1.0.1
dotnet add package Rulealize.Plugin.Graph --version 1.0.1
NuGet\Install-Package Rulealize.Plugin.Graph -Version 1.0.1
<PackageReference Include="Rulealize.Plugin.Graph" Version="1.0.1" />
<PackageVersion Include="Rulealize.Plugin.Graph" Version="1.0.1" />
<PackageReference Include="Rulealize.Plugin.Graph" />
paket add Rulealize.Plugin.Graph --version 1.0.1
#r "nuget: Rulealize.Plugin.Graph, 1.0.1"
#:package Rulealize.Plugin.Graph@1.0.1
#addin nuget:?package=Rulealize.Plugin.Graph&version=1.0.1
#tool nuget:?package=Rulealize.Plugin.Graph&version=1.0.1
Rulealize.Plugin.Graph
A vocabulary for Rulealize.
| Plugin id | Rulealize.Plugin.Graph |
| Namespace | graph |
| Reserved prefix | none |
| Depends on | Rulealize.Abstraction |
| Specification | doc/specification.md |
graph.of, graph.at, graph.adjacent, graph.reachable, graph.set.
A state field that is a fixed set of nodes joined by edges, with a value at each node.
The shape is in the schema; only the values are in the state. nodes and edges are
literals of graph.of, read once when the rule set is compiled, and nothing can compute
them. That is the one judgement this vocabulary is built around, and everything else follows
from it: an edge to a node that was never declared is a build error with the path in the
document where it happened rather than an empty sequence three moves in, and graph.adjacent
answers about the shape without reading the state at all. A rule set whose node set is
genuinely open — one that adds nodes as it runs — wants a different model, and
Decided says what and why.
Node names are Text and a graph is a Record, so cmp.eq, seq.count and rec.* work on
what these operations return without this vocabulary providing any of them. There is no
opaque value here.
The rule set it ships with
Four rooms, one of them lit, and you may light a room next to one that already is. It finishes in three moves, and every operation is on the path.
"rooms": {
"op": "graph.of",
"nodes": ["hall", "kitchen", "cellar", "attic"],
"edges": { "hall": ["kitchen", "attic"], "kitchen": ["cellar"] },
"value": { "op": "type.string" }
}
// the guard: the room is dark, and one of the rooms next to it is lit
{ "op": "logic.and", "all": [
{ "op": "cmp.eq",
"left": { "op": "graph.at", "graph": "$rooms", "node": "@room" }, "right": "dark" },
{ "op": "seq.any",
"source": { "op": "graph.adjacent", "graph": "$rooms", "node": "@room" },
"as": "next",
"predicate": { "op": "cmp.eq",
"left": { "op": "graph.at", "graph": "$rooms", "node": "@next" }, "right": "lit" } }
]}
// terminal: the walk from the hall through lit rooms reaches all four
{ "op": "graph.reachable", "graph": "$rooms", "from": "hall",
"as": "room",
"through": { "op": "cmp.eq",
"left": { "op": "graph.at", "graph": "$rooms", "node": "@room" }, "right": "lit" } }
That through is what makes graph.reachable worth calling: without it the answer is the
same from every position, because the shape is a build-time fact. With it the walk is over
the state.
Running it
dotnet tool restore # the rulealize command, pinned in .config
dotnet build # drops this vocabulary into plugin/
dotnet rulealize plugins # what loaded, and what it registered
dotnet rulealize restore ruleset/probe.json # fetch the standard vocabularies it also names
dotnet rulealize play ruleset/probe.json # walk it
plugin
1 assembly, 1 vocabulary
Rulealize.Plugin.Graph 1.0.1 (graph)
graph.of schema
graph.at expression
graph.adjacent expression
graph.reachable expression
graph.set effect
5 operations in total.
rulealize plugins lists what actually registered, read back off the loaded runtime, so an
operation written and not registered is simply absent from it.
One move at a time
dotnet rulealize moves ruleset/probe.json # what is legal here
dotnet rulealize apply ruleset/probe.json "light(room: kitchen)" > s1.json
dotnet rulealize moves ruleset/probe.json --state s1.json # what is legal now
dotnet rulealize apply ruleset/probe.json "light(room: cellar)" --state s1.json --write
probe@1.0.0 from the initial state (ongoing)
light(room: kitchen)
light(room: attic)
2 legal inputs, 4 candidates evaluated
Four candidates and two legal: hall is already lit and cellar has only the kitchen next
to it, which is dark. Those two numbers are the guard being exercised.
Naming an input goes through that list, so it can only apply something already offered. To check that the guard refuses what it should, write the input document and pass it:
// unreachable.json
{ "$schema": "rulealize/input/v1", "ruleSet": "probe@1.0.0",
"input": "light", "args": { "room": "cellar" } }
dotnet rulealize apply ruleset/probe.json --input unreachable.json
refused from the initial state: 'light' is not allowed in this state.
These are:
light(room: kitchen)
light(room: attic)
That refusal is the result you wanted, and the exit code is non-zero, so a script can assert on it.
Reading is lenient, writing is strict
The pair is worth knowing before writing a rule set against this.
graph.at(a name that is not a node) -> null
graph.set(the same name) -> faults
Reading past what exists returns null so that a rule set does not have to measure first. Writing to something that does not exist has to fault, because there is no node for the write to land on and discarding it silently would leave the rule set believing it happened.
Adding an operation
- Write the node, copying the shape of the nearest of the five
- Add a line to
RegisterinGraphPlugin.cs dotnet build && dotnet rulealize plugins— your name is in the list- Call it from
ruleset/probe.json, thendotnet rulealize check ruleset/probe.json dotnet rulealize play ruleset/probe.json— walk it
check reads the document and nothing else, so it is the fast half of the loop. Anything the
specification records as a build error should be reachable from it,
and a new one belongs there too.
Topology.cs |
the nodes and edges, read out of the document once |
GraphSchemaNode.cs |
graph.of — the field, and the JSON its values are written as |
GraphTarget.cs |
how the other four reach a graph, and check they were pointed at one |
AtNode.cs AdjacentNode.cs ReachableNode.cs |
the expressions |
SetNode.cs |
the effect |
Writing a vocabulary
is the guide this was built from: what Build and Evaluate are for, how to fail, what a
value is, and the one trap that has a compiler error waiting for it.
Licence
Copyright 2026 Reny. Apache-2.0.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Rulealize.Abstraction (>= 0.4.0)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.