Draft builder API (ADR-009) and generalized validation validate_bytes (ADR-010) for v0.1.0

- ADR-009: fluent Rust builder producing serde_json::Value, covers
  AlkType kinds + standard JSON Schema; resolves OQ-003 (alkcall is the
  unblocking consumer)
- ADR-010: AlkTypeEngine::validate_bytes(&[u8]) as the single-call
  binary-buffer validation entry point; materialize Value from bytes,
  then validate; two methods on one struct, not a trait
- builder.md: full builder API spec (Schema, Definitions, Discriminator)
  with four usage examples (channels chunk header, call input schema,
  SFTP Packet union, OperationSpec error schemas)
- validation.md: new validate_bytes subsection + entry-point comparison
  table; AlkTypeEngine impl block updated; design decisions table updated
- overview.md: builder.md added to component pointers; 'Not a schema
  builder' scope boundary retired; ADR-009/010 added to decisions table;
  OQ-003 marked resolved; Consumers table adds alkcall as first consumer
- open-questions.md + questions/003: OQ-003 moved from deferred(scope)
  to resolved (ADR-009)
- README.md: builder doc + ADR-009/010 added; OQ-003 marked resolved;
  two new Key Design Principles (9, 10) for v0.1.0 additions

Doc-only change; 346 tests pass, clippy clean.
This commit is contained in:
2026-08-11 05:41:49 +00:00
parent 57d8ed25ba
commit 1a8a44ed0e
8 changed files with 1250 additions and 39 deletions

View File

@@ -1,6 +1,6 @@
---
status: draft
last_updated: 2026-07-22
last_updated: 2026-08-11
---
# alktype — Validation
@@ -79,6 +79,9 @@ impl AlkTypeEngine {
pub fn offset_map(&self) -> Option<&OffsetMap>; // Some in aligned mode
pub fn layout_builder(&self) -> Option<&LayoutBuilder>; // Some in packed mode
pub fn sequential_reader(&self) -> Option<SequentialReader>; // owned fresh reader (ADR-007)
pub fn validate_json(&self, instance: &Value) -> Result<(), AlkTypeError>; // ADR-004
pub fn is_valid_json(&self, instance: &Value) -> bool; // ADR-004
pub fn validate_bytes(&self, buffer: &[u8]) -> Result<(), AlkTypeError>; // ADR-010
}
```
@@ -294,6 +297,75 @@ High-throughput paths can skip validation. Security-sensitive paths
(parsing incoming frames from untrusted peers) can validate every frame.
The choice is the consumer's.
### Access time: `engine.validate_bytes(&[u8])` — binary buffer validation
For binary-layout schemas (schemas declaring `AlkType:*` kinds), the
engine offers a single-call form of the two-step dance: walk the bytes
against the layout to materialize a `Value` tree, then validate that
`Value` against the compiled jsonschema validator. Decided in
[ADR-010](decisions/010-generalized-validation-validate-bytes.md).
```rust
pub fn validate_bytes(&self, buffer: &[u8]) -> Result<(), AlkTypeError>;
```
`validate_bytes` runs the existing machinery in sequence:
1. **Materialize `Value` from bytes.** A new internal helper
(`materialize_value`, alongside `SequentialReader::read_field_value`
in `src/sequential_reader.rs`) walks the buffer against the schema
and the engine's `Endian`, producing a `serde_json::Value` tree.
Composites are recursed into (`Struct` → object of field values;
`Array` → array of element values; `Union` → dispatch then recurse;
`Record` → object of key/value entries). The read phase reuses the
existing data-access functions and returns `AlkTypeError::Access`
(with field paths) on read failures.
2. **Validate the `Value`.** The materialized `Value` is passed to the
existing `self.validator.validate(&value)`, producing
`AlkTypeError::Validation` on failure.
Mode dispatch:
- **Packed mode** — walks with a fresh `SequentialReader` (the engine
is already a reader factory per ADR-007), materializing fields in
declaration order.
- **Aligned mode** — uses the `OffsetMap` to read fields at their
computed offsets, then materializes composites by recursing into the
offset map's nested entries.
Both modes produce the same `Value` form; the validator is
mode-agnostic (it operates on `Value`, not bytes — ADR-004).
#### When to use which entry point
| Entry point | Schema form | Input form | When |
|-------------|--------------|------------|------|
| `validate_json(&Value)` | Any (AlkType or plain JSON Schema) | Already-parsed `serde_json::Value` | Call's JSON payloads (`OperationSpec.input_schema`); TypeBox output; anything off `serde_json::from_slice` / `from_str` |
| `validate_bytes(&[u8])` | AlkType binary-layout schema | Raw `&[u8]` buffer | Channels' 8-byte chunk header; future binary call frames; SFTP packet buffers; metatensor index structs |
`validate_bytes` requires the engine's schema to declare `AlkType:*`
kinds — it materializes `Value` via the layout engine, which needs
binary-layout semantics. A pure JSON Schema (call's `input_schema`,
no AlkType kinds) compiled via `AlkTypeEngine::compile` would fail at
the materialize step (no `AlkType:Struct` at the root). For pure JSON
payloads, the consumer uses `serde_json::from_slice` then
`validate_json`. See [ADR-010](decisions/010-generalized-validation-validate-bytes.md)
§"Not a binary-payload validator for JSON-only schemas".
#### What `validate_bytes` is not
- **Not a new validation engine.** It runs the existing `jsonschema`
validator against the existing materialized `Value`. No new
validator code, no parallel validation path (ADR-001).
- **Not framing-aware.** It validates the bytes of *one* schema
instance. It does not strip length prefixes, parse
`[length: u32][payload]` framing, or handle multiple frames in a
buffer. That's the consumer's job. alktype validates what one
schema describes; it does not parse the wire envelope around it.
- **Not a `Validator` trait.** Two methods on one struct, not a trait
abstraction. See [ADR-010](decisions/010-generalized-validation-validate-bytes.md)
§"Not a `Validator` trait abstraction".
## Relationship to Read/Write
Validation and data access are independent operations on the same data.
@@ -315,13 +387,15 @@ representation first, then access the binary buffer.
| Decision | ADR | Summary |
|----------|-----|---------|
| Error handling and validation | [ADR-004](decisions/004-error-handling-validation-strategy.md) | `AlkTypeError` enum; load-time build, access-time check; field-path-carrying errors; jsonschema `ValidationError` wrapping |
| Generalized validation — `validate_bytes` | [ADR-010](decisions/010-generalized-validation-validate-bytes.md) | Single-call binary-buffer validation (materialize `Value` from bytes, then validate); two methods on one struct, not a trait |
| Purpose and scope | [ADR-001](decisions/001-alktype-purpose-scope-jsonschema-engine.md) | Why jsonschema not a custom engine |
## Open Questions
None specific to validation. The three alktype OQs (OQ-001, OQ-002,
OQ-003) are about layout, platform support, and schema construction —
not validation.
not validation. OQ-003 is resolved by [ADR-009](decisions/009-builder-api.md);
see [builder.md](builder.md).
## References