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
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="Rulealize.Plugin.Graph" Version="1.0.1" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="Rulealize.Plugin.Graph" Version="1.0.1" />
                    
Directory.Packages.props
<PackageReference Include="Rulealize.Plugin.Graph" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add Rulealize.Plugin.Graph --version 1.0.1
                    
#r "nuget: Rulealize.Plugin.Graph, 1.0.1"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package Rulealize.Plugin.Graph@1.0.1
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=Rulealize.Plugin.Graph&version=1.0.1
                    
Install as a Cake Addin
#tool nuget:?package=Rulealize.Plugin.Graph&version=1.0.1
                    
Install as a Cake Tool

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

  1. Write the node, copying the shape of the nearest of the five
  2. Add a line to Register in GraphPlugin.cs
  3. dotnet build && dotnet rulealize plugins — your name is in the list
  4. Call it from ruleset/probe.json, then dotnet rulealize check ruleset/probe.json
  5. 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 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. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
1.0.1 128 9/5/2026
1.0.0 226 8/22/2026