Graph Behavior
Idempotency, Merging, and Automatic Linking
Idempotency and Merging
rescile’s processing is idempotent. This means that if you define a rule to create a resource that already exists (identified by its type and primary key [name]), rescile will not create a duplicate or throw an error.
Instead, it intelligently enriches the existing resource by adding or updating its properties.
The exact behavior for handling property conflicts depends on the processing phase in which the rule is applied.
Merging in the Model Phase
During Model Application, when multiple [[create_resource]] rules operate on the same resource (i.e., they have the same resource_type and rendered name), the default behavior is to overwrite properties.
- If a rule defines a property that doesn’t exist on the resource, it is added.
- If a rule defines a property that does exist, the new value replaces the old one.
- To help debug unintended changes,
rescilewill issue aWARNlog message whenever an overwrite occurs.
This overwrite behavior is fundamental to building up a resource’s state across multiple models. For example, server.toml might create a server with a “provisioning” status, and a later network.toml model can update that same server’s status to “active” while adding network-specific properties.
# In server.toml
[create_resource.properties]
status = "provisioning"
# In network.toml, operating on the same server resource
[create_resource.properties]
# This will replace any existing 'status' property.
status = "active"
ip_address = "10.0.0.1"
Implicit Relationship Creation (Auto-Linking)
In addition to the explicit rules you define, rescile uses a powerful convention-over-configuration mechanism to automatically create relationships. This “auto-relation” phase runs after each major processing phase (Assets, Models, and Compliance), creating a cohesive graph without verbose configuration.
The rule is simple: rescile scans all resources for properties where the property name matches the type (or label) of another known resource in the graph. When a match is found, it creates a relationship.
Example of Implicit Linking:
- A model file,
server.toml, creates resources of typeserver. One such resource has the namebilling-api_server. - An asset file,
host.csv, defines physical hosts. It contains aservercolumn.
# data/assets/host.csv
name,ip_address,server
host-01,10.1.1.50,billing-api_server
During processing, rescile sees that the host-01 resource has a property named server. Since server is a known resource type (created by the model), rescile automatically creates a relationship: (host:host-01) -[server]-> (server:billing-api_server).
This feature significantly reduces the amount of configuration needed to wire your graph together. Because it runs after each phase, resources created by models can be automatically linked to by later models or by compliance rules, and so on.
Explicit vs. Implicit Relationships
The automatic linking described here is a powerful convention, but it creates implicit relationships that can sometimes feel like “magic.” While useful, this behavior can be less predictable than explicitly defining your graph’s structure.
For creating clear, intentional, and maintainable connections, it is strongly recommended to use explicit directives like
[[link_resources]]. These make your architectural intent obvious and provide more declarative control over how your graph is connected.
Preventing Implicit Linking
Sometimes, a property name may coincidentally match a resource type, but you don’t intend for a relationship to be created. You can prevent this automatic behavior by prefixing the property name with an underscore (_).
rescile will create the property on the resource without the leading underscore but will exclude it from the auto-linking process.
Example in a CSV Asset:
# data/assets/application.csv
name,_comment # This will not create a link to a 'comment' resource
billing-api,"This is just a note."
The billing-api resource will have a property comment with the value "This is just a note.", but no relationship will be formed.
Example in a Model:
# data/models/server.toml
origin_resource = "application"
[[create_resource]]
resource_type = "server"
name = "{{ origin_resource.name }}_server"
[create_resource.properties]
# This property will be named 'service', but will not be auto-linked
_service = "{{ origin_resource.service_name }}"
This simple convention gives you full control over when and where automatic relationships are created, blending the power of convention with the precision of explicit configuration.