Skip to content

feat(registry): runtime Standard Schema validation (insert/update/command)#20

Merged
grrowl merged 3 commits into
mainfrom
feat/sync-schema-validation
Jul 1, 2026
Merged

feat(registry): runtime Standard Schema validation (insert/update/command)#20
grrowl merged 3 commits into
mainfrom
feat/sync-schema-validation

Conversation

@grrowl

@grrowl grrowl commented Jun 29, 2026

Copy link
Copy Markdown
Owner

Stacked on #19 (base branch is feat/sync-schema, not main). Review #19 first; this PR's diff is only the validation layer.

What

The base PR ships the object schema with the Standard Schema slots typed but not enforced at runtime (a tombstone). This PR wires the runtime gate:

  • insert.schema checks the full-row cols. It also infers the collection's Row, so you drop the <Row> generic.
  • update.schema checks the partial patch. You pass a partial schema (e.g. Message.partial()) because an update sends only the changed fields. This is the "go further" piece — a full-row schema would reject every valid partial.
  • command schema checks args.
  • delete needs none (the key is wire-checked, and the pk was validated at insert).

Each runs in authorize, before the write, and throws to reject the frame. It is a gate, not a parser (ADR-0014): the original value flows to handlers, so transforms/defaults/coercion are not applied — rewriting an optimistically-applied row would manufacture divergence.

Any ~standard library works (zod 3.24+, valibot, arktype); the framework adds no validator dependency.

Schema home

The row schema lives on mutations.insert.schema (it validates inserts and types Row), per the discussion on #19 — the collection's "default" schema was only ever used by insert. Inference from the nested insert.schema was proven with a tsc --strict spike before implementing.

Tests

tests/schema-validation.test.ts drives a validated collection and a requireBody command through a hand-rolled Standard Schema (no validator dep): bad insert/update/args are rejected (mutation rejections carry the validation message; command rejections are generic per ADR-0012), good ones commit.

Verification

  • tsc --noEmit: 0 errors.
  • vitest run: 177/177 (171 base + 6 new).

Recipe

recipes/zod-standard-schema-collections.md documents it (insert + partial update + command, the gate rule), in the project's plain style.

🤖 Generated with Claude Code

@grrowl grrowl force-pushed the feat/sync-schema-validation branch 5 times, most recently from 9723ad9 to 9a21bc4 Compare July 1, 2026 02:28
@grrowl grrowl changed the base branch from feat/sync-schema to main July 1, 2026 02:28
@grrowl grrowl force-pushed the feat/sync-schema-validation branch 2 times, most recently from c0e6325 to 9de32dd Compare July 1, 2026 02:59
grrowl and others added 3 commits July 1, 2026 13:25
…rfacing (ADR-0014)

Wire the per-op schema slots to validate at runtime: insert.schema checks the
full-row cols, update.schema the partial patch, a command's schema its args —
each in authorize, before the write, throwing to reject (fail loud). A delete
carries no cols. It is a gate, not a parser: the original value flows to
handlers, so transforms/defaults/coercion are not applied.

Validation failures throw `ValidationError` and surface to the client with a
`VALIDATION` code. Error surfacing is now uniform for mutations and commands:
authorize and validation errors surface (the reason reaches the client), execute
errors stay sanitized. handleCall's authorize is split from its execute,
revising ADR-0012 D3, which had sanitized a command's authorize too.

Tests drive a `validated` collection and a `requireBody` command via a
hand-rolled Standard Schema (no validator dep): bad insert/update/args are
rejected with the reason + VALIDATION code, good ones commit; a command execute
throw stays generic.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DxmkLhbtb5w7oHHiJKprr
recipes/zod-standard-schema-collections.md: how to attach a schema to a
collection (insert plus a partial update) and a command, what is checked, and
the gate-not-parser rule. Written in the project's plain style.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DxmkLhbtb5w7oHHiJKprr
end-to-end-types.md was authored on feat/sync-schema, where the schema only
infers types. On this branch runtime Standard Schema validation is wired
(5031f7c), so the "runtime check is a follow-up" note no longer holds: the
schema infers the type and validates the value. Re-link the validation recipe.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018DxmkLhbtb5w7oHHiJKprr
@grrowl grrowl force-pushed the feat/sync-schema-validation branch from 9de32dd to 8142df3 Compare July 1, 2026 03:25
@grrowl grrowl marked this pull request as ready for review July 1, 2026 03:55
@grrowl grrowl merged commit 9f4f6ee into main Jul 1, 2026
1 check passed
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