Skip to content

feat: a documented default sends an attribute to optional and computed - #96

Merged
ShocOne merged 1 commit into
mainfrom
feat/documented-default-fills-the-response
Aug 14, 2026
Merged

feat: a documented default sends an attribute to optional and computed#96
ShocOne merged 1 commit into
mainfrom
feat/documented-default-fills-the-response

Conversation

@ShocOne

@ShocOne ShocOne commented Aug 14, 2026

Copy link
Copy Markdown
Member

mapping.md rows 3 and 10, and the headline figure in handoff.md:29.

The defect

An API that substitutes a value for an omitted field answers with that value
whether or not the request carried one. Emitted as Optional alone, terraform
holds null in config against a value in state and reports a diff on every
plan, forever
, for a field the practitioner never set.

docs/contract.md:172 already said so — "emitting it as Optional alone gives
the practitioner a perpetual diff" — and the ratio was 49 : 1 the wrong way.

A worked example

The document (Tests_API_AgentToServerWritablePropertyFields):

networkMeasurements:
  type: boolean
  default: false

ThousandEyes substitutes false when the field is omitted, so the create
response carries false either way.

 "network_measurements": schema.BoolAttribute{
     Optional:            true,
+    Computed:            true,
     MarkdownDescription: "View packet loss in 1-second intervals. …",
 },

Note the prose says nothing about a default. The signal is the default: key,
which nothing in the generation path read until now.

What changed

flat gains hasDefault, and the presence switch gains it as a third route to
Optional + Computed beside x-tfpfgen-server-default and the response
schema's required.

  • Read from the write side only. A default on a response schema says nothing
    about what happens when a request omits the field.
  • Presence is what counts, not the value. default: false, default: 0 and
    default: "" state it as much as any other value. The test covers all three.

The risk, accepted deliberately

docs/contract.md:178 previously rejected this route outright. That paragraph is
rewritten rather than deleted, and keeps the objection as a stated risk: a
default on a $ref'd property is written onto a schema every other use of that
type shares, so one declaration can move attributes that were never meant to move
together. A correction is the remedy where it is wrong.

The trade is a defect in every plan against a risk in some.

Measured effect

604 attributes moved, and nothing else did. computed and required are
byte-identical before and after — that is the tight acceptance criterion for a
presence change.

optional computed_optional
github 542 → 475 65 → 132
jamfpro 1,330 → 1,158 1 → 173
thousandeyes 1,372 → 1,007 0 → 365
total 3,244 → 2,640 66 → 670
before: {computed: 41604, optional: 3244, required: 1553, computed_optional: 66}
after : {computed: 41604, optional: 2640, required: 1553, computed_optional: 670}

The ratio handoff.md reports as 49 : 1 is now 3.9 : 1.

Refusals unchanged on all three pilots — 817 / 290 / 343. provider verify
reports no drift; postcheck passes.

Deliberately not done

  • No framework Default:. That changes apply semantics; mapping.md row 3
    asks for Optional + Computed, not a default value.
  • No UseStateForUnknown(). Row 2 qualifies it with "only if stable
    post-create", and stability is a volatility fact only the audit measures.

Gates

  • gofmt -l internal cmd — empty
  • scripts/repo_hygiene_gate.sh — pass
  • golangci-lint run — 0 issues
  • go build ./... && go vet ./... — pass
  • go test ./... — pass, coverage 91.4% total
  • provider generate + provider verify on all three — no drift

🤖 Generated with Claude Code

An API that substitutes a value for an omitted field answers with that
value whether or not the request carried one. Emitted as Optional
alone, terraform holds null in config against a value in state and
reports a diff on every plan, for a field the practitioner never set.

A default declared on a request property is the document stating that
fact, and joins the two routes already recognised. It is read from the
write side only: a default on a response schema says nothing about what
happens when a request omits the field. Presence is what counts, so a
declared false or empty string states it as much as any other value.

The risk this accepts is stated in docs/contract.md: a default on a
$ref'd property is shared by every use of that type, so one declaration
can move attributes that were never meant to move together. A
correction is the remedy where it is wrong.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ShocOne
ShocOne merged commit a6f0d83 into main Aug 14, 2026
2 checks passed
@ShocOne
ShocOne deleted the feat/documented-default-fills-the-response branch August 14, 2026 16:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant