Skip to content

feat(schema): pkg/schema engine with embedded schemas, custom keywords and guide validation (AFDEV-10) - #2

Merged
foae merged 2 commits into
mainfrom
feat/AFDEV-10-schema-engine
Sep 28, 2026
Merged

foae merged 2 commits into
mainfrom
feat/AFDEV-10-schema-engine

Conversation

@foae

@foae foae commented Sep 28, 2026

Copy link
Copy Markdown
Collaborator

What

pkg/schema, the v1 schema engine (package 2 of the v4 server work):

  • Embedded schemas. A one-file Go package schemas at the repository root embeds envelope.v1.json and kinds/*.json (go:embed cannot reach a parent directory and the files stay at the contract path). pkg/schema compiles every file at package init; there is no I/O at call time. List() exposes {kind, versions} including envelope, Document(kind, version) returns the file bytes verbatim for the later HTTP package.
  • Custom keywords. x-max-bytes, x-trim, x-normalize: token, x-recommended, x-unique-by, x-on-violation, plus the standard keywords the shipped schemas use. Compilation checks every node against a strict keyword and value-shape allow-list (the same set the Python reference and scripts/contract-check.py enforce) and panics at init on anything else, so no keyword is ever silently ignored. x-on-violation: move is accepted only at the envelope's additionalProperties.
  • Guide validation. Validate(kind, version, payload, placed) and (*Schema).Guide never reject: transforms rewrite the payload in place, every other violation is one Detail{code, pointer, message} with the code from conformance/warnings.json (a test asserts the Go keyword-to-code table equals the contract's). Unknown kinds and versions yield no_schema / unknown_schema_version.
  • Text and date-time primitives the decoder will call: Trim, LowerSimple, Token, NewlinesToSpaces, TruncateBytes, EscapeToken, ParseDateTime, NormalizeDateTime, FormatDateTime. The White_Space set is spelled out from the contract; lower-casing is the simple per-code-point mapping.
  • Value model. The guide works on the standard Go JSON tree with numbers as json.Number (source spelling kept, never converted). Integer classification and range/enum comparisons use a decimal comparator over the spelling (sign, significant digits, saturated exponent), so 2.0 is an integer and equals 2 in an enum, -0 equals 0, and a pathological exponent costs nothing.

Why a hand-written walker

The candidate library (github.com/santhosh-tekuri/jsonschema/v6, checked against v6.0.3) lets a custom keyword only return an error: it cannot rewrite the instance, it is silent where the guide wants unknown_field, and it reports every failing keyword where the guide wants one warning per member. A full walker is needed beside it anyway, so it would cover nine standard keywords and add two modules. The walker mirrors conformance/reference/guide.py line for line.

Tests

Table tests per keyword, placed members, pointer escaping, compile-time rejections, text primitives, comparator vectors, RFC 3339 vectors. Fixture-driven tests read conformance/ directly: normalisation vectors from every hash and decode fixture whose body encoding/json can parse faithfully (bodies with invalid UTF-8 or duplicate members are the decoder's and are skipped by name), the kind-schema guide fixtures end to end on the payload, and the occurred_at fixtures.

Docs

docs/develop.md: layout lines for pkg/schema/ and the schemas Go package, and the rule that pkg/schema is the one implementation of the contract's text and schema rules.

Verification

All gates green locally: just check (tree clean afterwards), go test -race -count=1 ./..., live e2e 55/55, shellcheck (CI and develop.md forms), skill tests 124/124, just contract. Also go vet and staticcheck 2026.2.1 clean on the whole module (run via go run; not a CI gate yet).

Out of scope: the decoder, serving schemas over HTTP, the canonical writer.

…s and guide validation (AFDEV-10)

Embed the v1 schema files through a root schemas package, compile them at init against a strict keyword allow-list, implement the x- keywords and the text and date-time primitives, and validate payloads as a guide that never rejects. Fixture-driven tests read conformance/ directly.
…t compile, items {} mirrors the reference (AFDEV-10)

Review findings: the decimal comparator keeps exponents exact instead of saturating; a schema file with a repeated member fails at init; an items schema without keywords is skipped like the reference; the fixture pre-check for duplicate members tracks object state correctly and the occurred_at test allows the context value cut.
@foae
foae merged commit 555a9e0 into main Sep 28, 2026
4 checks passed
@foae
foae deleted the feat/AFDEV-10-schema-engine branch September 28, 2026 13:36
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