Skip to content

A Rule interprets one managed or data resource instance in a plan JSON or state JSON. Its match decides eligibility; as assigns an optional Concept; emissions and composition add further architecture meaning. The Rule name alone creates no Concept or fact.

For the mental model, see Read a Rule.

Complete example

reference/dialect.rf.hcl RF
dialect "example" {
version = "0.1.0"
provider "hashicorp/aws" {
version = ">= 6.0.0, < 7.0.0"
}
}
rule "bucket" {
match {
type = "aws_s3_bucket"
}
as = rf.concept.object-storage-container
identity {
attributes = ["bucket"]
scope = "provider"
}
endpoint {
attributes = ["id", "bucket", "arn"]
}
}

The official AWS Dialect uses this shape for buckets. bucket is an instance identity attribute; id, bucket, and arn are endpoint paths that a verified saved-plan traversal may name. Neither block publishes its values. See Fact emissions for target resolution.

rule block

PropertyContract
PlacementTop level of a Dialect source root
CardinalityZero or more
LabelExactly one Rule name, unique within the Dialect
AttributesOptional as Concept reference
Nested blocksExactly one match; optional identity, endpoint, and composition; zero or more emissions

A Rule must have as, at least one emission, or a nonempty composition. A match-only Rule fails validation with RULE_NO_ARCHITECTURE. The Rule has no description attribute. See Member resolution for per-instance evidence and Unresolved members for what remains when one member cannot be established.

Parameters

NameTypeRequiredDefaultConstraint
LabelIdentifierYesNoneLowercase kebab case, 1–64 bytes
asConcept referenceNoNo ConceptLocal/current owner or supported rf.concept.*

Nested blocks

BlockCardinalityPurpose
matchExactly oneChoose eligible instances
identityZero or oneName value attributes for target matching
endpointZero or oneName attributes a saved-plan traversal can pair with this instance
context, relation, contributionZero or moreEmit facts; see Fact emissions
compositionZero or oneClaim ordered members

match block

Rule match RF
match {
kind = "resource"
type = "aws_s3_bucket"
where = source.bucket == "logs"
}
PropertyContract
PlacementExactly once inside rule or a composition member
Labels and nested blocksForbidden
Attributeskind, type, and optional where
NameTypeRequiredDefaultConstraint
kindStatic stringNo"resource""resource" or "data" for plan/state instances
typeStatic stringYesNoneExact, nonempty resource type; case-sensitive
wherePredicateNoKnown trueClosed predicate grammar

match has no label or nested block. where reads the instance's available source.* values. An unknown or sensitive operand can make the predicate indeterminate; it cannot make a candidate false. A bare traversal such as where = source.enabled is not a Boolean predicate and produces PREDICATE_UNRESOLVED during validation.

match.kind values

The language keeps a closed 15-value set. Plan/state analysis provides only the first two populations:

ValueConstructPlan/state instances
resourceManaged resourceYes
dataData sourceYes
settings, provider, ephemeral, action, moduleOther infrastructure constructsNo
variable, local, outputValues declared in configurationNo
moved, removed, import, check, languageOther configuration constructsNo

The other values remain accepted language syntax, but plan and state inputs contain no instances of those kinds, so a Rule that matches one never applies. type never glob-matches or follows the Rule name.

Identity and endpoint declarations

BlockAttributeRequiredDefaultMeaning
identityattributesYesNoneNonempty, distinct attribute paths eligible for an emission's match.by
identityscopeNo"provider""provider" requires compatible provider address and alias; "global" permits cross-provider candidates
endpointattributesYesNoneNonempty, distinct paths a verified traversal may use to identify the instance

These attributes are declared on a target Rule, not inferred from a Concept. A target with an unavailable provider alias remains a possible candidate, so Rootform cannot force a unique value match around it. scope = "global" changes candidate eligibility, not the meaning of an identity value. With --plan-file, a direct reference to a declared endpoint can establish the exact instance even if its evaluated value is unknown or shared. Traversals and scope gives the supported expression syntax variants.

Classification with as

as assigns one Concept to an interpreted instance. A Rule can instead emit facts or compose members without a Concept. There is no Concept inheritance or automatic classification from resource type. The built-in RF Vocabulary supplies shared Concepts; a Dialect can declare local ones.

Eligibility pipeline

For each instance, Rootform checks, in order:

  1. mode (resource or data);
  2. exact resource type;
  3. provider source address;
  4. optional where predicate.

The provider source address determines Dialect binding. A shorthand such as hashicorp/aws binds its corresponding Terraform and OpenTofu public-registry addresses; a fully qualified host binds only that host. An unbound provider is reported with PROVIDER_UNBOUND. The Dialect's provider version constraint declares its compatibility envelope, but Rule selection does not compare it with an observed exact provider version. A version constraint cannot distinguish two Rules for the same instance.

Selection precedence

PriorityConditionResult
1More than one Rule acceptedRULE_MATCH_AMBIGUOUS; no Rule applies
2An eligible Rule has an indeterminate predicateInterpretation indeterminate; no Rule selected around it
3Exactly one Rule acceptedApply it
4No Rule acceptedKeep the instance without an applied Rule or Concept

If a resource type could match but no selected Dialect binds its provider address, interpretation fails with PROVIDER_UNBOUND before Rule selection. There is no priority by file order, Dialect origin, or Rule name. A managed or data instance with no matching Rule still has a Representation in the Form. It has no invented classification or emissions. Policy selection can also include an instance whose possible Rule is indeterminate or failed, producing an indeterminate policy evaluation; see Evaluation. Rule selection places this step in the analysis pipeline.

Rejected syntax

invalid/match-only.rf.hcl RF
dialect "example" {
version = "0.1.0"
provider "hashicorp/aws" {
version = ">= 6.0.0"
}
}
rule "no-architecture" {
match {
type = "aws_s3_bucket"
}
}

rootform validate dialects reports RULE_NO_ARCHITECTURE. Add an as, an emission, or a nonempty composition only when it expresses intended meaning. Test and validate shows how to check a Rule against a plan fixture.