Files
alktype/docs/plans/bast-implementation.md
glm-5.2 62270b03ca Sync architecture docs and ADRs to BAST pivot (steps 9-10)
Step 9 (convert tests to BAST format) was a no-op: steps 4-8 converted
the tests as they went. The only remaining  reference in
src/tests was the intentional  rejection test at
src/schema.rs:462 (asserting the old keyword form is rejected). Full
suite passes: 389 tests (312 lib + 77 integration).

Step 10 (sync architecture docs and ADRs):

Descriptive docs rewritten/updated for BAST:
- schema-layer.md: rewritten for the BAST parser (BastDoc/BastDef/
  BastType typed tree, AlkTypeKind enum with to_bast_str/from_bast_str,
  what was removed). Points at bast-format.md for the normative format.
- validation.md: rewritten for the two-validator model
  (bast_validation for validate_bytes, standard jsonschema for
  validate_json). Documents the repurposed build_validator, the
  AlkTypeError::Validation uniform payload (D-BAST-009), and what is
  removed.
- builder.md: updated all output examples to BAST JSON
  (struct_() -> { kind: struct, fields: [...] }; object() -> standard
  JSON Schema). Documents build_doc, count(), and the field-name union
  fields requirement (D-BAST-005).
- overview.md: updated for BAST (what/why, schema-is-the-format table,
  dependencies, architecture pointers, design decisions table).
- README.md (architecture index): updated document table, ADR table
  (new ADR-BAST + ADR-VAL-SPLIT, superseded ADR-001), OQ table
  (OQ-007/OQ-008 resolutions updated for BAST-native validator), and
  key design principles (#1, #2, #7, #10 reworded for BAST).
- data-access.md: updated tunion function signatures to BastUnion and
  the variant resolution to return BastType (resolve_typeref for refs).
- layout-engine.md: updated construct signatures
  (LayoutBuilder::new(bast_doc, root_name), OffsetMap::compute(&doc),
  SequentialReader::new(bast_doc, root_name)), the recursive-walk
  description (BAST typed tree), and composite-kind headings
  (TStruct/TUnion/TArray -> struct/union/array). Added D-BAST-004
  note on array count requirement.

New ADRs:
- ADR-BAST (bast-bast-format.md): the BAST format, meta-schema,
  //kind vocabulary, design principles, what is removed, the
  enum index bounds bug fix. Supersedes ADR-001's format-specific
  content; records D-BAST-001..009.
- ADR-VAL-SPLIT (val-split-two-validator-model.md): the two-validator
  model (BAST-native for validate_bytes, standard jsonschema for
  validate_json), the repurposed build_validator, the uniform
  AlkTypeError::Validation payload. Refines ADR-004's validation
  strategy and ADR-010's validation step; records D-BAST-006/007/009.

Amended ADRs (supersession/amendment notes added; original decision
text preserved as historical record):
- ADR-001: format-specific content superseded by ADR-BAST;
  purpose/scope and schema-is-the-format principle retained.
- ADR-002: unchanged under the pivot; one-line note that the input
  format changed but the modes didn't.
- ADR-003: annotation semantics retained; annotation location moved
  to BAST type-level properties (amended by ADR-BAST).
- ADR-004: AlkTypeError enum retained (D-BAST-009); validation
  strategy section refined by ADR-VAL-SPLIT.
- ADR-009: builder API surface retained; build() output format
  amended to BAST / standard JSON Schema by ADR-BAST (D-BAST-008).
- ADR-010: validate_bytes two-step concept retained; validation step
  amended to the BAST-native validator by ADR-VAL-SPLIT.

Other:
- Cargo.toml description: JSON Schema with AlkType:* custom keywords
  -> BAST document.
- bast-pivot.md research record: status draft -> implemented, with a
  pointer to the ADRs that superseded its decisions.
- bast-implementation.md plan: status draft -> complete, with a note
  that step 9 was a no-op and step 10 is this commit.
- open-questions.md: OQ-006/OQ-007/OQ-008 resolutions updated for the
  BAST-native validator.
- questions/008-unionvalidator-variant-dispatch.md: added a
  post-BAST-pivot note pointing to the current bast_validation
  implementation; v0.1.0 resolution text preserved as historical
  record.

Verification:
- cargo test --release: 389 pass (312 lib + 77 integration)
- cargo clippy --all-targets -- -D warnings: clean
- cargo doc --no-deps: clean
- cross-reference check: every relative link in the new/updated docs
  resolves (verified by script).
2026-08-15 14:03:21 +00:00

31 KiB
Raw Permalink Blame History

status, created, last_updated
status created last_updated
complete 2026-08-15 2026-08-15

BAST Pivot — Implementation Plan

Status: complete. All 10 steps are implemented and pushed to origin/main (steps 1–8 in commits 66ab9d7 → 54fd112; step 9 was a no-op — steps 4–8 converted the tests as they went, leaving only the intentional from_bast_str rejection test referencing the "AlkType:Uint32" string; step 10 synced the architecture docs and ADRs in this commit). The two new ADRs (ADR-BAST, ADR-VAL-SPLIT) record the decisions; the amended ADRs (001, 002, 003, 004, 009, 010) carry supersession/amendment notes. The research record (bast-pivot.md) is flipped to implemented. What follows is the original plan, preserved as the historical execution record.


This is the execution plan for the BAST pivot: replacing alktype's v0.1.0 AlkType:* custom-keyword JSON Schema format with the BAST (Binary Abstract Syntax Tree) format. It is the entry point an implementing agent reads first.

Companion documents:

  • docs/architecture/bast-format.md — the normative BAST format spec (meta-schema, TypeRef, examples, validation model). Read this for what the format is.
  • docs/research/bast-pivot.md — the research record: motivation, POC scope and result, decisions D-BAST-001..009, risks. Read this for why and what was proved. The POC lives on branch bast-validator-poc (commit f371fe4) as src/bast_poc.rs — reference scaffolding, deliberately not merged.

Working order: read this plan top-to-bottom. The Semver Contract section is the scope-creep guardrail — consult it before each step. Each step links to the specific spec section it implements and the relevant D-BAST-* decision anchor. Implement steps in order; each step lists its verification gate.

Semver Contract

The crate is on crates.io at 0.1.0 with zero real consumers, so a breaking bump is free — but the contract is 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. This table is the authoritative scope-creep guardrail for the pivot.

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, not a custom-keyword validator built from the alktype schema. 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. Confirmed during step 3.

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.

Decisions deferred to their 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.

Steps

Step 1 — Add AlkTypeKind::from_str/to_str for BAST kind strings

Goal: Add the lowercase-string mapping ("uint32" ↔ AlkTypeKind::Uint32) that the BAST parser and validator dispatch on. This is the additive-only, zero-risk foundation — no existing code changes.

Spec reference: bast-format.md §Primitives, D-BAST-002.

Files: src/schema.rs (the AlkTypeKind impl block). No lib.rs change needed — the methods are inherent on the already-re-exported enum.

Implementation notes:

  • to_str(self) -> &'static str returns the lowercase BAST string.
  • from_str(s: &str) -> Result<AlkTypeKind, AlkTypeError> returns AlkTypeError::Schema for unknown strings. This is a new inherent method, distinct from the existing FromStr impl that parses the v0.1.0 "AlkType:Uint32" keyword form. Do not remove the existing FromStr yet — step 8 removes the v0.1.0 accessors.
  • Cover all 14 primitive kinds plus struct, union, array, record, enum (19 total, matching the enum variants). The lowercase strings are in the primitives table; composite kinds are "struct", "union", "array", "record", "enum".

Verification: cargo test --release (new unit tests for the mapping, both directions; existing tests unaffected). cargo clippy --all-targets -- -D warnings.


Step 2 — Embed the BAST meta-schema

Goal: Embed the BAST meta-schema as a serde_json::Value constant in the crate, available for validating BAST documents at compile time and for publishing at https://alk.dev/bast/v1/schema.

Spec reference: bast-format.md §The Meta-Schema.

Files: New src/bast_meta.rs (or a const in src/schema.rs — match existing module conventions). Re-export the meta-schema Value from lib.rs if consumers should be able to validate BAST documents themselves (likely yes — additive, not semver-relevant).

Implementation notes:

  • The meta-schema JSON is in bast-format.md §The Meta-Schema. Copy it verbatim into a serde_json::json! {...} macro invocation or parse it from an embedded string via serde_json::from_str.
  • No feature flags (AGENTS.md §6). The meta-schema is a compile-time constant, no I/O.
  • WASM-clean: no include_str! of an external file is needed if the json! macro is used; either way is wasm-safe.

Verification: cargo test --release. cargo build --target wasm32-unknown-unknown --release (meta-schema is a Value constant — wasm-relevant). cargo clippy --all-targets -- -D warnings.


Step 3 — BAST document parser

Goal: Implement the BAST document parser that the layout engines and materializer use instead of the get_alktype_kind* custom-keyword accessors. This is the natural entry point for the pivot — the largest step, and the one the rest of the steps build on.

Spec reference: bast-format.md (the whole document — the parser implements the format spec). D-BAST-001, D-BAST-003, D-BAST-005.

Files: New src/bast.rs (the parser). The existing src/schema.rs stays for now — steps 4–8 migrate callers off it. Update src/lib.rs to add pub mod bast; and re-export the parser's public surface.

Implementation notes:

  • The parser reads kind/fields/annotation properties from BAST nodes. It produces a typed surface (a small BastNode enum or equivalent) that the layout engines, materializer, and validator can walk without re-parsing the raw JSON at every node. The POC parsed lazily from raw JSON in both passes to keep the model honest; a typed tree is a straightforward follow-on optimization (POC observation 5). Either is acceptable for the production version; the typed tree is recommended since three consumers (layout, materialize, validate) walk the same tree.
  • $ref resolution: #/$defs/<name> only — a single hash lookup. No normalize_refs (BAST refs are always full JSON Pointers), no inline_union_variant_refs (union variant refs resolved lazily by the validator and materializer). See bast-format.md §TypeRef.
  • Untrusted input: every path that walks a BAST document must return Err(AlkTypeError::Schema) on a malformed document, never panic!/ unreachable! (AGENTS.md §3). The POC's malformed_document_produces_schema_error_not_panic test is the template.
  • Decide deferred decision #3 here: drop the schema::* helper re-exports from lib.rs, or keep them public. Leaning: drop. The BAST parser exposes a cleaner typed surface; the v0.1.0 accessors are engine-internal and not consumer API.

Verification: cargo test --release (port the POC's parser tests — the malformed-document test, the type-ref resolution tests). cargo clippy --all-targets -- -D warnings. The layout engines don't use the parser yet (step 4 wires it in), so the existing suite still passes on the old path.


Step 4 — Wire compile() to accept a BAST document + root name

Goal: Change AlkTypeEngine::compile to the new signature and have it use the BAST parser instead of the custom-keyword accessors. The layout engines (offset_map, layout_builder, sequential_reader) consume the BAST parser's typed output instead of walking raw JSON with get_alktype_kind*.

Spec reference: bast-format.md §Document Shape, D-BAST-001. Semver contract: compile is Breaking.

Files: src/engine.rs (the compile signature and body). The layout modules (src/offset_map.rs, src/layout_builder.rs, src/sequential_reader.rs) — their schema-walking code changes from get_alktype_kind* calls to BAST parser calls. src/lib.rs if the parser's public surface needs re-exporting (step 3 may have done this).

Implementation notes:

  • New signature: pub fn compile(bast_doc: &Value, root_name: &str, mode: LayoutMode) -> Result<Self, AlkTypeError>. Note &Value (not &mut Value) — BAST needs no in-place normalize_refs.
  • The engine stores the BAST document (or the parsed typed tree) for sequential_reader()'s factory construction and read_field's kind lookup. The Layout enum and mode dispatch are unchanged.
  • parse_endian, parse_align, parse_encoding, parse_discriminator are reworked to read BAST properties (struct/field-level) instead of keyword-value objects. Their semantics are unchanged (ADR-003); only their input location moves. Whether they stay as free functions or become methods on the typed BastNode is an implementation choice — the POC read properties inline.
  • The layout engines are format-agnostic beneath the accessors (checked offset arithmetic, the two modes, union dispatch). This step is an accessor swap, not a layout-engine rewrite.

Verification: cargo test --release (test inputs must be converted to BAST format — see step 9 for the full test conversion; this step converts the layout tests as a sanity check). cargo clippy --all-targets -- -D warnings. cargo build --target wasm32-unknown-unknown --release (layout/wasm-relevant).


Step 5 — BAST-native validator (production version)

Goal: Port the POC's BAST-native validator into a production module and wire it into validate_bytes as the validation step, replacing the jsonschema::Validator call on the bytes path.

Spec reference: bast-format.md §Validation Model, D-BAST-006, D-BAST-009. POC reference: src/bast_poc.rs on branch bast-validator-poc.

Files: New src/bast_validation.rs. src/engine.rs (validate_bytes body — swap the self.validator.validate(&value) call for the BAST-native validator). src/lib.rs — add pub mod bast_validation; (the validator is engine-internal; whether it's re-exported is an implementation choice, leaning no).

Implementation notes:

  • The POC is the reference. The validator is a single recursive function (validate_typeref) that dispatches on the BAST kind. The constraint table is in bast-format.md §Validation Model.
  • Construct AlkTypeError::Validation via jsonschema::ValidationError::custom — the variant's payload type is unchanged (D-BAST-009). The bytes path no longer touches jsonschema for validation, but the error type retains the jsonschema type for uniformity with the validate_json path.
  • The validator and materializer share the BAST-walking code structure. If step 3 produced a typed BastNode tree, both consume it. If step 3 parses lazily, the validator parses lazily too (POC approach).
  • Enum index bounds: check the materialized index against values.len() — this fixes the v0.1.0 dead constraint (the built-in enum keyword checked string membership, but the materializer emits Value::Number(index), which never matched). Net improvement.
  • Union variant dispatch: read __discriminator, look up the variant's BAST definition, recurse. Recovers OQ-008 per-variant constraint enforcement without custom keywords.

Verification: cargo test --release — the existing validate_bytes tests are the regression target (test inputs change to BAST format in step 9; expected validation outcomes must be identical). The POC's 20 tests are the reference. cargo clippy --all-targets -- -D warnings. cargo build --target wasm32-unknown-unknown --release.


Step 6 — validate_json against a consumer-provided JSON Schema

Goal: Update validate_json/is_valid_json to validate against a standard jsonschema::Validator compiled from a consumer-provided JSON Schema, not a custom-keyword validator built from the alktype schema.

Spec reference: bast-format.md §Validation Model, D-BAST-007. Semver contract: validate_json/is_valid_json are Breaking (behavioral); build_validator is Breaking (signature or removal).

Files: src/engine.rs (validate_json/is_valid_json bodies, and the engine's stored validator field if the JSON Schema is supplied at compile time). src/validation.rs (build_validator — repurposed or removed). src/lib.rs (the build_validator re-export if removed).

Implementation notes:

  • Decide deferred decision #1 here: does the consumer pass the JSON Schema to compile (engine carries a second validator) or to validate_json at call time? The former preserves single-call ergonomics; the latter is more flexible. Needs the alkcall call-site analysis. Not semver-relevant either way if the signature can absorb the change.
  • Decide deferred decision #2 here: build_validator removed vs repurposed. If repurposed, its signature stays but its contract changes (no custom keywords) — a behavioral break. If removed, drop the lib.rs re-export.
  • The jsonschema crate remains a direct dependency (for validate_json and for validating BAST documents against the meta-schema). Only the custom keyword integration is removed.
  • The engine may carry two validators: the BAST-native validator (for validate_bytes, from step 5) and the standard jsonschema::Validator (for validate_json, from this step). Or validate_json takes the JSON Schema at call time and builds a transient validator. The decision shapes the engine struct's fields.

Verification: cargo test --release (new tests for the consumer-provided JSON Schema path; existing validate_json tests converted — their schemas were custom-keyword, now standard). cargo clippy --all-targets -- -D warnings.


Step 7 — Builder API produces BAST JSON

Goal: Update the builder's build() methods to produce BAST JSON (for struct_()) and standard JSON Schema (for object()). Public method signatures are unchanged; only the output Value shape changes.

Spec reference: bast-format.md (the output format), D-BAST-008. Semver contract: Schema::build/Definitions::build are Breaking (output format).

Files: src/builder.rs. src/lib.rs if the builder's public surface changes (it shouldn't — method signatures are unchanged).

Implementation notes:

  • Schema::struct_().field(...).build() → BAST JSON (a $defs entry with kind: "struct", ordered fields array, type-level annotations).
  • Schema::object().field(...).build() → standard JSON Schema (no AlkType:* keywords, no BAST kind — just type/properties/ required).
  • Definitions::build()/merge_into() → a BAST $defs block.
  • The builder already distinguishes AlkType kinds from JSON Schema types via naming conventions (string() vs string_()). The construction API is the same; only the serialization differs.
  • The Discriminator builder is unchanged (semver contract: Unchanged).

Verification: cargo test --release (builder tests assert on the output Value — update the expected shapes). cargo clippy --all-targets -- -D warnings.


Step 8 — Remove v0.1.0 custom-keyword machinery

Goal: Remove the dead code now that all callers use the BAST parser and BAST-native validator.

Spec reference: bast-format.md §What is removed. Semver contract: the ~13 schema::* helper re-exports are Breaking (removal or rework) (decision #3, confirmed in step 3).

Files: src/schema.rs (remove get_alktype_kind*, normalize_refs, inline_union_variant_refs; rework or remove parse_*/resolve_*). src/validation.rs (remove the 19 jsonschema::Keyword implementations if not already removed in step 5/6). src/lib.rs (drop the removed items from the pub use block).

Implementation notes:

  • Remove: all 19 jsonschema::Keyword implementations (~200 lines), normalize_refs(), inline_union_variant_refs(), the get_alktype_kind* family.
  • Rework or remove: parse_align, parse_discriminator, parse_encoding, parse_endian, parse_max_length, resolve_ref, resolve_ref_or_inline. If the BAST parser subsumes them (likely), remove them. If any remain useful as free functions over the typed BastNode, keep them internal (not re-exported from lib.rs).
  • The jsonschema crate's with_keyword(...) registration calls are removed from compile/build_validator. The crate itself stays.
  • Drop the removed items from lib.rs's pub use schema::{ ... } block. The BAST parser's public surface replaces them.

Verification: cargo test --release. cargo clippy --all-targets -- -D warnings. cargo doc --no-deps (the public API surface changed — doc comments must build). cargo build --target wasm32-unknown-unknown --release (removing code shouldn't add platform deps).


Step 9 — Convert all tests to BAST format

Goal: Update the full test suite to use BAST format for inputs. Test assertions (expected validation outcomes, expected offsets, expected materialized values) must be identical — only the input schema shape changes.

Spec reference: bast-format.md (input format).

Files: tests/*.rs (integration tests), src/*.rs inline #[cfg(test)] modules (unit tests).

Implementation notes:

  • This may be partially done by steps 4–8 (each step converts the tests it touches as a sanity check). This step is the sweep: every test using AlkType:* keywords converts to BAST kind/fields.
  • The POC's 20 tests are the reference for BAST-shaped test inputs.
  • Expected validation outcomes are the regression target. The enum-index-bounds test is new behavior (the v0.1.0 dead constraint is now enforced) — that test's expectation changes (was: silently passed; now: AlkTypeError::Validation). This is the intended fix, not a regression.
  • Coverage: 310 crate + 86 integration tests (~396 total). All must pass.

Verification: cargo test --release (the full suite — this is the gate). cargo clippy --all-targets -- -D warnings.


Step 10 — Sync architecture docs and ADRs

Goal: Sync the descriptive docs and ADRs to the shipped code. This is the final step — per AGENTS.md, ADRs are written post-implementation, grounded in shipped code.

Spec reference: Semver Contract §ADR impact below.

Files: docs/architecture/README.md, docs/architecture/overview.md, docs/architecture/schema-layer.md (rewrite for the BAST parser), docs/architecture/validation.md (rewrite for the validator split), docs/architecture/builder.md (update build() output examples), src/lib.rs (module doc comment). New ADRs: ADR-BAST, ADR-VAL-SPLIT. Amended ADRs: 001 (superseded), 003, 004, 009, 010.

Implementation notes:

  • Rewrite schema-layer.md to describe the BAST parser (replaces the custom-keyword accessor walk-through). The current schema-layer.md content is the v0.1.0 reference; bast-format.md already contains the target spec. Either fold bast-format.md into schema-layer.md or keep both with schema-layer.md pointing at bast-format.md for the format and describing the parser module.
  • Rewrite validation.md for the validator split (the bast-format.md §Validation Model content moves here, expanded with the production validator's details).
  • Update builder.md output examples to BAST JSON.
  • Update src/lib.rs module doc comment: "Takes a JSON Schema with AlkType:* custom keywords" → "Takes a BAST document".
  • Update docs/architecture/README.md index — the document table, the ADR table (new ADRs, superseded ADR-001), the key design principles (#1, #2, #7, #10 change wording).
  • Remove stale TODOs referencing custom-keyword normalization, inline_union_variant_refs, or the rejected bare-name-ref design (AGENTS.md §"Architecture Context").
  • docs/research/bast-pivot.md is the research record — its status flips from draft to accepted/implemented and it gains a pointer to the ADRs that superseded its decisions.

Verification: cargo doc --no-deps (doc comments build). Cross-reference check: every link in this plan, bast-format.md, and the new/updated ADRs resolves. cargo test --release (no code change, but the doc sweep shouldn't break anything).

ADR Impact Checklist

Sync these ADRs when step 10 lands. Per AGENTS.md, ADRs are written post-implementation, grounded in shipped code.

ADR Action Reason
ADR-001 (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) records the BAST format as the realization of the principle. ADR-001 Status → Superseded by ADR-BAST.
ADR-002 (two layout modes) Unchanged Layout modes are format-agnostic. One-line note that the input format changed but the modes didn't.
ADR-003 (annotations) 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 (error handling, validation strategy) 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 (Int64/Uint64) Unchanged Kinds carry forward; JSON precision caveat unchanged.
ADR-006 (reject non-final inline in aligned mode) Unchanged Layout rule, format-agnostic.
ADR-007 (packed-mode read factory) Unchanged Reader factory semantics are format-agnostic.
ADR-008 (reject TUnion in aligned mode) Unchanged Layout rule, format-agnostic.
ADR-009 (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 (validate_bytes) 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 ADR-VAL-SPLIT.

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.
  • docs/architecture/README.md — update the document table, ADR table, and key design principles for the pivot.
  • docs/architecture/overview.md — update the "what" and "why" for BAST (the crate now takes a BAST document, not a custom-keyword JSON Schema).

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.

Verification Commands

Run these before committing each step. All must pass. Per AGENTS.md:

cargo test --release                                  # full suite (~396 tests: 310 crate + 86 integration)
cargo clippy --all-targets -- -D warnings
cargo doc --no-deps                                   # if docs changed (step 8, step 10)
cargo build --target wasm32-unknown-unknown --release # if layout/wasm-relevant code changed (step 2, 4, 5, 8)
cargo publish --dry-run --allow-dirty                 # before a release (post-step 10)