Add semver and ADR impact mapping for the BAST pivot

Scope-creep guardrail for the public API during implementation. Maps
every item re-exported from src/lib.rs to a class (breaking / additive /
unchanged) with the specific change, and every ADR (001-010) to an
action (supersede / amend / unchanged) with the reason.

Net breaking: compile (signature), validate_json/is_valid_json
(contract), Schema::build/Definitions::build (output format),
build_validator (signature or removal), and the ~13 schema::* helper
re-exports. Net additive: BAST parser, BAST-native validator,
AlkTypeKind::from_str/to_str. Net unchanged: the entire layout +
data-access + materialize + tunion layer, AlkTypeError (D-BAST-009),
the Discriminator builder, AlkTypeKind variants.

Flags three small decisions deferred to their implementation steps
(validate_json JSON Schema source, build_validator fate, schema::*
re-export retention) so they don't become drive-by semver changes.

This is a living guide — it may shift slightly during implementation,
but capturing the contract now prevents public-surface drift. Doc-only.
This commit is contained in:
glm-5.2 committed 2026-08-15 10:29:24 +00:00
1 parent 89f05850f2
commit 5796d1c22f
1 file changed
+94
+94
View File
@@ -613,6 +613,100 @@ over unchanged.
- BAST-native validator — a recursive walker over the BAST type tree that checks value-domain constraints on a materialized `Value`. Replaces the 19 custom keyword validators for the `validate_bytes` path. Enforces: integer ranges, float finiteness, string/bytes `maxLength`, RFC 3339 timestamp shape, enum index bounds, union variant dispatch (recursing into variants). See [The Validator Split](#the-validator-split).
- BAST meta-schema validation at compile time (optional but recommended — the engine can skip it and trust the caller, or validate as a guard)
## Semver and ADR Impact
This section is the scope-creep guardrail for the public API during
implementation. The crate is on crates.io at 0.1.0 with zero real
consumers, so a breaking bump is free — but the contract still needs to
be explicit so the implementation doesn't drift. Per AGENTS.md, the
0.1.0 public surface is the items re-exported from `src/lib.rs`.
### Public API delta
| Public item (from `lib.rs` re-exports) | Class | Change |
|---|---|---|
| `AlkTypeKind` (enum + variants + methods) | **Additive** | Unchanged. 19 variants, same methods. New `from_str()`/`to_str()` mapping for lowercase BAST kind strings (`"uint32"` ↔ `AlkTypeKind::Uint32`) — additive methods. |
| `Endian`, `VariableEncoding`, `DiscriminatorKind` | **Unchanged** | — |
| `AlkTypeEngine::compile` | **Breaking** | Signature: `compile(schema: &mut Value, mode)` → `compile(bast_doc: &Value, root_name: &str, mode)`. Adds required `root_name` param (D-BAST-001); drops `&mut` (BAST needs no in-place `normalize_refs`); input is a BAST document, not a custom-keyword JSON Schema. |
| `AlkTypeEngine::validate_json` | **Breaking (behavioral)** | Signature unchanged `(instance: &Value) -> Result<...>`, but the validator it runs is now a standard `jsonschema::Validator` from a consumer-provided JSON Schema supplied at `compile`, not a custom-keyword validator built from the alktype schema. The compiled engine must carry a separate JSON-Schema validator (or `validate_json` takes the JSON Schema at call time — to be decided in step 6). Either way the *contract* of what schema validates the instance changes. |
| `AlkTypeEngine::validate_bytes` | **Unchanged (contract)** | Same signature. Internally the validation step switches from `jsonschema::Validator` to the BAST-native validator. Error type unchanged (D-BAST-009). |
| `AlkTypeEngine::is_valid_json` | **Breaking (behavioral)** | Same caveat as `validate_json` — validates against the consumer JSON Schema, not the alktype schema. |
| `AlkTypeEngine` accessors (`endian`, `mode`, `offset_map`, `layout_builder`, `sequential_reader`, `read_field`, `write_field`, etc.) | **Unchanged** | Layout-layer accessors are format-agnostic. |
| `LayoutMode`, `OffsetMap`, `ByteRange` | **Unchanged** | — |
| `LayoutBuilder`, `PackedLayout`, `FieldPosition` | **Unchanged** | — |
| `SequentialReader`, `FieldValue` | **Unchanged** | — |
| `UnionDispatch` | **Unchanged** | — |
| `data_access::*` functions | **Unchanged** | — |
| `AlkTypeError` (all 4 variants) | **Unchanged** | D-BAST-009 keeps `Validation(jsonschema::ValidationError<'static>)`. |
| `Schema` builder (`struct_`, `object`, `field`, `build`, all setters) | **Breaking (output format)** | Public method signatures unchanged. `build()` output changes from custom-keyword JSON to BAST JSON (for `struct_`) / standard JSON Schema (for `object`). Callers that introspect the built `Value` break; callers that pass it straight to `compile` are source-compatible once `compile` takes BAST. |
| `Definitions` builder (`new`, `define`, `define_value`, `build`, `merge_into`) | **Breaking (output format)** | Same as `Schema` — signatures unchanged, `build()`/`merge_into()` output shape changes to BAST `$defs`. |
| `Discriminator` builder enum | **Unchanged** | — |
| `build_validator` (from `validation`) | **Breaking (signature or removal)** | Currently `build_validator(schema: &Value) -> Result<jsonschema::Validator, AlkTypeError>` builds a custom-keyword validator. Under the pivot it either (a) is removed (consumers call `jsonschema` directly for standard JSON Schema) or (b) is repurposed to build a standard `jsonschema::Validator` from a consumer-provided standard JSON Schema (no custom keywords). Decision belongs to step 6. Either way the current signature's contract breaks. |
| `get_alktype_kind`, `get_alktype_kind_enum`, `get_alktype_kind_loose`, `get_alktype_kind_loose_enum`, `normalize_refs`, `inline_union_variant_refs`, `resolve_ref`, `resolve_ref_or_inline`, `parse_align`, `parse_discriminator`, `parse_encoding`, `parse_endian`, `parse_max_length` | **Breaking (removal or rework)** | All currently re-exported from `lib.rs`. `normalize_refs` and `inline_union_variant_refs` are removed (BAST needs neither). The `get_alktype_kind*` family is removed (replaced by direct `kind` parsing). The `parse_*` and `resolve_*` functions are reworked to read BAST properties instead of keyword-value objects, or removed if subsumed by the BAST parser. **Open: which of these stay public vs become internal.** Current leaning — drop all from `lib.rs` re-exports (they're engine-internal accessors, not consumer API); the BAST parser exposes a new typed surface instead. |
**Net breaking surface:** `compile`, `validate_json`/`is_valid_json`
(contract), `Schema::build`/`Definitions::build` (output format),
`build_validator` (signature/removal), and the ~13 `schema::*` helper
re-exports. **Net additive:** BAST parser, BAST-native validator,
`AlkTypeKind::from_str`/`to_str`. **Net unchanged:** the entire layout
+ data-access + materialize + tunion layer, `AlkTypeError`, the
`Discriminator` builder, `AlkTypeKind` variants.
### ADR impact
| ADR | Action | Reason |
|---|---|---|
| [ADR-001](../architecture/decisions/001-alktype-purpose-scope-jsonschema-engine.md) (purpose, scope, "schema is the format") | **Supersede** | The "schema is the format" principle is retained and strengthened (BAST *is* the format), but the concrete format changes from custom-keyword JSON Schema to BAST. A new ADR (ADR-BAST or renumbered) records the BAST format as the realization of the principle. ADR-001's Status → Superseded by ADR-BAST. |
| [ADR-002](../architecture/decisions/002-two-layout-modes-packed-vs-aligned.md) (two layout modes) | **Unchanged** | Layout modes are format-agnostic. No content change; maybe a one-line note that the input format changed but the modes didn't. |
| [ADR-003](../architecture/decisions/003-schema-annotations.md) (endianness, alignment, encoding, discriminators) | **Amend** | Annotation *semantics* carry forward unchanged; annotation *location* moves from custom-keyword objects to BAST type-level properties. Amend the "where annotations live" sections, keep the semantics. |
| [ADR-004](../architecture/decisions/004-error-handling-validation-strategy.md) (`AlkTypeError`, load-time build, access-time check) | **Amend** | Error enum shape unchanged (D-BAST-009). The "validation strategy" section updates: bytes path uses BAST-native validator, JSON path uses standard `jsonschema`. The load-time/access-time split is retained. |
| [ADR-005](../architecture/decisions/005-int64-uint64-first-class-kinds.md) (Int64/Uint64, JSON precision caveat) | **Unchanged** | Kinds carry forward; JSON precision caveat is unchanged. |
| [ADR-006](../architecture/decisions/006-reject-non-final-inline-length-prefixed-in-aligned-mode.md) (reject non-final inline length-prefixed in aligned mode) | **Unchanged** | Layout rule, format-agnostic. |
| [ADR-007](../architecture/decisions/007-packed-mode-read-factory.md) (packed-mode read factory, `sequential_reader()` returns owned reader) | **Unchanged** | Reader factory semantics are format-agnostic. |
| [ADR-008](../architecture/decisions/008-reject-tunion-in-aligned-mode.md) (reject TUnion in aligned mode for v1) | **Unchanged** | Layout rule, format-agnostic. |
| [ADR-009](../architecture/decisions/009-builder-api.md) (builder API) | **Amend** | Public method surface unchanged; `build()` output format changes (BAST for `struct_`, standard JSON Schema for `object`). Amend the "output format" section; keep the method catalog. |
| [ADR-010](../architecture/decisions/010-generalized-validation-validate-bytes.md) (`validate_bytes` — materialize then validate) | **Amend** | The two-step concept (materialize → validate) is retained. The validation step's *implementation* changes from `jsonschema` custom keywords to the BAST-native validator. Amend the "validation step" section; add a pointer to D-BAST-006/D-BAST-009 and the Validator Split section. |
**New ADRs to write (post-implementation, grounded in shipped code):**
- **ADR-BAST** — the BAST format, meta-schema, and `$defs`/`$ref`/
`kind` vocabulary. Supersedes ADR-001's format-specific content.
- **ADR-VAL-SPLIT** (or fold into ADR-004's amend) — the two-validator
model: BAST-native for `validate_bytes`, standard `jsonschema` for
`validate_json`. Records D-BAST-006, D-BAST-007, D-BAST-009.
**Descriptive docs to sync (post-implementation):**
- `docs/architecture/schema-layer.md` — rewrite for the BAST parser
(replaces the custom-keyword accessor walk-through).
- `docs/architecture/validation.md` — rewrite for the validator split.
- `docs/architecture/builder.md` — update the `build()` output examples
to BAST JSON.
- `src/lib.rs` module doc comment — update the "Takes a JSON Schema
with `AlkType:*` custom keywords" preamble to BAST.
**Stale TODOs to remove:** any TODO referencing custom-keyword
normalization, `inline_union_variant_refs`, or the rejected
bare-name-ref design — align with the ADRs as AGENTS.md §"Architecture
Context" requires.
### Open decisions deferred to implementation steps
These are small enough to decide when the step is reached, but are
flagged here so they don't become drive-by semver changes:
1. **`validate_json` JSON Schema source** (step 6): does the consumer
pass the JSON Schema to `compile` (engine carries a second
validator) or to `validate_json` at call time? The former preserves
the current single-call ergonomics; the latter is more flexible. Not
semver-relevant either way if `validate_json`'s signature can absorb
a new param or stay as-is — needs the call-site analysis.
2. **`build_validator` fate** (step 6): removed vs repurposed. If
repurposed, its signature stays but its contract (no custom
keywords) changes — a behavioral break, not a type break.
3. **`schema::*` helper re-exports** (step 3): drop from `lib.rs`
(engine-internal) vs keep public for consumers that walk schemas.
Leaning: drop — they're accessors for the old format, and the BAST
parser exposes a cleaner typed surface. Confirmed during step 3.
## The Validator Split
A key architectural clarification: BAST separates two concerns that the