Feature Specification: Flat Schema Conversion
Created: 2026-09-22
Status: Implemented
Input: Reconstructed from the code at 3c0765d, the nix-unit suite, the README, the commit and pull request history of nix-tf-schema, the fediversity monorepo history from which the library was extracted, and the Claude Code logs.
User Scenarios & Testing (mandatory)
User Story 1 - Type the attributes of a provider schema in Nix (Priority: P1)
A maintainer has a provider schema. The maintainer gets Nix module types for its resources and data sources, so that the module system checks a configuration before Terraform sees it.
Why this priority: type checks at evaluation time are the main reason to use the library.
Independent Test: convert a set of attributes where one is required, one
is optional, one is computed and one is deprecated. Cast a value against the
result. The required and optional attributes are options, the optional one
defaults to null, and the computed and deprecated ones are absent.
Acceptance Scenarios:
- Given the Terraform type
[ "list" "string" ], When the maintainer converts it, Then the result islistOf str. - Given an optional attribute, When the maintainer converts it, Then the option has
default = null. - Given an attribute that is neither required nor optional, When the maintainer converts it, Then the conversion fails with a message that says so.
- Given an attribute flagged
sensitiveandallowSensitive = false, When the maintainer converts the set, Then the attribute is absent. - Given a full provider schema, When the maintainer converts it, Then the result has one option per provider, keyed without the
registry.opentofu.org/prefix, each withresource_schemasanddata_source_schemas.
User Story 2 - Declare Terraform variables from a provider schema (Priority: P1)
A maintainer writes a Terraform module that takes its values as variables. The
maintainer gets the variable blocks from the provider schema, so that they
do not drift from the provider.
Why this priority: the values reach Terraform as variables (see constitution principle III), so every settable attribute needs one.
Independent Test: wrap a set with one optional attribute of type
[ "list" "string" ] and one computed attribute. The result has one
variable with type = "list(string)" and default = null.
Acceptance Scenarios:
- Given an attribute schema, When the maintainer wraps it, Then the keys
description_kind,computed,requiredandoptionalare absent and the type is an HCL type string. - Given a full provider schema, When the maintainer wraps it, Then the result is keyed by provider without the
registry.opentofu.org/prefix.
User Story 3 - Map the attributes of one resource to options and references (Priority: P2)
A maintainer writes one resource by hand. The maintainer gets options for its
settable attributes and a matching ${var.<name>} reference for each, to use
as the resource body.
Why this priority: it is the flat form of 002-nested-resource-generation.
Consumers with flat resources use it.
Independent Test: map a resource with name (required), slug
(optional), tags (optional and computed) and id (computed). The options and
references have name, slug and tags, and not id.
Acceptance Scenarios:
- Given an attribute that is optional and computed, When the maintainer maps the resource, Then the attribute is settable and is kept.
- Given an attribute that is only computed, When the maintainer maps the resource, Then the attribute is absent.
Edge Cases
- The Terraform types
objectandtupleare not supported by the flat type conversion; the conversion fails with "to be implemented". [NEEDS CLARIFICATION: is the flat conversion meant to support them, or is002-nested-resource-generationthe replacement, so that the flat helpers stay for flat schemas only?] (#17) int64is typed as a signed 32-bit integer, andsetas a list. The README lists both as known limitations.- The helpers do not agree on "computed":
wrapTfAttrsandfromTfVarsdrop an attribute that has acomputedkey (alsocomputed = false, and also optional and computed), whileresourceAttrsToOptionskeeps an attribute that is optional and computed. [NEEDS CLARIFICATION: should all helpers use the rule ofsettableAttrs(drop only attributes that are computed and not optional)?] (#17) fromTfVarsdrops deprecated attributes;wrapTfAttrsandresourceAttrsToOptionskeep them. [NEEDS CLARIFICATION: is this difference intended?] (#17)
Requirements (mandatory)
Functional Requirements
- FR-001: The library MUST convert the Terraform primitive and collection types (
bool,number,string,int32,int64,float32,float64,dynamic,list,map,set) to nixpkgs option types. - FR-002: An optional attribute MUST become a nullable option with
default = null; a required attribute MUST become an option without a default. - FR-003: Computed attributes MUST NOT become options or variables, because Nix cannot set them.
- FR-004: The library MUST let the caller choose to keep or drop attributes flagged
sensitive. - FR-005: The library MUST render a Terraform type spec as an HCL type string (for example
list(string)). - FR-006: The library MUST convert at each level: one type, one attribute, a set of attributes, the sources of one provider, and a full provider schema output.
- FR-007: The library MUST give a module type that validates one Terraform attribute schema.
Key Entities
- Terraform type spec: a type string, or a list of type constructors (for example
[ "list" "string" ]). - Attribute schema: one attribute of a block, with
type,descriptionand the flagsrequired,optional,computed,sensitive,deprecated. - Variable block: the
variablepart of a Terraform module, one entry per settable attribute.
Success Criteria (mandatory)
Measurable Outcomes
- SC-001: The
wrapTf*,fromTf*,tfAttrTypeandresourceAttrsTo*cases of thenix-unitsuite pass in CI. - SC-002: Each helper of this capability has a doc-comment with inputs, type and example on the documentation site.
Assumptions
- The input is the JSON of
tofu providers schema -json, read withbuiltins.fromJSONor from004-provider-schema-extraction. - Terraform treats an argument set to
nullas omitted, sonullis a correct value for "unset".