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:

  1. Given the Terraform type [ "list" "string" ], When the maintainer converts it, Then the result is listOf str.
  2. Given an optional attribute, When the maintainer converts it, Then the option has default = null.
  3. Given an attribute that is neither required nor optional, When the maintainer converts it, Then the conversion fails with a message that says so.
  4. Given an attribute flagged sensitive and allowSensitive = false, When the maintainer converts the set, Then the attribute is absent.
  5. 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 with resource_schemas and data_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:

  1. Given an attribute schema, When the maintainer wraps it, Then the keys description_kind, computed, required and optional are absent and the type is an HCL type string.
  2. 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:

  1. Given an attribute that is optional and computed, When the maintainer maps the resource, Then the attribute is settable and is kept.
  2. Given an attribute that is only computed, When the maintainer maps the resource, Then the attribute is absent.

Edge Cases

  • The Terraform types object and tuple are 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 is 002-nested-resource-generation the replacement, so that the flat helpers stay for flat schemas only?] (#17)
  • int64 is typed as a signed 32-bit integer, and set as a list. The README lists both as known limitations.
  • The helpers do not agree on "computed": wrapTfAttrs and fromTfVars drop an attribute that has a computed key (also computed = false, and also optional and computed), while resourceAttrsToOptions keeps an attribute that is optional and computed. [NEEDS CLARIFICATION: should all helpers use the rule of settableAttrs (drop only attributes that are computed and not optional)?] (#17)
  • fromTfVars drops deprecated attributes; wrapTfAttrs and resourceAttrsToOptions keep 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, description and the flags required, optional, computed, sensitive, deprecated.
  • Variable block: the variable part of a Terraform module, one entry per settable attribute.

Success Criteria (mandatory)

Measurable Outcomes

  • SC-001: The wrapTf*, fromTf*, tfAttrType and resourceAttrsTo* cases of the nix-unit suite 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 with builtins.fromJSON or from 004-provider-schema-extraction.
  • Terraform treats an argument set to null as omitted, so null is a correct value for "unset".