Skip to content

feat: carry the document's declared constraints into the derivation - #91

Merged
ShocOne merged 1 commit into
mainfrom
feat/schema-constraint-keywords
Aug 14, 2026
Merged

feat: carry the document's declared constraints into the derivation#91
ShocOne merged 1 commit into
mainfrom
feat/schema-constraint-keywords

Conversation

@ShocOne

@ShocOne ShocOne commented Aug 14, 2026

Copy link
Copy Markdown
Member

Why

A document states more about a property than the derivation could see.

format was parsed but reached only the audit's value synthesiser
(audit/plan/synth.go, audit/run/strategize.go) — the IR flattener's flat
struct did not carry it, so format: password was structurally unable to
influence a generated schema. writeOnly, deprecated, uniqueItems,
minLength, maxLength, minItems and maxItems were not parsed at all:
specmodel's decoder is lookup-driven, so they were silently dropped.

This is the foundation three separate pieces of work need — Sensitive
(mapping.md row 4), the plan-time validators (row 9), and SetAttribute
(row 11) — and it is also what the date-time fixture defect uncovered in #89
turns out to require.

What changed

  • specmodel.Schema gains WriteOnly, Deprecated, UniqueItems,
    MinLength, MaxLength, MinItems, MaxItems. The four flags decode
    through one table, sharing readOnly's error message shape; the bounds share
    the existing numeric-bound table's.
  • flat gains all of them plus format, pattern and the numeric bounds.
    Bounds fold first-wins through foldBound, matching how the description
    and the enum already fold: a later allOf branch restating a bound states
    nothing new.
  • ir.Attribute gains Format, WriteOnly, Deprecated, UniqueItems,
    Pattern, Minimum, Maximum, MinLength, MaxLength, MinItems,
    MaxItems, all omitempty.

Constraints are taken from the write side: a constraint is a rule about what
may be sent, and a response schema restating it says nothing extra. WriteOnly
comes from the create side only — a response schema cannot declare it.

Nothing reads them yet. That is deliberate: this PR is parse-and-carry, so
it can be reviewed against a byte-identical output.

Measured effect

Generated output is byte-identical on all three pilots. Refusals are exactly
main's:

before after
github 937 937
jamfpro 319 319
thousandeyes 344 344

tfpfgen provider verify — no drift on all three.

What did change is --print-ir, which now carries the facts:

field github jamfpro thousandeyes total
format 7,355 810 757 8,922
write_only 36 127 163
deprecated 70 38 71 179
unique_items 2 2 33 37
min_length / max_length 16 / 47 124 / 54 140 / 101
minimum / maximum 2 / 29 57 / 36 112 / 196 171 / 261
min_items / max_items 11 / 19 — / 1 11 / 20
pattern 11 14 25

Formats that now reach the derivation include password ×43 (jamfpro),
date-time ×1,543, uuid ×39, uri ×5,640.

Those password and write_only counts are the security-visible defect
handoff.md:107 names: they generate as plain, visible attributes today. The
next PR acts on them.

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.3% total, internal/specmodel 95.0%
  • tfpfgen provider generate (postcheck) + provider verify on all three — no drift

New tests: internal/specmodel/constraints_test.go,
internal/intermediate_representation/constraints_test.go, and three refusal
cases for a non-boolean writeOnly / uniqueItems and a non-numeric maxLength.

🤖 Generated with Claude Code

A document states more about a property than the derivation could see.
format reached only the audit's value synthesis, and writeOnly,
deprecated, uniqueItems and the length and size bounds were not parsed
at all, so nothing downstream could act on them.

specmodel reads them and the attribute carries them. Bounds fold
first-wins, as the description and the enum do: a later branch stating
the same bound states nothing new. Constraints are taken from the write
side, because a constraint is a rule about what may be sent.

Nothing reads them yet: this is what the sensitivity, the deprecation
notice and the plan-time validators are decided from.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@ShocOne
ShocOne force-pushed the feat/schema-constraint-keywords branch from 1406b04 to 03709b6 Compare August 14, 2026 14:43
@ShocOne
ShocOne merged commit f0b20a4 into main Aug 14, 2026
1 of 2 checks passed
@ShocOne
ShocOne deleted the feat/schema-constraint-keywords branch August 14, 2026 14:45
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